Author: zhoresh
Date: Sat Aug 21 00:52:53 2010
New Revision: 987680

URL: http://svn.apache.org/viewvc?rev=987680&view=rev
Log:
http://codereview.appspot.com/1925042/
GadgetHandler restructure step 2: separate data construction 
(GadgetHandlerService) and JSON api (GadgetHandler)

Added:
    
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerService.java
    
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/GadgetHandlerServiceTest.java
Modified:
    
shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/conversion/BeanDelegator.java
    
shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/conversion/BeanFilter.java
    
shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/conversion/BeanDelegatorTest.java
    
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandler.java
    
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerApi.java
    
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerTest.java

Modified: 
shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/conversion/BeanDelegator.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/conversion/BeanDelegator.java?rev=987680&r1=987679&r2=987680&view=diff
==============================================================================
--- 
shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/conversion/BeanDelegator.java
 (original)
+++ 
shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/conversion/BeanDelegator.java
 Sat Aug 21 00:52:53 2010
@@ -48,6 +48,11 @@ import java.util.Map;
  */
 public class BeanDelegator {
 
+  /** Indicate NULL value for a field (To overcome shortcome of immutable map) 
*/
+  public static final String NULL = "NULL";
+
+  private static final Map<String, Object> EMPTY_FIELDS = ImmutableMap.of();
+
   /** List of Classes that are considered primitives and are not proxied **/
   public static final ImmutableSet<Class<?>> PRIMITIVE_TYPE_CLASSES = 
ImmutableSet.of(
     String.class, Integer.class, Long.class, Boolean.class, Uri.class);
@@ -57,6 +62,11 @@ public class BeanDelegator {
 
   private final Map<Enum<?>, Enum<?>> enumConvertionMap;
 
+  public BeanDelegator() {
+    this(ImmutableMap.<Class<?>, Class<?>>of(),
+        ImmutableMap.<Enum<?>, Enum<?>>of());
+  }
+
   public BeanDelegator(Map<Class<?>, Class<?>> delegatedClasses,
                        Map<Enum<?>, Enum<?>> enumConvertionMap) {
     this.delegatedClasses = delegatedClasses;
@@ -69,7 +79,7 @@ public class BeanDelegator {
    * @return proxied object according to map of classes to proxy
    */
   public Object createDelegator(Object source) {
-    if (source == null || delegatedClasses == null || delegatedClasses.size() 
== 0) {
+    if (source == null || delegatedClasses == null) {
       return null;
     }
 
@@ -113,12 +123,23 @@ public class BeanDelegator {
     if (delegatedClasses.containsKey(source.getClass())) {
       Class<?> apiInterface = delegatedClasses.get(source.getClass());
 
-      return Proxy.newProxyInstance( apiInterface.getClassLoader(),
-          new Class[] { apiInterface }, new DelegateInvocationHandler(source));
+      return createDelegator(source, apiInterface);
     }
     return source;
   }
 
+  @SuppressWarnings("unchecked")
+  public <T> T createDelegator(Object source, Class<T> apiInterface) {
+    return createDelegator(source, apiInterface, EMPTY_FIELDS);
+  }
+
+  @SuppressWarnings("unchecked")
+  public <T> T createDelegator(Object source, Class<T> apiInterface,
+                               Map<String, Object> extraFields) {
+    return (T) Proxy.newProxyInstance( apiInterface.getClassLoader(),
+      new Class[] { apiInterface }, new DelegateInvocationHandler(source, 
extraFields));
+  }
+
   public Enum<?> convertEnum(Enum<?> value) {
     if (enumConvertionMap.containsKey(value)) {
       return enumConvertionMap.get(value);
@@ -129,10 +150,18 @@ public class BeanDelegator {
   protected class DelegateInvocationHandler implements InvocationHandler {
     /** Proxied object */
     private final Object source;
+    /** Use the next values instead of proxying source */
+    private final Map<String, Object> extraFields;
 
     public DelegateInvocationHandler(Object source) {
+      this(source, EMPTY_FIELDS);
+    }
+
+    public DelegateInvocationHandler(Object source, Map<String, Object> 
extraFields) {
       Preconditions.checkNotNull(source);
+      Preconditions.checkNotNull(extraFields);
       this.source = source;
+      this.extraFields = extraFields;
     }
 
     /**
@@ -141,6 +170,14 @@ public class BeanDelegator {
      */
     public Object invoke(Object proxy, Method method, Object[] args) {
       Class<?> sourceClass = source.getClass();
+      // Return proxy fields if available
+      if (!extraFields.isEmpty() && method.getName().startsWith("get")) {
+        String field = method.getName().substring(3).toLowerCase();
+        if (extraFields.containsKey(field)) {
+          Object data = extraFields.get(field);
+          return (data == NULL ? null : data);
+        }
+      }
       try {
         Method sourceMethod = sourceClass.getMethod(
             method.getName(), method.getParameterTypes());
@@ -162,6 +199,7 @@ public class BeanDelegator {
   /**
    * Validate all proxied classes to see that all required functions are 
implemented.
    * Throws exception if failed validation.
+   * Note that it ignore the extra fields support.
    * @throws SecurityException
    * @throws NoSuchMethodException
    * @throws NoSuchFieldException

Modified: 
shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/conversion/BeanFilter.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/conversion/BeanFilter.java?rev=987680&r1=987679&r2=987680&view=diff
==============================================================================
--- 
shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/conversion/BeanFilter.java
 (original)
+++ 
shindig/trunk/java/common/src/main/java/org/apache/shindig/protocol/conversion/BeanFilter.java
 Sat Aug 21 00:52:53 2010
@@ -34,6 +34,7 @@ import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Proxy;
 import java.lang.reflect.Type;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -60,7 +61,7 @@ public class BeanFilter {
   /** Annotation for required field that should not be filtered */
   @Target(ElementType.METHOD)
   @Retention(RetentionPolicy.RUNTIME)
-  public @interface Required {}
+  public @interface Unfiltered {}
 
   /**
    * Create a proxy object that filter object fields according to set of 
fields.
@@ -131,7 +132,7 @@ public class BeanFilter {
           // Do not filter out primitive types, it will result in NPE
           && !method.getReturnType().isPrimitive()) {
         // Look for Required annotation
-        boolean required = (method.getAnnotation(Required.class) != null);
+        boolean required = (method.getAnnotation(Unfiltered.class) != null);
         fieldName = prefix + method.getName().substring(3).toLowerCase();
         if (!required && !fields.contains(fieldName)) {
           return null;
@@ -157,7 +158,7 @@ public class BeanFilter {
     }
   }
 
-  public Set<String> processBeanFields(Set<String> fields) {
+  public Set<String> processBeanFields(Collection<String> fields) {
     ImmutableSet.Builder<String> builder = ImmutableSet.builder();
     for (String field : fields) {
       builder.add(field.toLowerCase());

Modified: 
shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/conversion/BeanDelegatorTest.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/conversion/BeanDelegatorTest.java?rev=987680&r1=987679&r2=987680&view=diff
==============================================================================
--- 
shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/conversion/BeanDelegatorTest.java
 (original)
+++ 
shindig/trunk/java/common/src/test/java/org/apache/shindig/protocol/conversion/BeanDelegatorTest.java
 Sat Aug 21 00:52:53 2010
@@ -23,10 +23,11 @@ import com.google.common.collect.Immutab
 
 import junit.framework.Assert;
 
-import org.apache.shindig.protocol.conversion.BeanFilter.Required;
+import org.apache.shindig.protocol.conversion.BeanFilter.Unfiltered;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.beans.BeanInfo;
 import java.util.List;
 import java.util.Map;
 
@@ -53,7 +54,7 @@ public class BeanDelegatorTest extends A
     public Style getStyle();
 
     // Test of required
-    @Required
+    @Unfiltered
     public String getRequired();
   }
 
@@ -197,6 +198,26 @@ public class BeanDelegatorTest extends A
     assertEquals(item.getS(), interMap.get("item").getS());
   }
 
+  class TokenData {
+    public String getId() { return "id"; }
+  }
+
+  interface TokenInter {
+    public String getId();
+    public String getContainer();
+  }
+
+  @Test
+  public void testExtraFields() {
+    TokenData data = new TokenData();
+    String container = "data";
+    TokenInter p = beanDelegator.createDelegator(data, TokenInter.class,
+        ImmutableMap.<String, Object>of("container", container));
+
+    assertSame(data.getId(), p.getId());
+    assertSame(container, p.getContainer());
+  }
+
   // Make sure validate will actually fail
   @Test(expected = NoSuchMethodException.class)
   public void tesValidate() throws Exception {

Modified: 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandler.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandler.java?rev=987680&r1=987679&r2=987680&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandler.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandler.java
 Sat Aug 21 00:52:53 2010
@@ -20,25 +20,14 @@ package org.apache.shindig.gadgets.servl
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.inject.Inject;
 
-import org.apache.shindig.auth.SecurityToken;
-import org.apache.shindig.auth.SecurityTokenCodec;
 import org.apache.shindig.common.uri.Uri;
-import org.apache.shindig.gadgets.Gadget;
 import org.apache.shindig.gadgets.GadgetContext;
-import org.apache.shindig.gadgets.RenderingContext;
-import org.apache.shindig.gadgets.process.Processor;
-import org.apache.shindig.gadgets.spec.Feature;
 import org.apache.shindig.gadgets.spec.GadgetSpec;
-import org.apache.shindig.gadgets.spec.LinkSpec;
-import org.apache.shindig.gadgets.spec.ModulePrefs;
-import org.apache.shindig.gadgets.spec.UserPref;
-import org.apache.shindig.gadgets.spec.View;
-import org.apache.shindig.gadgets.spec.UserPref.EnumValuePair;
-import org.apache.shindig.gadgets.uri.IframeUriManager;
 import org.apache.shindig.protocol.BaseRequestItem;
 import org.apache.shindig.protocol.Operation;
 import org.apache.shindig.protocol.ProtocolException;
@@ -47,6 +36,7 @@ import org.apache.shindig.protocol.Servi
 import org.apache.shindig.protocol.conversion.BeanDelegator;
 import org.apache.shindig.protocol.conversion.BeanFilter;
 
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
@@ -65,63 +55,29 @@ public class GadgetsHandler {
   @VisibleForTesting
   static final String FAILURE_TOKEN = "Failed to get gadget token.";
 
-  private static final Set<String> DEFAULT_METADATA_FIELDS =
-      ImmutableSet.of("iframeUrl", "userPrefs.*", "modulePrefs.*", "views.*", 
"token");
+  private static final List<String> DEFAULT_METADATA_FIELDS =
+      ImmutableList.of("iframeUrl", "userPrefs.*", "modulePrefs.*", "views.*", 
"token");
+
+  private static final List<String> DEFAULT_TOKEN_FIELDS = 
ImmutableList.of("token");
 
   protected final ExecutorService executor;
-  protected final Processor processor;
-  protected final IframeUriManager iframeUriManager;
-  protected final SecurityTokenCodec securityTokenCodec;
+  protected final GadgetsHandlerService handlerService;
 
-  protected final BeanDelegator beanDelegator;
   protected final BeanFilter beanFilter;
-
-  // Map shindig data class to API interfaces
-  @VisibleForTesting
-  static final Map<Class<?>, Class<?>> apiClasses =
-      new ImmutableMap.Builder<Class<?>, Class<?>>()
-          .put(BaseResponseData.class, GadgetsHandlerApi.BaseResponse.class)
-          .put(MetadataResponseData.class, 
GadgetsHandlerApi.MetadataResponse.class)
-          .put(TokenResponseData.class, GadgetsHandlerApi.TokenResponse.class)
-          .put(View.class, GadgetsHandlerApi.View.class)
-          .put(UserPref.class, GadgetsHandlerApi.UserPref.class)
-          .put(EnumValuePair.class, GadgetsHandlerApi.EnumValuePair.class)
-          .put(ModulePrefs.class, GadgetsHandlerApi.ModulePrefs.class)
-          .put(Feature.class, GadgetsHandlerApi.Feature.class)
-          .put(LinkSpec.class, GadgetsHandlerApi.LinkSpec.class)
-          // Enums
-          .put(View.ContentType.class, GadgetsHandlerApi.ViewContentType.class)
-          .put(UserPref.DataType.class, 
GadgetsHandlerApi.UserPrefDataType.class)
-          .build();
-
-  // Provide mapping for internal enums to api enums
-  @VisibleForTesting
-  static final Map<Enum<?>, Enum<?>> enumConversionMap =
-      new ImmutableMap.Builder<Enum<?>, Enum<?>>()
-          // View.ContentType mapping
-          .putAll(BeanDelegator.createDefaultEnumMap(View.ContentType.class,
-              GadgetsHandlerApi.ViewContentType.class))
-          // UserPref.DataType mapping
-          .putAll(BeanDelegator.createDefaultEnumMap(UserPref.DataType.class,
-              GadgetsHandlerApi.UserPrefDataType.class))
-          .build();
+  protected final BeanDelegator beanDelegator;
 
   @Inject
-  public GadgetsHandler(ExecutorService executor, Processor processor,
-      IframeUriManager iframeUriManager, SecurityTokenCodec securityTokenCodec,
-      BeanFilter beanFilter) {
+  public GadgetsHandler(ExecutorService executor, GadgetsHandlerService 
handlerService,
+                        BeanFilter beanFilter) {
     this.executor = executor;
-    this.processor = processor;
-    this.iframeUriManager = iframeUriManager;
-    this.securityTokenCodec = securityTokenCodec;
+    this.handlerService = handlerService;
     this.beanFilter = beanFilter;
 
-    // TODO: maybe make this injectable
-    this.beanDelegator = new BeanDelegator(apiClasses, enumConversionMap);
+    this.beanDelegator = new BeanDelegator();
   }
 
   @Operation(httpMethods = {"POST", "GET"}, path = "metadata.get")
-  public Map<String, GadgetsHandlerApi.MetadataResponse> 
metadata(BaseRequestItem request)
+  public Map<String, GadgetsHandlerApi.BaseResponse> metadata(BaseRequestItem 
request)
       throws ProtocolException {
     return new AbstractExecutor<GadgetsHandlerApi.MetadataResponse>() {
       @Override
@@ -133,7 +89,7 @@ public class GadgetsHandler {
   }
 
   @Operation(httpMethods = {"POST", "GET"}, path = "token.get")
-  public Map<String, GadgetsHandlerApi.TokenResponse> token(BaseRequestItem 
request)
+  public Map<String, GadgetsHandlerApi.BaseResponse> token(BaseRequestItem 
request)
       throws ProtocolException {
     return new AbstractExecutor<GadgetsHandlerApi.TokenResponse>() {
       @Override
@@ -158,7 +114,7 @@ public class GadgetsHandler {
 
   private abstract class AbstractExecutor<R extends 
GadgetsHandlerApi.BaseResponse> {
     @SuppressWarnings("unchecked")
-    public Map<String, R> execute(BaseRequestItem request) {
+    public Map<String, GadgetsHandlerApi.BaseResponse> execute(BaseRequestItem 
request) {
       Set<String> gadgetUrls = 
ImmutableSet.copyOf(request.getListParameter("ids"));
       if (gadgetUrls.isEmpty()) {
         return ImmutableMap.of();
@@ -170,12 +126,12 @@ public class GadgetsHandler {
         completionService.submit(job);
       }
 
-      ImmutableMap.Builder<String, R> builder = ImmutableMap.builder();
+      ImmutableMap.Builder<String, GadgetsHandlerApi.BaseResponse> builder = 
ImmutableMap.builder();
       for (int numJobs = gadgetUrls.size(); numJobs > 0; numJobs--) {
         R response;
         try {
           response = completionService.take().get();
-          builder.put(response.getUrl(), response);
+          builder.put(response.getUrl().toString(), response);
         } catch (InterruptedException e) {
           throw new 
ProtocolException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
               "Processing interrupted.", e);
@@ -187,10 +143,10 @@ public class GadgetsHandler {
           RpcException cause = (RpcException) e.getCause();
           GadgetContext context = cause.getContext();
           if (context != null) {
-            String url = context.getUrl().toString();
-            R errorResponse =
-                (R) beanDelegator.createDelegator(new BaseResponseData(url, 
cause.getMessage()));
-            builder.put(url, errorResponse);
+            Uri url = context.getUrl();
+            GadgetsHandlerApi.BaseResponse errorResponse =
+                handlerService.createBaseResponse(url, cause.getMessage());
+            builder.put(url.toString(), errorResponse);
           }
         }
       }
@@ -203,23 +159,14 @@ public class GadgetsHandler {
   // Hook to override in sub-class.
   protected Callable<GadgetsHandlerApi.MetadataResponse> 
createMetadataJob(String url,
       BaseRequestItem request) {
-    final GadgetContext context = new MetadataGadgetContext(url, request);
-    final Set<String> fields =
-        
beanFilter.processBeanFields(request.getFields(DEFAULT_METADATA_FIELDS));
+    final MetadataRequestData metadataRequest = new MetadataRequestData(url, 
request);
     return new Callable<GadgetsHandlerApi.MetadataResponse>() {
       public GadgetsHandlerApi.MetadataResponse call() throws Exception {
         try {
-          Gadget gadget = processor.process(context);
-          String iframeUrl =
-              fields.contains("iframeurl") ? 
iframeUriManager.makeRenderingUri(gadget).toString()
-                  : null;
-          MetadataResponseData response =
-              new MetadataResponseData(context.getUrl().toString(), 
gadget.getSpec(), iframeUrl);
-          return (GadgetsHandlerApi.MetadataResponse) 
beanFilter.createFilteredBean(beanDelegator
-              .createDelegator(response), fields);
+          return handlerService.getMetadata(metadataRequest);
         } catch (Exception e) {
-          // Note: this error message is publicly visible in JSON-RPC response.
-          throw new RpcException(context, FAILURE_METADATA, e);
+          sendError(metadataRequest.getUrl(), e, FAILURE_METADATA);
+          return null;
         }
       }
     };
@@ -228,63 +175,87 @@ public class GadgetsHandler {
   // Hook to override in sub-class.
   protected Callable<GadgetsHandlerApi.TokenResponse> createTokenJob(String 
url,
       BaseRequestItem request) {
-    final TokenGadgetContext context = new TokenGadgetContext(url, request);
-    final Set<String> fields =
-        
beanFilter.processBeanFields(request.getFields(DEFAULT_METADATA_FIELDS));
+    final TokenRequestData tokenRequest = new TokenRequestData(url, request);
     return new Callable<GadgetsHandlerApi.TokenResponse>() {
       public GadgetsHandlerApi.TokenResponse call() throws Exception {
         try {
-          String token = securityTokenCodec.encodeToken(context.getToken());
-          TokenResponseData response = new 
TokenResponseData(context.getUrl().toString(), token);
-          return (GadgetsHandlerApi.TokenResponse) 
beanFilter.createFilteredBean(beanDelegator
-              .createDelegator(response), fields);
+          return handlerService.getToken(tokenRequest);
         } catch (Exception e) {
-          // Note: this error message is publicly visible in JSON-RPC response.
-          throw new RpcException(context, FAILURE_TOKEN, e);
+          sendError(tokenRequest.getUrl(), e, FAILURE_TOKEN);
+          return null;
         }
       }
     };
   }
 
+  private void sendError(final Uri url, Exception e, String msg)
+      throws RpcException {
+    GadgetContext context = new GadgetContext() {
+      @Override
+      public Uri getUrl() { return url; }
+    };
+    // Note: this error message is publicly visible in JSON-RPC response.
+    throw new RpcException(context, msg, e);
+  }
+
   /**
    * Gadget context classes used to translate JSON BaseRequestItem into a more
    * meaningful model objects that Java can work with.
    */
-
-  private abstract class AbstractGadgetContext extends GadgetContext {
+  private abstract class AbstractRequest implements 
GadgetsHandlerApi.BaseRequest {
     protected final Uri uri;
     protected final String container;
+    protected final List<String> fields;
     protected final BaseRequestItem request;
 
-    public AbstractGadgetContext(String url, BaseRequestItem request) {
+    public AbstractRequest(String url, BaseRequestItem request, List<String> 
defaultFields) {
       this.uri = Uri.parse(Preconditions.checkNotNull(url));
       this.request = Preconditions.checkNotNull(request);
       this.container = 
Preconditions.checkNotNull(request.getParameter("container"));
+      this.fields = processFields(request, defaultFields);
     }
 
-    @Override
     public Uri getUrl() {
       return uri;
     }
 
-    @Override
     public String getContainer() {
       return container;
     }
 
-    @Override
-    public RenderingContext getRenderingContext() {
-      return RenderingContext.METADATA;
+    public List<String> getFields() {
+      return fields;
+    }
+
+    private List<String> processFields(BaseRequestItem request, List<String> 
defaultList) {
+      List<String> value = request.getListParameter(BaseRequestItem.FIELDS);
+      return ((value == null || value.size() == 0) ? defaultList : value);
     }
   }
 
-  protected class MetadataGadgetContext extends AbstractGadgetContext {
+
+  protected class TokenRequestData extends AbstractRequest
+      implements GadgetsHandlerApi.TokenRequest {
+
+    public TokenRequestData(String url, BaseRequestItem request) {
+      super(url, request, DEFAULT_TOKEN_FIELDS);
+    }
+
+    public GadgetsHandlerApi.TokenData getToken() {
+      return beanDelegator.createDelegator(
+          request.getToken(), GadgetsHandlerApi.TokenData.class);
+    }
+  }
+
+
+  protected class MetadataRequestData extends AbstractRequest
+      implements GadgetsHandlerApi.MetadataRequest {
     protected final Locale locale;
     protected final boolean ignoreCache;
     protected final boolean debug;
 
-    public MetadataGadgetContext(String url, BaseRequestItem request) {
-      super(url, request);
+    public MetadataRequestData(String url, BaseRequestItem request) {
+      super(url, request, DEFAULT_METADATA_FIELDS);
       String lang = request.getParameter("language");
       String country = request.getParameter("country");
       this.locale =
@@ -294,120 +265,29 @@ public class GadgetsHandler {
       this.debug = Boolean.valueOf(request.getParameter("debug"));
     }
 
-    @Override
     public int getModuleId() {
       return 1; // TODO calculate?
     }
 
-    @Override
     public Locale getLocale() {
       return locale;
     }
 
-    @Override
     public boolean getIgnoreCache() {
       return ignoreCache;
     }
 
-    @Override
     public boolean getDebug() {
       return debug;
     }
 
-    @Override
     public String getView() {
       return request.getParameter("view", "default");
     }
 
-    @Override
-    public SecurityToken getToken() {
-      return request.getToken();
-    }
-  }
-
-  protected class TokenGadgetContext extends AbstractGadgetContext {
-    public TokenGadgetContext(String url, BaseRequestItem request) {
-      super(url, request);
-    }
-
-    @Override
-    public SecurityToken getToken() {
-      return request.getToken();
-    }
-  }
-
-  /**
-   * Response classes to represent data structure returned in JSON to common
-   * container JS. They must be public for reflection to work.
-   */
-
-  public static class BaseResponseData {
-    private final String url;
-    private final String error;
-
-    // Call this to indicate an error.
-    public BaseResponseData(String url, String error) {
-      this.url = url;
-      this.error = error;
-    }
-
-    // Have sub-class call this to indicate a success response.
-    protected BaseResponseData(String url) {
-      this(url, null);
-    }
-
-    public String getUrl() {
-      return url;
-    }
-
-    public String getError() {
-      return error;
+    public GadgetsHandlerApi.TokenData getToken() {
+      return beanDelegator.createDelegator(
+        request.getToken(), GadgetsHandlerApi.TokenData.class);
     }
   }
-
-  public static class MetadataResponseData extends BaseResponseData {
-    private final GadgetSpec spec;
-    private final String iframeUrl;
-
-    public MetadataResponseData(String url, GadgetSpec spec, String iframeUrl) 
{
-      super(url);
-      this.spec = spec;
-      this.iframeUrl = iframeUrl;
-    }
-
-    public String getIframeUrl() {
-      return iframeUrl;
-    }
-
-    public String getChecksum() {
-      return spec.getChecksum();
-    }
-
-    public ModulePrefs getModulePrefs() {
-      return spec.getModulePrefs();
-    }
-
-    public Map<String, UserPref> getUserPrefs() {
-      return spec.getUserPrefs();
-    }
-
-    public Map<String, View> getViews() {
-      return spec.getViews();
-    }
-  }
-
-
-  public static class TokenResponseData extends BaseResponseData {
-    private final String token;
-
-    public TokenResponseData(String url, String token) {
-      super(url);
-      this.token = token;
-    }
-
-    public String getToken() {
-      return token;
-    }
-  }
-
 }

Modified: 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerApi.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerApi.java?rev=987680&r1=987679&r2=987680&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerApi.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerApi.java
 Sat Aug 21 00:52:53 2010
@@ -19,10 +19,11 @@
 package org.apache.shindig.gadgets.servlet;
 
 import org.apache.shindig.common.uri.Uri;
-import org.apache.shindig.protocol.conversion.BeanFilter.Required;
+import org.apache.shindig.protocol.conversion.BeanFilter.Unfiltered;
 // Keep imports clean, so it is clear what is used by API
 
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 
 /**
@@ -33,10 +34,33 @@ import java.util.Map;
  */
 public class GadgetsHandlerApi {
 
+  public interface BaseRequest {
+    public Uri getUrl();
+    public String getContainer();
+    public List<String> getFields();
+  }
+
+  public interface MetadataRequest extends BaseRequest {
+    public Locale getLocale();
+    public boolean getIgnoreCache();
+    public boolean getDebug();
+    public String getView();
+    public TokenData getToken();
+  }
+
+  public interface TokenData {
+    public String getOwnerId();
+    public String getViewerId();
+  }
+
+  public interface TokenRequest extends BaseRequest {
+    public TokenData getToken();
+  }
+
   public interface BaseResponse {
-    @Required
-    public String getUrl();
-    @Required
+    @Unfiltered
+    public Uri getUrl();
+    @Unfiltered
     public String getError();
   }
 

Added: 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerService.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerService.java?rev=987680&view=auto
==============================================================================
--- 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerService.java
 (added)
+++ 
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerService.java
 Sat Aug 21 00:52:53 2010
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.gadgets.servlet;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Inject;
+
+import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.auth.SecurityTokenCodec;
+import org.apache.shindig.auth.SecurityTokenException;
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.gadgets.Gadget;
+import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.RenderingContext;
+import org.apache.shindig.gadgets.process.ProcessingException;
+import org.apache.shindig.gadgets.process.Processor;
+import org.apache.shindig.gadgets.spec.Feature;
+import org.apache.shindig.gadgets.spec.GadgetSpec;
+import org.apache.shindig.gadgets.spec.LinkSpec;
+import org.apache.shindig.gadgets.spec.ModulePrefs;
+import org.apache.shindig.gadgets.spec.UserPref;
+import org.apache.shindig.gadgets.spec.View;
+import org.apache.shindig.gadgets.spec.UserPref.EnumValuePair;
+import org.apache.shindig.gadgets.uri.IframeUriManager;
+import org.apache.shindig.protocol.conversion.BeanDelegator;
+import org.apache.shindig.protocol.conversion.BeanFilter;
+
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+public class GadgetsHandlerService {
+
+  // Map shindig data class to API interfaces
+  @VisibleForTesting
+  static final Map<Class<?>, Class<?>> apiClasses =
+      new ImmutableMap.Builder<Class<?>, Class<?>>()
+          .put(View.class, GadgetsHandlerApi.View.class)
+          .put(UserPref.class, GadgetsHandlerApi.UserPref.class)
+          .put(EnumValuePair.class, GadgetsHandlerApi.EnumValuePair.class)
+          .put(ModulePrefs.class, GadgetsHandlerApi.ModulePrefs.class)
+          .put(Feature.class, GadgetsHandlerApi.Feature.class)
+          .put(LinkSpec.class, GadgetsHandlerApi.LinkSpec.class)
+          // Enums
+          .put(View.ContentType.class, GadgetsHandlerApi.ViewContentType.class)
+          .put(UserPref.DataType.class, 
GadgetsHandlerApi.UserPrefDataType.class)
+          .build();
+
+  // Provide mapping for internal enums to api enums
+  @VisibleForTesting
+  static final Map<Enum<?>, Enum<?>> enumConversionMap =
+      new ImmutableMap.Builder<Enum<?>, Enum<?>>()
+          // View.ContentType mapping
+          .putAll(BeanDelegator.createDefaultEnumMap(View.ContentType.class,
+              GadgetsHandlerApi.ViewContentType.class))
+          // UserPref.DataType mapping
+          .putAll(BeanDelegator.createDefaultEnumMap(UserPref.DataType.class,
+              GadgetsHandlerApi.UserPrefDataType.class))
+          .build();
+
+  protected final Processor processor;
+  protected final IframeUriManager iframeUriManager;
+  protected final SecurityTokenCodec securityTokenCodec;
+
+  protected final BeanDelegator beanDelegator;
+  protected final BeanFilter beanFilter;
+
+
+
+  @Inject
+  public GadgetsHandlerService(Processor processor,
+      IframeUriManager iframeUriManager, SecurityTokenCodec securityTokenCodec,
+      BeanFilter beanFilter) {
+    this.processor = processor;
+    this.iframeUriManager = iframeUriManager;
+    this.securityTokenCodec = securityTokenCodec;
+    this.beanFilter = beanFilter;
+
+    this.beanDelegator = new BeanDelegator(apiClasses, enumConversionMap);
+  }
+
+  /**
+   * Get gadget metadata information and iframe url. Support filtering of 
fields
+   * @param request request parameters
+   * @return gadget metadata nd iframe url
+   * @throws ProcessingException
+   */
+  public GadgetsHandlerApi.MetadataResponse 
getMetadata(GadgetsHandlerApi.MetadataRequest request)
+      throws ProcessingException {
+    Set<String> fields = beanFilter.processBeanFields(request.getFields());
+    GadgetContext context = new MetadataGadgetContext(request);
+    Gadget gadget = processor.process(context);
+    String iframeUrl =
+        fields.contains("iframeurl") ? 
iframeUriManager.makeRenderingUri(gadget).toString()
+            : null;
+    return createMetadataResponse(context.getUrl(), gadget.getSpec(), 
iframeUrl, fields);
+  }
+
+  /**
+   * Create security token
+   * @param request token paramaters (gadget, owner and viewer)
+   * @return Security token
+   * @throws SecurityTokenException
+   */
+  public GadgetsHandlerApi.TokenResponse 
getToken(GadgetsHandlerApi.TokenRequest request)
+      throws SecurityTokenException {
+    Set<String> fields = beanFilter.processBeanFields(request.getFields());
+
+    SecurityToken tokenData = convertToken(request.getToken(), 
request.getContainer(),
+        request.getUrl().toString());
+    String token = securityTokenCodec.encodeToken(tokenData);
+    return createTokenResponse(request.getUrl(), token, fields);
+  }
+
+  /**
+   * GadgetContext for metadata request. Used by the gadget processor
+   */
+  protected class MetadataGadgetContext extends GadgetContext {
+
+    private final GadgetsHandlerApi.MetadataRequest request;
+    private final SecurityToken token;
+
+    public MetadataGadgetContext(GadgetsHandlerApi.MetadataRequest request) {
+      this.request = request;
+      this.token = convertToken(
+          request.getToken(), request.getContainer(), 
request.getUrl().toString());
+    }
+
+    @Override
+    public Uri getUrl() {
+      return request.getUrl();
+    }
+
+    @Override
+    public String getContainer() {
+      return request.getContainer();
+    }
+
+    @Override
+    public RenderingContext getRenderingContext() {
+      return RenderingContext.METADATA;
+    }
+
+    @Override
+    public int getModuleId() {
+      return 1;
+    }
+
+    @Override
+    public Locale getLocale() {
+      return request.getLocale();
+    }
+
+    @Override
+    public boolean getIgnoreCache() {
+      return request.getIgnoreCache();
+    }
+
+    @Override
+    public boolean getDebug() {
+      return request.getDebug();
+    }
+
+    @Override
+    public String getView() {
+      return request.getView();
+    }
+
+    @Override
+    public SecurityToken getToken() {
+      return token;
+    }
+  }
+
+  private SecurityToken convertToken(GadgetsHandlerApi.TokenData token,
+      String container, String url) {
+    return beanDelegator.createDelegator(token, SecurityToken.class,
+        ImmutableMap.<String, Object>of("container", container,
+            "appid", url, "appurl", url));
+  }
+
+  public GadgetsHandlerApi.BaseResponse createBaseResponse(Uri url, String 
error) {
+    return beanDelegator.createDelegator(error, 
GadgetsHandlerApi.BaseResponse.class,
+        ImmutableMap.<String, Object>of("url", url, "error", error));
+  }
+
+  private GadgetsHandlerApi.MetadataResponse createMetadataResponse(
+      Uri url, GadgetSpec spec, String iframeUrl, Set<String> fields) {
+    return (GadgetsHandlerApi.MetadataResponse) beanFilter.createFilteredBean(
+        beanDelegator.createDelegator(spec, 
GadgetsHandlerApi.MetadataResponse.class,
+            ImmutableMap.<String, Object>of("url", url, "error", 
BeanDelegator.NULL,
+                "iframeurl", iframeUrl)),
+        fields);
+  }
+
+  private GadgetsHandlerApi.TokenResponse createTokenResponse(
+      Uri url, String token, Set<String> fields) {
+    return (GadgetsHandlerApi.TokenResponse) beanFilter.createFilteredBean(
+        beanDelegator.createDelegator(token, 
GadgetsHandlerApi.TokenResponse.class,
+            ImmutableMap.<String, Object>of("url", url, "error", 
BeanDelegator.NULL, "token", token)),
+        fields);
+  }
+}

Added: 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/GadgetHandlerServiceTest.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/GadgetHandlerServiceTest.java?rev=987680&view=auto
==============================================================================
--- 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/GadgetHandlerServiceTest.java
 (added)
+++ 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/GadgetHandlerServiceTest.java
 Sat Aug 21 00:52:53 2010
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shindig.gadgets.servlet;
+
+import org.apache.shindig.common.EasyMockTestCase;
+import org.apache.shindig.protocol.conversion.BeanDelegator;
+import org.junit.Test;
+
+public class GadgetHandlerServiceTest extends EasyMockTestCase {
+
+  // Next test verify that the API data classes are configured correctly.
+  // The mapping is done using reflection in runtime, so this test verify 
mapping is complete
+  // this test will prevent from not intended change to the API.
+  // DO NOT REMOVE TEST
+  @Test
+  public void testHandlerDataDelegation() throws Exception {
+    BeanDelegator delegator = new BeanDelegator(
+        GadgetsHandlerService.apiClasses, 
GadgetsHandlerService.enumConversionMap);
+    delegator.validate();
+  }
+}

Modified: 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerTest.java
URL: 
http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerTest.java?rev=987680&r1=987679&r2=987680&view=diff
==============================================================================
--- 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerTest.java
 (original)
+++ 
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/servlet/GadgetsHandlerTest.java
 Sat Aug 21 00:52:53 2010
@@ -21,6 +21,8 @@ import com.google.common.collect.Immutab
 import com.google.common.collect.ImmutableSet;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
+
+import org.apache.shindig.auth.SecurityToken;
 import org.apache.shindig.auth.SecurityTokenCodec;
 import org.apache.shindig.auth.SecurityTokenException;
 import org.apache.shindig.common.EasyMockTestCase;
@@ -33,10 +35,10 @@ import org.apache.shindig.protocol.Defau
 import org.apache.shindig.protocol.HandlerExecutionListener;
 import org.apache.shindig.protocol.HandlerRegistry;
 import org.apache.shindig.protocol.RpcHandler;
-import org.apache.shindig.protocol.conversion.BeanDelegator;
 import org.apache.shindig.protocol.conversion.BeanFilter;
 import org.apache.shindig.protocol.conversion.BeanJsonConverter;
 import org.apache.shindig.protocol.multipart.FormDataItem;
+import org.easymock.Capture;
 import org.easymock.EasyMock;
 import org.json.JSONArray;
 import org.json.JSONException;
@@ -74,9 +76,11 @@ public class GadgetsHandlerTest extends 
   }
 
   private void registerGadgetsHandler(SecurityTokenCodec codec) {
+    BeanFilter beanFilter = new BeanFilter();
+    GadgetsHandlerService service =
+        new GadgetsHandlerService(processor, urlGenerator, codec, beanFilter);
     GadgetsHandler handler =
-        new GadgetsHandler(new TestExecutorService(), processor, urlGenerator, 
codec,
-          new BeanFilter());
+        new GadgetsHandler(new TestExecutorService(), service, beanFilter);
     registry = new DefaultHandlerRegistry(
         injector, converter, new HandlerExecutionListener.NoOpHandler());
     registry.addHandlers(ImmutableSet.<Object> of(handler));
@@ -176,7 +180,8 @@ public class GadgetsHandlerTest extends 
   @Test
   public void testTokenOneGadget() throws Exception {
     SecurityTokenCodec codec = EasyMock.createMock(SecurityTokenCodec.class);
-    EasyMock.expect(codec.encodeToken(token)).andReturn(TOKEN);
+    Capture<SecurityToken> tokenCapture = new Capture<SecurityToken>();
+    
EasyMock.expect(codec.encodeToken(EasyMock.capture(tokenCapture))).andReturn(TOKEN);
     replay(codec);
 
     registerGadgetsHandler(codec);
@@ -188,6 +193,12 @@ public class GadgetsHandlerTest extends 
     JSONObject gadget = response.getJSONObject(GADGET1_URL);
     assertEquals(TOKEN, gadget.getString("token"));
     assertFalse(gadget.has("error"));
+    // next checks verify all fiels that canbe used for token generation are 
passed in
+    assertEquals("container", tokenCapture.getValue().getContainer());
+    assertEquals(GADGET1_URL, tokenCapture.getValue().getAppId());
+    assertEquals(GADGET1_URL, tokenCapture.getValue().getAppUrl());
+    assertSame(token.getOwnerId(), tokenCapture.getValue().getOwnerId());
+    assertSame(token.getViewerId(), tokenCapture.getValue().getViewerId());
   }
 
   @Test
@@ -206,7 +217,8 @@ public class GadgetsHandlerTest extends 
   @Test
   public void testTokenOneGadgetFailure() throws Exception {
     SecurityTokenCodec codec = EasyMock.createMock(SecurityTokenCodec.class);
-    EasyMock.expect(codec.encodeToken(token)).andThrow(new 
SecurityTokenException("blah"));
+    EasyMock.expect(codec.encodeToken(EasyMock.isA(SecurityToken.class)))
+        .andThrow(new SecurityTokenException("blah"));
     replay(codec);
 
     registerGadgetsHandler(codec);
@@ -238,8 +250,10 @@ public class GadgetsHandlerTest extends 
   @Test
   public void testTokenMultipleGadgetsWithSuccessAndFailure() throws Exception 
{
     SecurityTokenCodec codec = EasyMock.createMock(SecurityTokenCodec.class);
-    EasyMock.expect(codec.encodeToken(token)).andReturn(TOKEN);
-    EasyMock.expect(codec.encodeToken(token)).andThrow(new 
SecurityTokenException("blah"));
+    EasyMock.expect(codec.encodeToken(EasyMock.isA(SecurityToken.class)))
+        .andReturn(TOKEN);
+    EasyMock.expect(codec.encodeToken(EasyMock.isA(SecurityToken.class)))
+        .andThrow(new SecurityTokenException("blah"));
     replay(codec);
 
     registerGadgetsHandler(codec);
@@ -275,16 +289,4 @@ public class GadgetsHandlerTest extends 
     assertNotNull("got gadget2", gadget2);
     assertEquals(GadgetsHandler.FAILURE_METADATA, gadget2.getString("error"));
   }
-
-  // Next test verify that the API data classes are configured correctly.
-  // The mapping is done using reflection in runtime, so this test verify 
mapping is complete
-  // this test will prevent from not intended change to the API.
-  // DO NOT REMOVE TEST
-  @Test
-  public void testHandlerDataDelegation() throws Exception {
-    BeanDelegator delegator = new BeanDelegator(
-        GadgetsHandler.apiClasses, GadgetsHandler.enumConversionMap);
-    delegator.validate();
-  }
-
 }


Reply via email to