Allow ServiceLocator instantiation to be controlled via RequestFactory ServiceLocator.
http://gwt-code-reviews.appspot.com/1427801/
http://code.google.com/p/google-web-toolkit/issues/detail?id=6264
Patch by: daniel.r.bell
Review by: bobv, t.broyer


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10021 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/web/bindery/requestfactory/server/LocatorServiceLayer.java b/user/src/com/google/web/bindery/requestfactory/server/LocatorServiceLayer.java
index 4a292d2..aa1c5d6 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/LocatorServiceLayer.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/LocatorServiceLayer.java
@@ -50,17 +50,23 @@
 
   @Override
   public Object createServiceInstance(Method contextMethod, Method domainMethod) {
-    Class<? extends ServiceLocator> locatorType =
-        getTop().resolveServiceLocator(contextMethod, domainMethod);
-    ServiceLocator locator = newInstance(locatorType, ServiceLocator.class);
+    Class<? extends ServiceLocator> locatorType = getTop().resolveServiceLocator(
+        contextMethod, domainMethod);
+    ServiceLocator locator = getTop().createServiceLocator(locatorType);
     // Enclosing class may be a parent class, so invoke on service class
     Class<?> declaringClass = contextMethod.getDeclaringClass();
-    Class<?> serviceClass =
-        getTop().resolveServiceClass(declaringClass.asSubclass(RequestContext.class));
+    Class<?> serviceClass = getTop().resolveServiceClass(
+        declaringClass.asSubclass(RequestContext.class));
     return locator.getInstance(serviceClass);
   }
 
   @Override
+  public <T extends ServiceLocator> T createServiceLocator(
+      Class<T> serviceLocatorType) {
+    return newInstance(serviceLocatorType, ServiceLocator.class);
+  }
+
+  @Override
   public Object getId(Object domainObject) {
     return doGetId(domainObject);
   }
@@ -94,7 +100,8 @@
    * method is non-static.
    */
   @Override
