|
#1
|
|||
|
|||
|
A few weeks ago, I took a few days of from regular work activities to do some research. Main topic was Ajax, and more specifically the Google Web Toolkit (GWT) and its applications.
As most of the GWT is related to client-side code, it has very little to do with Spring. As I see it now, it is more like a `Swing for the browser' like framework. However, GWT includes its own propriatery Remote Procedure Call (RPC) mechanism to fetch data from the server at runtime. Reading about GWT RPC servlets, I started wondering whether Spring could be of any help hosting the server-side implementation of these GWT RPC services. What I found on the internet was the following: * http://roberthanson.blogspot.com/200...pc-in-gwt.html * http://g.georgovassilis.googlepages....gthegwthandler Especially the second link provides a reasonably tight integration between Spring and GWT RPC. However, I wanted to be able to use standard Spring infrastructure together with GWT RPC, so I came up with the following integration idea... What I wanted to be able to write was the following piece of Spring configuration in my DispatcherServlet container: Code:
<bean class="package.services.implementation.TestServiceOneImpl">
<property name="someProperty" value="someValue"/>
</bean>
<bean class="package.services.implementation.TestServiceTwoImpl"/>
...
Code:
public interface TestServiceOne implements RemoteService {
public Result call( int a, int b );
}
public interface TestServiceOneAsync {
public Result call( int a, int b, AsyncCallback async );
}
public class TestServiceOneImpl implements TestServiceOne, RemoteService {
public Result call( int a, int b ) {
return new Result( a*b );
}
}
I figured there had to be a way to achieve this, similar to the way Controllers get picked up by the Spring MVC implementation, either by annotation, by classname or by bean name. To achieve this, I created (for demonstration purposes) the following class Code:
public class GwtHandlerMapping extends AbstractDetectingUrlHandlerMapping {
private static final Logger logger = Logger.getLogger(GwtHandlerMapping.class.getName());
protected String[] determineUrlsForHandler(String beanName) {
Object bean = getApplicationContext().getBean(beanName);
if ( bean instanceof RemoteService) {
return new String[]{ "/" + url };
}
return null;
}
}
Unfortunately, the above handler mapping only resolves the URL to the corresponding Handler bean. However, the DispatcherServlet has no way of knowing HOW to execute the handler to satisfy the http request. Spring MVC introduces the concept of a HandlerAdaptor to provide such mapping. Enter the GwtServiceHandlerAdapter. Before investigating the small-print details of this Adaptor, lets first summarize the complete configuration and its current execution flow Code:
<bean class="package.services.implementation.TestServiceOneImpl">
<property name="someProperty" value="someValue"/>
</bean>
<bean class="package.services.implementation.TestServiceTwoImpl"/>
<bean class="package.gwt.GwtHandlerMapping"/>
<bean class="package.gwt.GwtServiceHandlerAdapter"/>
...
Code:
HTTP GET /spring/TestServiceOneImpl --> (web.xml maps to) DispatcherServlet --> Find a suitable handler using the active HandlerMapping(s) >> /TestServiceOneImpl is mapped to the TestServiceOneImpl bean --> Find a suitable handler adaptor for the matched handler >> TestServiceOneImpl (extending RemoteService) is mapped on GwtServiceHandlerAdapter --> Call handle on the HandlerAdapter The implementation for the GwtServiceHandlerAdapter is taken from GWT example code found at: http://google-web-toolkit.googlecode...r/rpc/RPC.html Unfortunately, in the current implementation of the RemoteServiceServlet, the readPayloadAsUtf8 and writeResponse methods are private members. A scheduled refactoring http://code.google.com/p/google-web-...detail?id=1179 will extract these and make them static public or move them to a helper class. For the moment, for this implementation to work, you'll have to duplicate them yourself from the RemoteServiceServlet's source code. This results in the following implementation of the GwtServiceHandlerAdapter: Code:
public class GwtServiceHandlerAdapter implements HandlerAdapter {
private static final Logger logger = Logger.getLogger(GwtServiceHandlerAdapter.class.getName());
public long getLastModified(HttpServletRequest request, Object handler) {
return -1;
}
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String responseString;
try {
try {
assert handler instanceof RemoteService;
logger.info( "Handling request " + request.getRequestURI() );
String requestString = GwtUtil.readPayloadAsUtf8( request );
RPCRequest rpcRequest = RPC.decodeRequest( requestString );
final Method method = rpcRequest.getMethod();
try {
Object result = method.invoke(handler, rpcRequest.getParameters());
responseString = RPC.encodeResponseForSuccess(method, result);
} catch (IllegalArgumentException e) {
SecurityException securityException = new SecurityException("Blocked attempt to invoke method " + method);
securityException.initCause(e);
throw securityException;
} catch (IllegalAccessException e) {
SecurityException securityException = new SecurityException("Blocked attempt to access inaccessible method "
+ method + (handler != null ? " on target " + handler : ""));
securityException.initCause(e);
throw securityException;
} catch (InvocationTargetException e) {
responseString = RPC.encodeResponseForFailure(method, e.getCause());
}
} catch (IncompatibleRemoteServiceException e) {
responseString = RPC.encodeResponseForFailure(null, e);
}
} catch (Throwable e) {
responseString = RPC.encodeResponseForFailure(null, new SerializableException( e.getMessage() ));
}
GwtUtil.writeResponse(request,response,responseString );
return null;
}
public boolean supports(Object handler) {
return handler instanceof RemoteService;
}
}
|
|
#2
|
|||
|
|||
|
The purpose of my post was to share my experiment with the community, and to provoke some reactions on the cleanness / usability of this approach...
|
|
#3
|
|||
|
|||
|
This is a great post. Any chance you'd want to contribute the functionality to the spring-modules project?
__________________
Peter Mularien | Blog SCJP 5, Oracle DBA Any postings are my own opinion, and should not be attributed to my employer. |
|
#4
|
|||
|
|||
|
It would be nice to contribute these ideas to the community. I'd like to see a few more reactions/idea's first though.
And there's still the issue that I currently need to extract some source from the RemoteServiceServlet to get this approach to work (Now packaged in my GWTUtil file). |
|
#5
|
|||
|
|||
|
Hi,
I still have to try your approach, but that approach in conjunction with a generator that automatically generates endpoints would be great. I'm experimenting with that, based on this post: http://groups.google.com/group/Googl...c7b9bae8f1f729 |
|
#6
|
|||
|
|||
|
Hello,
If you plan on implementing a GWT-Spring integration into your project, following links might be of use: * http://gwt-widget.sourceforge.net/?q=node/49 * http://blog.digitalascent.com/2007/1...ing-2x_12.html The second link presents an approach very similar to the one I discussed above. |
![]() |
| Thread Tools | |
| Display Modes | |
|
|