The Support Pattern

January 31, 2009

Today I thought I might write about a little pattern that I like to call the support pattern. It’s probably got some other name, or exists as a composite of one of the original GoF patterns but I always refer to it as the support pattern. The reason I call it the support pattern is because of the Java PropertyChangeSupport class. If you’re not familiar with it, the PropertyChangeSupport class provides a simple means for POJOs to fire an event when a property value changes. Clients can then subscribe to these change events and decide what to do with them. This is particularly useful in MVC type scenarios where changes in the Model need to be propagated to the View. Really when it comes down to it the support pattern is best employed in situations where you want to augment an existing object with some commonly used behavior that doesn’t necessarily fall within what you would consider to be the object’s primary purpose (remember the single responsibility OO design principle).

Anyway last night I found myself needing to add tagging behavior to my Transaction and BudgetItem objects in my personal finance app Freedom. Being that I had to add this behavior in more than one place it was an immediate candidate for some level of encapsulation and/or abstraction. Say hello to the support pattern!

The ITaggable interface

First I created an interface that defines what a taggable object can do.

ITaggable.java

/**
 * Interface for tagging objects.
 *
 * @since 30/01/2009 7:36:24 AM
 * @revision $Revision$ $Date$
 * @author Dave Kuhn
 *
 */
public interface ITaggable {

	/**
	 * Each taggable object that has at least one tag set has a primary tag. The
	 * primary tag is usually the first tag set via {@link #tag(String)} or
	 * {@link #tag(String[])}. Otherwise the primary tag can be set explicitly
	 * via {@link #setPrimaryTag(String)}.
	 *
	 * @return the primary tag
	 */
	String getPrimaryTag();

	/**
	 * Returns all the tags associated with this object.
	 *
	 * @return all tags for this
	 */
	String[] getTags();

	/**
	 * Returns true if this object has the specified tag.
	 *
	 * @param tag the tag to check
	 * @return true if object has tag, otherwise false
	 */
	boolean hasTag(String tag);

	/**
	 * Removes the specified tag from this object. This has no effect if the tag
	 * does not exist on the object.
	 *
	 * @param tag the tag to remove
	 */
	void removeTag(String tag);

	/**
	 * Removes the specified tags from the object. This has no effect if the any
	 * or all of the tags in the array do not exist for this object.
	 *
	 * @param tags the tags to remove
	 */
	void removeTags(String[] tags);

	/**
	 * Sets the primary tag for this object. If the tag does not exist, it is
	 * added to the greater list of tags for this object. If it already exists
	 * then the matching tag is simply set to be the primary tag.
	 *

	 * Where a primary tag has already been set (e.g. via the first call to
	 * {@link #tag(String)}) then setting the primary tag simply relegates the
	 * incumbent tag to being a regular tag, it does not remove it.
	 *
	 * @param tag the tag to set as the primary tag
	 */
	void setPrimaryTag(String tag);

	/**
	 * Tags this object with the specified tag.
	 *
	 * @param tag the tag to apply to this
	 */
	void tag(String tag);

	/**
	 * Tags this object with the specified tags.
	 *
	 * @param tags an array of tags to apply to this
	 */
	void tag(String[] tags);

}

The TagSupport class

The next step was to create a support class that implemented the ITaggable interface. This encapsulates all the necessary knowledge of how to tag an object.

TagSupport.java

/**
 * Utility class that provides tagging support for objects implementing the
 * {@link ITaggable} interface.
 *
 * @since 30/01/2009 6:52:28 PM
 * @version $Revision$ $Date$
 * @author Dave Kuhn
 */
public class TagSupport implements ITaggable {

	private String primaryTag;
	private List tags;

	/**
	 * Constructs a TagSupportProvider initialised with an empty tag list.
	 */
	public TagSupport() {
		tags = new ArrayList();
	}

	/**
	 * Constructs a TagSupportProvider initialised with the tags provided.
	 *
	 * @param tags tags to add
	 */
	public TagSupport(String[] tags) {
		this();
		tag(tags);
	}

	@Override
	public String getPrimaryTag() {
		return primaryTag;
	}

	@Override
	public String[] getTags() {
		return tags.toArray(new String[tags.size()]);
	}

	@Override
	public boolean hasTag(String tag) {
		return tags.contains(tag);
	}

