Support RequestContext composition (issue 6234) and mix-in (issue 6035).
Remove no-method-overloads restriction by using a full method descriptor to
create the operation.
Use obfuscated type and operation tokens to reduce payload and JS size (issue
5394). Without this change, the operation tokens can be excessively lengthly.
The RequestFactory interface is used as the "gwt.rpc" analog.
Add an annotation processor to generate an obfuscated type manifest and add it
to the gwt-user project.  This is only necessary when using the JRE-compatible
RequestFactory client code.
Review at http://gwt-code-reviews.appspot.com/1447815
Patch by: bobv
Review by: rjrjr


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10311 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/eclipse/user/.classpath b/eclipse/user/.classpath
index eff0d1c..d570716 100644
--- a/eclipse/user/.classpath
+++ b/eclipse/user/.classpath
@@ -59,5 +59,10 @@
 	<classpathentry kind="var" path="GWT_TOOLS/lib/jboss/test-harness/jboss-test-harness-api-1.0.0.jar" sourcepath="/GWT_TOOLS/lib/jboss/test-harness/jboss-test-harness-api-1.0.0-sources.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/testng/testng-5.14.1-sources.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/testng/testng-5.14.1-nojunit.jar" sourcepath="/GWT_TOOLS/lib/testng/testng-5.14.1-sources.jar"/>
+	<classpathentry kind="src" path=".apt_generated">
+		<attributes>
+			<attribute name="optional" value="true"/>
+		</attributes>
+	</classpathentry>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/eclipse/user/.factorypath b/eclipse/user/.factorypath
new file mode 100644
index 0000000..3602e92
--- /dev/null
+++ b/eclipse/user/.factorypath
@@ -0,0 +1,3 @@
+<factorypath>
+    <factorypathentry kind="VARJAR" id="GWT_TOOLS/lib/requestfactory/requestfactory-apt.jar" enabled="true" runInBatchMode="false"/>
+</factorypath>
diff --git a/requestfactory/build.xml b/requestfactory/build.xml
index dae489d..8b3ed84 100755
--- a/requestfactory/build.xml
+++ b/requestfactory/build.xml
@@ -5,6 +5,7 @@
 
   <!-- Remove all output files -->
   <target name="clean" description="Cleans this project's output files">
+    <delete file="${gwt.build.lib}/requestfactory-apt.jar" />
     <delete file="${gwt.build.lib}/requestfactory-client.jar" />
     <delete file="${gwt.build.lib}/requestfactory-client-src.jar" />
     <delete file="${gwt.build.lib}/requestfactory-client+src.jar" />
@@ -40,6 +41,9 @@
   </macrodef>
  
   <!-- Targets for individual jars -->
+  <target name="requestfactory-apt" description="Build RequestFactory apt jar">
+    <requestfactory-jar target="apt"/>
+  </target>
 
   <target name="requestfactory-client" description="Build RequestFactory client jar">
     <requestfactory-jar target="client"/>
@@ -71,7 +75,7 @@
   </target>
 
   <!-- Build all client jars -->
-  <target name="clientjars" depends="requestfactory-client,requestfactory-client-src,requestfactory-client+src" description="Build requestfactory client jars" />
+  <target name="clientjars" depends="requestfactory-apt,requestfactory-client,requestfactory-client-src,requestfactory-client+src" description="Build requestfactory client jars" />
 
   <!-- Build all server jars -->
   <target name="serverjars" depends="requestfactory-server,requestfactory-server-src,requestfactory-server+src" description="Build requestfactory server jars" />
@@ -96,7 +100,9 @@
         <fileset dir="${gwt.tools.lib}" includes="hibernate/validator/hibernate-validator-4.1.0.Final.jar" />
         <fileset dir="${gwt.tools.lib}" includes="javax/validation/validation-api-1.0.0.GA.jar" />
         <fileset dir="${gwt.tools.lib}" includes="javax/xml/bind/jaxb-api-2.1.jar" />
+        <fileset dir="${gwt.build.lib}" includes="requestfactory-apt.jar" />
         <fileset dir="${gwt.build.lib}" includes="requestfactory-test+src.jar" />
+
       </classpath>
     </java>
   </target>
diff --git a/tools/api-checker/config/gwt23_24userApi.conf b/tools/api-checker/config/gwt23_24userApi.conf
index 595e1e1..7f371e7 100644
--- a/tools/api-checker/config/gwt23_24userApi.conf
+++ b/tools/api-checker/config/gwt23_24userApi.conf
@@ -61,6 +61,7 @@
 :com/google/gwt/validation/**\
 :com/google/web/bindery/autobean/**/impl/**\
 :com/google/web/bindery/autobean/shared/ValueCodexHelper.java\
+:com/google/web/bindery/requestfactory/apt/**\
 :com/google/web/bindery/requestfactory/gwt/client/impl/**\
 :com/google/web/bindery/requestfactory/shared/impl/**\
 
@@ -108,6 +109,7 @@
 :user/src/com/google/gwt/uibinder/testing/**\
 :user/src/com/google/gwt/util/**\
 :user/src/com/google/gwt/validation/**\
+:user/src/com/google/web/bindery/requestfactory/apt/**\
 :user/src/com/google/web/bindery/requestfactory/gwt/client/impl/**\
 :user/src/com/google/web/bindery/requestfactory/server/impl/**\
 :user/src/com/google/web/bindery/requestfactory/shared/impl/**\
diff --git a/user/build.xml b/user/build.xml
index 579e7a6..5b39170 100755
--- a/user/build.xml
+++ b/user/build.xml
@@ -106,6 +106,8 @@
       	<!-- Hibernate is included until we can provide the super source as an third party jar" -->
       	<pathelement location="${gwt.tools.lib}/hibernate/validator/hibernate-validator-4.1.0.Final.jar" />
         <pathelement location="${gwt.tools.lib}/hibernate/validator/hibernate-validator-4.1.0.Final-sources.jar" />
+        <!-- Bootstrap support needed for obfuscated type tokens for JRE runtime -->
+        <pathelement location="${gwt.tools.lib}/requestfactory/requestfactory-apt.jar" />
         <pathelement location="${gwt.dev.jar}" />
       </classpath>
     </gwt.javac>
@@ -136,6 +138,8 @@
         <pathelement location="${javac.out}" />
         <pathelement location="${gwt.tools.lib}/junit/junit-4.8.2.jar" />
         <pathelement location="${gwt.tools.lib}/selenium/selenium-java-client-driver.jar" />
+        <!-- Bootstrap support needed for obfuscated type tokens for JRE runtime -->
+        <pathelement location="${gwt.tools.lib}/requestfactory/requestfactory-apt.jar" />
         <path refid="test.extraclasspath" />
       </classpath>
     </gwt.javac>
diff --git a/user/src/com/google/gwt/i18n/client/Messages.java b/user/src/com/google/gwt/i18n/client/Messages.java
index 51f1440..d442349 100644
--- a/user/src/com/google/gwt/i18n/client/Messages.java
+++ b/user/src/com/google/gwt/i18n/client/Messages.java
@@ -360,7 +360,8 @@
      * be replaced during code generation with the default implementation.
      * </p>
      */
-    Class<? extends PluralRule> value() default PluralRule.class;
+    // http://bugs.sun.com/view_bug.do?bug_id=6512707
+    Class<? extends PluralRule> value() default com.google.gwt.i18n.client.PluralRule.class;
   }
 
   /**
diff --git a/user/src/com/google/gwt/resources/client/ImageResource.java b/user/src/com/google/gwt/resources/client/ImageResource.java
index df63fc1..5f72fb8 100644
--- a/user/src/com/google/gwt/resources/client/ImageResource.java
+++ b/user/src/com/google/gwt/resources/client/ImageResource.java
@@ -71,7 +71,8 @@
      * 
      * @see "CssResource documentation"
      */
-    RepeatStyle repeatStyle() default RepeatStyle.None;
+    // http://bugs.sun.com/view_bug.do?bug_id=6512707
+    RepeatStyle repeatStyle() default com.google.gwt.resources.client.ImageResource.RepeatStyle.None;
 
     /**
      * Set to a positive value to override the image's intrinsic width. The
diff --git a/user/src/com/google/web/bindery/requestfactory/apt/RfApt.java b/user/src/com/google/web/bindery/requestfactory/apt/RfApt.java
new file mode 100644
index 0000000..a4703a2
--- /dev/null
+++ b/user/src/com/google/web/bindery/requestfactory/apt/RfApt.java
@@ -0,0 +1,142 @@
+/*
+ * 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.apt;
+
+import static com.google.web.bindery.requestfactory.vm.impl.TypeTokenResolver.TOKEN_MANIFEST;
+
+import com.google.web.bindery.requestfactory.shared.BaseProxy;
+import com.google.web.bindery.requestfactory.vm.impl.OperationKey;
+import com.google.web.bindery.requestfactory.vm.impl.TypeTokenResolver;
+
+import java.io.IOException;
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedOptions;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.ElementScanner6;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+import javax.tools.Diagnostic.Kind;
+import javax.tools.FileObject;
+import javax.tools.StandardLocation;
+
+/**
+ * An annotation processor that creates an obfuscated type manifest that is used
+ * by {@link com.google.web.bindery.requestfactory.vm.RequestFactorySource} and
+ * related implementation classes.
+ */
+@SupportedAnnotationTypes("com.google.web.bindery.requestfactory.*")
+@SupportedSourceVersion(SourceVersion.RELEASE_6)
+@SupportedOptions("verbose")
+public class RfApt extends AbstractProcessor {
+
+  /**
+   * Looks for all types assignable to {@link BaseProxy} and adds them to the
+   * output state.
+   */
+  private class Finder extends ElementScanner6<Void, Void> {
+    // Only valid for a single round
+    TypeMirror baseProxyType = elements.getTypeElement(BaseProxy.class.getCanonicalName()).asType();
+
+    @Override
+    public Void visitType(TypeElement elt, Void arg1) {
+      String binaryName = elements.getBinaryName(elt).toString();
+      if (types.isSubtype(elt.asType(), baseProxyType)) {
+        String hash = OperationKey.hash(binaryName);
+        builder.addTypeToken(hash, binaryName);
+        log(elt, "Processed proxy %s %s", binaryName, hash);
+      }
+      return super.visitType(elt, arg1);
+    }
+  }
+
+  private TypeTokenResolver.Builder builder;
+  private Elements elements;
+  private Filer filer;
+  private boolean verbose;
+  private Types types;
+
+  @Override
+  public synchronized void init(ProcessingEnvironment processingEnv) {
+    super.init(processingEnv);
+    log("RfApt init");
+    elements = processingEnv.getElementUtils();
+    filer = processingEnv.getFiler();
+    types = processingEnv.getTypeUtils();
+    verbose = Boolean.parseBoolean(processingEnv.getOptions().get("verbose"));
+  }
+
+  @Override
+  public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+    log("RequestFactory processing a round");
+
+    if (builder == null) {
+      builder = new TypeTokenResolver.Builder();
+      // Try not to obliterate existing data
+      try {
+        FileObject resource = filer.getResource(StandardLocation.CLASS_OUTPUT, "", TOKEN_MANIFEST);
+        builder.load(resource.openInputStream());
+        log("Reusing old data");
+      } catch (IOException e) {
+        // Likely because the file does not exist
+        log("Not reusing existing manifest file: " + e.getMessage());
+      }
+    }
+
+    // Extract data
+    new Finder().scan(ElementFilter.typesIn(roundEnv.getRootElements()), null);
+    
+    // On the last round, write out accumulated data
+    if (roundEnv.processingOver()) {
+      TypeTokenResolver d = builder.build();
+      builder = null;
+      try {
+        FileObject res = filer.createResource(StandardLocation.CLASS_OUTPUT, "", TOKEN_MANIFEST);
+        d.store(res.openOutputStream());
+      } catch (IOException e) {
+        error("Could not write output: " + e.getMessage());
+      }
+      log("Finished!");
+    }
+    return false;
+  }
+
+  private void error(String message, Object... args) {
+    processingEnv.getMessager().printMessage(Kind.ERROR, "ERROR: " + String.format(message, args));
+  }
+
+  private void log(Element elt, String message, Object... args) {
+    if (verbose) {
+      processingEnv.getMessager().printMessage(Kind.NOTE, String.format(message, args), elt);
+    }
+  }
+
+  private void log(String message, Object... args) {
+    if (verbose) {
+      processingEnv.getMessager().printMessage(Kind.NOTE, String.format(message, args));
+    }
+  }
+}
diff --git a/user/src/com/google/web/bindery/requestfactory/gwt/rebind/RequestFactoryGenerator.java b/user/src/com/google/web/bindery/requestfactory/gwt/rebind/RequestFactoryGenerator.java
index 35c9821..718eaed 100644
--- a/user/src/com/google/web/bindery/requestfactory/gwt/rebind/RequestFactoryGenerator.java
+++ b/user/src/com/google/web/bindery/requestfactory/gwt/rebind/RequestFactoryGenerator.java
@@ -15,13 +15,6 @@
  */
 package com.google.web.bindery.requestfactory.gwt.rebind;
 
