فهرست منبع

AMBARI-6028 - Views: View managed resources need @Produces({text/plain}) to generate JSON

tbeerbower 11 سال پیش
والد
کامیت
81b755c606

+ 79 - 4
ambari-server/src/main/java/org/apache/ambari/server/api/services/BaseService.java

@@ -27,8 +27,10 @@ import org.apache.ambari.server.api.services.parsers.RequestBodyParser;
 import org.apache.ambari.server.api.services.serializers.JsonSerializer;
 import org.apache.ambari.server.api.services.serializers.ResultSerializer;
 import org.apache.ambari.server.controller.spi.Resource;
+import org.eclipse.jetty.util.ajax.JSON;
 
 import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import java.util.Iterator;
@@ -52,8 +54,9 @@ public abstract class BaseService {
 
 
   /**
-   * All requests are funneled through this method so that common logic can be executed.
-   * Creates a request instance and invokes it's process method.
+   * Requests are funneled through this method so that common logic can be executed.
+   * Creates a request instance and invokes it's process method.  Uses the default
+   * media type.
    *
    * @param headers      http headers
    * @param body         http body
@@ -66,6 +69,26 @@ public abstract class BaseService {
   protected Response handleRequest(HttpHeaders headers, String body, UriInfo uriInfo,
                                    Request.Type requestType, ResourceInstance resource) {
 
+    return handleRequest(headers, body, uriInfo, requestType, null, resource);
+  }
+
+  /**
+   * Requests are funneled through this method so that common logic can be executed.
+   * Creates a request instance and invokes it's process method.
+   *
+   * @param headers      http headers
+   * @param body         http body
+   * @param uriInfo      uri information
+   * @param requestType  http request type
+   * @param mediaType    the requested media type; may be null
+   * @param resource     resource instance that is being acted on
+   *
+   * @return the response of the operation in serialized form
+   */
+  protected Response handleRequest(HttpHeaders headers, String body,
+                                   UriInfo uriInfo, Request.Type requestType,
+                                   MediaType mediaType, ResourceInstance resource) {
+
     Result result = new ResultImpl(new ResultStatus(ResultStatus.STATUS.OK));
     try {
       Set<RequestBody> requestBodySet = getBodyParser().parse(body);
@@ -83,8 +106,16 @@ public abstract class BaseService {
       result =  new ResultImpl(new ResultStatus(ResultStatus.STATUS.BAD_REQUEST, e.getMessage()));
     }
 
-    return Response.status(result.getStatus().getStatusCode()).entity(
-        getResultSerializer().serialize(result)).build();
+    ResultSerializer serializer = mediaType == null ? getResultSerializer() : getResultSerializer(mediaType);
+
+    Response.ResponseBuilder builder = Response.status(result.getStatus().getStatusCode()).entity(
+        serializer.serialize(result));
+
+    if (mediaType != null) {
+      builder.type(mediaType);
+    }
+
+    return builder.build();
   }
 
   /**
@@ -108,6 +139,50 @@ public abstract class BaseService {
     return m_resourceFactory.createResource(type, mapIds);
   }
 
+  /**
+   * Get a serializer for the given media type.
+   *
+   * @param mediaType  the media type
+   *
+   * @return the result serializer
+   */
+  protected ResultSerializer getResultSerializer(final MediaType mediaType) {
+
+    final ResultSerializer serializer = getResultSerializer();
+
+    if (mediaType.equals(MediaType.TEXT_PLAIN_TYPE)){
+      return new ResultSerializer() {
+        @Override
+        public Object serialize(Result result) {
+          return serializer.serialize(result).toString();
+        }
+
+        @Override
+        public Object serializeError(ResultStatus error) {
+          return serializer.serializeError(error).toString();
+        }
+      };
+    } else if (mediaType.equals(MediaType.APPLICATION_JSON_TYPE)){
+      return new ResultSerializer() {
+        @Override
+        public Object serialize(Result result) {
+          return JSON.parse(serializer.serialize(result).toString());
+        }
+
+        @Override
+        public Object serializeError(ResultStatus error) {
+          return JSON.parse(serializer.serializeError(error).toString());
+        }
+      };
+    }
+    throw new IllegalArgumentException("The media type " + mediaType + " is not supported.");
+  }
+
+  /**
+   * Get the default serializer.
+   *
+   * @return the default serializer
+   */
   protected ResultSerializer getResultSerializer() {
     return m_serializer;
   }

+ 16 - 3
ambari-server/src/main/java/org/apache/ambari/server/api/services/ViewSubResourceService.java

@@ -73,9 +73,11 @@ public class ViewSubResourceService extends BaseService implements ViewResourceH
   // ----- ViewResourceHandler -----------------------------------------------
 
   @Override
-  public Response handleRequest(HttpHeaders headers, UriInfo ui, RequestType requestType, String resourceId) {
+  public Response handleRequest(HttpHeaders headers, UriInfo ui,
+                                RequestType requestType, MediaType mediaType,
+                                String resourceId) {
     return handleRequest(headers, null, ui, getRequestType(requestType),
-        createResource(resourceId));
+        getMediaType(mediaType), createResource(resourceId));
   }
 
   @Override
@@ -115,6 +117,17 @@ public class ViewSubResourceService extends BaseService implements ViewResourceH
       case QUERY_POST:
         return Request.Type.QUERY_POST;
     }
-    throw new IllegalArgumentException("Unknown type " + type);
+    throw new IllegalArgumentException("Unknown resource type " + type);
+  }
+
+  // get the JAX-RS media type from the view media type
+  private javax.ws.rs.core.MediaType getMediaType(MediaType type) {
+    switch (type) {
+      case TEXT_PLAIN:
+        return javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE;
+      case APPLICATION_JSON:
+        return javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
+    }
+    throw new IllegalArgumentException("Unknown media type " + type);
   }
 }