	@Override
	public void removeTag(String tag) {
		// Ignore nulls, empty strings and tags which aren't in list.
		if (tag == null || tag.isEmpty() || !hasTag(tag))
			return;

		tags.remove(tag);

		/* First tag in list becomes primary tag in the event that tag being
		 * removed is the primary tag. If the item being removed is the last
		 * in the list (i.e. there are no tags left) the primary tag is set to
		 * null. */
		if (primaryTag.equals(tag) && tags.size() > 0)
			primaryTag = tags.get(0);
		else
			primaryTag = null;
	}

	@Override
	public void removeTags(String[] tags) {
		for (String tag : tags)
			removeTag(tag);
	}

	@Override
	public void setPrimaryTag(String tag) {
		if (!hasTag(tag))
			tag(tag);

		primaryTag = tag;
	}

	@Override
	public void tag(String tag) {
		// Ignore nulls, empty strings and duplicates.
		if (tag == null || tag.isEmpty() || hasTag(tag))
			return;

		// First tag becomes primary tag by default.
		if (tags.size() == 0)
			setPrimaryTag(tag);

		tags.add(tag);
	}

	@Override
	public void tag(String[] tags) {
		for (String tag : tags)
			tag(tag);
	}

}

Putting it to work

Lastly I added a TagSupport instance to my Transaction and BudgetItem classes, implemented the ITaggable interface, and then delegated to my member TagSupport instance like so:

Transaction.java

/**
 * A stateful transaction implementation.
 *
 * @since 30/01/2009 9:54:00 PM
 * @version $Revision$ $Date$
 * @author Dave Kuhn
 */
public class Transaction implements ITaggable {
	...

	private ITaggable tagSupport = new TagSupport();

	...

	@Override
	public void tag(String tag) {
		tagSupport.tag(tag);
	}

}

That’s all there is to it!

Some might claim that I’m rehashing a topic that has already been solved however I’m compelled to write this article as I personally struggled to connect all the disparate dots in order to make Google Web Toolkit (GWT) integrate with the Spring Framework. This is intended to be the article that I wished I’d stumbled upon in the first 10 minutes of looking for a solution to the problem. Before we start I’d also like to acknowledge that I’ve borrowed heavily from the concepts that people like George Georgovassilis pioneered. There’s nothing really new or groundbreaking here, I’ve simply combined all the knowledge that I’ve gained from all over the place into one convenient guide.

Update: Since publishing this article I’ve been contacted by Richard Bondi with regard to some great work that he’s also done on the matter of which I wish to also acknowledge his fantastic work. If you want to know more on this subject then I heartily recommend you check out his article on how to Integrate Spring with GWT and once you’ve done that, stubbing in hosted mode. Thanks again Richard.

Why would anyone want to integrate Spring and GWT?

If you’re familar with GWT (and I’m assuming you are) you may have marvelled, as I did, with how seamless and uncomplicated client-server interaction is. You are also probably aware that GWT RPC services are not web services. In fact they’re just plain old servlets that are endowed with an ability to serialize and deserialize information across the wire using a rather compact encoding developed by Google.

The RemoteServiceServlet class is great insofar as it handles all the grunt work of the RPC side of things whilst letting you get down to the business of writing your business logic for your services. The fact that you have to extend RemoteServiceServlet raises a few gnarly questions however:

1. What happens if I’ve already got my business logic implemented as an existing Java class with it’s own inheritance heirarchy?

2. What if you want to reuse that same class to implement a SOAP web service or any other RESTful interface for that matter?

3. What if I want to use the service class internally and don’t need all the RPC stuff?

4. What if something better than GWT comes out and I just want to replace the presentation layer technology?

Ouch! Suddenly extending RemoteServiceServlet starts to look a bit painful and far less useful than it did before. Now we could either try to imitate the ostrich and pretend that this isn’t an issue, or we could address the problem head on. We’re going to take the latter option.

Enter Spring

One answer to the above questions is to use a technique called Dependency Injection (DI). The Spring Framework happens to come with quite a good IoC container implementation and has become an industry standard. Without getting into the details about what DI is and how it works, Spring was designed with the intent of making your code as loosely coupled to its dependencies as possible and promoting POJO based development for enterprise applications.

So what does this look like in practice? Well currently out RPC model looks like this:

Standard GWT RPC plumbing

Standard GWT RPC plumbing (image courtesy of Google)