-import com.google.web.bindery.autobean.gwt.rebind.model.JBeanMethod;
-import com.google.web.bindery.autobean.shared.AutoBean;
-import com.google.web.bindery.autobean.shared.AutoBean.PropertyName;
-import com.google.web.bindery.autobean.shared.AutoBeanFactory;
-import com.google.web.bindery.autobean.shared.AutoBeanFactory.Category;
-import com.google.web.bindery.autobean.shared.AutoBeanFactory.NoWrap;
-import com.google.web.bindery.autobean.shared.impl.EnumMap.ExtraEnums;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.ext.Generator;
 import com.google.gwt.core.ext.GeneratorContext;
@@ -38,24 +31,32 @@
 import com.google.gwt.editor.rebind.model.ModelUtils;
 import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
 import com.google.gwt.user.rebind.SourceWriter;
+import com.google.web.bindery.autobean.gwt.rebind.model.JBeanMethod;
+import com.google.web.bindery.autobean.shared.AutoBean;
+import com.google.web.bindery.autobean.shared.AutoBean.PropertyName;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory.Category;
+import com.google.web.bindery.autobean.shared.AutoBeanFactory.NoWrap;
+import com.google.web.bindery.autobean.shared.impl.EnumMap.ExtraEnums;
 import com.google.web.bindery.requestfactory.gwt.client.impl.AbstractClientRequestFactory;
 import com.google.web.bindery.requestfactory.gwt.rebind.model.AcceptsModelVisitor;
 import com.google.web.bindery.requestfactory.gwt.rebind.model.ContextMethod;
 import com.google.web.bindery.requestfactory.gwt.rebind.model.EntityProxyModel;
+import com.google.web.bindery.requestfactory.gwt.rebind.model.EntityProxyModel.Type;
 import com.google.web.bindery.requestfactory.gwt.rebind.model.ModelVisitor;
 import com.google.web.bindery.requestfactory.gwt.rebind.model.RequestFactoryModel;
 import com.google.web.bindery.requestfactory.gwt.rebind.model.RequestMethod;
-import com.google.web.bindery.requestfactory.gwt.rebind.model.EntityProxyModel.Type;
 import com.google.web.bindery.requestfactory.shared.EntityProxyId;
 import com.google.web.bindery.requestfactory.shared.JsonRpcContent;
 import com.google.web.bindery.requestfactory.shared.impl.AbstractRequest;
 import com.google.web.bindery.requestfactory.shared.impl.AbstractRequestContext;
+import com.google.web.bindery.requestfactory.shared.impl.AbstractRequestContext.Dialect;
 import com.google.web.bindery.requestfactory.shared.impl.AbstractRequestFactory;
 import com.google.web.bindery.requestfactory.shared.impl.BaseProxyCategory;
 import com.google.web.bindery.requestfactory.shared.impl.EntityProxyCategory;
 import com.google.web.bindery.requestfactory.shared.impl.RequestData;
 import com.google.web.bindery.requestfactory.shared.impl.ValueProxyCategory;
-import com.google.web.bindery.requestfactory.shared.impl.AbstractRequestContext.Dialect;
+import com.google.web.bindery.requestfactory.vm.impl.OperationKey;
 
 import java.io.PrintWriter;
 import java.util.Collection;
@@ -363,14 +364,14 @@
         // makeRequestData()
         sw.println("@Override protected %s makeRequestData() {", RequestData.class
             .getCanonicalName());
-        // return new RequestData("Foo::bar", {parameters}, propertyRefs,
-        // List.class, FooProxy.class);
         String elementType =
             request.isCollectionType() ? request.getCollectionElementType()
                 .getQualifiedSourceName()
                 + ".class" : "null";
         String returnTypeBaseQualifiedName =
             ModelUtils.ensureBaseType(request.getDataType()).getQualifiedSourceName();
+        // return new RequestData("ABC123", {parameters}, propertyRefs,
+        // List.class, FooProxy.class);
         sw.indentln("return new %s(\"%s\", new Object[] {%s}, propertyRefs, %s.class, %s);",
             RequestData.class.getCanonicalName(), operation, parameterArray,
             returnTypeBaseQualifiedName, elementType);
@@ -456,11 +457,11 @@
     sw.indent();
     for (EntityProxyModel type : model.getAllProxyModels()) {
       // tokensToTypes.put("Foo", Foo.class);
-      sw.println("tokensToTypes.put(\"%s\", %s.class);", type.getQualifiedBinaryName(), type
-          .getQualifiedSourceName());
+      sw.println("tokensToTypes.put(\"%s\", %s.class);", OperationKey.hash(type
+          .getQualifiedBinaryName()), type.getQualifiedSourceName());
       // typesToTokens.put(Foo.class, Foo);
-      sw.println("typesToTokens.put(%s.class, \"%s\");", type.getQualifiedSourceName(), type
-          .getQualifiedBinaryName());
+      sw.println("typesToTokens.put(%s.class, \"%s\");", type.getQualifiedSourceName(),
+          OperationKey.hash(type.getQualifiedBinaryName()));
       // fooProxyTypes.add(MyFooProxy.class);
       sw.println("%s.add(%s.class);", type.getType().equals(Type.ENTITY) ? "entityProxyTypes"
           : "valueProxyTypes", type.getQualifiedSourceName());
@@ -469,6 +470,9 @@
     sw.println("}");
 
     // Write instance methods
+    sw.println("@Override public String getFactoryTypeToken() {");
+    sw.indentln("return \"%s\";", model.getFactoryType().getQualifiedBinaryName());
+    sw.println("}");
     sw.println("@Override protected Class getTypeFromToken(String typeToken) {");
     sw.indentln("return tokensToTypes.get(typeToken);");
     sw.println("}");
diff --git a/user/src/com/google/web/bindery/requestfactory/gwt/rebind/model/RequestFactoryModel.java b/user/src/com/google/web/bindery/requestfactory/gwt/rebind/model/RequestFactoryModel.java
index 6af1dbb..dc9a23c 100644
--- a/user/src/com/google/web/bindery/requestfactory/gwt/rebind/model/RequestFactoryModel.java
+++ b/user/src/com/google/web/bindery/requestfactory/gwt/rebind/model/RequestFactoryModel.java
@@ -206,7 +206,7 @@
       }
 
       RequestMethod.Builder methodBuilder = new RequestMethod.Builder();
-      methodBuilder.setDeclarationMethod(method);
+      methodBuilder.setDeclarationMethod(contextType, method);
 
       if (!validateContextMethodAndSetDataType(methodBuilder, method,
           jsonRpcAnnotation != null)) {
@@ -272,7 +272,7 @@
           continue;
         }
         RequestMethod.Builder methodBuilder = new RequestMethod.Builder();
-        methodBuilder.setDeclarationMethod(method);
+        methodBuilder.setDeclarationMethod(entityProxyType, method);
 
         JType transportedType;
         String name = method.getName();
diff --git a/user/src/com/google/web/bindery/requestfactory/gwt/rebind/model/RequestMethod.java b/user/src/com/google/web/bindery/requestfactory/gwt/rebind/model/RequestMethod.java
index ec5ef6d..6a1c596 100644
--- a/user/src/com/google/web/bindery/requestfactory/gwt/rebind/model/RequestMethod.java
+++ b/user/src/com/google/web/bindery/requestfactory/gwt/rebind/model/RequestMethod.java
@@ -17,7 +17,9 @@
 
 import com.google.gwt.core.ext.typeinfo.JClassType;
 import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.web.bindery.requestfactory.shared.JsonRpcWireName;
+import com.google.web.bindery.requestfactory.vm.impl.OperationKey;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -68,15 +70,22 @@
       toReturn.dataType = dataType;
     }
 
