From 18e3e8b2a1a670f2b59d8a24dcd91d46d9bd96e6 Mon Sep 17 00:00:00 2001 From: Christopher Viel Date: Fri, 28 Nov 2014 13:00:12 -0500 Subject: [PATCH] Re-implement and adapt resource delegate generators as an extension. Summary of squashed commits: Extract part of delegated method generation to velocity + framework. Extracted the complete output of delegated methods to template Added basic root pom to help IDE import Imported client and test code for delegates. Use parameterized name when possible canGenerate() should not throw Generate delegates for sub resources Ensure delegate methods are valid before generating Don't use die() to fake a return value Improved method generator restrictions --- dispatch-rest-delegates/pom.xml | 108 +++++++++++ .../client/AbstractResourceDelegate.java | 95 +++++++++ .../delegates/client/ResourceDelegate.java | 93 +++++++++ .../AbstractDelegatedMethodGenerator.java | 78 ++++++++ .../delegates/rebind/DelegateDefinition.java | 48 +++++ .../rebind/DelegateExtensionGenerator.java | 90 +++++++++ .../delegates/rebind/DelegateGenerator.java | 169 ++++++++++++++++ .../rebind/DelegateMethodDefinition.java | 50 +++++ .../rebind/DelegateMethodGenerator.java | 180 ++++++++++++++++++ .../rest/delegates/rebind/DelegateModule.java | 37 ++++ .../DelegatedActionMethodGenerator.java | 84 ++++++++ .../DelegatedDelegateMethodGenerator.java | 87 +++++++++ .../rebind/DelegatedMethodContext.java | 43 +++++ .../DelegatedSubResourceMethodGenerator.java | 100 ++++++++++ .../test/AbstractDelegateStubber.java | 151 +++++++++++++++ .../rest/delegates/test/DelegateMocking.java | 132 +++++++++++++ .../delegates/test/DelegateTestUtils.java | 63 ++++++ .../test/FailureDelegateStubber.java | 56 ++++++ .../test/SuccessDelegateStubber.java | 57 ++++++ .../rest/delegates/ResourceDelegate.gwt.xml | 9 + .../rest/delegates/rebind/Delegate.vm | 35 ++++ .../rest/delegates/rebind/DelegateMethod.vm | 10 + .../delegates/rebind/DelegatedActionMethod.vm | 7 + .../rebind/DelegatedDelegateMethod.vm | 9 + .../rebind/DelegatedSubResourceMethod.vm | 9 + pom.xml | 20 ++ 26 files changed, 1820 insertions(+) create mode 100644 dispatch-rest-delegates/pom.xml create mode 100644 dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/client/AbstractResourceDelegate.java create mode 100644 dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/client/ResourceDelegate.java create mode 100644 dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/AbstractDelegatedMethodGenerator.java create mode 100644 dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateDefinition.java create mode 100644 dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateExtensionGenerator.java create mode 100644 dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateGenerator.java create mode 100644 dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateMethodDefinition.java create mode 100644 dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateMethodGenerator.java create mode 100644 dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateModule.java create mode 100644 dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedActionMethodGenerator.java create mode 100644 dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedDelegateMethodGenerator.java create mode 100644 dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedMethodContext.java create mode 100644 dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedSubResourceMethodGenerator.java create mode 100644 dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/AbstractDelegateStubber.java create mode 100644 dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/DelegateMocking.java create mode 100644 dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/DelegateTestUtils.java create mode 100644 dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/FailureDelegateStubber.java create mode 100644 dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/SuccessDelegateStubber.java create mode 100644 dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/ResourceDelegate.gwt.xml create mode 100644 dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/Delegate.vm create mode 100644 dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateMethod.vm create mode 100644 dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedActionMethod.vm create mode 100644 dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedDelegateMethod.vm create mode 100644 dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedSubResourceMethod.vm create mode 100644 pom.xml diff --git a/dispatch-rest-delegates/pom.xml b/dispatch-rest-delegates/pom.xml new file mode 100644 index 0000000..da80af1 --- /dev/null +++ b/dispatch-rest-delegates/pom.xml @@ -0,0 +1,108 @@ + + + 4.0.0 + + + org.sonatype.oss + oss-parent + 9 + + + com.gwtplatform.extensions + dispatch-rest-delegates + ${gwtp.version} + jar + Rest-Dispatch Resource Delegates + Allow Rest-Dispatch resources to return their raw result type directly. + 2014 + http://arcbees.github.io/GWTP/ + + + 1.4-SNAPSHOT + 2.7.0 + 1.10.8 + + 2.13 + + + + + com.gwtplatform + gwtp-dispatch-rest + ${gwtp.version} + + + com.google.gwt + gwt-user + ${gwt.version} + + + com.google.gwt + gwt-dev + ${gwt.version} + + + + + org.mockito + mockito-core + ${mockito.version} + compile + true + + + + + + + + src/main/java + + **/*.java + + + + src/main/resources + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${maven-checkstyle-plugin.version} + + + /gwtp-checkstyle.xml + /gwtp-suppressions.xml + /gwtp-checkstyle.properties + basedir=${basedir} + true + true + false + true + + + + + com.gwtplatform + gwtp-build-tools + ${gwtp.version} + + + + + + validate + validate + + checkstyle + + + + + + + diff --git a/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/client/AbstractResourceDelegate.java b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/client/AbstractResourceDelegate.java new file mode 100644 index 0000000..60ad4cc --- /dev/null +++ b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/client/AbstractResourceDelegate.java @@ -0,0 +1,95 @@ +/** + * Copyright 2014 ArcBees 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.gwtplatform.dispatch.rest.delegates.client; + +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.gwtplatform.dispatch.client.DelegatingDispatchRequest; +import com.gwtplatform.dispatch.rest.client.RestDispatch; +import com.gwtplatform.dispatch.rest.shared.RestAction; +import com.gwtplatform.dispatch.shared.DispatchRequest; + +/** + * Common code used by generated implementations of {@link ResourceDelegate}. + * + * @param The resource used by this delegate. + */ +public abstract class AbstractResourceDelegate implements ResourceDelegate, Cloneable { + private static final AsyncCallback NO_OP_CALLBACK = new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + } + + @Override + public void onSuccess(Object result) { + } + }; + + protected final RestDispatch dispatcher; + + protected AsyncCallback callback; + protected DelegatingDispatchRequest delegatingDispatchRequest; + + protected AbstractResourceDelegate(RestDispatch dispatcher) { + this.dispatcher = dispatcher; + } + + @Override + public ResourceDelegate withDelegatingDispatchRequest(DelegatingDispatchRequest delegatingDispatchRequest) { + AbstractResourceDelegate delegate = createCopy(); + delegate.delegatingDispatchRequest = delegatingDispatchRequest; + + return this; + } + + @Override + public T withoutCallback() { + return withCallback(NO_OP_CALLBACK); + } + + @Override + public T withCallback(AsyncCallback callback) { + AbstractResourceDelegate delegate = createCopy(); + delegate.callback = callback; + + return delegate.asResource(); + } + + @SuppressWarnings({"unchecked"}) + protected void execute(RestAction action) { + DispatchRequest dispatchRequest = dispatcher.execute(action, (AsyncCallback) callback); + + if (delegatingDispatchRequest != null) { + delegatingDispatchRequest.setDelegate(dispatchRequest); + } + } + + protected void copyFields(AbstractResourceDelegate delegate) { + delegate.delegatingDispatchRequest = delegatingDispatchRequest; + delegate.callback = callback; + } + + protected abstract AbstractResourceDelegate newInstance(); + + protected abstract T asResource(); + + private AbstractResourceDelegate createCopy() { + AbstractResourceDelegate delegate = newInstance(); + copyFields(delegate); + + return delegate; + } +} diff --git a/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/client/ResourceDelegate.java b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/client/ResourceDelegate.java new file mode 100644 index 0000000..3d47173 --- /dev/null +++ b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/client/ResourceDelegate.java @@ -0,0 +1,93 @@ +/** + * Copyright 2014 ArcBees 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.gwtplatform.dispatch.rest.delegates.client; + +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.gwtplatform.dispatch.client.DelegatingDispatchRequest; +import com.gwtplatform.dispatch.shared.DispatchRequest; + +/** + * Delegate used to build and call HTTP resources. You can inject this interface instead of injecting your resource + * interface and {@link com.gwtplatform.dispatch.rest.client.RestDispatch} to simplify your code. Note that if your + * resource interfaces don't return {@link com.gwtplatform.dispatch.rest.shared.RestAction RestAction<?>}s but the + * result type directly, you will need to inject a {@link ResourceDelegate} to use your resource. + *