+ 140 - 4
ambari-server/src/test/java/org/apache/ambari/server/api/services/ViewSubResourceServiceTest.java

@@ -21,18 +21,31 @@ package org.apache.ambari.server.api.services;
 import org.apache.ambari.server.api.resources.ResourceInstance;
 import org.apache.ambari.server.api.services.parsers.RequestBodyParser;
 import org.apache.ambari.server.api.services.serializers.ResultSerializer;
+import org.apache.ambari.server.api.util.TreeNode;
 import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.orm.entities.ViewInstanceEntity;
 import org.apache.ambari.server.orm.entities.ViewInstanceEntityTest;
+import org.junit.Test;
 
 import javax.ws.rs.PathParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 /**
  * ViewSubResourceService tests
@@ -80,6 +93,124 @@ public class ViewSubResourceServiceTest extends BaseServiceTest {
     return listInvocations;
   }
 
+  @Test
+  public void testGetResultSerializer_Text() throws Exception {
+    UriInfo uriInfo = createMock(UriInfo.class);
+    Resource resource = createMock(Resource.class);
+
+    Result result = new ResultImpl(true);
+    result.setResultStatus(new ResultStatus(ResultStatus.STATUS.OK));
+    TreeNode<Resource> tree = result.getResultTree();
+    TreeNode<Resource> child = tree.addChild(resource, "resource1");
+    child.setProperty("href", "this is an href");
+
+    // resource properties
+    HashMap<String, Object> mapRootProps = new HashMap<String, Object>();
+    mapRootProps.put("prop1", "value1");
+    mapRootProps.put("prop2", "value2");
+
+    HashMap<String, Object> mapCategoryProps = new HashMap<String, Object>();
+    mapCategoryProps.put("catProp1", "catValue1");
+    mapCategoryProps.put("catProp2", "catValue2");
+
+    Map<String, Map<String, Object>> propertyMap = new HashMap<String, Map<String, Object>>();
+
+    propertyMap.put(null, mapRootProps);
+    propertyMap.put("category", mapCategoryProps);
+
+    //expectations
+    expect(resource.getPropertiesMap()).andReturn(propertyMap).anyTimes();
+    expect(resource.getType()).andReturn(Resource.Type.Cluster).anyTimes();
+
+    replay(uriInfo, resource);
+
+    //execute test
+    ViewInstanceEntity viewInstanceEntity = ViewInstanceEntityTest.getViewInstanceEntity();
+
+    Resource.Type type = new Resource.Type("subResource");
+
+    // get resource
+    ViewSubResourceService service = new ViewSubResourceService(type, viewInstanceEntity);
+
+    ResultSerializer serializer = service.getResultSerializer(MediaType.TEXT_PLAIN_TYPE);
+
+    Object o = serializer.serialize(result);
+
+    String expected = "{\n" +
+        "  \"href\" : \"this is an href\",\n" +
+        "  \"prop2\" : \"value2\",\n" +
+        "  \"prop1\" : \"value1\",\n" +
+        "  \"category\" : {\n" +
+        "    \"catProp1\" : \"catValue1\",\n" +
+        "    \"catProp2\" : \"catValue2\"\n" +
+        "  }\n" +
+        "}";
+
+    assertEquals(expected, o);
+
+    verify(uriInfo, resource);
+  }
+
+  @Test
+  public void testGetResultSerializer_Json() throws Exception {
+    UriInfo uriInfo = createMock(UriInfo.class);
+    Resource resource = createMock(Resource.class);
+
+    Result result = new ResultImpl(true);
+    result.setResultStatus(new ResultStatus(ResultStatus.STATUS.OK));
+    TreeNode<Resource> tree = result.getResultTree();
+    TreeNode<Resource> child = tree.addChild(resource, "resource1");
+    child.setProperty("href", "this is an href");
+
+    // resource properties
+    HashMap<String, Object> mapRootProps = new HashMap<String, Object>();
+    mapRootProps.put("prop1", "value1");
+    mapRootProps.put("prop2", "value2");
+
+    HashMap<String, Object> mapCategoryProps = new HashMap<String, Object>();
+    mapCategoryProps.put("catProp1", "catValue1");
+    mapCategoryProps.put("catProp2", "catValue2");
+
+    Map<String, Map<String, Object>> propertyMap = new HashMap<String, Map<String, Object>>();
+
+    propertyMap.put(null, mapRootProps);
+    propertyMap.put("category", mapCategoryProps);
+
+    //expectations
+    expect(resource.getPropertiesMap()).andReturn(propertyMap).anyTimes();
+    expect(resource.getType()).andReturn(Resource.Type.Cluster).anyTimes();
+
+    replay(uriInfo, resource);
+
+    //execute test
+    ViewInstanceEntity viewInstanceEntity = ViewInstanceEntityTest.getViewInstanceEntity();
+
+    Resource.Type type = new Resource.Type("subResource");
+
+    // get resource
+    ViewSubResourceService service = new ViewSubResourceService(type, viewInstanceEntity);
+
+    ResultSerializer serializer = service.getResultSerializer(MediaType.APPLICATION_JSON_TYPE);
+
+    Object o = serializer.serialize(result);
+
+    assertTrue(o instanceof Map);
+    Map map = (Map) o;
+    assertEquals(4, map.size());
+    assertEquals("value1", map.get("prop1"));
+    assertEquals("value2", map.get("prop2"));
+    assertEquals("this is an href", map.get("href"));
+    Object o2 = map.get("category");
+    assertNotNull(o2);
+    assertTrue(o2 instanceof Map);
+    Map subMap = (Map) o2;
+    assertEquals(2, subMap.size());
+    assertEquals("catValue1", subMap.get("catProp1"));
+    assertEquals("catValue2", subMap.get("catProp2"));
+
+    verify(uriInfo, resource);
+  }
+
   private class TestViewSubResourceService extends ViewSubResourceService {
 
     /**
@@ -98,25 +229,25 @@ public class ViewSubResourceServiceTest extends BaseServiceTest {
     public Response getSubResource2(@Context HttpHeaders headers, @Context UriInfo ui,
                                    @PathParam("resourceId") String resourceId) {
 
-      return handleRequest(headers, ui, RequestType.GET, resourceId);
+      return handleRequest(headers, ui, RequestType.GET, MediaType.TEXT_PLAIN, resourceId);
     }
 
     public Response postSubResource(@Context HttpHeaders headers, @Context UriInfo ui,
                                    @PathParam("resourceId") String resourceId) {
 
-      return handleRequest(headers, ui, RequestType.POST, resourceId);
+      return handleRequest(headers, ui, RequestType.POST, MediaType.TEXT_PLAIN, resourceId);
     }
 
     public Response putSubResource(@Context HttpHeaders headers, @Context UriInfo ui,
                                     @PathParam("resourceId") String resourceId) {
 
-      return handleRequest(headers, ui, RequestType.PUT, resourceId);
+      return handleRequest(headers, ui, RequestType.PUT, MediaType.TEXT_PLAIN, resourceId);
     }
 
     public Response deleteSubResource(@Context HttpHeaders headers, @Context UriInfo ui,
                                     @PathParam("resourceId") String resourceId) {
 
-      return handleRequest(headers, ui, RequestType.DELETE, resourceId);
+      return handleRequest(headers, ui, RequestType.DELETE, MediaType.TEXT_PLAIN, resourceId);
     }
 
     @Override
@@ -138,6 +269,11 @@ public class ViewSubResourceServiceTest extends BaseServiceTest {
     protected ResultSerializer getResultSerializer() {
       return getTestResultSerializer();
     }
+
+    @Override
+    protected ResultSerializer getResultSerializer(javax.ws.rs.core.MediaType mediaType) {
+      return getTestResultSerializer();
+    }
   }
 }
 

+ 3 - 2
ambari-views/examples/weather-view/src/main/java/org/apache/ambari/view/weather/CityService.java

@@ -51,10 +51,11 @@ public class CityService {
    */
   @GET
   @Path("{cityName}")