-  public boolean requiresServiceLocator(Method contextMethod, Method domainMethod) {
+  public boolean requiresServiceLocator(Method contextMethod,
+      Method domainMethod) {
     return Request.class.isAssignableFrom(contextMethod.getReturnType())
         && !Modifier.isStatic(domainMethod.getModifiers());
   }
@@ -102,7 +109,8 @@
   @Override
   public Class<? extends Locator<?, ?>> resolveLocator(Class<?> domainType) {
     // Find the matching BaseProxy
-    Class<?> proxyType = getTop().resolveClientType(domainType, BaseProxy.class, false);
+    Class<?> proxyType = getTop().resolveClientType(domainType,
+        BaseProxy.class, false);
     if (proxyType == null) {
       return null;
     }
@@ -118,12 +126,14 @@
     } else if (ln != null && ln.locator().length() > 0) {
       try {
         @SuppressWarnings("unchecked")
-        Class<? extends Locator<?, ?>> found =
-            (Class<? extends Locator<?, ?>>) Class.forName(ln.locator(), false,
-                getTop().getDomainClassLoader()).asSubclass(Locator.class);
+        Class<? extends Locator<?, ?>> found = (Class<? extends Locator<?, ?>>) Class.forName(
+            ln.locator(), false, getTop().getDomainClassLoader()).asSubclass(
+            Locator.class);
         locatorType = found;
       } catch (ClassNotFoundException e) {
-        return die(e, "Could not find the locator type specified in the @%s annotation %s",
+        return die(
+            e,
+            "Could not find the locator type specified in the @%s annotation %s",
             ProxyForName.class.getCanonicalName(), ln.value());
       }
     } else {
@@ -134,8 +144,8 @@
   }
 
   @Override
-  public Class<? extends ServiceLocator> resolveServiceLocator(Method contextMethod,
-      Method domainMethod) {
+  public Class<? extends ServiceLocator> resolveServiceLocator(
+      Method contextMethod, Method domainMethod) {
     Class<? extends ServiceLocator> locatorType;
 
     // Look at the RequestContext
@@ -146,11 +156,12 @@
       locatorType = l.locator();
     } else if (ln != null && ln.locator().length() > 0) {
       try {
-        locatorType =
-            Class.forName(ln.locator(), false, getTop().getDomainClassLoader()).asSubclass(
-                ServiceLocator.class);
+        locatorType = Class.forName(ln.locator(), false,
+            getTop().getDomainClassLoader()).asSubclass(ServiceLocator.class);
       } catch (ClassNotFoundException e) {
-        return die(e, "Could not find the locator type specified in the @%s annotation %s",
+        return die(
+            e,
+            "Could not find the locator type specified in the @%s annotation %s",
             ServiceName.class.getCanonicalName(), ln.value());
       }
     } else {
@@ -201,7 +212,8 @@
 
   @SuppressWarnings("unchecked")
   private <T, I> Locator<T, I> getLocator(Class<T> domainType) {
-    Class<? extends Locator<?, ?>> locatorType = getTop().resolveLocator(domainType);
+    Class<? extends Locator<?, ?>> locatorType = getTop().resolveLocator(
+        domainType);
     if (locatorType == null) {
       return null;
     }
@@ -217,7 +229,8 @@
     } catch (IllegalAccessException e) {
       ex = e;
     }
-    return this.<T> die(ex, "Could not instantiate %s %s. Is it default-instantiable?", base
-        .getSimpleName(), clazz.getCanonicalName());
+    return this.<T> die(ex,
+        "Could not instantiate %s %s. Is it default-instantiable?",
+        base.getSimpleName(), clazz.getCanonicalName());
   }
 }
diff --git a/user/src/com/google/web/bindery/requestfactory/server/ServiceLayer.java b/user/src/com/google/web/bindery/requestfactory/server/ServiceLayer.java
index 2664b86..484f954 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/ServiceLayer.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/ServiceLayer.java
@@ -61,8 +61,8 @@
   public static ServiceLayer create(ServiceLayerDecorator... decorators) {
     List<ServiceLayerDecorator> list = new ArrayList<ServiceLayerDecorator>();
     // Always hit the cache first
-    ServiceLayerDecorator cache =
-        ENABLE_CACHE ? new ServiceLayerCache() : new ServiceLayerDecorator();
+    ServiceLayerDecorator cache = ENABLE_CACHE ? new ServiceLayerCache()
+        : new ServiceLayerDecorator();
     list.add(cache);
     // The the user-provided decorators
     if (decorators != null) {
@@ -125,7 +125,18 @@
    * @param domainMethod the method that the service object must implement
    * @return an instance of the requested service object
    */
-  public abstract Object createServiceInstance(Method contextMethod, Method domainMethod);
+  public abstract Object createServiceInstance(Method contextMethod,
+      Method domainMethod);
+
+  /**
+   * Create an instance of the requested {@link ServiceLocator} type.
+   * 
+   * @param <T> the requested ServiceLocator type
+   * @param clazz the requested ServiceLocator type
+   * @return an instance of the requested ServiceLocator type
+   */
+  public abstract <T extends ServiceLocator> T createServiceLocator(
+      Class<T> clazz);
 
   /**
    * Returns the ClassLoader that should be used when attempting to access
@@ -253,7 +264,8 @@
    * @return the requested objects, elements of which may be {@code null} if the
    *         requested objects were irretrievable
    */
-  public abstract List<Object> loadDomainObjects(List<Class<?>> classes, List<Object> domainIds);
+  public abstract List<Object> loadDomainObjects(List<Class<?>> classes,
+      List<Object> domainIds);
 
   /**
    * Determines if the invocation of a domain method requires a
@@ -264,7 +276,8 @@
    * @param domainMethod a domain method
    * @return {@code true} if a ServiceLocator is required
    */
-  public abstract boolean requiresServiceLocator(Method contextMethod, Method domainMethod);
+  public abstract boolean requiresServiceLocator(Method contextMethod,
+      Method domainMethod);
 
   /**
    * Given a type token previously returned from
@@ -288,8 +301,8 @@
    * @return a class that represents {@code domainClass} on the client which is
    *         assignable to {@code clientType}
    */
-  public abstract <T> Class<? extends T> resolveClientType(Class<?> domainClass,
-      Class<T> clientType, boolean required);
+  public abstract <T> Class<? extends T> resolveClientType(
+      Class<?> domainClass, Class<T> clientType, boolean required);
 
   /**
    * Determine the domain (server-side) type that the given client type is
@@ -318,7 +331,8 @@
    * @return the type of Locator to use, or {@code null} if the type conforms to
    *         the RequestFactory entity protocol
    */
-  public abstract Class<? extends Locator<?, ?>> resolveLocator(Class<?> domainType);
+  public abstract Class<? extends Locator<?, ?>> resolveLocator(
+      Class<?> domainType);
 
   /**
    * Find a RequestContext method declaration by name.
@@ -330,29 +344,32 @@
    * @return the method declaration, or {@code null} if the method does not
    *         exist
    */
-  public abstract Method resolveRequestContextMethod(String requestContextClass, String methodName);
+  public abstract Method resolveRequestContextMethod(
+      String requestContextClass, String methodName);
 
   /**
    * Given a {@link RequestContext} method, find the service class referenced in
    * the {@link Service} or {@link ServiceName} annotation.
-   *
+   * 
    * @param requestContextClass a RequestContext interface
    * @return the type of service to use
    */
-  public abstract Class<?> resolveServiceClass(Class<? extends RequestContext> requestContextClass);
+  public abstract Class<?> resolveServiceClass(
+      Class<? extends RequestContext> requestContextClass);
 
   /**
    * Given a RequestContext method declaration, resolve the
    * {@link ServiceLocator} that should be used when invoking the domain method.
-   * This method will only be called if {@link #requiresServiceLocator(Method, Method)}
-   * returned {@code true} for the associated domain method.
+   * This method will only be called if
+   * {@link #requiresServiceLocator(Method, Method)} returned {@code true} for
+   * the associated domain method.
    * 
    * @param contextMethod a RequestContext method declaration
    * @param domainMethod the domain method that will be invoked
    * @return the type of ServiceLocator to use
    */
-  public abstract Class<? extends ServiceLocator> resolveServiceLocator(Method contextMethod,
-      Method domainMethod);
+  public abstract Class<? extends ServiceLocator> resolveServiceLocator(
+      Method contextMethod, Method domainMethod);
 
   /**
    * Return a string used to represent the given type in the wire protocol.
@@ -370,8 +387,8 @@
    * @param expectedType the type of the property
    * @param value the new value
    */
-  public abstract void setProperty(Object domainObject, String property, Class<?> expectedType,
-      Object value);
+  public abstract void setProperty(Object domainObject, String property,
+      Class<?> expectedType, Object value);
 
   /**
    * Invoke a JSR 303 validator on the given domain object. If no validator is
diff --git a/user/src/com/google/web/bindery/requestfactory/server/ServiceLayerDecorator.java b/user/src/com/google/web/bindery/requestfactory/server/ServiceLayerDecorator.java
index 2bbf261..b2a7f89 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/ServiceLayerDecorator.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/ServiceLayerDecorator.java
@@ -61,6 +61,11 @@
   }
 
   @Override
+  public <T extends ServiceLocator> T createServiceLocator(Class<T> clazz) {
+    return getNext().createServiceLocator(clazz);
+  }
+
+  @Override
   public ClassLoader getDomainClassLoader() {
     return getNext().getDomainClassLoader();
   }
@@ -116,12 +121,14 @@
   }
 
   @Override
-  public List<Object> loadDomainObjects(List<Class<?>> classes, List<Object> domainIds) {
+  public List<Object> loadDomainObjects(List<Class<?>> classes,
+      List<Object> domainIds) {
     return getNext().loadDomainObjects(classes, domainIds);
   }
 
   @Override
-  public boolean requiresServiceLocator(Method contextMethod, Method domainMethod) {
+  public boolean requiresServiceLocator(Method contextMethod,
+      Method domainMethod) {
     return getNext().requiresServiceLocator(contextMethod, domainMethod);
   }
 
@@ -131,8 +138,8 @@
   }
 
   @Override
-  public <T> Class<? extends T> resolveClientType(Class<?> domainClass, Class<T> clientType,
-      boolean required) {
+  public <T> Class<? extends T> resolveClientType(Class<?> domainClass,
+      Class<T> clientType, boolean required) {
     return getNext().resolveClientType(domainClass, clientType, required);
   }
 
@@ -152,18 +159,21 @@
   }
 
   @Override
-  public Method resolveRequestContextMethod(String requestContextClass, String methodName) {
-    return getNext().resolveRequestContextMethod(requestContextClass, methodName);
+  public Method resolveRequestContextMethod(String requestContextClass,
+      String methodName) {
+    return getNext().resolveRequestContextMethod(requestContextClass,
+        methodName);
   }
 
   @Override
-  public Class<?> resolveServiceClass(Class<? extends RequestContext> requestContextClass) {
+  public Class<?> resolveServiceClass(
+      Class<? extends RequestContext> requestContextClass) {
     return getNext().resolveServiceClass(requestContextClass);
   }
 
   @Override
-  public Class<? extends ServiceLocator> resolveServiceLocator(Method contextMethod,
-      Method domainMethod) {
+  public Class<? extends ServiceLocator> resolveServiceLocator(
+      Method contextMethod, Method domainMethod) {
     return getNext().resolveServiceLocator(contextMethod, domainMethod);
   }
 
@@ -173,7 +183,8 @@
   }
 
   @Override
-  public void setProperty(Object domainObject, String property, Class<?> expectedType, Object value) {
+  public void setProperty(Object domainObject, String property,
+      Class<?> expectedType, Object value) {
     getNext().setProperty(domainObject, property, expectedType, value);
   }
 
@@ -194,7 +205,8 @@
    * @throws UnexpectedException this method never returns normally
    * @see #report(String, Object...)
    */
-  protected final <T> T die(Throwable e, String message, Object... args) throws UnexpectedException {
+  protected final <T> T die(Throwable e, String message, Object... args)
+      throws UnexpectedException {
     String msg = String.format(message, args);
     log.log(Level.SEVERE, msg, e);
     throw new UnexpectedException(msg, e);
@@ -217,8 +229,8 @@
    * Report an exception thrown by code that is under the control of the
    * end-developer.
    * 
-   * @param userGeneratedException an {@link InvocationTargetException} thrown by an invocation of
-   *          user-provided code
+   * @param userGeneratedException an {@link InvocationTargetException} thrown
+   *          by an invocation of user-provided code
    * @throws ReportableException this method never returns normally
    */
   protected final <T> T report(InvocationTargetException userGeneratedException)
@@ -235,7 +247,8 @@
    * @throws ReportableException this method never returns normally
    * @see #die(Throwable, String, Object...)
    */
-  protected final <T> T report(String msg, Object... args) throws ReportableException {
+  protected final <T> T report(String msg, Object... args)
+      throws ReportableException {
     throw new ReportableException(String.format(msg, args));
   }
 
diff --git a/user/test/com/google/web/bindery/requestfactory/server/ServiceLocatorTest.java b/user/test/com/google/web/bindery/requestfactory/server/ServiceLocatorTest.java
new file mode 100644
index 0000000..3fee0cd
--- /dev/null
+++ b/user/test/com/google/web/bindery/requestfactory/server/ServiceLocatorTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2011 Google Inc.
+ * 
+ * Licensed 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 com.google.web.bindery.requestfactory.server;
+
+import com.google.web.bindery.requestfactory.shared.ServiceLocator;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests creating of ServiceLocators with custom ServiceLayerDecorators
+ */
+public class ServiceLocatorTest extends TestCase {
+
+  static class CustomLocatorLayer extends ServiceLayerDecorator {
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T extends ServiceLocator> T createServiceLocator(Class<T> clazz) {
+      return (T) ServiceLocatorImpl.INSTANCE;
+    }
+  }
+
+  static class ServiceLocatorImpl implements ServiceLocator {
+    static final ServiceLocatorImpl INSTANCE = new ServiceLocatorImpl();
+
+    public Object getInstance(Class<?> clazz) {
+      return new Object();
+    }
+  }
+
+  public void testGetsServiceLocatorFromDecorator() {
+    ServiceLayer layer = ServiceLayer.create(new CustomLocatorLayer());
+    ServiceLocatorImpl locator = layer.createServiceLocator(ServiceLocatorImpl.class);
+    assertSame(ServiceLocatorImpl.INSTANCE, locator);
+  }
+
+  public void testInstantiatesServiceLocatorByDefault() {
+    ServiceLayer layer = ServiceLayer.create();
+    ServiceLocatorImpl locator = layer.createServiceLocator(ServiceLocatorImpl.class);
+    assertNotNull(locator);
+    assertNotSame(ServiceLocatorImpl.INSTANCE, locator);
+  }
+}
diff --git a/user/test/com/google/web/bindery/requestfactory/vm/RequestFactoryJreSuite.java b/user/test/com/google/web/bindery/requestfactory/vm/RequestFactoryJreSuite.java
index 3187318..5b7d5ed 100644
--- a/user/test/com/google/web/bindery/requestfactory/vm/RequestFactoryJreSuite.java
+++ b/user/test/com/google/web/bindery/requestfactory/vm/RequestFactoryJreSuite.java
@@ -24,6 +24,7 @@
 import com.google.web.bindery.requestfactory.server.RequestFactoryJreTest;
 import com.google.web.bindery.requestfactory.server.RequestFactoryUnicodeEscapingJreTest;
 import com.google.web.bindery.requestfactory.server.ServiceInheritanceJreTest;
+import com.google.web.bindery.requestfactory.server.ServiceLocatorTest;
 import com.google.web.bindery.requestfactory.shared.impl.SimpleEntityProxyIdTest;
 
 import junit.framework.Test;
@@ -50,6 +51,7 @@
     suite.addTestSuite(RequestFactoryJreTest.class);
     suite.addTestSuite(RequestFactoryUnicodeEscapingJreTest.class);
     suite.addTestSuite(ServiceInheritanceJreTest.class);
+    suite.addTestSuite(ServiceLocatorTest.class);
     suite.addTestSuite(SimpleEntityProxyIdTest.class);
 
     return suite;