Ideally we want to look more like this:

New and improved GWT RPC plumbing with Spring integration

New and improved GWT RPC plumbing with Spring integration

Not too different really. With the exception of two new classes and two new interfaces you could be forgiven for thinking that I’d hardly done a thing, not to mention that three of them are Spring MVC classes! Well that’s actually the truth of it, I’ve done bugger all. It’s also worth noting that all the work is on the server-side with nothing new for the client.

Going back to our sample code, essentially all we need to do now is wire up our DispatcherServlet (a front-controller) and point it to our new GwtRpcController class for all RPC requests. We’ll also need to break the inheritance chain on the YourServiceImpl (a.k.a. QuoteServiceImpl) class to prove a point.

Getting down to business

In order to illustrate the point, I’m going to walk you through an adaptation of a random quote program from another GWT tutorial originally found on netbeans.org. I’ve adapted the original example to Eclipse as it’s my IDE of choice and I’m using Tomcat 6 as my test server.

For this tutorial you’ll require the following

If you’re impatient like me you might also want access to the project source code which can be found here. Please note that you’ll have to download the dependencies separately as I don’t want to inadvertently infringe on any copyright restrictions.

Okay so maybe I lied a little beforehand, there’s a bit more that we have to do in the way of re-jigging our project structure to make it all hang together and work. Anyway, time to cut some code!

Creating the project

Open the terminal/command line and go to the folder where you’ve unzipped the GWT SDK. And run the following command to create your eclipse project:

./projectCreator -ant gwt-wisdom -eclipse gwt-wisdom -out /home/dave/eclipse/workspace/gwt-wisdom

Note: replace the -out parameter argument with your own project destination. Also I’m using Linux, Windows users should just get rid of the preceeding “./” and change all forward slashes to back slashes and the commands will work just fine.

Now run this command to create your GWT project structure:

./applicationCreator -eclipse gwt-wisdom -out ~/eclipse/workspace/gwt-wisdom org.example.gwtwisdom.client.GwtWisdom

Again replace the -out parameter with the same location you used for the command above

Next import the new application into Eclipse using the File > Import menu option. Choose the Existing Projects option from under the General category and then browse to where ever you output the project files to.At this stage you should be able to hit run and test that the application skeleton is working.

Additional project setup

So that your application will compile once we start adding Spring into the mix add the the Spring framework and Spring Web MVC jars (spring.jar, spring-webmvc.jar) to your applications build path.

Now we need somewhere to put all our configuration files and deployment time dependencies. Create the following directory structure at the base of your project WebContent/WEB-INF/lib. This is almost identical to the Eclipse WTP project structure and will become useful later when we automate the project build with an Ant script.

In the WebContent/WEB-INF folder create a web.xml file an copy and paste the text below into it:

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.5"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <display-name>GWT Wisdom</display-name>

    <!-- Initialise the Spring MVC DispatcherServlet -->
    <servlet>
        <servlet-name>spring</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- Map the DispatcherServlet to only intercept RPC requests -->
    <servlet-mapping>
        <servlet-name>spring</servlet-name>
        <url-pattern>*.rpc</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>GwtWisdom.html</welcome-file>
    </welcome-file-list>

</web-app>

Now I’m aware that the integrated Tomcat instance that comes with GWT has a web.xml file but we can’t actually modify the Tomcat web.xml file as it is dynamically regenerated by the compiler every time we start hosted mode. Hence why we’re creating our own.

If you were paying attention in the last step you would have seen that we defined a servlet in the web.xml file. This is the Spring Web MVC DispatcherServlet which will intercept our RPC requests and route them to the correct service. Each DispatcherServlet instance requires a bean definition file of the same with a “-servlet.xml” suffix. So in the same folder create a file called spring-servlet.xml and copy and paste the following into it:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    <!-- The application context definition for the DispatcherServlet -->

    <!-- Maps the request through to a concrete controller instance -->
    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <value>
            /**/quote.rpc=quoteController
            </value>
        </property>
    </bean>

    <!-- GwtRpcController wraps our service in order to decode the incoming -->
    <!-- request then delegates processing of the call to the POJO service -->
    <!-- and then encodes the return value forwarding the response. -->
    <bean id="quoteController" class="org.example.gwtwisdom.server.GwtRpcController">
        <property name="remoteService">
            <bean class="org.example.gwtwisdom.server.QuoteServiceImpl" />
        </property>
    </bean>

