Skip to content

Commit

Permalink
Re-implement and adapt resource delegate generators as an extension.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Chris-V committed Dec 8, 2014
1 parent 58df940 commit 18e3e8b
Show file tree
Hide file tree
Showing 26 changed files with 1,820 additions and 0 deletions.
108 changes: 108 additions & 0 deletions dispatch-rest-delegates/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>9</version>
</parent>

<groupId>com.gwtplatform.extensions</groupId>
<artifactId>dispatch-rest-delegates</artifactId>
<version>${gwtp.version}</version>
<packaging>jar</packaging>
<name>Rest-Dispatch Resource Delegates</name>
<description>Allow Rest-Dispatch resources to return their raw result type directly.</description>
<inceptionYear>2014</inceptionYear>
<url>http://arcbees.github.io/GWTP/</url>

<properties>
<gwtp.version>1.4-SNAPSHOT</gwtp.version>
<gwt.version>2.7.0</gwt.version>
<mockito.version>1.10.8</mockito.version>

<maven-checkstyle-plugin.version>2.13</maven-checkstyle-plugin.version>
</properties>

<dependencies>
<dependency>
<groupId>com.gwtplatform</groupId>
<artifactId>gwtp-dispatch-rest</artifactId>
<version>${gwtp.version}</version>
</dependency>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-user</artifactId>
<version>${gwt.version}</version>
</dependency>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-dev</artifactId>
<version>${gwt.version}</version>
</dependency>

<!-- For testing tools -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
</dependencies>

<build>
<resources>
<!-- Bundle sources with the jar, so they are visible to GWT's compiler -->
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.java</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>

<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>${maven-checkstyle-plugin.version}</version>

<configuration>
<configLocation>/gwtp-checkstyle.xml</configLocation>
<suppressionsLocation>/gwtp-suppressions.xml</suppressionsLocation>
<propertiesLocation>/gwtp-checkstyle.properties</propertiesLocation>
<propertyExpansion>basedir=${basedir}</propertyExpansion>
<consoleOutput>true</consoleOutput>
<failsOnError>true</failsOnError>
<linkXRef>false</linkXRef>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
</configuration>

<dependencies>
<dependency>
<groupId>com.gwtplatform</groupId>
<artifactId>gwtp-build-tools</artifactId>
<version>${gwtp.version}</version>
</dependency>
</dependencies>

<executions>
<execution>
<id>validate</id>
<phase>validate</phase>
<goals>
<goal>checkstyle</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -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 <T> The resource used by this delegate.
*/
public abstract class AbstractResourceDelegate<T> implements ResourceDelegate<T>, Cloneable {
private static final AsyncCallback<Object> NO_OP_CALLBACK = new AsyncCallback<Object>() {
@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<T> withDelegatingDispatchRequest(DelegatingDispatchRequest delegatingDispatchRequest) {
AbstractResourceDelegate<T> delegate = createCopy();
delegate.delegatingDispatchRequest = delegatingDispatchRequest;

return this;
}

@Override
public T withoutCallback() {
return withCallback(NO_OP_CALLBACK);
}

@Override
public T withCallback(AsyncCallback<?> callback) {
AbstractResourceDelegate<T> delegate = createCopy();
delegate.callback = callback;

return delegate.asResource();
}

@SuppressWarnings({"unchecked"})
protected <R> void execute(RestAction<R> action) {
DispatchRequest dispatchRequest = dispatcher.execute(action, (AsyncCallback<R>) callback);

if (delegatingDispatchRequest != null) {
delegatingDispatchRequest.setDelegate(dispatchRequest);
}
}

protected void copyFields(AbstractResourceDelegate<?> delegate) {
delegate.delegatingDispatchRequest = delegatingDispatchRequest;
delegate.callback = callback;
}

protected abstract AbstractResourceDelegate<T> newInstance();

protected abstract T asResource();

private AbstractResourceDelegate<T> createCopy() {
AbstractResourceDelegate<T> delegate = newInstance();
copyFields(delegate);

return delegate;
}
}
Original file line number Diff line number Diff line change
@@ -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&lt;?&gt;}s but the
* result type directly, you will need to inject a {@link ResourceDelegate} to use your resource.
* <p/>
* 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.
* <p/>
* The following example shows how to retrieve the {@link DispatchRequest} and delete a potential car:
* <pre><code>
* {@literal @}Path("/cars")
* public interface CarsResource {
* CarResource car(int id);
* }
* <p/>
* public interface CarResource {
* {@literal @}DELETE
* void delete();
* }
* <p/>
* public class CarPresenter {
* private final ResourceDelegate&lt;CarsResource&gt; carsResourceDelegate;
* <p/>
* {@literal @}Inject
* CarPresenter(ResourceDelegate&lt;CarsResource&gt; carsResourceDelegate) {
* this.carsResourceDelegate = carsResourceDelegate;
* }
* <p/>
* {@literal @}Override
* public void onReveal() {
* DelegatingDispatchRequest dispatchRequest = new DelegatingDispatchRequest();
* <p/>
* carsResourceDelegate
* .withDelegatingDispatchRequest(dispatchRequest)
* .withCallback(new AsyncCallback&lt;Void&gt;() {/{@literal * snip *}/});
* .car(8)
* .delete();
* }
* }
* </code></pre>
*
* @param <T> The type of the resource used by this delegate.
*/
public interface ResourceDelegate<T> {
/**
* 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<T> 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();
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Loading

0 comments on commit 18e3e8b

Please sign in to comment.