Wednesday, July 20, 2011

Using GWT MVP with multiple views on the page

The Issue:
When developing a GWT app with the MVP approach it is common to make use of Activities and Places as described in the developer guide
Under this approach an ActivityManager makes use of an ActivityMapper which you define in order to respond to PlaceChangeEvents by running the relevant Activity. The Activity is passed a reference to an AcceptsOneWidget panel which is typically a container for the entire application. The Activity is responsible for constructing the application view and setting it into this container.
The potential pitfall of this approach is that every time a new activity is run we clear out the entire application view and reconstruct everything from scratch. If your application is composed of multiple distinct regions, for example a Menu and a Content area, and the view for each region can change independently of the other, then you will be forcing the browser to do extra unnecessary work to rebuild the DOM with the Menu region, even though only the Content region has changed.
A Possible Solution:
The solution is presented in two stages
1) Examine a simple approach that will allow for multiple regions on a page to be managed and changed independently of one another.
2) Explore a way to dynamically remove regions that are not required in a given context (e.g. removing the menu region when on the login screen)
We will use this simple example application layout
Login ScreenMain Screen

We will first define our application layout giving each region it's own AcceptsOneWidget panel.
appContainer = new DockLayoutPanel(Unit.PX);
mainContent = new ScrollPanel();
leftMenu = new ScrollPanel();
banner = new SimplePanel();

appContainer.addNorth(banner, 50);
appContainer.addWest(leftMenu, 300);
appContainer.add(mainContent);
For each region that we want to be able to update the view independently of the other regions (the menu and the content region) we will define a new ActivityMapper and ActivityManager
ActivityMapper contentActivityMapper = new ContentActivityMapper(clientFactory);
ActivityManager contentActivityManager = new ActivityManager(contentActivityMapper, eventBus);       
contentActivityManager.setDisplay(mainContent);
               
ActivityMapper menuActivityMapper = new MenuActivityMapper(clientFactory);
ActivityManager menuActivityManager = new ActivityManager(menuActivityMapper, eventBus);
menuActivityManager.setDisplay(leftMenu);
Note that a different panel is set into the display of each ActivityManager.
Because both managers share an EventBus, both ActivityMappers will be called whenever a PlaceChangeEvent is fired. Since each manager should only run a new activity in response to a subset of the possible Places in your app, we will make some changes to the mapper so that it only responds to the Places that affect the given region. By having the mapper hold a reference to its currently executing Activity, it can just return this same activity whenever a Place change it is not interested in gets fired. This will have no affect as the currently executing Activity will not be run again.
public class MenuActivityMapper implements ActivityMapper {

    private Activity currentActivity;
   
    @Override
    public Activity getActivity(Place place) {
        if (place instanceof MainMenuPlace)
        {
            return new MainMenuActivity();
        } else if (place instanceof SubMenuPlace) {
            return new SubMenuActivity();
        } else {
            //Some other content Place that we aren't interested in
            return currentActivity;
        } 
}  
That completes the first stage. Now say we want to allow the menu region to be hidden when the user is at the login screen and just use the content region to display the login view.
Create a new panel HideableScrollPanel which extends ScrollPanel. Set the menu to use the new panel instead of ScrollPanel. HideableScrollPanel will take as input the parent DockLayoutPanel container and the expected size of the panel when it is displayed. Override the setWidget method so that it will minimize the panel if null is set.
public void setWidget(IsWidget activityWidget) {
        Widget widget = Widget.asWidgetOrNull(activityWidget);                       
        if (widget == null)
        {                   
            parent.setWidgetSize(this, 0);
        } else {
            parent.setWidgetSize(this, size);
        }
        super.setWidget(widget);
    }
Now you can define a HideActivity which when run simply sets the AcceptOneWidget panel it is passed a value of null. If the AcceptOneWidget panel is an instance of your HideableScrollPanel then the panel will be removed from the screen. When a LoginPlace change is received the MenuActivityMapper should return a HideActivity.

Saturday, April 23, 2011

How to build and package a GWT app to a WAR file using Ant

This article is intended as a step by step guide for automating the construction of a WAR file from your GWT application using an Ant script. It covers the following steps
  • Establishing the project structure
  • Compiling the source - Java & Javascript
  • Packaging and accessing static resources - Images, CSS, property files
  • Packaging the deployment archives - JAR & WAR files
Establishing the project structure
Create a new GWT Web Application project and setup the directory structure as per the following screenshot

com.rubiconred.buildtutorial: This is where the .gwt.xml module file lives and we have added a public directory. The public directory is where we will place all of our static resources (e.g. Image files) that are accessed directly from within our GWT source code (see further below).
com.rubiconred.buildtutorial.client: This is where we place our GWT source code that is not referenced anywhere in our server code. This code will be compiled to Javascript but not to .class files. Note that you should not be putting RemoteService classes in here as these interfaces are implemented by server-side RemoteServiceServlet classes.
com.rubiconred.buildtutorial.shared: This is where we place our GWT source code that is referenced by both client and server code. This code will be compiled to both Javascript and to .class files. This is the place to put RemoteService classes.
com.rubiconred.buildtutorial.server: This is where we have access to the full Java APIs and will be the location for our RemoteServiceServlet classes. This code will not be compiled to Javascript but only to .class files.
Note: To ensure the client & shared packages will both be compiled to Javascript the following must be present in your .gwt.xml module file (generated by default if using Eclipse plugin)
<!-- Specify the paths for translatable code -->
  <source path='client'/>
  <source path='shared'/>
war/client_resources: We will use this directory to store our static resources that are accessed directly from the BuildTutorial.html or BuildTutorial.css files. Remember static resources accessed from within the GWT code should be placed in the public directory as described above.
war/server_resources: We will use this directory to store static resources used by our server code, such as properties or XML files.
build.xml: Create a new file build.xml and place it on the project root directory. Following is the skeleton of this build script which we will flesh out over the next few steps to automate the build and packaging process for us.
<?xml version="1.0"?>
<project name="BuildTutorial" basedir="." default="war">