</beans>

This will be used to configure the DispatcherServlet to route calls to our services. It does so via the SimpleUrlHandlerMapper which uses regular expression syntax to match the request url to a controller bean ID.

Now you’ll need to add the following jars to the WebContent\WEB-INF\lib folder: commons-logging.jar, gwt-servlet.jar, gwt-user.jar, spring.jar, spring-webmvc.jar. This is necessary for when it comes time to deploy the app.

Creating the RPC quote service

Next we’re going to create our RPC service. Start by creating a subpackage of the *.client package and call it “service”.

Create a new interface called QuoteService and make it look like the code below:

package org.example.gwtwisdom.client.service;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath("quote.rpc")
public interface QuoteService extends RemoteService {
    public String getQuote();
}

Those already familiar with GWT will know that the next step is to create an async service interface to pair with the QuoteService interface we just created. Create a new interface again in the *.client.service package called QuoteServiceAsync and modify it so it looks like the following:

package org.example.gwtwisdom.client.service;

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface QuoteServiceAsync {
    public void getQuote(AsyncCallback<String> callback);
}

Enough of interfaces. Now it’s time to code up the actual service itself. Create a new package under the org.example.gwtwisdom package called server. Once that’s done create a new class and call it QuoteServiceImpl.

package org.example.gwtwisdom.server;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.example.gwtwisdom.client.service.QuoteService;

public class QuoteServiceImpl implements QuoteService {

    private Random randomizer = new Random();
    private static List<String> quotes = new ArrayList<String>();

    static {
        quotes.add("No great thing is created suddenly - Epictetus");
        quotes.add("Well done is better than well said - Ben Franklin");
        quotes.add("No wind favors he who has no destined port - Montaigne");
        quotes.add("Sometimes even to live is an act of courage - Seneca");
        quotes.add("Know thyself - Socrates");
    }

    public String getQuote()
    {
        return quotes.get(randomizer.nextInt(quotes.size()));
    }

}

Now some of you might be saying to yourselves “hey wait a minute, don’t we have to extend RemoteServiceServlet?”. Well read on. I’m about to show you a magic trick straight out of the Gang of Four (GoF) playbook.

To cap it all off before we continue, we should probably update our module configuration file to include our new service mapping. Copy and paste the following into your GwtWisdom.gwt.xml file:

<!-- RPC service servlet declarations                             -->
<servlet path="/quote.rpc"
    class="org.example.gwtwisdom.server.QuoteServiceImpl"/>

All rise for the GwtRpcController

Here’s the fun part, now we’re going to create the GwtRpcController. In the org.example.gwtwisdom.server package create a class called, you guessed it GwtRpcController. Then copy and paste the following code into it:

package org.example.gwtwisdom.server;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.context.ServletContextAware;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPC;
import com.google.gwt.user.server.rpc.RPCRequest;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class GwtRpcController extends RemoteServiceServlet implements
        Controller, ServletContextAware {

    private ServletContext servletContext;

    private RemoteService remoteService;

    private Class remoteServiceClass;

    public ModelAndView handleRequest(HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        super.doPost(request, response);
        return null;
    }

    @Override
    public String processCall(String payload) throws SerializationException {
        try {

            RPCRequest rpcRequest = RPC.decodeRequest(payload,
                    this.remoteServiceClass);

            // delegate work to the spring injected service
            return RPC.invokeAndEncodeResponse(this.remoteService, rpcRequest
                    .getMethod(), rpcRequest.getParameters());
        } catch (IncompatibleRemoteServiceException ex) {
            getServletContext()
                    .log(
                            "An IncompatibleRemoteServiceException was thrown while processing this call.",
                            ex);
            return RPC.encodeResponseForFailure(null, ex);
        }
    }

    @Override
    public ServletContext getServletContext() {
        return servletContext;
    }

    public void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

    public void setRemoteService(RemoteService remoteService) {
        this.remoteService = remoteService;
        this.remoteServiceClass = this.remoteService.getClass();
    }

}

This class works on the principle that it is both a RemoteServiceServlet AND a Spring MVC Controller (don’t you just love polymorphism?) meaning that it can not only be used as a target by the DispatcherServlet for handling incoming RPC requests, but also to decode the incoming RPC requests. Remember I previously mentioned the Gang of Four? Well you’re looking right at the strategy pattern baby. Pretty cool huh?

