Stubbing RPC calls in Google Web Toolkit's Hosted Mode

Level: Intermediate
Date: Originally published 10/07, updated 4/08.
Author: Richard Bondi,

In this article I describe a way to "fake" or "stub" your RPC calls in GWT's hosted mode. It allows you to deploy to web mode without having to change any code, change any configurations, and without any stub or development-only code being deployed. It's very clean, and will work with any web application setup (for example, Spring is optional).

Prerequisites

This article assumes that you have a good grasp of the following GWT fundamentals:


Preamble: Two advantages of the RPC stubbing technique

This technique allows you to not only create wireframes, something GWT is well-suited for anyway, but allows you to do so on top of servlet (as well as UI) prototypes or tracer bullets. This can be a good stone soup with which to introduce GWT gradually into your organization.


The other unique advantage of this technique is that it allows Java web programmers to specialize on programming the front end, without having to first learn a system's entire stack and its related technologies. This is something new in Java. Currently, adding a single HTML field can involve a breath-taking number of fields and technologies: the schema and sql for the corresponding table column, Hibernate, Spring, service and mapping layers, Struts or Webwork or some other framework and its tags, JSP, JSTL, Javascript libraries like Prototype, and so on. For this reasons alone, it can take a very long time before a junior Java programmer can program the UI, and front-end programming will always be slow. With this stubbing technique and GWT, all a front-end programmer essentially needs to know anymore is Java, HTML, CSS, and Javascript, and coding it can be remarkably swift. At the same time, the front-end is tightly coupled to the Java middleware. The front-end and back-end programmers' code cannot diverge wildly from each other: shared Java interfaces ensure there will be compiler errors if they do. It is the best of both worlds: front-end programming unencumbered by heavy back-end baggage, yet always in synch with it.

Step 1: How to set up GWT for stubbing

The setup has two steps. In step 1, you set up GWT to call one RPC URL for hosted mode, and another for web mode. In step 2, you stub your GWT service and configure GWT to call the stub only in hosted mode.

Step 1A: Set hosted mode to use a specific servlet

In GWT, Java services on the server-side receive an Ajax/RPC call from the client. These "services" are simply servlets which extend RemoteServiceServlet. RemoteServiceServlet itself is just an extension of javax's Servlet class. Hosted mode launches a little Tomcat server to run these servlets in. In a normal Tomcat server, you have to edit web.xml to tell Tomcat which URLs to route to which Servlets. In hosted mode though, there is no web.xml to edit; instead, you use your GWT module's *.gwt.xml file. (For web mode, which runs in a real Tomcat or other web server, you must associate URLs with servlets in the normal way.)


In the example below, the servlet tag is saying to hosted mode's Tomcat: "If you get sent a request URL that ends in 'gumby', pass that request on to the class MyExampleServiceImpl. It's a Servlet, so it will know what to do with the request." The path attribute specifies a part of a URL. The class attribute specifies the RemoteServiceServlet to use.

[Figure 1: a fictional module file MyExample.gwt.xml]
<module>
<source path ="client"/>
<public path ="public"/>

<entry-point class="com.foo.gwt.myexample.client.MyExample"/>

<servlet path ="/gumby"
         class ="com.foo.gwt.myexample.server.MyExampleServiceImpl"/>
</module>

NOTE: This <servlet> tag has no meaning in web mode. It is completely ignored when you deploy.

Step 1B: Set the client to call different URLs for hosted and web mode

We've just set up your project to use a certain servlet in hosted mode only, if a certain URL is used. But how do we tell the GWT client code to call this URL in hosted mode?


This is described on the official GWT site here. What is not explained there is how to make your client-side code automatically detect whether it is in hosted or web mode, and use different URLs for each.

Figure 2 shows Google's example code for making an RPC call on the client-side; Figure 3 shows how to modify it to detect hosted and web mode.
 