-    public void setDeclarationMethod(JMethod declarationMethod) {
+    public void setDeclarationMethod(JClassType contextType, JMethod declarationMethod) {
       toReturn.declarationMethod = declarationMethod;
 
       JClassType returnClass = declarationMethod.getReturnType().isClassOrInterface();
       JsonRpcWireName annotation = returnClass == null ? null
           : returnClass.getAnnotation(JsonRpcWireName.class);
       if (annotation == null) {
-        toReturn.operation = declarationMethod.getEnclosingType().getQualifiedBinaryName()
-            + "::" + declarationMethod.getName();
+        StringBuilder sb = new StringBuilder("(");
+        for (JType type : declarationMethod.getParameterTypes()) {
+          sb.append(type.getJNISignature());
+        }
+        // Return type ignored
+        sb.append(")V");
+        toReturn.operation =
+            new OperationKey(contextType.getQualifiedBinaryName(), declarationMethod.getName(), sb
+                .toString()).get();
       } else {
         toReturn.operation = annotation.value();
         toReturn.apiVersion = annotation.version();
diff --git a/user/src/com/google/web/bindery/requestfactory/server/Deobfuscator.java b/user/src/com/google/web/bindery/requestfactory/server/Deobfuscator.java
new file mode 100644
index 0000000..683c6a6
--- /dev/null
+++ b/user/src/com/google/web/bindery/requestfactory/server/Deobfuscator.java
@@ -0,0 +1,121 @@
+/*
+ * 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.gwt.dev.asm.Type;
+import com.google.gwt.dev.util.Name.BinaryName;
+import com.google.web.bindery.requestfactory.vm.impl.OperationKey;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Provides access to payload deobfuscation services.
+ */
+class Deobfuscator {
+  public static class Builder {
+    private Deobfuscator d = new Deobfuscator();
+    {
+      d.operationData = new HashMap<OperationKey, OperationData>();
+      d.typeTokens = new HashMap<String, Type>();
+    }
+
+    public Builder addOperation(OperationKey key, OperationData data) {
+      d.operationData.put(key, data);
+      return this;
+    }
+
+    public Builder addOperationData(Map<OperationKey, OperationData> operationData) {
+      d.operationData.putAll(operationData);
+      return this;
+    }
+
+    public Builder addRawTypeToken(String token, String binaryName) {
+      d.typeTokens.put(token, Type.getObjectType(BinaryName.toInternalName(binaryName)));
+      return this;
+    }
+
+    public Builder addRawTypeTokens(Map<String, String> typeTokens) {
+      for (Map.Entry<String, String> entry : typeTokens.entrySet()) {
+        addRawTypeToken(entry.getKey(), entry.getValue());
+      }
+      return this;
+    }
+
+    public Deobfuscator build() {
+      Deobfuscator toReturn = d;
+      toReturn.operationData = Collections.unmodifiableMap(toReturn.operationData);
+      toReturn.typeTokens = Collections.unmodifiableMap(toReturn.typeTokens);
+      d = null;
+      return toReturn;
+    }
+
+    /**
+     * This method should be removed in favor of having a map of RequestFactory
+     * to Deobfuscators in ResolverServiceLayer and getting rid of the static
+     * validator instance.
+     */
+    public Builder setOperationData(Map<OperationKey, OperationData> operationData) {
+      d.operationData = operationData;
+      return this;
+    }
+
+    /**
+     * To be removed as well.
+     */
+    public Builder setTypeTokens(Map<String, Type> typeTokens) {
+      d.typeTokens = typeTokens;
+      return this;
+    }
+  }
+
+  private Map<OperationKey, OperationData> operationData;
+
+  /**
+   * Map of obfuscated ids to binary class names.
+   */
+  private Map<String, Type> typeTokens;
+
+  Deobfuscator() {
+  }
+
+  /**
+   * Returns a method descriptor that should be invoked on the service object.
+   */
+  public String getDomainMethodDescriptor(String operation) {
+    return operationData.get(new OperationKey(operation)).getDomainMethodDescriptor();
+  }
+
+  public String getRequestContext(String operation) {
+    return operationData.get(new OperationKey(operation)).getRequestContext();
+  }
+
+  public String getRequestContextMethodDescriptor(String operation) {
+    return operationData.get(new OperationKey(operation)).getClientMethodDescriptor();
+  }
+
+  public String getRequestContextMethodName(String operation) {
+    return operationData.get(new OperationKey(operation)).getMethodName();
+  }
+
+  /**
+   * Returns a type's binary name based on an obfuscated token.
+   */
+  public String getTypeFromToken(String token) {
+    return typeTokens.get(token).getClassName();
+  }
+}
\ No newline at end of file
diff --git a/user/src/com/google/web/bindery/requestfactory/server/FindServiceLayer.java b/user/src/com/google/web/bindery/requestfactory/server/FindServiceLayer.java
new file mode 100644
index 0000000..e5277ed
--- /dev/null
+++ b/user/src/com/google/web/bindery/requestfactory/server/FindServiceLayer.java
@@ -0,0 +1,72 @@
+/*
+ * 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.server.impl.FindService;
+import com.google.web.bindery.requestfactory.shared.EntityProxyId;
+import com.google.web.bindery.requestfactory.shared.RequestContext;
+import com.google.web.bindery.requestfactory.shared.impl.Constants;
+import com.google.web.bindery.requestfactory.shared.impl.FindRequest;
+
+import java.lang.reflect.Method;
+
+/**
+ * Allows the use of a very short operation name for the find method. This also
+ * avoids the need to introduce special-case code for FindRequest into
+ * RequestFactoryInterfaceValidator.
+ */
+public class FindServiceLayer extends ServiceLayerDecorator {
+
+  @Override
+  public Method resolveDomainMethod(String operation) {
+    if (Constants.FIND_METHOD_OPERATION.equals(operation)) {
+      Throwable ex;
+      try {
+        return FindService.class.getMethod("find", Object.class);
+      } catch (SecurityException e) {
+        ex = e;
+      } catch (NoSuchMethodException e) {
+        ex = e;
+      }
+      die(ex, "Could not retrieve %s.find() method", FindService.class.getCanonicalName());
+    }
+    return super.resolveDomainMethod(operation);
+  }
+
+  @Override
+  public Class<? extends RequestContext> resolveRequestContext(String operation) {
+    if (Constants.FIND_METHOD_OPERATION.equals(operation)) {
+      return FindRequest.class;
+    }
+    return super.resolveRequestContext(operation);
+  }
+
+  @Override
+  public Method resolveRequestContextMethod(String operation) {
+    if (Constants.FIND_METHOD_OPERATION.equals(operation)) {
+      Throwable ex;
+      try {
+        return FindRequest.class.getMethod("find", EntityProxyId.class);
+      } catch (SecurityException e) {
+        ex = e;
+      } catch (NoSuchMethodException e) {
+        ex = e;
+      }
+      die(ex, "Could not retrieve %s.find() method", FindRequest.class.getCanonicalName());
+    }
+    return super.resolveRequestContextMethod(operation);
+  }
+}
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 0dee2b8..1f0dad9 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/LocatorServiceLayer.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/LocatorServiceLayer.java
@@ -49,14 +49,10 @@
   }
 
   @Override
-  public Object createServiceInstance(Method contextMethod, Method domainMethod) {
-    Class<? extends ServiceLocator> locatorType =
-        getTop().resolveServiceLocator(contextMethod, domainMethod);
+  public Object createServiceInstance(Class<? extends RequestContext> requestContext) {
+    Class<? extends ServiceLocator> locatorType = getTop().resolveServiceLocator(requestContext);
     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(requestContext);
     return locator.getInstance(serviceClass);
   }
 
@@ -139,14 +135,12 @@
   }
 
   @Override
-  public Class<? extends ServiceLocator> resolveServiceLocator(Method contextMethod,
-      Method domainMethod) {
+  public Class<? extends ServiceLocator> resolveServiceLocator(
+      Class<? extends RequestContext> requestContext) {
     Class<? extends ServiceLocator> locatorType;
 
-    // Look at the RequestContext
-    Class<?> requestContextClass = contextMethod.getDeclaringClass();
-    Service l = requestContextClass.getAnnotation(Service.class);
-    ServiceName ln = requestContextClass.getAnnotation(ServiceName.class);
+    Service l = requestContext.getAnnotation(Service.class);
+    ServiceName ln = requestContext.getAnnotation(ServiceName.class);
     if (l != null && !ServiceLocator.class.equals(l.locator())) {
       locatorType = l.locator();
     } else if (ln != null && ln.locator().length() > 0) {
diff --git a/user/src/com/google/web/bindery/requestfactory/server/OperationData.java b/user/src/com/google/web/bindery/requestfactory/server/OperationData.java
new file mode 100644
index 0000000..7e10fb9
--- /dev/null
+++ b/user/src/com/google/web/bindery/requestfactory/server/OperationData.java
@@ -0,0 +1,96 @@
+/*
+ * 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.gwt.dev.asm.Type;
+import com.google.gwt.dev.asm.commons.Method;
+
+/**
+ * Describes operations that the client may ask the server to perform.
+ */
+class OperationData {
+  /**
+   * Creates {@link OperationData} instances.
+   */
+  public static class Builder {
+    OperationData d = new OperationData();
+
+    public OperationData build() {
+      OperationData toReturn = d;
+      d = null;
+
+      if (toReturn.clientMethodDescriptor != null) {
+        // Strip return types
+        Method noReturn =
+            new Method(toReturn.methodName, Type.VOID_TYPE, Type
+                .getArgumentTypes(toReturn.clientMethodDescriptor));
+        toReturn.clientMethodDescriptor = noReturn.getDescriptor();
+      }
+      if (toReturn.domainMethodDescriptor != null) {
+        Method noReturn =
+            new Method(toReturn.methodName, Type.VOID_TYPE, Type
+                .getArgumentTypes(toReturn.domainMethodDescriptor));
+        toReturn.domainMethodDescriptor = noReturn.getDescriptor();
+      }
+
+      return toReturn;
+    }
+
+    public Builder setClientMethodDescriptor(String clientMethodDescriptor) {
+      d.clientMethodDescriptor = clientMethodDescriptor;
+      return this;
+    }
+
+    public Builder setDomainMethodDescriptor(String domainMethodDescriptor) {
+      d.domainMethodDescriptor = domainMethodDescriptor;
+      return this;
+    }
+
+    public Builder setMethodName(String methodName) {
+      d.methodName = methodName;
+      return this;
+    }
+
+    public Builder setRequestContext(String requestContext) {
+      d.requestContextBinaryName = requestContext;
+      return this;
+    }
+  }
+
+  private String clientMethodDescriptor;
+  private String domainMethodDescriptor;
+  private String methodName;
+  private String requestContextBinaryName;
+
+  OperationData() {
+  }
+
+  public String getClientMethodDescriptor() {
+    return clientMethodDescriptor;
+  }
+
+  public String getDomainMethodDescriptor() {
+    return domainMethodDescriptor;
+  }
+
+  public String getMethodName() {
+    return methodName;
+  }
+
+  public String getRequestContext() {
+    return requestContextBinaryName;
+  }
+}
\ No newline at end of file
diff --git a/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryInterfaceValidator.java b/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryInterfaceValidator.java
index 4aa5bab..b0933bc 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryInterfaceValidator.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryInterfaceValidator.java
@@ -15,7 +15,6 @@
  */
 package com.google.web.bindery.requestfactory.server;
 
-import com.google.web.bindery.autobean.shared.ValueCodex;
 import com.google.gwt.dev.asm.AnnotationVisitor;
 import com.google.gwt.dev.asm.ClassReader;
 import com.google.gwt.dev.asm.ClassVisitor;
@@ -29,6 +28,7 @@
 import com.google.gwt.dev.util.Name;
 import com.google.gwt.dev.util.Name.BinaryName;
 import com.google.gwt.dev.util.Name.SourceOrBinaryName;
+import com.google.web.bindery.autobean.shared.ValueCodex;
 import com.google.web.bindery.requestfactory.shared.BaseProxy;
 import com.google.web.bindery.requestfactory.shared.EntityProxy;
 import com.google.web.bindery.requestfactory.shared.InstanceRequest;
@@ -41,6 +41,7 @@
 import com.google.web.bindery.requestfactory.shared.ServiceName;
 import com.google.web.bindery.requestfactory.shared.SkipInterfaceValidation;
 import com.google.web.bindery.requestfactory.shared.ValueProxy;
+import com.google.web.bindery.requestfactory.vm.impl.OperationKey;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -649,19 +650,30 @@
    */
   private final Map<Type, Set<RFMethod>> methodsInHierarchy = new HashMap<Type, Set<RFMethod>>();
   /**
+   * Used to resolve obfuscated type tokens.
+   */
+  private final Map<String, Type> typeTokens = new HashMap<String, Type>();
+  /**
    * The type {@link Object}.
    */
   private final Type objectType = Type.getObjectType("java/lang/Object");
+  /**
+   * Maps obfuscated operation names to dispatch information.
+   */
+  private final Map<OperationKey, OperationData> operationData =
+      new HashMap<OperationKey, OperationData>();
   private final ErrorContext parentLogger;
   private boolean poisoned;
   /**
    * The type {@link Request}.
    */
   private final Type requestIntf = Type.getType(Request.class);
+
   /**
    * The type {@link RequestContext}.
    */
   private final Type requestContextIntf = Type.getType(RequestContext.class);
+
   /**
    * A map of a type to all types that it could be assigned to.
    */
@@ -716,6 +728,11 @@
     poisoned = false;
   }
 
+  public Deobfuscator getDeobfuscator() {
+    return new Deobfuscator.Builder().setOperationData(operationData).setTypeTokens(typeTokens)
+        .build();
+  }
+
   /**
    * Returns true if validation failed.
    */
@@ -829,8 +846,17 @@
       }
 
       // Check the client method against the domain
-      checkClientMethodInDomain(logger, method, domainServiceType, !clientToLocatorMap
-          .containsKey(requestContextType));
+      Method found =
+          checkClientMethodInDomain(logger, method, domainServiceType, !clientToLocatorMap
+              .containsKey(requestContextType));
+      if (found != null) {
+        OperationKey key = new OperationKey(binaryName, method.getName(), method.getDescriptor());
+        OperationData data =
+            new OperationData.Builder().setClientMethodDescriptor(method.getDescriptor())
+                .setDomainMethodDescriptor(found.getDescriptor()).setMethodName(method.getName())
+                .setRequestContext(requestContextType.getClassName()).build();
+        operationData.put(key, data);
+      }
       maybeCheckReferredProxies(logger, method);
     }
 
@@ -981,7 +1007,7 @@
    * Check that a given method RequestContext method declaration can be mapped
    * to the server's domain type.
    */
-  private void checkClientMethodInDomain(ErrorContext logger, RFMethod method,
+  private RFMethod checkClientMethodInDomain(ErrorContext logger, RFMethod method,
       Type domainServiceType, boolean requireStaticMethodsForRequestType) {
     logger = logger.setMethod(method);
 
@@ -1007,6 +1033,7 @@
             + " service method is not static", method.getName(), Request.class.getCanonicalName());
       }
     }
+    return found;
   }
 
   /**
@@ -1215,7 +1242,7 @@
    */
   private RFMethod findCompatibleServiceMethod(final ErrorContext logger, Type domainType,
       Method searchFor, boolean mustFind) {
-    return findCompatibleMethod(logger, domainType, searchFor, mustFind, false, true);
+    return findCompatibleMethod(logger, domainType, searchFor, mustFind, true, true);
   }
 
   /**
@@ -1473,6 +1500,7 @@
     }
 
     Type proxyType = Type.getObjectType(BinaryName.toInternalName(binaryName));
+    typeTokens.put(OperationKey.hash(binaryName), proxyType);
     ErrorContext logger = parentLogger.setType(proxyType);
 
     // Quick sanity check for calling code
diff --git a/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryJarExtractor.java b/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryJarExtractor.java
index bbb33b4..34d03ba 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryJarExtractor.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/RequestFactoryJarExtractor.java
@@ -31,6 +31,7 @@
 import com.google.gwt.dev.util.Name;
 import com.google.gwt.dev.util.Util;
 import com.google.web.bindery.event.shared.SimpleEventBus;
+import com.google.web.bindery.requestfactory.apt.RfApt;
 import com.google.web.bindery.requestfactory.server.RequestFactoryInterfaceValidator.ClassLoaderLoader;
 import com.google.web.bindery.requestfactory.server.RequestFactoryInterfaceValidator.ErrorContext;
 import com.google.web.bindery.requestfactory.server.RequestFactoryInterfaceValidator.Loader;
@@ -62,12 +63,15 @@
 import com.google.web.bindery.requestfactory.shared.ValueProxy;
 import com.google.web.bindery.requestfactory.shared.WriteOperation;
 import com.google.web.bindery.requestfactory.vm.RequestFactorySource;
+import com.google.web.bindery.requestfactory.vm.impl.TypeTokenResolver;
 
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -90,6 +94,8 @@
 import java.util.logging.Logger;
 import java.util.zip.ZipEntry;
 
+import javax.annotation.processing.Processor;
+
 /**
  * Used to extract RequestFactory client jars from {@code gwt-user.jar}.
  */