Upon receiving a request we delegate handling of this to the Controller’s superclass (the RemoteServiceServlet) to handle the unmarshalling of the request. We then intercept the processing of the message by overriding RemoteServiceServlet.processCall method and delegate the processing to the RemoteService POJO.

Where did we get this magical POJO from you ask? Well we injected it into the class via the setRemoteService setter back in the spring-servlet.xml bean definition file earlier.

Okay one last thing before we continue on to preparing the app for deployment. We need to tie all of this together by doing some work to display our quotes in the GWT entry point class and fixing up some of the HTML.

In the GwtWisdom.html file copy and paste the following in below the body tag:

<p>
This is an AJAX application that retrieves a random quote from
the Random Quote service every three seconds. The data is retrieved
and the quote updated without refreshing the page!
</p>

Once you’ve done that, replace the method body of the GwtWisdom classes onModuleLoad method with the following:

    public void onModuleLoad() {
        final Label quoteText = new Label();

        Timer timer = new Timer() {

            public void run() {
                // create an async callback to handle the result:
                AsyncCallback<String> callback = new AsyncCallback<String>() {

                    public void onFailure(Throwable t) {
                        // display error text if we can't get the quote:
                        quoteText.setText("Failed to get a quote");
                    }

                    public void onSuccess(String result) {
                        // display the retrieved quote in the label:
                        quoteText.setText(result);
                    }
                };
                QuoteServiceAsync service = (QuoteServiceAsync) GWT
                        .create(QuoteService.class);
                service.getQuote(callback);
            }
        };

        timer.scheduleRepeating(3000);
        RootPanel.get().add(quoteText);
    }

Note: since this is client-side code be careful that you use the google timer implementation as opposed to the stock Java one otherwise it won’t work like you expect.

That should take care of the display side of things. If you want you can also jazz it up a bit by putting some styles in the GwtWisdom.css file.

Ditching the GWT tomcat server

Remember that earlier on we created our own custom web.xml to initialise the Spring context via a DispatcherServler? Well because of this we can no longer rely upon the integrated tomcat server that comes with GWT to run our app. Fortunately the hosted mode browser comes with a -noserver switch which allows us to configure it to use the server of our choice during development.

There’s a bit of a knack to getting this running all running nice and seamlessly within Eclipse. First we’re going to have to compile the application then package it up and deploy it to the server just this once just so the server has knowledge of your app. Don’t worry, once it’s running in hosted mode the app will use your workspace code, but trust me we need to do this just once.

I’m actually going to use Ant to build and package the app. Conveniently, this is going to work out great in future when I need to bundle the app up into a war file and deploy it elsewhere.

First create a file called build.xml in the root of your project and copy and paste the script below into the build file.