-  @Produces({"text/plain", "application/json"})
+  @Produces({"application/json"})
   public Response getCity(@Context HttpHeaders headers, @Context UriInfo ui,
                           @PathParam("cityName") String cityName) {
-    return resourceHandler.handleRequest(headers, ui, cityName);
+    return resourceHandler.handleRequest(headers, ui, ViewResourceHandler.RequestType.GET,
+        ViewResourceHandler.MediaType.APPLICATION_JSON, cityName);
   }
 
   /**

+ 14 - 6
ambari-views/src/main/java/org/apache/ambari/view/ViewResourceHandler.java

@@ -28,7 +28,7 @@ import javax.ws.rs.core.UriInfo;
  */
 public interface ViewResourceHandler {
   /**
-   * Enum of request types.
+   * Request types.
    */
   public enum RequestType {
     GET,
@@ -39,21 +39,29 @@ public interface ViewResourceHandler {
   }
 
   /**
-   * Handle the API request.
+   * Supported media types.
+   */
+  public enum MediaType {
+    TEXT_PLAIN,
+    APPLICATION_JSON
+  }
+
+  /**
+   * Handle the API request for the given request and media type.
    *
    * @param headers      the headers
    * @param ui           the URI info
    * @param requestType  the request type
+   * @param mediaType    the requested media type
    * @param resourceId   the resource id; may be null for collection resources
    *
    * @return the response
    */
-  public Response handleRequest(HttpHeaders headers, UriInfo ui, RequestType requestType, String resourceId);
+  public Response handleRequest(HttpHeaders headers, UriInfo ui, RequestType requestType,
+                                MediaType mediaType, String resourceId);
 
   /**
-   * Handle the API request with a request type of GET. Same as
-   * {@link ViewResourceHandler#handleRequest(HttpHeaders, UriInfo, ViewResourceHandler.RequestType, String)}
-   * for {@link ViewResourceHandler.RequestType#GET}.
+   * Handle the API request with a request type of GET and media type of TEXT_PLAIN.
    *
    * @param headers     the headers
    * @param ui          the URI info