Community   SpringSource   Projects    Downloads    Documentation    Forums    Training   Exchange   Blogs

Go Back   Spring Community Forums > Core Spring Projects > Web

Reply
 
Thread Tools Display Modes
  #1  
Old Apr 17th, 2008, 08:54 AM
Daboe_ Daboe_ is offline
Junior Member
 
Join Date: Apr 2008
Posts: 6
Default GWT RPC services and spring

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"/>
    ...
where each TestService implements a GWT RPC service interface, e.g.
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 );
	}
}
What I wanted to do was to get the above Spring configuration to effectively host the RPC services *without adding a lot of extra configuration* or altering the implementation classes. In particular, I didn't want to extend the RemoteServiceServlet from GWT, because my RPC implementation already resides inside a servlet, namely the Spring DispatcherServlet.

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;
    }
}
(where the base class is standard Spring). Adding this bean to your bean container will automatically associate each bean that extends RemoteService bean to the url /<beanName>

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"/>
    ...
Using this configuration (and this configuration alone) I was able to make an RPC call from a GWT client application to our TestServiceOne method through the DispatcherServlet setup. The flow is approximately as follows:
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
In my personal oppinion, the above Spring configuration is clean and compact, and adheres to the DispatcherServlet handler adapter methodology.

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;
    }
}
Reply With Quote
  #2  
Old Apr 17th, 2008, 08:55 AM
Daboe_ Daboe_ is offline
Junior Member
 
Join Date: Apr 2008
Posts: 6
Default

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...
Reply With Quote
  #3  
Old Apr 17th, 2008, 02:51 PM
pmularien pmularien is offline
Senior Member
 
Join Date: Sep 2004
Location: Manchester, NH
Posts: 760
Default

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.

Reply With Quote
  #4  
Old Apr 18th, 2008, 03:44 AM
Daboe_ Daboe_ is offline
Junior Member
 
Join Date: Apr 2008
Posts: 6
Default

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).
Reply With Quote
  #5  
Old Apr 24th, 2008, 10:40 AM
miguelping miguelping is offline
Junior Member
 
Join Date: Jan 2008
Posts: 8
Default

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
Reply With Quote
  #6  
Old Apr 25th, 2008, 10:04 AM
Daboe_ Daboe_ is offline
Junior Member
 
Join Date: Apr 2008
Posts: 6
Default

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.
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -5. The time now is 05:32 PM.


Contegix provides first-class managed hosting and partial sponsorship of these forums.

Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.