    <target name="prepare"/>
    <target name="clean"/>
    <!-- Compile the java source code using javac -->
    <target name="compile" depends="prepare/">       
    <!-- Invoke the GWT compiler to create the Javascript for us -->
    <target name="gwt-compile" depends="compile"/>
    <!-- Package the compiled Java source into a JAR file -->
    <target name="jar" depends="compile"/>   
    <!-- Copy the static server resources into the required
    directory ready for packaging -->   
    <target name="copy-resources"/>   
    <!-- Package the JAR file, Javascript, static resources
    and external libraries into a WAR file -->
    <target name="war" depends="gwt-compile, jar, copy-resources"/>
    <!-- Deploy the WAR file (optional) -->
    <target name="deploy" depends="war"/>
   
</project>
Compiling the project source
We will begin by establishing the Ant tasks in our build script that will compile our source code. For a GWT app this includes the additional task of invoking the GWT compiler to build our client and shared code to Javascript.
First we should establish the properties that we will reference throughout the build script. Add the following to the top of build.xml and update the property values to match your own project and domain names.
    <property name="gwt.module.name" value="com.rubiconred.buildtutorial.BuildTutorial"/>
    <property name="server.resources.name" value="server_resources"/>
    <property name="jar.name" value="buildtutorial.jar"/>
    <property name="war.name" value="buildtutorial.war"/>
    <property name="src.dir" location="src"/>
    <property name="server.resources.dir" location="war/${server.resources.name}"/>
    <property name="build.dir" location="build"/>   
    <property name="build.server.resources.dir" location="war/WEB-INF/classes/server_resources"/>       
    <property name="lib.dir" location="war/WEB-INF/lib"/>
    <property name="gwt.client.dir" location="com/rubiconred/soauiext/client"/>
Next add the following to establish the classpath that we will reference when compiling. Any third party JARs you are using in your project should be available in the war/WEB-INF/lib directory.       
    <path id="project.classpath">       
        <fileset dir="${lib.dir}">
            <include name="**/*.jar"/>
        </fileset>
    </path>
Setup Ant tasks to create and delete the build directory. The build directory is used as a temporary staging area for the compiled source code during the build process.
    <target name="prepare">
        <mkdir dir="${build.dir}"/>
    </target>
   
