Correctly process @SkipInterfaceValidation on RequestContext methods
Also adds @Retention(RUNTIME) to @ExtraTypes to allow for external,
reflection-based, Deobfuscator.Builder generators.
Change-Id: I346b2b7f5688b765f9b9968d3ef065db4a236915
diff --git a/requestfactory/build.xml b/requestfactory/build.xml
index bbb9b87..89c1340 100755
--- a/requestfactory/build.xml
+++ b/requestfactory/build.xml
@@ -122,6 +122,7 @@
<arg value="com.google.web.bindery.requestfactory.shared.BoxesAndPrimitivesTest.Factory" />
<arg value="com.google.web.bindery.requestfactory.shared.ComplexKeysTest.Factory" />
<arg value="com.google.web.bindery.requestfactory.shared.LocatorTest.Factory" />
+ <arg value="com.google.web.bindery.requestfactory.shared.MethodProvidedByServiceLayerTest.Factory" />
<arg value="com.google.web.bindery.requestfactory.shared.MultipleFactoriesTest.Factory1" />
<arg value="com.google.web.bindery.requestfactory.shared.MultipleFactoriesTest.Factory2" />
<arg value="com.google.web.bindery.requestfactory.shared.ServiceInheritanceTest$Factory" />
diff --git a/user/src/com/google/web/bindery/requestfactory/apt/DeobfuscatorBuilder.java b/user/src/com/google/web/bindery/requestfactory/apt/DeobfuscatorBuilder.java
index e826efd..44cd2be 100644
--- a/user/src/com/google/web/bindery/requestfactory/apt/DeobfuscatorBuilder.java
+++ b/user/src/com/google/web/bindery/requestfactory/apt/DeobfuscatorBuilder.java
@@ -72,6 +72,7 @@
String requestContextBinaryName =
state.elements.getBinaryName(requestContextElement).toString();
String clientMethodDescriptor = x.asType().accept(new DescriptorBuilder(), state);
+ String domainMethodDescriptor = null;
ExecutableElement domainElement = (ExecutableElement) state.getClientToDomainMap().get(x);
if (domainElement == null) {
/*
@@ -80,13 +81,12 @@
* the server by running ValidationTool.
*/
if (state.mustResolveAllAnnotations()) {
- state.poison(requestContextElement, Messages
+ state.poison(x, Messages
.deobfuscatorMissingContext(requestContextElement.getSimpleName()));
}
- return super.visitExecutable(x, state);
+ } else {
+ domainMethodDescriptor = domainElement.asType().accept(new DescriptorBuilder(), state);
}
- String domainMethodDescriptor =
- domainElement.asType().accept(new DescriptorBuilder(), state);
String methodName = x.getSimpleName().toString();
OperationKey key =
@@ -94,7 +94,9 @@
println("withOperation(new OperationKey(\"%s\"),", key.get());
println(" new OperationData.Builder()");
println(" .withClientMethodDescriptor(\"%s\")", clientMethodDescriptor);
- println(" .withDomainMethodDescriptor(\"%s\")", domainMethodDescriptor);
+ if (domainMethodDescriptor != null) {
+ println(" .withDomainMethodDescriptor(\"%s\")", domainMethodDescriptor);
+ }
println(" .withMethodName(\"%s\")", methodName);
println(" .withRequestContext(\"%s\")", requestContextBinaryName);
println(" .build());");
diff --git a/user/src/com/google/web/bindery/requestfactory/apt/DomainChecker.java b/user/src/com/google/web/bindery/requestfactory/apt/DomainChecker.java
index 4fcec55..d0fef2b 100644
--- a/user/src/com/google/web/bindery/requestfactory/apt/DomainChecker.java
+++ b/user/src/com/google/web/bindery/requestfactory/apt/DomainChecker.java
@@ -187,8 +187,13 @@
// Did not find a service method
StringBuilder sb = new StringBuilder();
sb.append(returnType).append(" ").append(name).append("(");
+ boolean first = true;
for (TypeMirror param : lookFor) {
+ if (!first) {
+ sb.append(", ");
+ }
sb.append(param);
+ first = false;
}
sb.append(")");
diff --git a/user/src/com/google/web/bindery/requestfactory/shared/ExtraTypes.java b/user/src/com/google/web/bindery/requestfactory/shared/ExtraTypes.java
index 0273c97..557a5b1 100644
--- a/user/src/com/google/web/bindery/requestfactory/shared/ExtraTypes.java
+++ b/user/src/com/google/web/bindery/requestfactory/shared/ExtraTypes.java
@@ -16,6 +16,8 @@
package com.google.web.bindery.requestfactory.shared;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
@@ -24,6 +26,7 @@
* include additional polymorphic proxy types that are not explicitly
* referenced.
*/
+@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ExtraTypes {
Class<? extends BaseProxy>[] value();
diff --git a/user/test/com/google/web/bindery/requestfactory/gwt/RequestFactorySuite.gwt.xml b/user/test/com/google/web/bindery/requestfactory/gwt/RequestFactorySuite.gwt.xml
index 5ec1e8b..e0fb9c2 100644
--- a/user/test/com/google/web/bindery/requestfactory/gwt/RequestFactorySuite.gwt.xml
+++ b/user/test/com/google/web/bindery/requestfactory/gwt/RequestFactorySuite.gwt.xml
@@ -20,5 +20,5 @@
<inherits name='com.google.gwt.junit.JUnit'/>
<inherits name='com.google.gwt.json.JSON'/>
<servlet path='/gwtRequest'
- class='com.google.web.bindery.requestfactory.server.RequestFactoryServlet' />
+ class='com.google.web.bindery.requestfactory.server.TestRequestFactoryServlet' />
</module>
diff --git a/user/test/com/google/web/bindery/requestfactory/gwt/RequestFactorySuite.java b/user/test/com/google/web/bindery/requestfactory/gwt/RequestFactorySuite.java
index 70dbf61..7d97052 100644
--- a/user/test/com/google/web/bindery/requestfactory/gwt/RequestFactorySuite.java
+++ b/user/test/com/google/web/bindery/requestfactory/gwt/RequestFactorySuite.java
@@ -30,6 +30,7 @@
import com.google.web.bindery.requestfactory.shared.ComplexKeysTest;
import com.google.web.bindery.requestfactory.shared.FanoutReceiverTest;
import com.google.web.bindery.requestfactory.shared.LocatorTest;
+import com.google.web.bindery.requestfactory.shared.MethodProvidedByServiceLayerTest;
import com.google.web.bindery.requestfactory.shared.MultipleFactoriesTest;
import com.google.web.bindery.requestfactory.shared.ServiceInheritanceTest;
import com.google.web.bindery.requestfactory.shared.impl.RequestPayloadTest;
@@ -51,6 +52,7 @@
suite.addTestSuite(FindServiceTest.class);
suite.addTestSuite(JsonRpcRequestFactoryTest.class);
suite.addTestSuite(LocatorTest.class);
+ suite.addTestSuite(MethodProvidedByServiceLayerTest.class);
suite.addTestSuite(MultipleFactoriesTest.class);
suite.addTestSuite(RequestFactoryTest.class);
suite.addTestSuite(RequestFactoryChainedContextTest.class);
diff --git a/user/test/com/google/web/bindery/requestfactory/server/MethodProvidedByServiceLayerJreTest.java b/user/test/com/google/web/bindery/requestfactory/server/MethodProvidedByServiceLayerJreTest.java
new file mode 100644
index 0000000..0113e76
--- /dev/null
+++ b/user/test/com/google/web/bindery/requestfactory/server/MethodProvidedByServiceLayerJreTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2014 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.http.client.Request;
+import com.google.web.bindery.requestfactory.shared.InstanceRequest;
+import com.google.web.bindery.requestfactory.shared.MethodProvidedByServiceLayerTest;
+import com.google.web.bindery.requestfactory.vm.impl.OperationKey;
+
+import java.lang.reflect.Method;
+
+/**
+ * JRE version of {@link MethodProvidedByServiceLayerTest}.
+ */
+public class MethodProvidedByServiceLayerJreTest extends MethodProvidedByServiceLayerTest {
+
+ static class Decorator extends ServiceLayerDecorator {
+ private static final String MISSING_DOMAIN_METHOD;
+ private static final String MISSING_DOMAIN_TYPE;
+ private static final String MISSING_DOMAIN_TYPE_INSTANCE_METHOD;
+
+ private static String getTypeDescriptor(Class<?> clazz) {
+ return "L" + clazz.getName().replace('.', '/') + ";";
+ }
+
+ static {
+ String proxyTypeDescriptor = getTypeDescriptor(Proxy.class);
+ String requestTypeDescriptor = getTypeDescriptor(Request.class);
+ MISSING_DOMAIN_METHOD =
+ new OperationKey(Context.class.getName(), "missingDomainMethod", "("
+ + getTypeDescriptor(String.class) + ")" + requestTypeDescriptor).get();
+ MISSING_DOMAIN_TYPE =
+ new OperationKey(Context.class.getName(), "missingDomainType", "(" + proxyTypeDescriptor
+ + ")" + requestTypeDescriptor).get();
+ MISSING_DOMAIN_TYPE_INSTANCE_METHOD =
+ new OperationKey(Context.class.getName(), "missingDomainTypeInstanceMethod", "()"
+ + getTypeDescriptor(InstanceRequest.class)).get();
+ }
+
+ @Override
+ public Method resolveDomainMethod(String operation) {
+ if (MISSING_DOMAIN_METHOD.equals(operation)) {
+ try {
+ return getClass().getDeclaredMethod("echo", String.class);
+ } catch (NoSuchMethodException e) {
+ return this.die(e, "Cannot find " + getClass().getCanonicalName() + "::echo method");
+ }
+ } else if (MISSING_DOMAIN_TYPE.equals(operation)) {
+ try {
+ return SimpleFoo.class.getDeclaredMethod("echo", SimpleFoo.class);
+ } catch (NoSuchMethodException e) {
+ return this.die(e, "Cannot find " + SimpleFoo.class.getCanonicalName() + "::echo method");
+ }
+ } else if (MISSING_DOMAIN_TYPE_INSTANCE_METHOD.equals(operation)) {
+ try {
+ return SimpleFoo.class.getDeclaredMethod("persistAndReturnSelf");
+ } catch (NoSuchMethodException e) {
+ return this.die(e, "Cannot find " + SimpleFoo.class.getCanonicalName()
+ + "::persistAndReturnSelf method");
+ }
+ }
+ return super.resolveDomainMethod(operation);
+ }
+
+ @Override
+ public Class<?> resolveDomainClass(Class<?> clazz) {
+ if (Proxy.class.equals(clazz)) {
+ return SimpleFoo.class;
+ }
+ return super.resolveDomainClass(clazz);
+ }
+
+ @Override
+ public <T> Class<? extends T> resolveClientType(Class<?> domainClass, Class<T> clientType,
+ boolean required) {
+ if (SimpleFoo.class.equals(domainClass) && Proxy.class.equals(clientType)) {
+ return Proxy.class.asSubclass(clientType);
+ }
+ return super.resolveClientType(domainClass, clientType, required);
+ }
+
+ public static final String echo(String s) {
+ return s;
+ }
+ }
+
+ @Override
+ public String getModuleName() {
+ return null;
+ }
+
+ @Override
+ protected Factory createFactory() {
+ return RequestFactoryJreTest.createInProcess(Factory.class);
+ }
+}
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 04a3685..4d531a2 100644
--- a/user/test/com/google/web/bindery/requestfactory/server/RequestFactoryJreTest.java
+++ b/user/test/com/google/web/bindery/requestfactory/server/RequestFactoryJreTest.java
@@ -44,7 +44,7 @@
throw new RuntimeException(e);
}
} else {
- ServiceLayer serviceLayer = ServiceLayer.create();
+ ServiceLayer serviceLayer = ServiceLayer.create(new MethodProvidedByServiceLayerJreTest.Decorator());
SimpleRequestProcessor processor = new SimpleRequestProcessor(serviceLayer);
req.initialize(eventBus, new InProcessRequestTransport(processor));
}
diff --git a/user/test/com/google/web/bindery/requestfactory/server/TestRequestFactoryServlet.java b/user/test/com/google/web/bindery/requestfactory/server/TestRequestFactoryServlet.java
new file mode 100644
index 0000000..ea75e6f
--- /dev/null
+++ b/user/test/com/google/web/bindery/requestfactory/server/TestRequestFactoryServlet.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 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;
+
+/**
+ * RequestFactoryServlet with special ServiceLayerDecorator(s).
+ */
+public class TestRequestFactoryServlet extends RequestFactoryServlet {
+
+ public TestRequestFactoryServlet() {
+ super(new DefaultExceptionHandler(), new MethodProvidedByServiceLayerJreTest.Decorator());
+ }
+}
diff --git a/user/test/com/google/web/bindery/requestfactory/shared/MethodProvidedByServiceLayerTest.java b/user/test/com/google/web/bindery/requestfactory/shared/MethodProvidedByServiceLayerTest.java
new file mode 100644
index 0000000..894431e
--- /dev/null
+++ b/user/test/com/google/web/bindery/requestfactory/shared/MethodProvidedByServiceLayerTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2014 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.shared;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.web.bindery.event.shared.SimpleEventBus;
+
+/**
+ * Tests advanced usage of RequestFactory where a ServiceLayerDecorator
+ * provides a service method at runtime, skipping interface validation at
+ * compile-time.
+ */
+public class MethodProvidedByServiceLayerTest extends GWTTestCase {
+
+ /** The factory under test. */
+ public interface Factory extends RequestFactory {
+ Context context();
+ }
+
+ /**
+ * RequestContext whose actual server-side methods will be provided
+ * dynamically at runtime by a ServiceLayerDecorator.
+ * <p>
+ * Note: the {@link SkipInterfaceValidation} is put on each method to test
+ * that it's actually looked up at that location (it was searched on the
+ * RequestContext only at some point).
+ */
+ @Service(ServiceImpl.class)
+ public interface Context extends RequestContext {
+ @SkipInterfaceValidation
+ Request<String> missingDomainMethod(String string);
+
+ // mapped to SimpleFoo#echo(SimpleFoo)
+ @SkipInterfaceValidation
+ Request<Proxy> missingDomainType(Proxy proxy);
+
+ // mapped to SimpleFoo#persistAndReturnSelf
+ @SkipInterfaceValidation
+ InstanceRequest<Proxy, Proxy> missingDomainTypeInstanceMethod();
+ }
+
+ /** Proxy for an inexistent domain class; mapped at runtime to SimpleFoo. */
+ @SkipInterfaceValidation
+ @ProxyForName("does.not.exist")
+ public interface Proxy extends EntityProxy {
+ }
+
+ /**
+ * Dummy service for interface validation.
+ * <p>
+ * All actual service methods are provided at runtime by
+ * MethodProvidedByServiceLayerJreTest.Decorator.
+ */
+ public static class ServiceImpl {
+ }
+
+ private static final int TEST_DELAY = 5000;
+
+ private Factory factory;
+
+ @Override
+ public String getModuleName() {
+ return "com.google.web.bindery.requestfactory.gwt.RequestFactorySuite";
+ }
+
+ protected Factory createFactory() {
+ Factory toReturn = GWT.create(Factory.class);
+ toReturn.initialize(new SimpleEventBus());
+ return toReturn;
+ }
+
+ public void testMissingDomainMethod() {
+ delayTestFinish(TEST_DELAY);
+ Context ctx = context();
+ ctx.missingDomainMethod("foo").fire(new Receiver<String>() {
+ @Override
+ public void onSuccess(String response) {
+ assertEquals("foo", response);
+ finishTest();
+ }
+ });
+ }
+
+ public void testMissingDomainType() {
+ delayTestFinish(TEST_DELAY);
+ Context ctx = context();
+ final Proxy proxy = ctx.create(Proxy.class);
+ ctx.missingDomainType(proxy).fire(new Receiver<Proxy>() {
+ @Override
+ public void onSuccess(Proxy response) {
+ // we only check that the call succeeds
+ finishTest();
+ }
+ });
+ }
+
+ public void testMissingDomainTypeInstanceMethod() {
+ delayTestFinish(TEST_DELAY);
+ Context ctx = context();
+ final Proxy proxy = ctx.create(Proxy.class);
+ ctx.missingDomainTypeInstanceMethod().using(proxy).fire(new Receiver<Proxy>() {
+ @Override
+ public void onSuccess(Proxy response) {
+ // we only check that the call succeeds
+ finishTest();
+ }
+ });
+ }
+
+ @Override
+ protected void gwtSetUp() {
+ factory = createFactory();
+ }
+
+ private Context context() {
+ return factory.context();
+ }
+}
diff --git a/user/test/com/google/web/bindery/requestfactory/vm/RequestFactoryJreSuite.java b/user/test/com/google/web/bindery/requestfactory/vm/RequestFactoryJreSuite.java
index 591b92c..c5f80a7 100644
--- a/user/test/com/google/web/bindery/requestfactory/vm/RequestFactoryJreSuite.java
+++ b/user/test/com/google/web/bindery/requestfactory/vm/RequestFactoryJreSuite.java
@@ -21,6 +21,7 @@
import com.google.web.bindery.requestfactory.server.FindServiceJreTest;
import com.google.web.bindery.requestfactory.server.JsonRpcRequestFactoryJreTest;
import com.google.web.bindery.requestfactory.server.LocatorJreTest;
+import com.google.web.bindery.requestfactory.server.MethodProvidedByServiceLayerJreTest;
import com.google.web.bindery.requestfactory.server.MultipleFactoriesJreTest;
import com.google.web.bindery.requestfactory.server.RequestFactoryChainedContextJreTest;
import com.google.web.bindery.requestfactory.server.RequestFactoryExceptionPropagationJreTest;
@@ -50,6 +51,7 @@
suite.addTestSuite(FindServiceJreTest.class);
suite.addTestSuite(JsonRpcRequestFactoryJreTest.class);
suite.addTestSuite(LocatorJreTest.class);
+ suite.addTestSuite(MethodProvidedByServiceLayerJreTest.class);
suite.addTestSuite(MultipleFactoriesJreTest.class);
suite.addTestSuite(RequestFactoryChainedContextJreTest.class);
suite.addTestSuite(RequestFactoryExceptionPropagationJreTest.class);