[Figure 2: Client-side code for making an RPC call to a server-side service; the code is from Google's example here.]
// (1) Create the client proxy. Note that although you are creating the
// service interface proper, you cast the result to the asynchronous
// version of
// the interface. The cast is always safe because the generated proxy
// implements the asynchronous interface automatically.
//
MyEmailServiceAsync emailService = (MyEmailServiceAsync) GWT.create(MyEmailService.class);

// (2) Specify the URL at which our service implementation is running.
// Note that the target URL must reside on the same domain and port from
// which the host page was served.
//
ServiceDefTarget endpoint = (ServiceDefTarget) emailService;
String moduleRelativeURL = GWT.getModuleBaseURL() + "email";
endpoint.setServiceEntryPoint(moduleRelativeURL);


// (3) Create an asynchronous callback to handle the result.
//
AsyncCallback callback = new AsyncCallback() {
  public void onSuccess(Object result) {
    // do some UI stuff to show success
  }

  public void onFailure(Throwable caught) {
    // do some UI stuff to show failure
  }
};


// (4) Make the call. Control flow will continue immediately and later
// 'callback' will be invoked when the RPC completes.
//
emailService.emptyMyInbox(fUsername, fPassword, callback);


We only have to change a few lines of the Figure 2 code to make it distinguish between web and hosted mode. The changes are shown in Figure 3 in bold.
 
[Figure 3: Client-side RPC code that distinguishes between hosted and web mode]
// (2) Specify the URL at which our service implementation is running.
// Note that the target URL must reside on the same domain and port from
// which the host page was served.
//
ServiceDefTarget endpoint = (ServiceDefTarget) emailService;
String moduleRelativeURL = null;
if(GWT.isScript()) // hosted mode
  moduleRelativeURL = GWT.getModuleBaseURL()  + "gumby";
else // web mode
  moduleRelativeURL = GWT.getModuleBaseURL() + "email";
endpoint.setServiceEntryPoint(moduleRelativeURL);

In Figure 1, we configured the server-side to detect any URL containing "/gumby". The line in Figure 3 moduleRelativeURL = GWT.getModuleBaseURL()  + "gumby"; adds "/gumby" to the base URL. Note that

GWT.getModuleBaseURL() is guaranteed to add a forward slash when necessary (that's what its Javadoc says), so "/gumby" or "gumby" will both work here.


We have now configured our GWT project to load on particular servlet in hosted mode, and a different servlet in web mode. We can now proceed to set up a (hosted mode) stub servlet that stubs a (web mode) production servlet.

Step 2: Stubbing your GWT Services

We now know how to make GWT call one URL in hosted mode, and another in web mode.

What we want is to call production code in hosted mode, but with fake data. That way we don't have to start our web and/or application server(s) and databases every time we want to test GWT apps. Typically this involves starting Apache, Tomcat and/or Weblogic or JBoss or whatever, which perhaps will load Spring, Hibernate or iBatis, a database, and hundreds if not thousands of objects that we absolutely do not need to test our itty bitty GWT page.

Let's pretend that MyExampleServiceImpl from Figure 1 is a RemoteServiceServlet used in production. Internally it uses a service (in the non-GWT sense of "service", i.e. it is from an application's middle-tier service layer) called com.foo.service.InfoSvc which access a database. Perhaps our application uses Spring to inject InfoSvc into MyExampleServiceImpl; perhaps not, it doesn't matter. Either way, com.foo.gwt.MyModule.MyExample.server.MyExampleServiceImpl will have either a setter for InfoSvc (like MyExampleServiceImpl.setInfoSvc(InfoSvc infoSvc)), or it will receive it through the constructor. Figure 4 shows the source code of MyExampleServiceImpl.

[Figure 4: com.foo.gwt.myexample.server.MyExampleServiceImpl.java]
public class MyExampleServiceImpl extends RemoteServiceServlet implements MyExampleService
{
  protected InfoSvc infoSvc;

  public MyExampleServiceImpl() {}

  public MyExampleServiceImpl( InfoSvc infoSvc )
  {
    this.infoSvc = infoSvc;
  }

  public String getSomeData(String someData) throws SerializableException
  {
    return invoSvc.infoData(someData);
  }
}


In web mode, you set MyExampleServiceImpl.infoSvc via the constructor (perhaps Spring sets it for you with a *-servlet.xml context, perhaps you hard-code setting it; it doesn't matter).

But if you try to make an RPC call to this class in hosted mode, invoSvc.infoData(someData) will throw a NullPointerException, because invoSvc will be null. GWT's hosted mode Tomcat server doesn't know anything about InfoSvc. As shown in Figure 1, all it knows is to instantiate MyExampleServiceImpl when a URL contains "/gumby". So our problem is how to test GWT pages that depend on MyExampleServiceImpl in hosted mode.

What we are going to is stub InfoSvc. We are going to do three things:

  1. Write MyExampleServiceImplStub, which extends MyExampleServiceImpl. (I make the word stub red to make it easier for the reader to distinguish the two classes.)
  2. In the constructor of MyExampleServiceImplStub, we create a stub of an InfoSvc instance, and set the field infoSvc to this stub. (That's why the infoSvc field has to be protected in MyExampleServiceImpl, so that it can be accessed by extending classes like MyExampleServiceImplStub.) You can populate the stub with data just like you would in a unit test.
  3. In MyExample.gwt.xml, change <servlet> to point to MyExampleServiceImplStub instead of MyExampleServiceImpl.

In hosted mode, when your client code makes an RPC call, GWT's Tomcat will instantiate MyExampleServiceImplStub using its empty constructor. Inside this constructor, we will create the InfoSvc stub, and set it to the protected field infoSvc. From that point on, all calls to MyExampleServiceImplStub will call code from MyExampleServiceImpl -- and that's the beauty of this setup. Apart from the stub data, we use our actual production, web mode code in hosted mode!

[Figure 5: com.foo.gwt.MyModule.MyExample.MyExampleServiceImplStub.java]
public class MyExampleServiceImplStub extends MyExampleServiceImpl
{
  protected InfoSvc infoSvc;

  /** GWT's Tomcat will call this constructor on the first RPC call. */
  public MyExampleServiceImpl()
  {
    super();
    infoSvc = new InfoSvc(...) // feed InfoSvc's constructor
                               // whatever it needs to stay happy:
                               // we only need to stub it!
    {
      public String infoData(String data)
      {
        return "Some stub data.";
      }
    }; // end InfoSvc stub
   } // end constructor
} // end MyExampleServiceImplStub

[Figure 6: MyExample.gwt.xml configured with a stub]
<module>

  <source path="client"/>
  <public path="public"/>

  <entry-point class="com.foo.gwt.myexample.client.MyEntryPoint"/>

  <servlet path="/gumby" class="com.foo.gwt.myexample.server.MyExampleServiceImplStub"/>

</module>


This approach is very clean. The only development-only code consists of the <servlet> tag in our *.gwt.xml file, the Stub class, and the GWT.isScript() conditional in your client code. You don't have to touch any of this to deploy to web mode, or your production environment. The <servlet> tag is for hosted mode only anyway. The Stub class -- or classes, because you'll find you want to write more than one for different kinds of stub data -- can simply be excluded from deployment by editing your deployment scripts to ignore all files in the /server directory which end in "Stub". In fact, a good place to keep them is in your unit test directory, which is obviously never deployed.


An especially nice feature of GWT and this approach is that, perhaps for the first time in Java web development, a front-end Java developer really only needs to know front-end stuff (CSS, HTML, Javascript) and Java, and nothing else. She doesn't need to understand Spring, Hibernate, JSTL, JSP, Velocity, Struts, Webwork, MVC, SQL, or anything else in that seemingly endless list of technologies. With this stubbing approach, if a middle-tier programmer changes the signature of service, the GWT stubs won't even compile anymore -- it's hard for them to get out of synch accidentally!

Finally, front-end programmers can concentrate on what they do best, the front-end. This is one of the most valuable advantages GWT offers over other Java web application frameworks' UI solutions.



Creative Commons License
This work is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States License.