    <target name="clean">
        <delete dir="${build.dir}"/>
    </target>
Next is the Ant task to invoke the Java compiler. Note that while we are compiling everything in the src dir (client, shared, server) later when we package this into the JAR we will exclude the client classes. The client classes are only included here as they are required by the GWT compiler in the next task.
    <target name="compile" depends="prepare">       
        <javac srcdir="${src.dir}" destdir="${build.dir}">
            <classpath refid="project.classpath"/>
        </javac>       
    </target>
Now we are ready to invoke the GWT compiler. In order for the GWT compiler to work the following JAR files must be on the classpath
  • gwt-dev.jar
  • gwt-user.jar
Add these files to the war/WEB-INF/lib directory. You should find them in your Eclipse installation directory (GWT 2.2) ECLIPSE_HOME\plugins\com.google.gwt.eclipse.sdkbundle.2.2.0_2.2.0.v201102111811\gwt-2.2.0
    <target name="gwt-compile" depends="compile">
        <java failonerror="true" fork="true" classname="com.google.gwt.dev.Compiler">
            <classpath>
                <!-- src dir is added to ensure the module.xml file(s) are on the classpath -->
                <pathelement location="${src.dir}"/>               
                <pathelement location="${build.dir}"/>
                <path refid="project.classpath"/>
            </classpath>
            <jvmarg value="-Xmx256M"/>
            <arg value="${gwt.module.name}"/>
         </java>
     </target>
Packaging and accessing static resources
One of the trickiest parts of packaging and deploying a GWT app is figuring out where to place the various types of static resource files and how to access them in your code so that they can be located after you have deployed to an application server just as they are in your development environment. The following list shows a strategy that I have found effective
Resource Type
Client resources accessed directly within your GWT code
e.g. Image files used by an Image widget


Access Method
Image image = new Image(GWT.getModuleBaseURL() + "/images/myImage.png");
Resource Type
Client resources accessed from the HTML or CSS files
    
Access Method
background-image
: url(client_resources/images/light_blue_vert.png);
Resource Type Server resources accessed from server side code
e.g. Properties file
 
Access Method
 InputStream in = this.getClass().getClassLoader().getResourceAsStream("server_resources/serverProps.properties");
props = new Properties();
props.load(in);

As part of the build process we need to copy the server_resources into the WEB-INF/classes directory so that they will be properly packaged into the WAR file and can be accessed when we deploy. The following Ant task will take care of this. It may also be necessary to invoke this target during development to keep the resource directories in sync so that they can be accessed when running in development mode too.
   <target name="copy-resources">
        <copy todir="${build.server.resources.dir}" preservelastmodified="true">
            <fileset dir="${server.resources.dir}"/>           
        </copy>
    </target>
Packaging the deployment archives
The first step here is to package the compiled Java source into a JAR file that will then be packaged into the WAR file in the WEB-INF/lib directory as part of the next step. Because we don't want to deploy any unnecessary code to the server we exclude the client classes from the package.
    <target name="jar" depends="compile">       
        <jar jarfile="${lib.dir}/${jar.name}" basedir="${build.dir}/">
            <!-- Don't wrap any of the client only code into the JAR -->
            <exclude name="${gwt.client.dir}/**/*.class"/>
        </jar>   
    </target>
Now we are ready to build the WAR file. The following task packages the JAR, Javascript, static resources and third party JAR files (excluding gwt-dev.jar & gwt-user.jar) that we established during the previous build targets into the deployable WAR file that we require.
    <target name="war" depends="gwt-compile, jar, copy-resources">
        <war basedir="war" destfile="${war.name}" webxml="war/WEB-INF/web.xml">
            <exclude name="WEB-INF/**" />
            <exclude name="${server.resources.name}/**"/>
            <webinf dir="war/WEB-INF/">
                <include name="classes/${server.resources.name}/**" />
                <include name="**/*.jar" />
                <exclude name="**/gwt-dev.jar" />
                <exclude name="**/gwt-user.jar" />
            </webinf>
        </war>
    </target>
Final Steps
Run the default task by right clicking on the build.xml file and selecting Run As > Ant Build. If you refresh the project directory in Eclipse you will notice a new build directory containing your compiled source has been added. You can remove this using the Ant target clean if you wish. You will also find buildtutorial.war has been added to the root directory of your project. If you copy this WAR file over to your server and deploy it onto the application server you will be able to run your GWT app in the deployed environment. It is also possible to automate the deployment step with Ant but that is beyond the scope of this article.
And here is the final build.xml contents
<?xml version="1.0"?>
<project name="BuildTutorial" basedir="." default="war">
    <property name="gwt.module.name" value="com.rubiconred.buildtutorial.BuildTutorial"/>
    <property name="server.resources.name" value="server_resources"/>
    <property name="jar.name" value="buildtutorial.jar"/>
    <property name="war.name" value="buildtutorial.war"/>
    <property name="src.dir" location="src"/>
    <property name="server.resources.dir" location="war/${server.resources.name}"/>
    <property name="build.dir" location="build"/>   
    <property name="build.server.resources.dir" location="war/WEB-INF/classes/server_resources"/>       
    <property name="lib.dir" location="war/WEB-INF/lib"/>
    <property name="gwt.client.dir" location="com/rubiconred/soauiext/client"/>
    <path id="project.classpath">       
        <fileset dir="${lib.dir}">
            <include name="**/*.jar"/>
        </fileset>
    </path>  
    <target name="prepare">
        <mkdir dir="${build.dir}"/>
    </target>
   