@@ -310,6 +316,24 @@
     }
   }
 
+  private class EmitOneResource implements Callable<Void> {
+    private final byte[] contents;
+    private final String path;
+
+    private EmitOneResource(String path, byte[] contents) {
+      this.path = path;
+      this.contents = contents;
+    }
+
+    @Override
+    public Void call() throws Exception {
+      if (mode.isEmitClasses()) {
+        emitter.emit(path, new ByteArrayInputStream(contents));
+      }
+      return null;
+    }
+  }
+
   /**
    * A unit of work to write one class and its source file into the Emitter.
    */
@@ -624,11 +648,13 @@
 
     List<Class<?>> clientClasses = new ArrayList<Class<?>>();
     clientClasses.addAll(sharedClasses);
+    clientClasses.add(RfApt.class);
 
     List<Class<?>> serverClasses = new ArrayList<Class<?>>();
     serverClasses.addAll(Arrays.<Class<?>> asList(SERVER_CLASSES));
     serverClasses.addAll(sharedClasses);
 
+    SEEDS.put("apt", Collections.unmodifiableList(Arrays.<Class<?>> asList(RfApt.class)));
     SEEDS.put("client", Collections.unmodifiableList(clientClasses));
     SEEDS.put("server", Collections.unmodifiableList(serverClasses));
 
@@ -671,17 +697,39 @@
       System.err.println("Unknown target: " + target);
       System.exit(1);
     }
+    Map<String, byte[]> resources = createResources(target, seeds);
     Mode mode = Mode.match(target);
     Logger errorContext = Logger.getLogger(RequestFactoryJarExtractor.class.getName());
     ClassLoaderLoader classLoader =
         new ClassLoaderLoader(Thread.currentThread().getContextClassLoader());
     JarEmitter jarEmitter = new JarEmitter(new File(args[1]));
     RequestFactoryJarExtractor extractor =
-        new RequestFactoryJarExtractor(errorContext, classLoader, jarEmitter, seeds, mode);
+        new RequestFactoryJarExtractor(errorContext, classLoader, jarEmitter, seeds, resources,
+            mode);
     extractor.run();
     System.exit(extractor.isExecutionFailed() ? 1 : 0);
   }
 
+  private static Map<String, byte[]> createResources(String target, List<Class<?>> seeds)
+      throws UnsupportedEncodingException, IOException {
+    Map<String, byte[]> resources;
+    if (seeds.contains(RfApt.class)) {
+      // Add the annotation processor manifest
+      resources =
+          Collections.singletonMap("META-INF/services/" + Processor.class.getCanonicalName(),
+              RfApt.class.getCanonicalName().getBytes("UTF-8"));
+    } else if (("test" + CODE_AND_SOURCE).equals(target)) {
+      // Combine all type token maps to run tests
+      ByteArrayOutputStream out = new ByteArrayOutputStream();
+      TypeTokenResolver resolver = TypeTokenResolver.loadFromClasspath();
+      resolver.store(out);
+      resources = Collections.singletonMap(TypeTokenResolver.TOKEN_MANIFEST, out.toByteArray());
+    } else {
+      resources = Collections.emptyMap();
+    }
+    return resources;
+  }
+
   /**
    * Given a Type, return a path-prefix based on the type's package.
    */
@@ -731,16 +779,18 @@
   private final ErrorContext logger;
   private final Loader loader;
   private final Mode mode;