+ * This delegate will not send the HTTP request until you call a method, that is not a sub-resource, from the underlying + * resource. The underlying resource is returned when either {@link #withoutCallback()} or {@link + * #withCallback(AsyncCallback)} are called. + *

+ * The following example shows how to retrieve the {@link DispatchRequest} and delete a potential car: + *


+ * {@literal @}Path("/cars")
+ * public interface CarsResource {
+ *      CarResource car(int id);
+ * }
+ * 

+ * public interface CarResource { + * {@literal @}DELETE + * void delete(); + * } + *

+ * public class CarPresenter { + * private final ResourceDelegate<CarsResource> carsResourceDelegate; + *

+ * {@literal @}Inject + * CarPresenter(ResourceDelegate<CarsResource> carsResourceDelegate) { + * this.carsResourceDelegate = carsResourceDelegate; + * } + *

+ * {@literal @}Override + * public void onReveal() { + * DelegatingDispatchRequest dispatchRequest = new DelegatingDispatchRequest(); + *

+ * carsResourceDelegate + * .withDelegatingDispatchRequest(dispatchRequest) + * .withCallback(new AsyncCallback<Void>() {/{@literal * snip *}/}); + * .car(8) + * .delete(); + * } + * } + *

+ * + * @param The type of the resource used by this delegate. + */ +public interface ResourceDelegate { + /** + * Used as a mean to access the {@link DispatchRequest} instance returned by the underlying HTTP call. This may be + * useful for canceling a long running call. {@code delegatingDispatchRequest} will be populated when the HTTP call + * is sent, that is when you call any method from the service used by this delegate. + * + * @param delegatingDispatchRequest the {@link DelegatingDispatchRequest} to populate when the HTTP call is sent. + * + * @return a copy of this {@link ResourceDelegate} using the provided {@link DelegatingDispatchRequest}. + */ + ResourceDelegate withDelegatingDispatchRequest(DelegatingDispatchRequest delegatingDispatchRequest); + + /** + * Provide the callback when the HTTP call returns or if any error occur. + * + * @param callback The callback to use when the HTTP call returns or if any error occur. + * + * @return the service wrapped by this delegate. + */ + T withCallback(AsyncCallback callback); + + /** + * This method is the same as colling {@link #withCallback(AsyncCallback)} with a no-op callback. + */ + T withoutCallback(); +} diff --git a/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/AbstractDelegatedMethodGenerator.java b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/AbstractDelegatedMethodGenerator.java new file mode 100644 index 0000000..e028ff2 --- /dev/null +++ b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/AbstractDelegatedMethodGenerator.java @@ -0,0 +1,78 @@ +/** + * Copyright 2014 ArcBees 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.gwtplatform.dispatch.rest.delegates.rebind; + +import org.apache.velocity.app.VelocityEngine; + +import com.google.gwt.core.ext.GeneratorContext; +import com.google.gwt.core.ext.typeinfo.JMethod; +import com.gwtplatform.dispatch.rest.rebind.AbstractVelocityGenerator; +import com.gwtplatform.dispatch.rest.rebind.resource.MethodContext; +import com.gwtplatform.dispatch.rest.rebind.resource.MethodDefinition; +import com.gwtplatform.dispatch.rest.rebind.resource.MethodGenerator; +import com.gwtplatform.dispatch.rest.rebind.resource.ResourceDefinition; +import com.gwtplatform.dispatch.rest.rebind.utils.Logger; + +public abstract class AbstractDelegatedMethodGenerator extends AbstractVelocityGenerator implements MethodGenerator { + private DelegatedMethodContext context; + + protected AbstractDelegatedMethodGenerator( + Logger logger, + GeneratorContext context, + VelocityEngine velocityEngine) { + super(logger, context, velocityEngine); + } + + @Override + public byte getPriority() { + // This really need to run before core method generators + return (byte) (super.getPriority() - 5); + } + + protected void setContext(MethodContext context) { + this.context = (DelegatedMethodContext) context; + } + + protected ResourceDefinition getResourceDefinition() { + return context.getResourceDefinition(); + } + + protected MethodDefinition getMethodDefinition() { + return context.getMethodDefinition(); + } + + protected JMethod getMethod() { + return getMethodDefinition().getMethod(); + } + + protected void replaceMethodContent(StringBuilder methodBuilder, String newContent) { + int openBraceIndex = methodBuilder.indexOf("{"); + int closeBraceIndex = methodBuilder.lastIndexOf("}"); + + methodBuilder.replace(openBraceIndex + 1, closeBraceIndex, newContent); + } + + @Override + protected String getImplName() { + return getResourceDefinition() + DelegateGenerator.IMPL + "#" + getMethod().getName(); + } + + @Override + protected String getPackageName() { + return getResourceDefinition().getPackageName(); + } +} diff --git a/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateDefinition.java b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateDefinition.java new file mode 100644 index 0000000..40195f9 --- /dev/null +++ b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateDefinition.java @@ -0,0 +1,48 @@ +/** + * Copyright 2014 ArcBees 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.gwtplatform.dispatch.rest.delegates.rebind; + +import java.util.List; + +import com.google.common.collect.Lists; +import com.gwtplatform.dispatch.rest.rebind.resource.MethodDefinition; +import com.gwtplatform.dispatch.rest.rebind.resource.ResourceDefinition; +import com.gwtplatform.dispatch.rest.rebind.utils.ClassDefinition; + +public class DelegateDefinition extends ClassDefinition { + private final ResourceDefinition resourceDefinition; + private final List methodDefinitions; + + public DelegateDefinition( + String packageName, + String className, + ResourceDefinition resourceDefinition, + List methodDefinitions) { + super(packageName, className); + + this.resourceDefinition = resourceDefinition; + this.methodDefinitions = methodDefinitions; + } + + public ResourceDefinition getResourceDefinition() { + return resourceDefinition; + } + + public List getMethodDefinitions() { + return Lists.newArrayList(methodDefinitions); + } +} diff --git a/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateExtensionGenerator.java b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateExtensionGenerator.java new file mode 100644 index 0000000..f62c0ca --- /dev/null +++ b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateExtensionGenerator.java @@ -0,0 +1,90 @@ +/** + * Copyright 2014 ArcBees 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.gwtplatform.dispatch.rest.delegates.rebind; + +import java.util.Collection; +import java.util.List; + +import javax.inject.Inject; + +import com.google.common.collect.Lists; +import com.google.gwt.core.ext.GeneratorContext; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.gwtplatform.dispatch.rest.rebind.AbstractGenerator; +import com.gwtplatform.dispatch.rest.rebind.extension.ExtensionContext; +import com.gwtplatform.dispatch.rest.rebind.extension.ExtensionGenerator; +import com.gwtplatform.dispatch.rest.rebind.extension.ExtensionPoint; +import com.gwtplatform.dispatch.rest.rebind.resource.MethodDefinition; +import com.gwtplatform.dispatch.rest.rebind.resource.ResourceDefinition; +import com.gwtplatform.dispatch.rest.rebind.subresource.SubResourceMethodDefinition; +import com.gwtplatform.dispatch.rest.rebind.utils.ClassDefinition; +import com.gwtplatform.dispatch.rest.rebind.utils.Logger; + +public class DelegateExtensionGenerator extends AbstractGenerator implements ExtensionGenerator { + private final DelegateGenerator delegateGenerator; + + private List definitions; + + @Inject + DelegateExtensionGenerator( + Logger logger, + GeneratorContext context, + DelegateGenerator delegateGenerator) { + super(logger, context); + + this.delegateGenerator = delegateGenerator; + } + + @Override + public boolean canGenerate(ExtensionContext context) { + return context.getExtensionPoint() == ExtensionPoint.AFTER_RESOURCES + && !context.getResourceDefinitions().isEmpty(); + } + + @Override + public Collection generate(ExtensionContext context) throws UnableToCompleteException { + definitions = Lists.newArrayList(); + + generate(context.getResourceDefinitions()); + + return definitions; + } + + private void generate(Collection resourceDefinitions) throws UnableToCompleteException { + for (ResourceDefinition resourceDefinition : resourceDefinitions) { + maybeGenerateDelegate(resourceDefinition); + } + } + + private void maybeGenerateDelegate(ResourceDefinition resourceDefinition) throws UnableToCompleteException { + if (delegateGenerator.canGenerate(resourceDefinition)) { + generateSubResourceDelegates(resourceDefinition); + + DelegateDefinition definition = delegateGenerator.generate(resourceDefinition); + definitions.add(definition); + } + } + + private void generateSubResourceDelegates(ResourceDefinition resourceDefinition) throws UnableToCompleteException { + for (MethodDefinition methodDefinition : resourceDefinition.getMethodDefinitions()) { + if (methodDefinition instanceof SubResourceMethodDefinition) { + SubResourceMethodDefinition resourceMethodDefinition = (SubResourceMethodDefinition) methodDefinition; + generate(resourceMethodDefinition.getResourceDefinitions()); + } + } + } +} diff --git a/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateGenerator.java b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateGenerator.java new file mode 100644 index 0000000..dae4a56 --- /dev/null +++ b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateGenerator.java @@ -0,0 +1,169 @@ +/** + * Copyright 2014 ArcBees 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.gwtplatform.dispatch.rest.delegates.rebind; + +import java.io.PrintWriter; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; +import javax.ws.rs.Path; + +import org.apache.velocity.app.VelocityEngine; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.common.eventbus.EventBus; +import com.google.gwt.core.ext.GeneratorContext; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.JGenericType; +import com.google.gwt.core.ext.typeinfo.JParameterizedType; +import com.gwtplatform.dispatch.rest.delegates.client.ResourceDelegate; +import com.gwtplatform.dispatch.rest.rebind.AbstractVelocityGenerator; +import com.gwtplatform.dispatch.rest.rebind.GeneratorWithInput; +import com.gwtplatform.dispatch.rest.rebind.events.RegisterGinBindingEvent; +import com.gwtplatform.dispatch.rest.rebind.resource.MethodDefinition; +import com.gwtplatform.dispatch.rest.rebind.resource.MethodGenerator; +import com.gwtplatform.dispatch.rest.rebind.resource.ResourceDefinition; +import com.gwtplatform.dispatch.rest.rebind.utils.ClassDefinition; +import com.gwtplatform.dispatch.rest.rebind.utils.Logger; +import com.gwtplatform.dispatch.rest.shared.RestAction; + +import static com.gwtplatform.dispatch.rest.rebind.utils.Generators.findGenerator; + +public class DelegateGenerator extends AbstractVelocityGenerator + implements GeneratorWithInput { + static final String IMPL = "Delegate"; + + private static final String TEMPLATE = "com/gwtplatform/dispatch/rest/delegates/rebind/Delegate.vm"; + + private final EventBus eventBus; + private final Set methodGenerators; + private final List generatedDelegates; + + private ResourceDefinition resourceDefinition; + private List methodDefinitions; + private Set imports; + + @Inject + DelegateGenerator( + Logger logger, + GeneratorContext context, + VelocityEngine velocityEngine, + EventBus eventBus, + Set methodGenerators) { + super(logger, context, velocityEngine); + + this.eventBus = eventBus; + this.methodGenerators = methodGenerators; + this.generatedDelegates = Lists.newArrayList(); + } + + @Override + public boolean canGenerate(ResourceDefinition resourceDefinition) { + this.resourceDefinition = resourceDefinition; + + return !generatedDelegates.contains(getClassDefinition()); + } + + @Override + public DelegateDefinition generate(ResourceDefinition resourceDefinition) throws UnableToCompleteException { + this.resourceDefinition = resourceDefinition; + + imports = Sets.newTreeSet(); + imports.add(RestAction.class.getName()); + imports.add(resourceDefinition.getResourceInterface().getQualifiedSourceName()); + imports.add(resourceDefinition.getQualifiedName()); + + methodDefinitions = Lists.newArrayList(); + + generateMethods(); + + PrintWriter printWriter = tryCreate(); + mergeTemplate(printWriter); + commit(printWriter); + + maybeRegisterGinBinding(); + + DelegateDefinition definition = + new DelegateDefinition(getPackageName(), getImplName(), resourceDefinition, methodDefinitions); + generatedDelegates.add(definition); + return definition; + } + + @Override + protected String getTemplate() { + return TEMPLATE; + } + + @Override + protected String getPackageName() { + return resourceDefinition.getPackageName(); + } + + @Override + protected String getImplName() { + return resourceDefinition.getResourceInterface().getName() + IMPL; + } + + @Override + protected void populateTemplateVariables(Map variables) { + JClassType resourceInterface = resourceDefinition.getResourceInterface(); + + variables.put("resourceType", new ClassDefinition(resourceInterface).getParameterizedClassName()); + variables.put("methods", methodDefinitions); + variables.put("imports", imports); + } + + private void generateMethods() throws UnableToCompleteException { + for (MethodDefinition methodDefinition : resourceDefinition.getMethodDefinitions()) { + generateMethod(methodDefinition); + } + } + + private void generateMethod(MethodDefinition methodDefinition) throws UnableToCompleteException { + DelegatedMethodContext context = new DelegatedMethodContext(resourceDefinition, methodDefinition); + MethodGenerator generator = findGenerator(methodGenerators, context); + + if (generator != null) { + MethodDefinition delegatedDefinition = generator.generate(context); + + methodDefinitions.add(delegatedDefinition); + imports.addAll(delegatedDefinition.getImports()); + } else { + getLogger().die("Unable to find a delegated method generator for `%s#%s`", + resourceDefinition.getQualifiedName(), methodDefinition.getMethod().getName()); + } + } + + private void maybeRegisterGinBinding() throws UnableToCompleteException { + if (!isSubResource()) { + JGenericType resourceDelegateType = getType(ResourceDelegate.class).isGenericType(); + JParameterizedType parameterizedResourceDelegateType = getContext().getTypeOracle().getParameterizedType( + resourceDelegateType, new JClassType[]{resourceDefinition.getResourceInterface()}); + ClassDefinition definition = new ClassDefinition(parameterizedResourceDelegateType); + + RegisterGinBindingEvent.postSingleton(eventBus, definition, getClassDefinition()); + } + } + + private boolean isSubResource() { + return !resourceDefinition.getResourceInterface().isAnnotationPresent(Path.class); + } +} diff --git a/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateMethodDefinition.java b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateMethodDefinition.java new file mode 100644 index 0000000..457ca56 --- /dev/null +++ b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateMethodDefinition.java @@ -0,0 +1,50 @@ +/** + * Copyright 2014 ArcBees 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.gwtplatform.dispatch.rest.delegates.rebind; + +import java.util.List; + +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.JMethod; +import com.gwtplatform.dispatch.rest.rebind.Parameter; +import com.gwtplatform.dispatch.rest.rebind.action.ActionMethodDefinition; + +public class DelegateMethodDefinition extends ActionMethodDefinition { + private final String returnValue; + private final String actionMethodName; + + public DelegateMethodDefinition( + JMethod method, + List parameters, + List inheritedParameters, + JClassType resultType, + String returnValue, + String actionMethodName) { + super(method, parameters, inheritedParameters, resultType); + + this.returnValue = returnValue; + this.actionMethodName = actionMethodName; + } + + public String getActionMethodName() { + return actionMethodName; + } + + public String getReturnValue() { + return returnValue; + } +} diff --git a/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateMethodGenerator.java b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateMethodGenerator.java new file mode 100644 index 0000000..aa3eb87 --- /dev/null +++ b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateMethodGenerator.java @@ -0,0 +1,180 @@ +/** + * Copyright 2014 ArcBees 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.gwtplatform.dispatch.rest.delegates.rebind; + +import java.io.StringWriter; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; + +import org.apache.velocity.app.VelocityEngine; + +import com.google.gwt.core.ext.GeneratorContext; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.google.gwt.core.ext.typeinfo.JPrimitiveType; +import com.google.gwt.core.ext.typeinfo.JType; +import com.google.gwt.core.ext.typeinfo.TypeOracleException; +import com.gwtplatform.dispatch.rest.rebind.Parameter; +import com.gwtplatform.dispatch.rest.rebind.action.ActionContext; +import com.gwtplatform.dispatch.rest.rebind.action.ActionDefinition; +import com.gwtplatform.dispatch.rest.rebind.action.ActionGenerator; +import com.gwtplatform.dispatch.rest.rebind.action.ActionMethodGenerator; +import com.gwtplatform.dispatch.rest.rebind.resource.MethodContext; +import com.gwtplatform.dispatch.rest.rebind.resource.MethodDefinition; +import com.gwtplatform.dispatch.rest.rebind.utils.Logger; +import com.gwtplatform.dispatch.rest.shared.RestAction; + +public class DelegateMethodGenerator extends ActionMethodGenerator { + private static final String TEMPLATE = "com/gwtplatform/dispatch/rest/delegates/rebind/DelegateMethod.vm"; + private static final String ACTION_METHOD_SUFFIX = "$action"; + + private DelegateMethodDefinition methodDefinition; + + @Inject + DelegateMethodGenerator( + Logger logger, + GeneratorContext context, + Set actionGenerators, + VelocityEngine velocityEngine) { + super(logger, context, actionGenerators, velocityEngine); + } + + @Override + public boolean canGenerate(MethodContext methodContext) { + setContext(methodContext); + + return hasValidReturnType() + && hasExactlyOneHttpVerb() + && canGenerateAction(); + } + + @Override + public MethodDefinition generate(MethodContext methodContext) throws UnableToCompleteException { + setContext(methodContext); + + List parameters = resolveParameters(); + List inheritedParameters = resolveInheritedParameters(); + JClassType resultType = parseResultType(); + String returnValue = resolveReturnValue(); + String actionMethodName = getMethod().getName() + ACTION_METHOD_SUFFIX; + + methodDefinition = new DelegateMethodDefinition(getMethod(), parameters, inheritedParameters, resultType, + returnValue, actionMethodName); + methodDefinition.addImport(RestAction.class.getName()); + + generateAction(); + generateMethods(); + + return methodDefinition; + } + + @Override + protected String getTemplate() { + return TEMPLATE; + } + + @Override + protected void populateTemplateVariables(Map variables) { + String resultTypeName = methodDefinition.getResultType().getParameterizedQualifiedSourceName(); + + List actionParameters = methodDefinition.getInheritedParameters(); + actionParameters.addAll(methodDefinition.getParameters()); + + variables.put("resultType", resultTypeName); + variables.put("returnType", getMethod().getReturnType().getParameterizedQualifiedSourceName()); + variables.put("returnValue", methodDefinition.getReturnValue()); + variables.put("methodName", getMethod().getName()); + variables.put("actionMethodName", methodDefinition.getActionMethodName()); + variables.put("methodParameters", methodDefinition.getParameters()); + variables.put("actionParameters", actionParameters); + variables.put("action", methodDefinition.getActionDefinitions().get(0)); + } + + private String resolveReturnValue() { + String returnValue = null; + JType returnType = getMethod().getReturnType(); + JPrimitiveType primitiveType = returnType.isPrimitive(); + + if (primitiveType != null) { + if (primitiveType != JPrimitiveType.VOID) { + returnValue = primitiveType.getUninitializedFieldExpression(); + } + } else { + returnValue = "null"; + } + + return returnValue; + } + + private void generateAction() throws UnableToCompleteException { + ActionContext actionContext = new ActionContext(getMethodContext(), methodDefinition); + ActionDefinition definition = generateAction(actionContext); + + methodDefinition.addAction(definition); + } + + private void generateMethods() throws UnableToCompleteException { + StringWriter writer = new StringWriter(); + mergeTemplate(writer); + + methodDefinition.setOutput(writer.toString()); + } + + private JClassType parseResultType() throws UnableToCompleteException { + JType returnType = getMethod().getReturnType(); + JPrimitiveType primitiveType = returnType.isPrimitive(); + JClassType classType; + + if (primitiveType != null) { + classType = convertPrimitiveToBoxed(primitiveType); + } else { + classType = returnType.isClassOrInterface(); + } + + return classType; + } + + private JClassType convertPrimitiveToBoxed(JPrimitiveType primitive) throws UnableToCompleteException { + JClassType boxedType = null; + try { + String boxedSourceName = primitive.getQualifiedBoxedSourceName(); + boxedType = getContext().getTypeOracle().parse(boxedSourceName).isClass(); + } catch (TypeOracleException e) { + getLogger().die("Unable to convert '%s' to a boxed type.", primitive); + } + + return boxedType; + } + + private boolean hasValidReturnType() { + JType returnType = getMethod().getReturnType(); + + if (returnType != null) { + JClassType restActionType = findType(RestAction.class); + JClassType classType = returnType.isClassOrInterface(); + JPrimitiveType primitiveType = returnType.isPrimitive(); + + return (classType != null && restActionType != null && !classType.isAssignableTo(restActionType)) + || primitiveType != null; + } + + return false; + } +} diff --git a/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateModule.java b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateModule.java new file mode 100644 index 0000000..331262e --- /dev/null +++ b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateModule.java @@ -0,0 +1,37 @@ +/** + * Copyright 2014 ArcBees 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.gwtplatform.dispatch.rest.delegates.rebind; + +import javax.inject.Singleton; + +import com.google.inject.AbstractModule; + +import static com.gwtplatform.dispatch.rest.rebind.extension.ExtensionModule.addExtensionGenerator; +import static com.gwtplatform.dispatch.rest.rebind.resource.ResourceModule.addMethodGenerator; + +public class DelegateModule extends AbstractModule { + @Override + protected void configure() { + addExtensionGenerator(binder()).to(DelegateExtensionGenerator.class); + addMethodGenerator(binder()).to(DelegateMethodGenerator.class); + addMethodGenerator(binder()).to(DelegatedDelegateMethodGenerator.class); + addMethodGenerator(binder()).to(DelegatedActionMethodGenerator.class); + addMethodGenerator(binder()).to(DelegatedSubResourceMethodGenerator.class); + + bind(DelegateGenerator.class).in(Singleton.class); + } +} diff --git a/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedActionMethodGenerator.java b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedActionMethodGenerator.java new file mode 100644 index 0000000..97628fb --- /dev/null +++ b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedActionMethodGenerator.java @@ -0,0 +1,84 @@ +/** + * Copyright 2014 ArcBees 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.gwtplatform.dispatch.rest.delegates.rebind; + +import java.io.StringWriter; +import java.util.Map; + +import javax.inject.Inject; + +import org.apache.velocity.app.VelocityEngine; + +import com.google.gwt.core.ext.GeneratorContext; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.gwtplatform.dispatch.rest.rebind.action.ActionDefinition; +import com.gwtplatform.dispatch.rest.rebind.action.ActionMethodDefinition; +import com.gwtplatform.dispatch.rest.rebind.resource.MethodContext; +import com.gwtplatform.dispatch.rest.rebind.resource.MethodDefinition; +import com.gwtplatform.dispatch.rest.rebind.utils.Logger; + +public class DelegatedActionMethodGenerator extends AbstractDelegatedMethodGenerator { + private static final String TEMPLATE = "com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedActionMethod.vm"; + + @Inject + DelegatedActionMethodGenerator( + Logger logger, + GeneratorContext context, + VelocityEngine velocityEngine) { + super(logger, context, velocityEngine); + } + + @Override + public boolean canGenerate(MethodContext context) { + return context instanceof DelegatedMethodContext + && ((DelegatedMethodContext) context).getMethodDefinition() instanceof ActionMethodDefinition; + } + + @Override + public MethodDefinition generate(MethodContext context) throws UnableToCompleteException { + setContext(context); + + StringWriter writer = new StringWriter(); + mergeTemplate(writer); + + MethodDefinition delegateDefinition = new MethodDefinition(getMethodDefinition()); + delegateDefinition.setOutput(writer.toString()); + + return delegateDefinition; + } + + @Override + protected void populateTemplateVariables(Map variables) { + ActionDefinition actionDefinition = getMethodDefinition().getActionDefinitions().get(0); + JClassType resultType = actionDefinition.getResultType(); + + variables.put("resultType", resultType.getParameterizedQualifiedSourceName()); + variables.put("methodName", getMethod().getName()); + variables.put("parameters", getMethodDefinition().getParameters()); + } + + @Override + protected ActionMethodDefinition getMethodDefinition() { + return (ActionMethodDefinition) super.getMethodDefinition(); + } + + @Override + protected String getTemplate() { + return TEMPLATE; + } +} diff --git a/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedDelegateMethodGenerator.java b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedDelegateMethodGenerator.java new file mode 100644 index 0000000..84d39d2 --- /dev/null +++ b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedDelegateMethodGenerator.java @@ -0,0 +1,87 @@ +/** + * Copyright 2014 ArcBees 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.gwtplatform.dispatch.rest.delegates.rebind; + +import java.io.StringWriter; +import java.util.Map; + +import javax.inject.Inject; + +import org.apache.velocity.app.VelocityEngine; + +import com.google.gwt.core.ext.GeneratorContext; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.gwtplatform.dispatch.rest.rebind.action.ActionDefinition; +import com.gwtplatform.dispatch.rest.rebind.resource.MethodContext; +import com.gwtplatform.dispatch.rest.rebind.resource.MethodDefinition; +import com.gwtplatform.dispatch.rest.rebind.utils.Logger; + +public class DelegatedDelegateMethodGenerator extends AbstractDelegatedMethodGenerator { + private static final String TEMPLATE = "com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedDelegateMethod.vm"; + + @Inject + DelegatedDelegateMethodGenerator( + Logger logger, + GeneratorContext context, + VelocityEngine velocityEngine) { + super(logger, context, velocityEngine); + } + + @Override + public boolean canGenerate(MethodContext context) { + return context instanceof DelegatedMethodContext + && ((DelegatedMethodContext) context).getMethodDefinition() instanceof DelegateMethodDefinition; + } + + @Override + public MethodDefinition generate(MethodContext context) throws UnableToCompleteException { + setContext(context); + + StringWriter writer = new StringWriter(); + mergeTemplate(writer); + + MethodDefinition delegateDefinition = new MethodDefinition(getMethodDefinition()); + delegateDefinition.setOutput(writer.toString()); + + return delegateDefinition; + } + + @Override + protected void populateTemplateVariables(Map variables) { + ActionDefinition actionDefinition = getMethodDefinition().getActionDefinitions().get(0); + JClassType resultType = actionDefinition.getResultType(); + + variables.put("resultType", resultType.getParameterizedQualifiedSourceName()); + variables.put("returnType", getMethod().getReturnType().getParameterizedQualifiedSourceName()); + variables.put("returnValue", getMethodDefinition().getReturnValue()); + variables.put("resourceImplType", getResourceDefinition().getParameterizedClassName()); + variables.put("actionMethodName", getMethodDefinition().getActionMethodName()); + variables.put("methodName", getMethod().getName()); + variables.put("parameters", getMethodDefinition().getParameters()); + } + + @Override + protected DelegateMethodDefinition getMethodDefinition() { + return (DelegateMethodDefinition) super.getMethodDefinition(); + } + + @Override + protected String getTemplate() { + return TEMPLATE; + } +} diff --git a/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedMethodContext.java b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedMethodContext.java new file mode 100644 index 0000000..8ccdc66 --- /dev/null +++ b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedMethodContext.java @@ -0,0 +1,43 @@ +/** + * Copyright 2014 ArcBees 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.gwtplatform.dispatch.rest.delegates.rebind; + +import com.gwtplatform.dispatch.rest.rebind.resource.MethodContext; +import com.gwtplatform.dispatch.rest.rebind.resource.MethodDefinition; +import com.gwtplatform.dispatch.rest.rebind.resource.ResourceDefinition; + +public class DelegatedMethodContext extends MethodContext { + private final ResourceDefinition resourceDefinition; + private final MethodDefinition methodDefinition; + + public DelegatedMethodContext( + ResourceDefinition resourceDefinition, + MethodDefinition methodDefinition) { + super(resourceDefinition, null, methodDefinition.getMethod()); + + this.resourceDefinition = resourceDefinition; + this.methodDefinition = methodDefinition; + } + + public ResourceDefinition getResourceDefinition() { + return resourceDefinition; + } + + public MethodDefinition getMethodDefinition() { + return methodDefinition; + } +} diff --git a/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedSubResourceMethodGenerator.java b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedSubResourceMethodGenerator.java new file mode 100644 index 0000000..1a12a86 --- /dev/null +++ b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedSubResourceMethodGenerator.java @@ -0,0 +1,100 @@ +/** + * Copyright 2014 ArcBees 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.gwtplatform.dispatch.rest.delegates.rebind; + +import java.io.StringWriter; +import java.util.Map; + +import javax.inject.Inject; + +import org.apache.velocity.app.VelocityEngine; + +import com.google.gwt.core.ext.GeneratorContext; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.typeinfo.JClassType; +import com.gwtplatform.dispatch.rest.rebind.resource.MethodContext; +import com.gwtplatform.dispatch.rest.rebind.resource.MethodDefinition; +import com.gwtplatform.dispatch.rest.rebind.resource.ResourceDefinition; +import com.gwtplatform.dispatch.rest.rebind.subresource.SubResourceMethodDefinition; +import com.gwtplatform.dispatch.rest.rebind.utils.ClassDefinition; +import com.gwtplatform.dispatch.rest.rebind.utils.Logger; + +public class DelegatedSubResourceMethodGenerator extends AbstractDelegatedMethodGenerator { + private static final String TEMPLATE = + "com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedSubResourceMethod.vm"; + + private ClassDefinition subDelegateTypeDefinition; + private ClassDefinition subResourceTypeDefinition; + + @Inject + DelegatedSubResourceMethodGenerator( + Logger logger, + GeneratorContext context, + VelocityEngine velocityEngine) { + super(logger, context, velocityEngine); + } + + @Override + public boolean canGenerate(MethodContext context) { + return context instanceof DelegatedMethodContext + && ((DelegatedMethodContext) context).getMethodDefinition() instanceof SubResourceMethodDefinition; + } + + @Override + public MethodDefinition generate(MethodContext context) throws UnableToCompleteException { + setContext(context); + + resolveTypeDefinitions(); + + StringWriter writer = new StringWriter(); + mergeTemplate(writer); + + MethodDefinition delegateDefinition = new MethodDefinition(getMethodDefinition()); + delegateDefinition.addImport(subResourceTypeDefinition.getQualifiedName()); + delegateDefinition.addImport(subDelegateTypeDefinition.getQualifiedName()); + delegateDefinition.setOutput(writer.toString()); + + return delegateDefinition; + } + + @Override + protected void populateTemplateVariables(Map variables) { + variables.put("subResourceType", subResourceTypeDefinition.getParameterizedClassName()); + variables.put("subDelegateType", subDelegateTypeDefinition.getParameterizedClassName()); + variables.put("methodName", getMethod().getName()); + variables.put("parameters", getMethodDefinition().getParameters()); + } + + @Override + protected SubResourceMethodDefinition getMethodDefinition() { + return (SubResourceMethodDefinition) super.getMethodDefinition(); + } + + @Override + protected String getTemplate() { + return TEMPLATE; + } + + private void resolveTypeDefinitions() { + ResourceDefinition subResourceDefinition = getMethodDefinition().getResourceDefinitions().get(0); + JClassType subResourceInterface = subResourceDefinition.getResourceInterface(); + + subResourceTypeDefinition = new ClassDefinition(subResourceInterface); + subDelegateTypeDefinition = new ClassDefinition(subResourceTypeDefinition.getPackageName(), + subResourceTypeDefinition.getClassName() + DelegateGenerator.IMPL); + } +} diff --git a/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/AbstractDelegateStubber.java b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/AbstractDelegateStubber.java new file mode 100644 index 0000000..fbf3cd0 --- /dev/null +++ b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/AbstractDelegateStubber.java @@ -0,0 +1,151 @@ +/** + * Copyright 2014 ArcBees 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.gwtplatform.dispatch.rest.delegates.test; + +import javax.ws.rs.core.Response.Status; + +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import com.google.gwt.http.client.Response; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.gwtplatform.dispatch.shared.DispatchRequest; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; + +/** + * Common stubbing context for a {@link com.gwtplatform.dispatch.rest.delegates.client.ResourceDelegate + * ResourceDelegate} call. + * + * @param a resource type. + */ +public abstract class AbstractDelegateStubber { + private final DelegateMocking delegateMocking; + + private Response response; + private DispatchRequest dispatchRequest; + + AbstractDelegateStubber(DelegateMocking delegateMocking) { + this.delegateMocking = delegateMocking; + } + + /** + * Syntactic sugar. + * + * @return this stubbing context. + */ + public S and() { + return self(); + } + + /** + * Specify the {@link Response} that will be assigned to {@link com.gwtplatform.dispatch.rest.client + * .RestCallback#setResponse(Response) + * RestCallback#setResponse(Response)} when the call represented by the current context is performed. + * + * @param response the result object passed to the {@link com.gwtplatform.dispatch.rest.client.RestCallback + * RestCallback}. + * + * @return this stubbing context. + */ + public S withResponse(Response response) { + this.response = response; + return self(); + } + + /** + * This is similar than calling {@link #withResponse(Response)}. Instead, a mock of {@link Response} is created and + * configured to return status. + *

+ * If {@link #withResponse(Response)} was previously called, the previously configured {@link Response} will be + * overwritten. + * + * @param status the {@link Status} that should be returned by the response. + * + * @return this stubbing context. + */ + public S withStatus(Status status) { + response = mock(Response.class); + given(response.getStatusCode()).willReturn(status.getStatusCode()); + + return self(); + } + + /** + * Specify the {@link DispatchRequest} that will be assigned to the {@link com.gwtplatform.dispatch.client + * .DelegatingDispatchRequest + * DelegatingDispatchRequest} that was passed to {@link com.gwtplatform.dispatch.rest.delegates.client + * .ResourceDelegate#withDelegatingDispatchRequest( + *com.gwtplatform.dispatch.client.DelegatingDispatchRequest) ResourceDelegate#withDelegatingDispatchRequest()}. + * + * @param dispatchRequest the {@link DispatchRequest} that will be passed to the configured {@link + * com.gwtplatform.dispatch.client.DelegatingDispatchRequest DelegatingDispatchRequest}. + * + * @return this stubbing context. + */ + public S withDispatchRequest(DispatchRequest dispatchRequest) { + this.dispatchRequest = dispatchRequest; + return self(); + } + + /** + * Return a stubber based on the underlying resource. You will then call the method(s) you expect to be called on + * your resource. + * + * @return The stubbing context of the underlying resource. + */ + public R when() { + return when(delegateMocking.getResource()); + } + + /** + * Return a stubber based on mock. You will then call the method(s) you expect to be called on your + * mock. + * + * @return The stubbing context of mock. + */ + public T when(T mock) { + return doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + updateResponse(); + updateDispatchRequest(); + updateCallback(delegateMocking.getCallback()); + + return null; + } + }).when(mock); + } + + protected abstract void updateCallback(AsyncCallback callback); + + protected abstract S self(); + + private void updateResponse() { + if (response != null) { + delegateMocking.getRestCallback().setResponse(response); + } + } + + private void updateDispatchRequest() { + if (dispatchRequest != null) { + delegateMocking.getDelegatingDispatchRequest().setDelegate(dispatchRequest); + } + } +} diff --git a/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/DelegateMocking.java b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/DelegateMocking.java new file mode 100644 index 0000000..571efef --- /dev/null +++ b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/DelegateMocking.java @@ -0,0 +1,132 @@ +/** + * Copyright 2014 ArcBees 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.gwtplatform.dispatch.rest.delegates.test; + +import org.mockito.ArgumentCaptor; + +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.gwtplatform.dispatch.client.DelegatingDispatchRequest; +import com.gwtplatform.dispatch.rest.client.RestCallback; +import com.gwtplatform.dispatch.rest.delegates.client.ResourceDelegate; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Stubbing context for a {@link ResourceDelegate}. + * + * @param a resource type. + */ +public class DelegateMocking { + private final ResourceDelegate delegate; + + private R resource; + private ArgumentCaptor callbackCaptor; + private ArgumentCaptor delegatingDispatchRequestCaptor; + + DelegateMocking(ResourceDelegate delegate) { + this.delegate = delegate; + } + + /** + * Create a mock of resourceClass and associate the current delegate to it. Must be called only + * once and before any other method is called. + * + * @param resourceClass the resourceClass to associate to the current delegate. + * + * @return this instance so you can start mocking the resource. + */ + public DelegateMocking useResource(Class resourceClass) { + return useResource(mock(resourceClass)); + } + + /** + * Associate the current delegate to resource. Must be called only once and before any + * other method is called. + * + * @param resource the resource to associate to the current delegate. Must be a mock. + * + * @return this instance so you can start mocking the resource. + */ + public DelegateMocking useResource(R resource) { + assert this.resource == null + : "useResource(R) called more than once. Did you forget to call DelegateTestUtils.init()?"; + + this.resource = resource; + callbackCaptor = ArgumentCaptor.forClass(AsyncCallback.class); + delegatingDispatchRequestCaptor = ArgumentCaptor.forClass(DelegatingDispatchRequest.class); + + when(delegate.withCallback(callbackCaptor.capture())).thenReturn(resource); + when(delegate.withDelegatingDispatchRequest(delegatingDispatchRequestCaptor.capture())).thenReturn(delegate); + + return this; + } + + /** + * Syntactic sugar. + * + * @return this instance + */ + public DelegateMocking and() { + return this; + } + + /** + * Create a stubbing context for a {@link ResourceDelegate} call that should succeed. + */ + public SuccessDelegateStubber succeed() { + verifyReadyToStub(); + + return new SuccessDelegateStubber(this); + } + + /** + * Create a stubbing context for a {@link ResourceDelegate} call that should fail. + */ + public FailureDelegateStubber fail() { + verifyReadyToStub(); + + return new FailureDelegateStubber(this); + } + + ResourceDelegate getDelegate() { + return delegate; + } + + R getResource() { + return resource; + } + + DelegatingDispatchRequest getDelegatingDispatchRequest() { + return delegatingDispatchRequestCaptor.getValue(); + } + + AsyncCallback getCallback() { + return callbackCaptor.getValue(); + } + + RestCallback getRestCallback() { + AsyncCallback callback = getCallback(); + + assert callback instanceof RestCallback; + return (RestCallback) callback; + } + + private void verifyReadyToStub() { + assert resource != null : "You must call useResource(R) before stubbing a delegate."; + } +} diff --git a/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/DelegateTestUtils.java b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/DelegateTestUtils.java new file mode 100644 index 0000000..2985c27 --- /dev/null +++ b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/DelegateTestUtils.java @@ -0,0 +1,63 @@ +/** + * Copyright 2014 ArcBees 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.gwtplatform.dispatch.rest.delegates.test; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import com.gwtplatform.dispatch.rest.delegates.client.ResourceDelegate; + +/** + * Utility methods to help mocking {@link com.gwtplatform.dispatch.rest.delegates.client.ResourceDelegate}s. + *

+ * Important:

  • {@link #init()} must be called before each test. Not doing it may cause unexpected + * results. Using JUnit's {@link org.junit.Before @Before} is perfect for that.
  • {@link + * DelegateMocking#useResource(Object) useResource(Object)} must be called before any other methods exactly once + * and before any other methods for the delegate is called. Assertion errors will be thrown otherwise.
  • + *
+ */ +public class DelegateTestUtils { + public static final Map, DelegateMocking> delegateMockings + = Collections.synchronizedMap(new HashMap, DelegateMocking>()); + + /** + * Initializes the mocking utils. Must be called before each test. + */ + public static void init() { + delegateMockings.clear(); + } + + /** + * Access or create the mocking context of delegate. + * + * @return the mocking context of delegate + */ + @SuppressWarnings("unchecked") + public static DelegateMocking givenDelegate(ResourceDelegate delegate) { + DelegateMocking delegateMocking; + + if (delegateMockings.containsKey(delegate)) { + delegateMocking = (DelegateMocking) delegateMockings.get(delegate); + } else { + delegateMocking = new DelegateMocking(delegate); + delegateMockings.put(delegate, delegateMocking); + } + + return delegateMocking; + } +} diff --git a/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/FailureDelegateStubber.java b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/FailureDelegateStubber.java new file mode 100644 index 0000000..692ec0f --- /dev/null +++ b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/FailureDelegateStubber.java @@ -0,0 +1,56 @@ +/** + * Copyright 2014 ArcBees 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.gwtplatform.dispatch.rest.delegates.test; + +import com.google.gwt.user.client.rpc.AsyncCallback; + +/** + * Stubbing context for a {@link com.gwtplatform.dispatch.rest.delegates.client.ResourceDelegate ResourceDelegate} call + * that should fail. + * + * @param a resource type. + */ +public class FailureDelegateStubber extends AbstractDelegateStubber> { + private Throwable throwable; + + FailureDelegateStubber(DelegateMocking delegateMocking) { + super(delegateMocking); + } + + /** + * Specify the {@link Throwable} that will be assigned to {@link AsyncCallback#onFailure(Throwable)} when the call + * represented by the current context is performed. + * + * @param throwable the throwable passed to the {@link AsyncCallback}. + * + * @return this stubbing context. + */ + public FailureDelegateStubber withThrowable(Throwable throwable) { + this.throwable = throwable; + return self(); + } + + @Override + protected void updateCallback(AsyncCallback callback) { + callback.onFailure(throwable); + } + + @Override + protected FailureDelegateStubber self() { + return this; + } +} diff --git a/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/SuccessDelegateStubber.java b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/SuccessDelegateStubber.java new file mode 100644 index 0000000..26d5d47 --- /dev/null +++ b/dispatch-rest-delegates/src/main/java/com/gwtplatform/dispatch/rest/delegates/test/SuccessDelegateStubber.java @@ -0,0 +1,57 @@ +/** + * Copyright 2014 ArcBees 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.gwtplatform.dispatch.rest.delegates.test; + +import com.google.gwt.user.client.rpc.AsyncCallback; + +/** + * Stubbing context for a {@link com.gwtplatform.dispatch.rest.delegates.client.ResourceDelegate ResourceDelegate} call + * that should fail. + * + * @param a resource type. + */ +public class SuccessDelegateStubber extends AbstractDelegateStubber> { + private Object result; + + SuccessDelegateStubber(DelegateMocking delegateMocking) { + super(delegateMocking); + } + + /** + * Specify the result object that will be assigned to {@link AsyncCallback#onSuccess(Object)} when the call + * represented by the current context is performed. + * + * @param result the result object passed to the {@link AsyncCallback}. + * + * @return this stubbing context. + */ + public SuccessDelegateStubber withResult(Object result) { + this.result = result; + return self(); + } + + @SuppressWarnings("unchecked") + @Override + protected void updateCallback(AsyncCallback callback) { + callback.onSuccess(result); + } + + @Override + protected SuccessDelegateStubber self() { + return this; + } +} diff --git a/dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/ResourceDelegate.gwt.xml b/dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/ResourceDelegate.gwt.xml new file mode 100644 index 0000000..71d664f --- /dev/null +++ b/dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/ResourceDelegate.gwt.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/Delegate.vm b/dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/Delegate.vm new file mode 100644 index 0000000..c02d6a8 --- /dev/null +++ b/dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/Delegate.vm @@ -0,0 +1,35 @@ +package $package; + +import javax.inject.Inject; + +import com.gwtplatform.dispatch.rest.client.RestDispatch; +import com.gwtplatform.dispatch.rest.delegates.client.AbstractResourceDelegate; +#foreach ($import in $imports) +import $import; +#end + +public class $impl extends AbstractResourceDelegate<$resourceType> implements $resourceType { + private final $resourceType resource; + + @Inject + public ${impl}( + RestDispatch dispatcher, + $resourceType resource) { + super(dispatcher); + + this.resource = resource; + } + +#foreach ($method in $methods) +$method.output +#end + @Override + protected $resourceType asResource() { + return this; + } + + @Override + protected $impl newInstance() { + return new ${impl}(dispatcher, resource); + } +} diff --git a/dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateMethod.vm b/dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateMethod.vm new file mode 100644 index 0000000..2252cfc --- /dev/null +++ b/dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/DelegateMethod.vm @@ -0,0 +1,10 @@ + @Override + public $returnType ${methodName}(#generateMethodSignature($methodParameters)) { +#if($returnValue) + return $returnValue; +#end + } + + RestAction<$resultType> ${actionMethodName}(#generateMethodSignature($methodParameters)) { + return new ${action.className}(defaultDateFormat#generateAppendedCallSignature($actionParameters)); + } diff --git a/dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedActionMethod.vm b/dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedActionMethod.vm new file mode 100644 index 0000000..4538c73 --- /dev/null +++ b/dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedActionMethod.vm @@ -0,0 +1,7 @@ + @Override + public RestAction<$resultType> ${methodName}(#generateMethodSignature($parameters)) { + RestAction<$resultType> action = resource.${methodName}(#generateCallSignature($parameters)); + execute(action); + + return action; + } diff --git a/dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedDelegateMethod.vm b/dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedDelegateMethod.vm new file mode 100644 index 0000000..6e7bee8 --- /dev/null +++ b/dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedDelegateMethod.vm @@ -0,0 +1,9 @@ + @Override + public $returnType ${methodName}(#generateMethodSignature($parameters)) { + RestAction<$resultType> action = (($resourceImplType) resource).${actionMethodName}(#generateCallSignature($parameters)); + execute(action); +#if($returnValue) + + return $returnValue; +#end + } diff --git a/dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedSubResourceMethod.vm b/dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedSubResourceMethod.vm new file mode 100644 index 0000000..42c09a4 --- /dev/null +++ b/dispatch-rest-delegates/src/main/resources/com/gwtplatform/dispatch/rest/delegates/rebind/DelegatedSubResourceMethod.vm @@ -0,0 +1,9 @@ + @Override + public $subResourceType ${methodName}(#generateMethodSignature($parameters)) { + $subResourceType subResource = resource.${methodName}(#generateCallSignature($parameters)); + $subDelegateType subDelegate = new ${subDelegateType}(dispatcher, subResource); + + copyFields(subDelegate); + + return subDelegate; + } diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..8c9a7bb --- /dev/null +++ b/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + + org.sonatype.oss + oss-parent + 9 + + + com.gwtplatform + extensions + 1 + pom + + + ginuibinder + dispatch-rest-delegates + +