    <target name="clean">
        <delete dir="${build.dir}"/>
    </target>  
    <!-- Compile the java source code using javac -->
    <target name="compile" depends="prepare">       
        <javac srcdir="${src.dir}" destdir="${build.dir}">
            <classpath refid="project.classpath"/>
        </javac>       
    </target>      
    <!-- Invoke the GWT compiler to create the Javascript for us -->
   <target name="gwt-compile" depends="compile">
        <java failonerror="true" fork="true" classname="com.google.gwt.dev.Compiler">
            <classpath>
                <!-- src dir is added to ensure the module.xml file(s) are on the classpath -->
                <pathelement location="${src.dir}"/>               
                <pathelement location="${build.dir}"/>
                <path refid="project.classpath"/>
            </classpath>
            <jvmarg value="-Xmx256M"/>
            <arg value="${gwt.module.name}"/>
         </java>
     </target>
    <!-- Package the compiled Java source into a JAR file -->
    <target name="jar" depends="compile">       
        <jar jarfile="${lib.dir}/${jar.name}" basedir="${build.dir}/">
            <!-- Don't wrap any of the client only code into the JAR -->
            <exclude name="${gwt.client.dir}/**/*.class"/>
        </jar>   
    </target>
    <!-- Copy the static server resources into the required
    directory ready for packaging -->   
    <target name="copy-resources">
        <copy todir="${build.server.resources.dir}" preservelastmodified="true">
            <fileset dir="${server.resources.dir}"/>           
        </copy>
    </target>   
    <!-- Package the JAR file, Javascript, static resources
    and external libraries into a WAR file -->
    <target name="war" depends="gwt-compile, jar, copy-resources">
        <war basedir="war" destfile="${war.name}" webxml="war/WEB-INF/web.xml">
            <exclude name="WEB-INF/**" />
            <exclude name="${server.resources.name}/**"/>
            <webinf dir="war/WEB-INF/">
                <include name="classes/${server.resources.name}/**" />
                <include name="**/*.jar" />
                <exclude name="**/gwt-dev.jar" />
                <exclude name="**/gwt-user.jar" />
            </webinf>
        </war>
    </target>   
   
</project>



Wednesday, April 6, 2011

GWT SerializationException on RPC call when providing List from a ListDataProvider

The Issue: When attempting to send a List<T> object as a parameter of an RPC method call from client to server, the RPC call failed reporting a com.google.gwt.user.client.rpc.SerializationException had been thrown. Our first response then was to assume that our list objects, T, did not properly adhere to the GWT serialization requirements as outlined here Serializable Types. However once we confirmed this was not the case we were left wondering what could cause this issue.

Up until now we had generally assumed that sending a List<T> across the wire would not cause any serialization issues so long as T could be serialized. However this is not strictly true, the List implementation itself must also be serializable for the RPC call to succeed. Since most of the time your List implementations will likely be an ArrayList or a LinkedList, both of which implement the Java Serializable interface, it could be easy to forget to check this.

However this issue may begin to crop up more often with the introduction of the ListDataProvider in GWT 2.1. The ListDataProvider class wraps a List object in order to provide data binding to the new GWT cell widgets. What may surprise you is that the wrapped List is not implemented as an ArrayList or LinkedList but as a ListWrapper. The ListWrapper is an inner class of the ListDataProvider which implements List<T> but does not implement any Serializable interface.

Therefore the following code will throw the aforementioned com.google.gwt.user.client.rpc.SerializationException

       rpcService.anRpcCall(listDataProvider.getList(), new AsyncCallback<Void>) {... //tries to send a ListWrapper

The solution: Do not attempt to directly pass the result of listDataProvider.getList() to the server. Instead you need to construct a List implementation you know is Serializable and pass in the contents of the ListWrapper.

      List<MySerializableType> serializableList = new ArrayList<MySerializableType>();
      serializableList.addAll(listDataProvider.getList());
      rpcService.anRpcCall(serializableList, new AsyncCallback<Void>) {...

More generally, always check that your Collection implementations are serializable before transmitting them across the wire. You may even consider declaring the parameter types of your RPC calls to be a concrete type that you know to be serializable instead of an interface - for example declare ArrayList<T> instead of List<T>.