+  private final Map<String, byte[]> resources;
   private final List<Class<?>> seeds;
   private final Map<Type, Type> seen = new ConcurrentHashMap<Type, Type>();
   private final Set<String> sources = new ConcurrentSkipListSet<String>();
   private final ExecutorService writerService;
 
   public RequestFactoryJarExtractor(Logger logger, Loader loader, Emitter emitter,
-      List<Class<?>> seeds, Mode mode) {
+      List<Class<?>> seeds, Map<String, byte[]> resources, Mode mode) {
     this.logger = new ErrorContext(logger);
     this.loader = loader;
     this.emitter = emitter;
+    this.resources = resources;
     this.seeds = seeds;
     this.mode = mode;
 
@@ -756,6 +806,9 @@
     for (Class<?> seed : seeds) {
       processType("seeds", Type.getType(seed));
     }
+    for (Map.Entry<String, byte[]> entry : resources.entrySet()) {
+      writerService.submit(new EmitOneResource(entry.getKey(), entry.getValue()));
+    }
     // Wait for all tasks to be completed
     while (!inProcess.isEmpty()) {
       try {
diff --git a/user/src/com/google/web/bindery/requestfactory/server/ResolverServiceLayer.java b/user/src/com/google/web/bindery/requestfactory/server/ResolverServiceLayer.java
index f95e896..bc4767f 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/ResolverServiceLayer.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/ResolverServiceLayer.java
@@ -15,16 +15,16 @@
  */
 package com.google.web.bindery.requestfactory.server;
 
+import com.google.gwt.dev.asm.Type;
 import com.google.web.bindery.autobean.vm.impl.TypeUtils;
 import com.google.web.bindery.requestfactory.shared.BaseProxy;
-import com.google.web.bindery.requestfactory.shared.EntityProxy;
-import com.google.web.bindery.requestfactory.shared.EntityProxyId;
 import com.google.web.bindery.requestfactory.shared.ProxyFor;
 import com.google.web.bindery.requestfactory.shared.ProxyForName;
 import com.google.web.bindery.requestfactory.shared.RequestContext;
+import com.google.web.bindery.requestfactory.shared.RequestFactory;
 import com.google.web.bindery.requestfactory.shared.Service;
 import com.google.web.bindery.requestfactory.shared.ServiceName;
-import com.google.web.bindery.requestfactory.shared.ValueProxy;
+import com.google.web.bindery.requestfactory.vm.impl.OperationKey;
 
 import java.lang.reflect.Method;
 import java.util.List;
@@ -47,6 +47,7 @@
       new RequestFactoryInterfaceValidator(log,
           new RequestFactoryInterfaceValidator.ClassLoaderLoader(ServiceLayer.class
               .getClassLoader()));
+  private static final Deobfuscator deobfuscator = validator.getDeobfuscator();
 
   @Override
   public ClassLoader getDomainClassLoader() {
@@ -55,19 +56,16 @@
 
   @Override
   public Class<? extends BaseProxy> resolveClass(String typeToken) {
-    Class<?> found = forName(typeToken);
-    if (!EntityProxy.class.isAssignableFrom(found) && !ValueProxy.class.isAssignableFrom(found)) {
-      die(null, "The requested type %s is not assignable to %s or %s", typeToken, EntityProxy.class
-          .getCanonicalName(), ValueProxy.class.getCanonicalName());
-    }
+    String deobfuscated;
     synchronized (validator) {
-      validator.antidote();
-      validator.validateProxy(found.getName());
-      if (validator.isPoisoned()) {
-        die(null, "The type %s did not pass RequestFactory validation", found.getCanonicalName());
-      }
+      deobfuscated = deobfuscator.getTypeFromToken(typeToken);
     }
-    return found.asSubclass(BaseProxy.class);
+
+    if (deobfuscated == null) {
+      die(null, "No type for token %s", typeToken);
+    }
+
+    return forName(deobfuscated).asSubclass(BaseProxy.class);
   }
 
   @Override
@@ -116,60 +114,88 @@
   }
 
   @Override
-  public Method resolveDomainMethod(Method requestContextMethod) {
-    Class<?> declaringClass = requestContextMethod.getDeclaringClass();
-    Class<?> searchIn =
-        getTop().resolveServiceClass(declaringClass.asSubclass(RequestContext.class));
-    Class<?>[] parameterTypes = requestContextMethod.getParameterTypes();
-    Class<?>[] domainArgs = new Class<?>[parameterTypes.length];
-    for (int i = 0, j = domainArgs.length; i < j; i++) {
-      if (BaseProxy.class.isAssignableFrom(parameterTypes[i])) {
-        domainArgs[i] = getTop().resolveDomainClass(parameterTypes[i].asSubclass(BaseProxy.class));
-      } else if (EntityProxyId.class.isAssignableFrom(parameterTypes[i])) {
-        domainArgs[i] =
-            TypeUtils.ensureBaseType(TypeUtils.getSingleParameterization(EntityProxyId.class,
-                requestContextMethod.getGenericParameterTypes()[i]));
-      } else {
-        domainArgs[i] = parameterTypes[i];
-      }
+  public Method resolveDomainMethod(String operation) {
+    /*
+     * The validator has already determined the mapping from the RequsetContext
+     * method to a domain method signature. We'll reuse this calculation instead
+     * of iterating over all methods.
+     */
+    String domainDescriptor;
+    synchronized (validator) {
+      domainDescriptor = deobfuscator.getDomainMethodDescriptor(operation);
     }
 
+    if (domainDescriptor == null) {
+      return die(null, "No domain method descriptor is mapped to operation %s", operation);
+    }
+
+    Class<?>[] domainArgs = getArgumentTypes(domainDescriptor);
+    Class<? extends RequestContext> requestContext = getTop().resolveRequestContext(operation);
+    Class<?> serviceImplementation = getTop().resolveServiceClass(requestContext);
+
+    // Request<FooProxy> someMethod(int a, double b, FooProxy c);
+    Method requestContextMethod = getTop().resolveRequestContextMethod(operation);
+
     Throwable ex;
     try {
-      return searchIn.getMethod(requestContextMethod.getName(), domainArgs);
+      return serviceImplementation.getMethod(requestContextMethod.getName(), domainArgs);
     } catch (SecurityException e) {
       ex = e;
     } catch (NoSuchMethodException e) {
-      return report("Could not locate domain method %s", requestContextMethod.getName());
+      ex = e;
     }
-    return die(ex, "Could not get domain method %s in type %s", requestContextMethod.getName(),
-        searchIn.getCanonicalName());
+
+    return die(ex,
+        "Could not find method in implementation %s matching descriptor %s for operation %s",
+        serviceImplementation.getCanonicalName(), domainDescriptor, operation);
   }
 
   @Override
-  public Method resolveRequestContextMethod(String requestContextClass, String methodName) {
+  public Class<? extends RequestContext> resolveRequestContext(String operation) {
+    String requestContextClass;
+    synchronized (validator) {
+      requestContextClass = deobfuscator.getRequestContext(operation);
+    }
+    if (requestContextClass == null) {
+      die(null, "No RequestContext for operation %s", operation);
+    }
+    return forName(requestContextClass).asSubclass(RequestContext.class);
+  }
+
+  @Override
+  public Method resolveRequestContextMethod(String operation) {
+    Class<?> searchIn = getTop().resolveRequestContext(operation);
+    String methodName;
+    String descriptor;
+    synchronized (validator) {
+      methodName = deobfuscator.getRequestContextMethodName(operation);
+      descriptor = deobfuscator.getRequestContextMethodDescriptor(operation);
+    }
+    Class<?>[] params = getArgumentTypes(descriptor);
+    try {
+      return searchIn.getMethod(methodName, params);
+    } catch (NoSuchMethodException ex) {
+      return report("Could not locate %s operation %s", RequestContext.class.getSimpleName(),
+          operation);
+    }
+  }
+
+  @Override
+  public Class<? extends RequestFactory> resolveRequestFactory(String binaryName) {
     synchronized (validator) {
       validator.antidote();
-      validator.validateRequestContext(requestContextClass);
+      validator.validateRequestFactory(binaryName);
       if (validator.isPoisoned()) {
-        die(null, "The RequestContext type %s did not pass validation", requestContextClass);
+        die(null, "The RequestFactory %s did not pass validation", binaryName);
       }
     }
-    Class<?> searchIn = forName(requestContextClass);
-    for (Method method : searchIn.getMethods()) {
-      if (method.getName().equals(methodName)) {
-        return method;
-      }
-    }
-    return report("Could not locate %s method %s::%s", RequestContext.class.getSimpleName(),
-        requestContextClass, methodName);
+    return forName(binaryName).asSubclass(RequestFactory.class);
   }
 
   @Override
   public Class<?> resolveServiceClass(Class<? extends RequestContext> requestContextClass) {
     Class<?> searchIn = null;
     Service s = requestContextClass.getAnnotation(Service.class);
-    // TODO Handle case when both annotations are present
     if (s != null) {
       searchIn = s.value();
     }
@@ -186,7 +212,7 @@
 
   @Override
   public String resolveTypeToken(Class<? extends BaseProxy> clazz) {
-    return clazz.getName();
+    return OperationKey.hash(clazz.getName());
   }
 
   /**
@@ -200,4 +226,43 @@
       return die(e, "Could not locate class %s", name);
     }
   }
+
+  private Class<?>[] getArgumentTypes(String descriptor) {
+    Type[] types = Type.getArgumentTypes(descriptor);
+    Class<?>[] params = new Class<?>[types.length];
+    for (int i = 0, j = types.length; i < j; i++) {
+      params[i] = getClass(types[i]);
+    }
+    return params;
+  }
+
+  private Class<?> getClass(Type type) {
+    switch (type.getSort()) {
+      case Type.BOOLEAN:
+        return boolean.class;
+      case Type.BYTE:
+        return byte.class;
+      case Type.CHAR:
+        return char.class;
+      case Type.DOUBLE:
+        return double.class;
+      case Type.FLOAT:
+        return float.class;
+      case Type.INT:
+        return int.class;
+      case Type.LONG:
+        return long.class;
+      case Type.OBJECT:
+        return forName(type.getClassName());
+      case Type.SHORT:
+        return short.class;
+      case Type.VOID:
+        return void.class;
+      case Type.ARRAY:
+        return die(null, "Unsupported Type used in operation descriptor %s", type.getDescriptor());
+      default:
+        // Error in this switch statement
+        return die(null, "Unhandled Type: %s", type.getDescriptor());
+    }
+  }
 }
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 07d8226..6b7b978 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/ServiceLayer.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/ServiceLayer.java
@@ -18,6 +18,7 @@
 import com.google.web.bindery.requestfactory.shared.BaseProxy;
 import com.google.web.bindery.requestfactory.shared.Locator;
 import com.google.web.bindery.requestfactory.shared.RequestContext;
+import com.google.web.bindery.requestfactory.shared.RequestFactory;
 import com.google.web.bindery.requestfactory.shared.ServiceLocator;
 
 import java.lang.reflect.Method;
@@ -72,6 +73,8 @@
     list.add(new LocatorServiceLayer());
     // Interact with domain objects
     list.add(new ReflectiveServiceLayer());
+    // Add shortcut for find's operation
+    list.add(new FindServiceLayer());
     // Locate domain objects
     list.add(new ResolverServiceLayer());
 
@@ -121,11 +124,11 @@
    * Create an instance of a service object that can be used as the target for
    * the given method invocation.
    * 
-   * @param contextMethod a method defined in a RequestContext
-   * @param domainMethod the method that the service object must implement
+   * @param requestContext the RequestContext type for which a service object
+   *          must be instantiated.
    * @return an instance of the requested service object
    */
-  public abstract Object createServiceInstance(Method contextMethod, Method domainMethod);
+  public abstract Object createServiceInstance(Class<? extends RequestContext> requestContext);
 
   /**
    * Create an instance of the requested {@link ServiceLocator} type.
@@ -314,10 +317,13 @@
    * declaration. The {@code requestContextMethod} will have been previously
    * resolved by {@link #resolveRequestContextMethod(String, String)}.
    * 
-   * @param requestContextMethod a RequestContext method declaration.
+   * @param requestContext the RequestContext requested by the client
+   * @param requestContextMethod a RequestContext method declaration. Note that
+   *          this Method may be defined in a supertype of
+   *          {@code requestContext}
    * @return the domain service method that should be invoked
    */
-  public abstract Method resolveDomainMethod(Method requestContextMethod);
+  public abstract Method resolveDomainMethod(String operation);
 
   /**
    * Return the type of {@link Locator} that should be used to access the given
@@ -330,20 +336,38 @@
   public abstract Class<? extends Locator<?, ?>> resolveLocator(Class<?> domainType);
 
   /**
+   * Find a RequestContext that should be used to fulfill the requested
+   * operation.
+   * 
+   * @param operation the operation
+   * @return the RequestContext or {@code null} if no RequestContext exists that
+   *         can fulfill the operation
+   */
+  public abstract Class<? extends RequestContext> resolveRequestContext(String operation);
+
+  /**
    * Find a RequestContext method declaration by name.
    * 
-   * @param requestContextClass the fully-qualified binary name of the
-   *          RequestContext
-   * @param methodName the name of the service method declared within the
-   *          RequestContext
+   * @param operation the operation's name
    * @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 operation);
+
+  /**
+   * Loads and validates a RequestFactory interface.
+   * 
+   * @param token the RequestFactory's type token (usually the type's binary
+   *          name)
+   * @return the RequestFactory type
+   */
+  public abstract Class<? extends RequestFactory> resolveRequestFactory(String token);
 
   /**
    * Given a {@link RequestContext} method, find the service class referenced in
-   * the {@link Service} or {@link ServiceName} annotation.
+   * the {@link com.google.web.bindery.requestfactory.shared.Service Service} or
+   * {@link com.google.web.bindery.requestfactory.shared.ServiceName
+   * ServiceName} annotation.
    * 
    * @param requestContextClass a RequestContext interface
    * @return the type of service to use
@@ -357,12 +381,12 @@
    * {@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
+   * @param requestContext the RequestContext for which a ServiceLocator must be
+   *          located
    * @return the type of ServiceLocator to use
    */
-  public abstract Class<? extends ServiceLocator> resolveServiceLocator(Method contextMethod,
-      Method domainMethod);
+  public abstract Class<? extends ServiceLocator> resolveServiceLocator(
+      Class<? extends RequestContext> requestContext);
 
   /**
    * Return a string used to represent the given type in the wire protocol.
diff --git a/user/src/com/google/web/bindery/requestfactory/server/ServiceLayerCache.java b/user/src/com/google/web/bindery/requestfactory/server/ServiceLayerCache.java
index 4d8ac6e..277f9ab 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/ServiceLayerCache.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/ServiceLayerCache.java
@@ -18,6 +18,8 @@
 import com.google.gwt.rpc.server.Pair;
 import com.google.web.bindery.requestfactory.shared.BaseProxy;
 import com.google.web.bindery.requestfactory.shared.Locator;
+import com.google.web.bindery.requestfactory.shared.RequestContext;
+import com.google.web.bindery.requestfactory.shared.RequestFactory;
 import com.google.web.bindery.requestfactory.shared.ServiceLocator;
 
 import java.lang.ref.SoftReference;
@@ -55,13 +57,16 @@
   private static final Method resolveDomainClass;
   private static final Method resolveDomainMethod;
   private static final Method resolveLocator;
+  private static final Method resolveRequestContext;
   private static final Method resolveRequestContextMethod;
+  private static final Method resolveRequestFactory;
+  private static final Method resolveServiceClass;
   private static final Method resolveServiceLocator;
   private static final Method resolveTypeToken;
 
   static {
     createLocator = getMethod("createLocator", Class.class);
-    createServiceInstance = getMethod("createServiceInstance", Method.class, Method.class);
+    createServiceInstance = getMethod("createServiceInstance", Class.class);
     getDomainClassLoader = getMethod("getDomainClassLoader");
     getGetter = getMethod("getGetter", Class.class, String.class);
     getIdType = getMethod("getIdType", Class.class);
@@ -71,11 +76,13 @@
     resolveClass = getMethod("resolveClass", String.class);
     resolveClientType = getMethod("resolveClientType", Class.class, Class.class, boolean.class);
     resolveDomainClass = getMethod("resolveDomainClass", Class.class);
-    resolveDomainMethod = getMethod("resolveDomainMethod", Method.class);
+    resolveDomainMethod = getMethod("resolveDomainMethod", String.class);
     resolveLocator = getMethod("resolveLocator", Class.class);
-    resolveRequestContextMethod =
-        getMethod("resolveRequestContextMethod", String.class, String.class);
-    resolveServiceLocator = getMethod("resolveServiceLocator", Method.class, Method.class);
+    resolveRequestContext = getMethod("resolveRequestContext", String.class);
+    resolveRequestContextMethod = getMethod("resolveRequestContextMethod", String.class);
+    resolveRequestFactory = getMethod("resolveRequestFactory", String.class);
+    resolveServiceClass = getMethod("resolveServiceClass", Class.class);
+    resolveServiceLocator = getMethod("resolveServiceLocator", Class.class);
     resolveTypeToken = getMethod("resolveTypeToken", Class.class);
   }
 
@@ -106,9 +113,8 @@
   }
 
   @Override
-  public Object createServiceInstance(Method contextMethod, Method domainMethod) {
-    return getOrCache(createServiceInstance, new Pair<Method, Method>(contextMethod, domainMethod),
-        Object.class, contextMethod, domainMethod);
+  public Object createServiceInstance(Class<? extends RequestContext> requestContext) {
+    return getOrCache(createServiceInstance, requestContext, Object.class, requestContext);
   }
 
   @Override
@@ -166,8 +172,8 @@
   }
 
   @Override
-  public Method resolveDomainMethod(Method requestContextMethod) {
-    return getOrCache(resolveDomainMethod, requestContextMethod, Method.class, requestContextMethod);
+  public Method resolveDomainMethod(String operation) {
+    return getOrCache(resolveDomainMethod, operation, Method.class, operation);
   }
 
   @Override
@@ -177,17 +183,31 @@
   }
 
   @Override
-  public Method resolveRequestContextMethod(String requestContextClass, String methodName) {
-    return getOrCache(resolveRequestContextMethod, new Pair<String, String>(requestContextClass,
-        methodName), Method.class, requestContextClass, methodName);
+  public Class<? extends RequestContext> resolveRequestContext(String operation) {
+    Class<?> clazz = getOrCache(resolveRequestContext, operation, Class.class, operation);
+    return clazz.asSubclass(RequestContext.class);
   }
 
   @Override
-  public Class<? extends ServiceLocator> resolveServiceLocator(Method contextMethod,
-      Method domainMethod) {
-    Class<?> clazz =
-        getOrCache(resolveServiceLocator, new Pair<Method, Method>(contextMethod, domainMethod),
-            Class.class, contextMethod, domainMethod);
+  public Method resolveRequestContextMethod(String operation) {
+    return getOrCache(resolveRequestContextMethod, operation, Method.class, operation);
+  }
+
+  @Override
+  public Class<? extends RequestFactory> resolveRequestFactory(String binaryName) {
+    Class<?> clazz = getOrCache(resolveRequestFactory, binaryName, Class.class, binaryName);
+    return clazz.asSubclass(RequestFactory.class);
+  }
+
+  @Override
+  public Class<?> resolveServiceClass(Class<? extends RequestContext> requestContextClass) {
+    return getOrCache(resolveServiceClass, requestContextClass, Class.class, requestContextClass);
+  }
+
+  @Override
+  public Class<? extends ServiceLocator> resolveServiceLocator(
+      Class<? extends RequestContext> requestContext) {
+    Class<?> clazz = getOrCache(resolveServiceLocator, requestContext, Class.class, requestContext);
     return clazz == null ? null : clazz.asSubclass(ServiceLocator.class);
   }
 
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 f6c8311..942d7de 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/ServiceLayerDecorator.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/ServiceLayerDecorator.java
@@ -18,6 +18,7 @@
 import com.google.web.bindery.requestfactory.shared.BaseProxy;
 import com.google.web.bindery.requestfactory.shared.Locator;
 import com.google.web.bindery.requestfactory.shared.RequestContext;
+import com.google.web.bindery.requestfactory.shared.RequestFactory;
 import com.google.web.bindery.requestfactory.shared.ServiceLocator;
 
 import java.lang.reflect.InvocationTargetException;
@@ -56,8 +57,8 @@
   }
 
   @Override
-  public Object createServiceInstance(Method contextMethod, Method domainMethod) {
-    return getNext().createServiceInstance(contextMethod, domainMethod);
+  public Object createServiceInstance(Class<? extends RequestContext> requestContext) {
+    return getNext().createServiceInstance(requestContext);
   }
 
   @Override
@@ -147,8 +148,8 @@
   }
 
   @Override
-  public Method resolveDomainMethod(Method requestContextMethod) {
-    return getNext().resolveDomainMethod(requestContextMethod);
+  public Method resolveDomainMethod(String operation) {
+    return getNext().resolveDomainMethod(operation);
   }
 
   @Override
@@ -157,8 +158,18 @@
   }
 
   @Override
-  public Method resolveRequestContextMethod(String requestContextClass, String methodName) {
-    return getNext().resolveRequestContextMethod(requestContextClass, methodName);
+  public Class<? extends RequestContext> resolveRequestContext(String operation) {
+    return getNext().resolveRequestContext(operation);
+  }
+
+  @Override
+  public Method resolveRequestContextMethod(String operation) {
+    return getNext().resolveRequestContextMethod(operation);
+  }
+
+  @Override
+  public Class<? extends RequestFactory> resolveRequestFactory(String binaryName) {
+    return getNext().resolveRequestFactory(binaryName);
   }
 
   @Override
@@ -167,9 +178,9 @@
   }
 
   @Override
-  public Class<? extends ServiceLocator> resolveServiceLocator(Method contextMethod,
-      Method domainMethod) {
-    return getNext().resolveServiceLocator(contextMethod, domainMethod);
+  public Class<? extends ServiceLocator> resolveServiceLocator(
+      Class<? extends RequestContext> requestContext) {
+    return getNext().resolveServiceLocator(requestContext);
   }
 
   @Override
diff --git a/user/src/com/google/web/bindery/requestfactory/server/SimpleRequestProcessor.java b/user/src/com/google/web/bindery/requestfactory/server/SimpleRequestProcessor.java
index d998dbe..0df5c46 100644
--- a/user/src/com/google/web/bindery/requestfactory/server/SimpleRequestProcessor.java
+++ b/user/src/com/google/web/bindery/requestfactory/server/SimpleRequestProcessor.java
@@ -29,6 +29,7 @@
 import com.google.web.bindery.requestfactory.shared.EntityProxyId;
 import com.google.web.bindery.requestfactory.shared.InstanceRequest;
 import com.google.web.bindery.requestfactory.shared.Request;
+import com.google.web.bindery.requestfactory.shared.RequestContext;
 import com.google.web.bindery.requestfactory.shared.ServerFailure;
 import com.google.web.bindery.requestfactory.shared.WriteOperation;
 import com.google.web.bindery.requestfactory.shared.impl.BaseProxyCategory;
@@ -195,6 +196,10 @@
    */
   void process(RequestMessage req, ResponseMessage resp) {
     final RequestState source = new RequestState(service);
+
+    // Make sure the RequestFactory is valid
+    service.resolveRequestFactory(req.getRequestFactory());
+
     // Apply operations
     processOperationMessages(source, req);
 
@@ -414,13 +419,13 @@
     for (InvocationMessage invocation : invocations) {
       try {
         // Find the Method
-        String[] operation = invocation.getOperation().split("::");
-        Method contextMethod = service.resolveRequestContextMethod(operation[0], operation[1]);
+        String operation = invocation.getOperation();
+        Method contextMethod = service.resolveRequestContextMethod(operation);
         if (contextMethod == null) {
           throw new UnexpectedException("Cannot resolve operation " + invocation.getOperation(),
               null);
         }
-        Method domainMethod = service.resolveDomainMethod(contextMethod);
+        Method domainMethod = service.resolveDomainMethod(operation);
         if (domainMethod == null) {
           throw new UnexpectedException(
               "Cannot resolve domain method " + invocation.getOperation(), null);
@@ -430,7 +435,8 @@
         List<Object> args = decodeInvocationArguments(state, invocation, contextMethod);
         // Possibly use a ServiceLocator
         if (service.requiresServiceLocator(contextMethod, domainMethod)) {
-          Object serviceInstance = service.createServiceInstance(contextMethod, domainMethod);
+          Class<? extends RequestContext> requestContext = service.resolveRequestContext(operation);
+          Object serviceInstance = service.createServiceInstance(requestContext);
           args.add(0, serviceInstance);
         }
         // Invoke it
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/ProxyFor.java b/user/src/com/google/web/bindery/requestfactory/shared/ProxyFor.java
index 77dc4c2..317935b 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/ProxyFor.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/ProxyFor.java
@@ -38,5 +38,6 @@
    * An optional {@link Locator} that provides instances of the domain objects.
    */
   @SuppressWarnings("rawtypes")
-  Class<? extends Locator> locator() default Locator.class;
+  // http://bugs.sun.com/view_bug.do?bug_id=6512707
+  Class<? extends Locator> locator() default com.google.web.bindery.requestfactory.shared.Locator.class;
 }
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/Service.java b/user/src/com/google/web/bindery/requestfactory/shared/Service.java
index 27f8b5a..70be615 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/Service.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/Service.java
@@ -40,5 +40,6 @@
    * objects used when invoking instance methods on the type returned by
    * {@link #value()}.
    */
-  Class<? extends ServiceLocator> locator() default ServiceLocator.class;
+  // http://bugs.sun.com/view_bug.do?bug_id=6512707
+  Class<? extends ServiceLocator> locator() default com.google.web.bindery.requestfactory.shared.ServiceLocator.class;
 }
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestContext.java b/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestContext.java
index bbbec60..912c62b 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestContext.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestContext.java
@@ -291,6 +291,7 @@
       // Create the outer envelope message
       AutoBean<RequestMessage> bean = f.request();
       RequestMessage requestMessage = bean.as();
+      requestMessage.setRequestFactory(getRequestFactory().getFactoryTypeToken());
       if (!invocationMessages.isEmpty()) {
         requestMessage.setInvocations(invocationMessages);
       }
@@ -531,8 +532,7 @@
       @Override
       protected RequestData makeRequestData() {
         // This method is normally generated, hence the ugly constructor
-        return new RequestData(
-            "com.google.web.bindery.requestfactory.shared.impl.FindRequest::find",
+        return new RequestData(Constants.FIND_METHOD_OPERATION,
             new Object[] {proxyId}, propertyRefs, proxyId.getProxyClass(), null);
       }
     };
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestFactory.java b/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestFactory.java
index 502a700..74db7ae 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestFactory.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/impl/AbstractRequestFactory.java
@@ -66,6 +66,12 @@
     return eventBus;
   }
 
+  /**
+   * Returns a type token for the RequestFactory instance, which is used to seed
+   * operation and type token resolution on the server.
+   */
+  public abstract String getFactoryTypeToken();
+
   public String getHistoryToken(Class<? extends EntityProxy> clazz) {
     return getTypeToken(clazz);
   }
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/impl/Constants.java b/user/src/com/google/web/bindery/requestfactory/shared/impl/Constants.java
index 279bc86..4787191 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/impl/Constants.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/impl/Constants.java
@@ -20,6 +20,7 @@
  */
 public interface Constants {
   String DOMAIN_OBJECT = "domainObject";
+  String FIND_METHOD_OPERATION = "?";
   String IN_RESPONSE = "inResponse";
   String PARENT_OBJECT = "parentObject";
   String REQUEST_CONTEXT_STATE = "requestContext";
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/messages/RequestMessage.java b/user/src/com/google/web/bindery/requestfactory/shared/messages/RequestMessage.java
index 8f35588..544a60a 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/messages/RequestMessage.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/messages/RequestMessage.java
@@ -23,6 +23,7 @@
  * The message sent from the client to the server.
  */
 public interface RequestMessage extends VersionedMessage {
+  String FACTORY = "F";
   String INVOCATION = "I";
   String OPERATIONS = "O";
 
@@ -32,9 +33,15 @@
   @PropertyName(OPERATIONS)
   List<OperationMessage> getOperations();
 
+  @PropertyName(FACTORY)
+  String getRequestFactory();
+
   @PropertyName(INVOCATION)
   void setInvocations(List<InvocationMessage> value);
 
   @PropertyName(OPERATIONS)
   void setOperations(List<OperationMessage> value);
+
+  @PropertyName(FACTORY)
+  void setRequestFactory(String value);
 }
diff --git a/user/src/com/google/web/bindery/requestfactory/vm/InProcessRequestContext.java b/user/src/com/google/web/bindery/requestfactory/vm/InProcessRequestContext.java
index d912119..111e77f 100644
--- a/user/src/com/google/web/bindery/requestfactory/vm/InProcessRequestContext.java
+++ b/user/src/com/google/web/bindery/requestfactory/vm/InProcessRequestContext.java
@@ -28,6 +28,7 @@
 import com.google.web.bindery.requestfactory.shared.impl.AbstractRequestContext;
 import com.google.web.bindery.requestfactory.shared.impl.AbstractRequestFactory;
 import com.google.web.bindery.requestfactory.shared.impl.RequestData;
+import com.google.web.bindery.requestfactory.vm.impl.OperationKey;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.InvocationHandler;
@@ -94,9 +95,16 @@
 
       final RequestData data;
       if (dialect.equals(Dialect.STANDARD)) {
-        String operation = method.getDeclaringClass().getName() + "::" + method.getName();
+        StringBuilder descriptor = new StringBuilder("(");
+        for (Class<?> param : method.getParameterTypes()) {
+          descriptor.append(com.google.gwt.dev.asm.Type.getDescriptor(param));
+        }
+        // Don't care about the return type
+        descriptor.append(")V");
+        OperationKey operation =
+            new OperationKey(context.getName(), method.getName(), descriptor.toString());
 
-        data = new RequestData(operation, actualArgs, returnType, elementType);
+        data = new RequestData(operation.get(), actualArgs, returnType, elementType);
       } else {
         // Calculate request metadata
         JsonRpcWireName wireInfo = method.getReturnType().getAnnotation(JsonRpcWireName.class);
@@ -172,10 +180,13 @@
   }
 
   static final Object[] NO_ARGS = new Object[0];
+  private final Class<? extends RequestContext> context;
   private final Dialect dialect;
 
-  protected InProcessRequestContext(AbstractRequestFactory factory, Dialect dialect) {
+  protected InProcessRequestContext(AbstractRequestFactory factory, Dialect dialect,
+      Class<? extends RequestContext> context) {
     super(factory, dialect);
+    this.context = context;
     this.dialect = dialect;
   }
 
diff --git a/user/src/com/google/web/bindery/requestfactory/vm/InProcessRequestFactory.java b/user/src/com/google/web/bindery/requestfactory/vm/InProcessRequestFactory.java
index 78a844c..da49296 100644
--- a/user/src/com/google/web/bindery/requestfactory/vm/InProcessRequestFactory.java
+++ b/user/src/com/google/web/bindery/requestfactory/vm/InProcessRequestFactory.java
@@ -16,8 +16,6 @@
 package com.google.web.bindery.requestfactory.vm;
 
 import com.google.web.bindery.autobean.shared.AutoBeanFactory;
-import com.google.web.bindery.autobean.shared.AutoBeanFactory.Category;
-import com.google.web.bindery.autobean.shared.AutoBeanFactory.NoWrap;
 import com.google.web.bindery.autobean.vm.AutoBeanFactorySource;
 import com.google.web.bindery.event.shared.EventBus;
 import com.google.web.bindery.requestfactory.shared.BaseProxy;
@@ -33,7 +31,10 @@
 import com.google.web.bindery.requestfactory.shared.impl.EntityProxyCategory;
 import com.google.web.bindery.requestfactory.shared.impl.ValueProxyCategory;
 import com.google.web.bindery.requestfactory.vm.InProcessRequestContext.RequestContextHandler;
+import com.google.web.bindery.requestfactory.vm.impl.OperationKey;
+import com.google.web.bindery.requestfactory.vm.impl.TypeTokenResolver;
 
+import java.io.IOException;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -43,8 +44,9 @@
  * A JRE-compatible implementation of RequestFactory.
  */
 class InProcessRequestFactory extends AbstractRequestFactory {
-  @Category(value = {EntityProxyCategory.class, ValueProxyCategory.class, BaseProxyCategory.class})
-  @NoWrap(EntityProxyId.class)
+  @AutoBeanFactory.Category(value = {
+      EntityProxyCategory.class, ValueProxyCategory.class, BaseProxyCategory.class})
+  @AutoBeanFactory.NoWrap(EntityProxyId.class)
   interface Factory extends AutoBeanFactory {
   }
 
@@ -65,12 +67,29 @@
           method.getReturnType().isAnnotationPresent(JsonRpcService.class) ? Dialect.JSON_RPC
               : Dialect.STANDARD;
       RequestContextHandler handler =
-          new InProcessRequestContext(InProcessRequestFactory.this, dialect).new RequestContextHandler();
+          new InProcessRequestContext(InProcessRequestFactory.this, dialect, context).new RequestContextHandler();
       return context.cast(Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
           new Class<?>[] {context}, handler));
     }
   }
 
+  private final Class<? extends RequestFactory> requestFactoryInterface;
+  private final TypeTokenResolver deobfuscator;
+
+  public InProcessRequestFactory(Class<? extends RequestFactory> requestFactoryInterface) {
+    this.requestFactoryInterface = requestFactoryInterface;
+    try {
+      deobfuscator = TypeTokenResolver.loadFromClasspath();
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  @Override
+  public String getFactoryTypeToken() {
+    return requestFactoryInterface.getName();
+  }
+
   @Override
   public void initialize(EventBus eventBus) {
     throw new UnsupportedOperationException("An explicit RequestTransport must be provided");
@@ -94,9 +113,13 @@
   @Override
   @SuppressWarnings("unchecked")
   protected <P extends BaseProxy> Class<P> getTypeFromToken(String typeToken) {
+    String deobfuscated = deobfuscator.getTypeFromToken(typeToken);
+    if (deobfuscated == null) {
+      throw new RuntimeException("Did not have deobfuscation data for " + typeToken);
+    }
     try {
       Class<? extends BaseProxy> found =
-          Class.forName(typeToken, false, Thread.currentThread().getContextClassLoader())
+          Class.forName(deobfuscated, false, Thread.currentThread().getContextClassLoader())
               .asSubclass(BaseProxy.class);
       return (Class<P>) found;
     } catch (ClassNotFoundException e) {
@@ -106,6 +129,6 @@
 
   @Override
   protected String getTypeToken(Class<? extends BaseProxy> clazz) {
-    return isEntityType(clazz) || isValueType(clazz) ? clazz.getName() : null;
+    return isEntityType(clazz) || isValueType(clazz) ? OperationKey.hash(clazz.getName()) : null;
   }
 }
diff --git a/user/src/com/google/web/bindery/requestfactory/vm/RequestFactorySource.java b/user/src/com/google/web/bindery/requestfactory/vm/RequestFactorySource.java
index e893d46..bc4e146 100644
--- a/user/src/com/google/web/bindery/requestfactory/vm/RequestFactorySource.java
+++ b/user/src/com/google/web/bindery/requestfactory/vm/RequestFactorySource.java
@@ -40,7 +40,8 @@
    * @see InProcessRequestTransport
    */
   public static <T extends RequestFactory> T create(Class<T> requestFactory) {
-    RequestFactoryHandler handler = new InProcessRequestFactory().new RequestFactoryHandler();
+    RequestFactoryHandler handler =
+        new InProcessRequestFactory(requestFactory).new RequestFactoryHandler();
     return requestFactory.cast(Proxy.newProxyInstance(Thread.currentThread()
         .getContextClassLoader(), new Class<?>[] {requestFactory}, handler));
   }
diff --git a/user/src/com/google/web/bindery/requestfactory/vm/impl/OperationKey.java b/user/src/com/google/web/bindery/requestfactory/vm/impl/OperationKey.java
new file mode 100644
index 0000000..87b9d2c
--- /dev/null
+++ b/user/src/com/google/web/bindery/requestfactory/vm/impl/OperationKey.java
@@ -0,0 +1,71 @@
+/*
+ * 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.vm.impl;
+
+import com.google.gwt.dev.asm.Type;
+import com.google.gwt.dev.asm.commons.Method;
+import com.google.gwt.dev.util.StringKey;
+import com.google.gwt.user.server.Base64Utils;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Utility class for encoding RequestFactory operation ids.
+ */
+public class OperationKey extends StringKey {
+  /**
+   * The expected length of values returned from {@link #hash(String)}.
+   */
+  public static final int HASH_LENGTH = 28;
+
+  static {
+    assert HASH_LENGTH == hash("").length();
+  }
+
+  /**
+   * Compute a base64-encoded SHA1 hash of the input string's UTF8
+   * representation. The result is a {@value #HASH_LENGTH}-character sequence.
+   */
+  public static String hash(String raw) {
+    try {
+      MessageDigest md = MessageDigest.getInstance("SHA1");
+      byte[] data = md.digest(raw.getBytes("UTF-8"));
+      return Base64Utils.toBase64(data);
+    } catch (NoSuchAlgorithmException e) {
+      throw new RuntimeException("No MD5 algorithm", e);
+    } catch (UnsupportedEncodingException e) {
+      throw new RuntimeException("No UTF-8", e);
+    }
+  }
+
+  private static String key(String requestContextBinaryName, String methodName, String descriptor) {
+    Method m = new Method(methodName, Type.VOID_TYPE, Type.getArgumentTypes(descriptor));
+    String raw = requestContextBinaryName + "::" + methodName + m.getDescriptor();
+    return raw.length() >= HASH_LENGTH ? hash(raw) : raw;
+  }
+
+  public OperationKey(String encoded) {
+    super(encoded);
+    assert encoded.length() == HASH_LENGTH : "Expecting only " + HASH_LENGTH
+        + " characters, received " + encoded.length();
+  }
+
+  public OperationKey(String requestContextBinaryName, String methodName, String descriptor) {
+    super(key(requestContextBinaryName, methodName, descriptor));
+  }
+}
diff --git a/user/src/com/google/web/bindery/requestfactory/vm/impl/TypeTokenResolver.java b/user/src/com/google/web/bindery/requestfactory/vm/impl/TypeTokenResolver.java
new file mode 100644
index 0000000..96caf85
--- /dev/null
+++ b/user/src/com/google/web/bindery/requestfactory/vm/impl/TypeTokenResolver.java
@@ -0,0 +1,106 @@
+/*
+ * 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.vm.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Resolves payload type tokens to binary class names.
+ */
+public class TypeTokenResolver {
+  /**
+   * Constructs {@link TypeTokenResolver} instances.
+   */
+  public static class Builder {
+    private TypeTokenResolver d = new TypeTokenResolver();
+
+    /**
+     * To be removed as well.
+     */
+    public Builder addTypeToken(String token, String binaryName) {
+      d.typeTokens.put(token, binaryName);
+      return this;
+    }
+
+    public TypeTokenResolver build() {
+      TypeTokenResolver toReturn = d;
+      toReturn.typeTokens = Collections.unmodifiableMap(toReturn.typeTokens);
+      d = null;
+      return toReturn;
+    }
+
+    /**
+     * Adds data from a single InputStream to the state accumulated by the
+     * Builder.
+     */
+    public void load(InputStream in) throws IOException {
+      Properties props = new Properties();
+      props.load(in);
+      for (Map.Entry<Object, Object> entry : props.entrySet()) {
+        addTypeToken(entry.getKey().toString(), entry.getValue().toString());
+      }
+      in.close();
+    }
+  }
+
+  public static final String TOKEN_MANIFEST = "META-INF/requestFactory/typeTokens";
+
+  /**
+   * Loads all resources on path {@value #TOKEN_MANIFEST} and creates a
+   * TypeTokenResolver.
+   */
+  public static TypeTokenResolver loadFromClasspath() throws IOException {
+    Builder builder = new Builder();
+    Enumeration<URL> locations =
+        Thread.currentThread().getContextClassLoader().getResources(TOKEN_MANIFEST);
+    if (!locations.hasMoreElements()) {
+      throw new RuntimeException("No token manifest found.  Did the RequestFactory annotation"
+          + " processor run? Check classpath for " + TOKEN_MANIFEST + " file and ensure that"
+          + " your proxy types are compiled with the requestfactory-apt.jar on javac's classpath.");
+    }
+    while (locations.hasMoreElements()) {
+      builder.load(locations.nextElement().openStream());
+    }
+    return builder.build();
+  }
+
+  /**
+   * Map of obfuscated ids to binary class names.
+   */
+  private Map<String, String> typeTokens = new HashMap<String, String>();
+
+  public String getTypeFromToken(String typeToken) {
+    return typeTokens.get(typeToken);
+  }
+
+  /**
+   * Closes the OutputStream.
+   */
+  public void store(OutputStream out) throws IOException {
+    Properties props = new Properties();
+    props.putAll(typeTokens);
+    props.store(out, "GENERATED FILE -- DO NOT EDIT");
+    out.close();
+  }
+}
diff --git a/user/test/com/google/gwt/dev/jjs/test/AnnotationsTest.java b/user/test/com/google/gwt/dev/jjs/test/AnnotationsTest.java
index 0fa15ed..2095807 100644
--- a/user/test/com/google/gwt/dev/jjs/test/AnnotationsTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/AnnotationsTest.java
@@ -43,9 +43,11 @@
       FOO1, FOO2
     }
 
-    NestedEnum value() default NestedEnum.FOO1;
+    // http://bugs.sun.com/view_bug.do?bug_id=6512707
+    NestedEnum value() default com.google.gwt.dev.jjs.test.AnnotationsTest.IFoo.NestedEnum.FOO1;
 
-    Class<? extends NestedEnum> valueClass() default NestedEnum.class;
+    // http://bugs.sun.com/view_bug.do?bug_id=6512707
+    Class<? extends NestedEnum> valueClass() default com.google.gwt.dev.jjs.test.AnnotationsTest.IFoo.NestedEnum.class;
   }
 
   public String getModuleName() {
diff --git a/user/test/com/google/web/bindery/requestfactory/gwt/client/RequestFactoryTest.java b/user/test/com/google/web/bindery/requestfactory/gwt/client/RequestFactoryTest.java
index 3cb88f3..00cc001 100644
--- a/user/test/com/google/web/bindery/requestfactory/gwt/client/RequestFactoryTest.java
+++ b/user/test/com/google/web/bindery/requestfactory/gwt/client/RequestFactoryTest.java
@@ -1121,7 +1121,7 @@
    */
   public void testNullValueInIntegerListRequest() {
     delayTestFinish(DELAY_TEST_FINISH);
-    List<Integer> list = Arrays.asList(new Integer[] {1, 2, null});
+    List<Integer> list = Arrays.asList(new Integer[]{1, 2, null});
     final Request<Void> fooReq = req.simpleFooRequest().receiveNullValueInIntegerList(list);
     fooReq.fire(new Receiver<Void>() {
       @Override
@@ -1136,7 +1136,7 @@
    */
   public void testNullValueInStringListRequest() {
     delayTestFinish(DELAY_TEST_FINISH);
-    List<String> list = Arrays.asList(new String[] {"nonnull", "null", null});
+    List<String> list = Arrays.asList(new String[]{"nonnull", "null", null});
     final Request<Void> fooReq = req.simpleFooRequest().receiveNullValueInStringList(list);
     fooReq.fire(new Receiver<Void>() {
       @Override
@@ -2000,10 +2000,22 @@
 
   public void testPrimitiveParameter() {
     delayTestFinish(DELAY_TEST_FINISH);
-    simpleFooRequest().add(3, 5).fire(new Receiver<Integer>() {
+    SimpleFooRequest ctx = simpleFooRequest();
+    ctx.add(3, 5).to(new Receiver<Integer>() {
       @Override
       public void onSuccess(Integer response) {
         assertTrue(8 == response);
+      }
+    });
+    ctx.add(4.0, 5.0).to(new Receiver<Double>() {
+      @Override
+      public void onSuccess(Double response) {
+        assertTrue(9.0 == response);
+      }
+    });
+    ctx.fire(new Receiver<Void>() {
+      @Override
+      public void onSuccess(Void response) {
         finishTestAndReset();
       }
     });
@@ -2639,7 +2651,7 @@
             assertEquals(value, v.getRootBean());
             assertEquals(value, v.getLeafBean());
             assertEquals("shouldBeNull", v.getPropertyPath().toString());
-            
+
             // Forward to onViolation()
             super.onConstraintViolation(errors);
           }
diff --git a/user/test/com/google/web/bindery/requestfactory/server/RequestFactoryJreTest.java b/user/test/com/google/web/bindery/requestfactory/server/RequestFactoryJreTest.java
index d17b608..d954fe9 100644
--- a/user/test/com/google/web/bindery/requestfactory/server/RequestFactoryJreTest.java
+++ b/user/test/com/google/web/bindery/requestfactory/server/RequestFactoryJreTest.java
@@ -19,9 +19,16 @@
 import com.google.web.bindery.event.shared.SimpleEventBus;
 import com.google.web.bindery.requestfactory.gwt.client.RequestFactoryTest;
 import com.google.web.bindery.requestfactory.server.testing.InProcessRequestTransport;
+import com.google.web.bindery.requestfactory.shared.BaseProxy;
 import com.google.web.bindery.requestfactory.shared.RequestFactory;
+import com.google.web.bindery.requestfactory.shared.SimpleBarProxy;
+import com.google.web.bindery.requestfactory.shared.SimpleFooProxy;
 import com.google.web.bindery.requestfactory.shared.SimpleRequestFactory;
 import com.google.web.bindery.requestfactory.vm.RequestFactorySource;
+import com.google.web.bindery.requestfactory.vm.impl.OperationKey;
+import com.google.web.bindery.requestfactory.vm.impl.TypeTokenResolver;
+
+import java.io.IOException;
 
 /**
  * Runs the RequestFactory tests in-process.
@@ -42,8 +49,20 @@
     return null;
   }
 
+  public void testTypeTokenResolver() throws IOException {
+    TypeTokenResolver resolver = TypeTokenResolver.loadFromClasspath();
+    testResolver(resolver, SimpleBarProxy.class);
+    testResolver(resolver, SimpleFooProxy.class);
+  }
+
   @Override
   protected SimpleRequestFactory createFactory() {
     return createInProcess(SimpleRequestFactory.class);
   }
+
+  private void testResolver(TypeTokenResolver resolver, Class<? extends BaseProxy> type) {
+    String token = OperationKey.hash(type.getName());
+    String typeName = resolver.getTypeFromToken(token);
+    assertEquals(type.getName(), typeName);
+  }
 }
diff --git a/user/test/com/google/web/bindery/requestfactory/server/SimpleFoo.java b/user/test/com/google/web/bindery/requestfactory/server/SimpleFoo.java
index 1f3e61e..46ce807 100644
--- a/user/test/com/google/web/bindery/requestfactory/server/SimpleFoo.java
+++ b/user/test/com/google/web/bindery/requestfactory/server/SimpleFoo.java
@@ -46,7 +46,11 @@
 
   private static Long nextId = 1L;
 
-  public static Integer add(Integer a, int b) {
+  public static Double add(double a, double b) {
+    return a + b;
+  }
+
+  public static Integer add(int a, int b) {
     return a + b;
   }
 
diff --git a/user/test/com/google/web/bindery/requestfactory/shared/ComplexKeysTest.java b/user/test/com/google/web/bindery/requestfactory/shared/ComplexKeysTest.java
index 8c43a16..3cb9f82 100644
--- a/user/test/com/google/web/bindery/requestfactory/shared/ComplexKeysTest.java
+++ b/user/test/com/google/web/bindery/requestfactory/shared/ComplexKeysTest.java
@@ -23,7 +23,6 @@
  * Tests the use of non-trivial EntityProxy and ValueProxy key types.
  */
 public class ComplexKeysTest extends GWTTestCase {
-
   /**
    * The factory being tested.
    */
diff --git a/user/test/com/google/web/bindery/requestfactory/shared/ServiceInheritanceTest.java b/user/test/com/google/web/bindery/requestfactory/shared/ServiceInheritanceTest.java
index ee604b9..c6ea72a 100644
--- a/user/test/com/google/web/bindery/requestfactory/shared/ServiceInheritanceTest.java
+++ b/user/test/com/google/web/bindery/requestfactory/shared/ServiceInheritanceTest.java
@@ -20,8 +20,7 @@
 import com.google.web.bindery.event.shared.SimpleEventBus;
 
 /**
- * Tests the ability of instance services to inherit methods
- * from a base class.
+ * Tests the ability of instance services to inherit methods from a base class.
  */
 public class ServiceInheritanceTest extends GWTTestCase {
 
@@ -45,15 +44,22 @@
    */
   protected interface Factory extends RequestFactory {
     SumServiceBase baseContext();
+
     SumServiceSub subContext();
   }
 
   /**
+   * Demonstrate that mix-in interfaces work correctly.
+   */
+  interface HasAdd {
+    Request<Integer> add(int n);
+  }
+
+  /**
    * Specifies the base class implementation.
    */
   @Service(value = BaseImpl.class, locator = SumServiceLocator.class)
-  interface SumServiceBase extends RequestContext {
-    Request<Integer> add(int n);
+  interface SumServiceBase extends RequestContext, HasAdd {
     Request<Integer> subtract(int n);
   }
 
@@ -61,9 +67,7 @@
    * Specifies the subclass implementation.
    */
   @Service(value = SubclassImpl.class, locator = SumServiceLocator.class)
-  interface SumServiceSub extends RequestContext {
-    Request<Integer> add(int n);
-    Request<Integer> subtract(int n);
+  interface SumServiceSub extends SumServiceBase {
   }
 
   /**
@@ -79,15 +83,14 @@
     public Integer add(int n) {
       return initialValue + n;
     }
-    
+
     public Integer subtract(int n) {
       return initialValue - n;
     }
   }
 
   /**
-   * Subclass implementation of {@link SumServiceSub}
-   * inherits the add() method.
+   * Subclass implementation of {@link SumServiceSub} inherits the add() method.
    */
   static class SubclassImpl extends BaseImpl {
     public SubclassImpl() {
@@ -97,7 +100,7 @@
        */
       initialValue = 8;
     }
-    
+
     @Override
     public Integer subtract(int n) {
       return 0;
diff --git a/user/test/com/google/web/bindery/requestfactory/shared/SimpleFooRequest.java b/user/test/com/google/web/bindery/requestfactory/shared/SimpleFooRequest.java
index ba305a2..ebc02c0 100644
--- a/user/test/com/google/web/bindery/requestfactory/shared/SimpleFooRequest.java
+++ b/user/test/com/google/web/bindery/requestfactory/shared/SimpleFooRequest.java
@@ -26,7 +26,10 @@
  */
 @Service(com.google.web.bindery.requestfactory.server.SimpleFoo.class)
 public interface SimpleFooRequest extends RequestContext {
-  Request<Integer> add(Integer a, int b);
+  Request<Double> add(double a, double b);
+
+  // Test overloaded method names
+  Request<Integer> add(int a, int b);
 
   Request<Long> countSimpleFoo();
 
diff --git a/user/test/com/google/web/bindery/requestfactory/shared/SimpleRequestFactory.java b/user/test/com/google/web/bindery/requestfactory/shared/SimpleRequestFactory.java
index 63944e2..74f4b0d 100644
--- a/user/test/com/google/web/bindery/requestfactory/shared/SimpleRequestFactory.java
+++ b/user/test/com/google/web/bindery/requestfactory/shared/SimpleRequestFactory.java
@@ -20,7 +20,6 @@
  * UserInformation and Logging services.
  */
 public interface SimpleRequestFactory extends BasicRequestFactory {
-
   InstanceServiceRequest instanceServiceRequest();
 
   InstanceServiceRequestByName instanceServiceRequestByName();