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);