<?xml version="1.0" encoding="utf-8"?>
<project name="gwt-wisdom" default="compile" basedir=".">
    <description>
        Build file. This is used to package up your project as a war, if you
        want to distribute/deploy it. This isn't needed for normal operation.
    </description>

    <property file="build.number"/>

    <!-- GWT properties - change these to suit your distribution (i.e. windows, mac or linux) -->
    <property name="platform" value="linux"/>
    <property name="gwt.home" value="/home/dave/eclipse/lib/gwt/linux/1.5.1"/>

    <!-- Build properties - shouldn't need changing -->
    <property name="name" value="GwtWisdom"/>
    <property name="module" value="org.example.gwtwisdom.GwtWisdom"/>
    <property name="src.dir" value="src"/>
    <property name="web.dir" value="WebContent"/>
    <property name="lib.dir" value="${web.dir}/WEB-INF/lib"/>
    <property name="bin.dir" value="${web.dir}/WEB-INF/classes"/>
    <property name="test.dir" value="test"/>
    <property name="out.dir" value="build"/>
    <property name="dist.dir" value="dist"/>

    <!-- set classpath -->
    <path id="project.class.path">
        <pathelement path="${java.class.path}/"/>
        <pathelement path="${lib.dir}/gwt-user.jar"/>

        <!-- Additional dependencies go here -->
        <pathelement path="${lib.dir}/spring.jar"/>
        <pathelement path="${lib.dir}/spring-webmvc.jar"/>
        <pathelement path="${lib.dir}/commons-logging.jar"/>
    </path>

    <target name="clean">
        <delete file="${dist.dir}/${name}.war"/>
        <!-- Delete the bin directory tree -->
        <delete>
            <fileset dir="${bin.dir}" includes="**/*.class"/>
        </delete>
    </target>

    <target name="compile" description="Compile both client &amp; server code">
        <!-- cross-compile client-side java classes -->
        <mkdir dir="${out.dir}"/>
        <java classname="com.google.gwt.dev.GWTCompiler" dir="${basedir}" fork="true">
            <jvmarg value="-Xmx256M"/>
            <classpath>
                <pathelement path="${java.class.path}"/>
                <pathelement location="${src.dir}"/>
                <pathelement location="${bin.dir}"/>
                <pathelement path="${gwt.home}/gwt-user.jar"/>
                <pathelement path="${gwt.home}/gwt-dev-${platform}.jar"/>
            </classpath>
            <arg line="-out ${basedir}/${out.dir}"/>
            <arg value="%*"/>
            <arg value="${module}"/>
        </java>
        <copy todir="${web.dir}">
            <fileset dir="${out.dir}/${module}"/>
        </copy>

        <!-- compile server-side java classes -->
        <mkdir dir="${web.dir}/WEB-INF/classes"/>
        <javac srcdir="${src.dir}:${test.dir}" destdir="${bin.dir}" includes="**" debug="on" debuglevel="lines,vars,source" source="1.5">
            <classpath refid="project.class.path"/>
        </javac>
    </target>

    <target name="package" depends="compile" description="Package up the project as a war">
        <mkdir dir="${dist.dir}"/>
        <war destfile="${dist.dir}/${name}.war" webxml="${web.dir}/WEB-INF/web.xml">
            <fileset dir="${out.dir}/${module}">
                <include name="**/*.*"/>
            </fileset>
            <fileset dir="${web.dir}">
                <include name="**/*.*"/>
                <exclude name="WEB-INF/web.xml"/>
            </fileset>
        </war>
    </target>

    <target name="all" depends="package"/>

</project>

Run the build script using the package target (Alt+Shift+X then Q). Alternatively if you’re using Ant from the command line then running “ant package” from the project directory should do the trick. This should compile and package the app automatically for you into a war file.

If the build fails for any reason you might have to edit some of the property elements listed at the top of the file depending on your platform (windows, mac or linux) and where you unzipped your GWT libs. Just change these as appropriate and everything should work fine.

From here you can just go ahead and fire up Tomcat and deploy the war file onto the app server either via the admin console or the autodeploy directory. At this point you should be able to test your application by opening your web browser and navigating to http://localhost:8080/GwtWisdom/GwtWisdom.html. If this works then we’re all good to proceed to the next stage.

Getting hosted mode to run with -noserver

Okay we’re getting close now. This step isn’t essential really, what I’ve shown you so far should be more than enough to demonstrate the application in action. If however you want to continue debugging and testing your app in hosted mode for development like I do then read on.

We’re going to need to edit the launch configuration in order to run the app in hosted mode using our own Tomcat instance as opposed to the integrated Tomcat server. You can do this one of two ways, either edit the GwtWisdom.launch file directly or you edit them via the Eclipse run configuration manager. We’re going to do it via the run configuration manager.

You can open the manager by right-clicking on the project Run As > Run Configurations. Once there, you should see in the left-hand tree view under Java Applications the GwtWisdom launch configuration. Select it and then click the Arguments tab over on the right hand side. Check that the program arguments resemble those below. Your configuration may differ slightly as far as the port is concerned but should read something like:

-out www GwtWisdom/GwtWisdom.html
-noserver
-port 8080

This configuration tells the hosted mode browser to use an external server on the given port and where it should find your applications home page on the server.

You might also have to add the contents of your WebContent/WEB-INF/lib folder to the classpath tab in order to get it working but try it first by clicking apply and then run.

Sweet sweet success!

All that’s left to do is run the project. If you’ve configured it correctly it should fire up the app in all its hosted glory. Your services are now free from the tyranny of RemoteServiceServlet!

Now if only we could figure out a way to remove the dependency on the QuoteService interface from the service. Then it really would be a bona-fide POJO…another day, another challenge.

Follow

Get every new post delivered to your Inbox.