by Klaus P. Berg

Some Recipes to Improve Your Google Web Toolkit Development

how-to
Apr 26, 200726 mins

Experiences, tips and techniques to assist you in creating GWT Web applications.

According to Google’s announcements on the GWT home page, GWT makes writing AJAX applications easy for developers “who don’t speak browser quirks as a second language”. Starting with version 1.3.RC in December 2006, GWT is 100% open source under the Apache 2.0 license; at the time of this writing we have version 1.3.3 as a stable release. The heart of the toolkit is its GUI library with the Java-to-JavaScript compiler, the asynchronous remote procedure call (RPC) and object serialization mechanisms, and the full debugging support for client and server-side code when working with an IDE like Eclipse or IntelliJ’s IDEA.

Lots of introductory material has been published about GWT since its early days – primarily as world-wide web articles and tutorials (e.g., Jeff Hanson’s JavaWorld article, see Resources) – we can now read whole books about it. Therefore, in this article I will not discuss subjects like what GWT is all about or how to write a currency converter with GWT. But rather by looking not only at the UI client-side aspects of GWT, I will try to give you some tips, sometimes some tricks, and also some issues to think about when you start coding — even before you start creating your project structure and environment. I hope you can profit by my experiences implementing a GWT web application from scratch, and then doing a lot of refactoring, redesign and restructuring to make my life simpler and the application user feel better.

GWT Development Lessons Learned

After writing several hundred lines of GWT- and GWT-related servlet code for Tomcat I would not claim that I’m a GWT guru, but as outlined in the abstract, I try to present some problems and solutions that are not covered by “getting started” tutorials and articles. I will assume that you are familiar with Java, GWT and servlet basics and with tools like Eclipse, Ant, and JUnit.

Of course, I cannot cover every possible GWT topic here, that would break the context of such an article, and – to be honest – I have not used all major GWT features in my case study; so, e.g., I have no practice with GWT security aspects or internationalization. However, the following eight tips will address some typical areas in developing, testing and deploying your GWT applications, assuming your server part is covered by servlet technology.

Tip 1: Divide and conquer

We all know, GWT applications are Java applications. However, the point is “which Java”? We have to keep in mind that GWT compiles Java sources that are compatible with J2SE 1.4.2 or earlier! Furthermore, only a subset of the J2SE 1.4.2 APIs is supported, namely the java.lang and java.util. package. Even when working with these packages you should study Google’s remarks on runtime library support (to be found at the GWT home page) very carefully and take the corresponding tip to heart: “You’ll save yourself a lot of frustration if you make sure that you use only translatable classes in your client-side code from the very beginning. To help you identify problems early, your code is checked against the JRE emulation library whenever you run in hosted mode. As a result, most uses of unsupported libraries will be caught the first time you attempt to run your application. So, run early and often.”

Now, my new tip is “divide et impera”, a Latin phrase meaning, in our context, divide your application code from the beginning into three different parts for client-side code, RPC-related code, and server-side code, and create corresponding Eclipse projects to conquer your task. In doing so, you can make use of different Java language versions for your client and server part. I created the server part of the application (the servlet code) with Java 5, but in the following list you can replace Java 5 by Java 6, if you are using the Mustang release right now. Even if you are still using J2SE 1.4.2 on the server-side, such a division offers you more flexibility for the future and clearly separates your code (“separation of concerns”), without limiting your debugging actions in GWT hosted mode. If you have all sections in only one Eclipse project, you will need to be very disciplined, particularly on the server-side. Otherwise, compile or runtime problems will occur. My advice is to use a special naming convention that clearly identifies your distinct projects and that will make the deployment script easier. You can use an Eclipse working set named, e.g., GWT- to include all three projects. Here, “ModuleName” is the name of the GWT module that identifies your web application; in my environment it was called “JUnit2MR”, because the web application connected JUnit error messages to ClearQuest modification requests (MRs) by means of IBM Rational’s new Java teamapi. Using a working set, everything you need is close together.

  • Client-Side code: Contains your UI related code that will be translated to JavaScript. Therefore you are limited to J2SE 1.4.2 and GWT runtime support. Enable Eclipse Java compiler settings and “Java Compiler Errors/Warnings” per project, adjust Java compliance level to “1.4” and source and class file compatibility to “1.4” (assuming you do not use JDK versions prior to 1.4). The name of this project is -client, e.g., “JUnit2MR-client”, and it depends on the -rpc project in your build path settings. The package name is something like .gwt..client.

  • RPC-related code: Contains your RPC related code that will be translated to JavaScript. This project follows the same guidelines as the client-side code project above. The project name is -rpc, e.g., “JUnit2MR-rpc”, and it depends on no other project. The package name is the same as for the -client project. The RPC-project contains remote interfaces on the client-side, data transfer objects that are serialized by GWT during RPC, and global constant classes that will contain public constants used by client and server code (e.g., to handle input form data that is identified by unique names). Just a new remark on data containers: GWT forces your data containers used by RPC to implement the IsSerializable interface. The obvious problem with this approach is that on the server side you might have the same classes implement java.io.Serializable, which is not allowed by the GWT Java-to-JavaScript compiler. One possible solution is to have a JavaBean class on the server side implementing Serializable only, with exactly the same member variables as the data container used by RPC. Then you call PropertyUtils.copyProperties(serverBean, rpcDataContainer)or PropertyUtils.copyProperties(rpcDataContainer, serverBean) to copy your values back and forth. PropertyUtils is part of the Jakarta Commons BeanUtils project.

  • Server-side code: Contains your servlet code, if your server-side is made up by Java servlets. If you are using Tomcat 5.5 or Tomcat 6, you can use the full power of Java 5+ here. Enable Eclipse compiler settings per project, and use the Java 5 compiler settings with compliance level set to “5.0”. You can also enable all of the Eclipse “Compiler->Error/Warnings” features that meet your needs, e.g., to address missing @override annotations or the like. If you have Eclipse 3.2.2, then its new “Source->Clean up” feature is also worth configuring. The name of this project is -server’, e.g., “JUnit2MR-server”, and it depends on the -rpc project in your build path settings. The package name is .gwt..server, if you are programming according to GWT’s default package proposals.

You can see the resulting directory structure for our example application in figure 1. If you look inside the JUnit2MR-client directory, you can see additional directories created during the deployment process, as well as an Eclipse launch configuration to start our GWT application “JUnit2MR” in hosted mode. My tip is to collect project-specific jar files in a directory called “lib”, e.g., inside JUnit2MR-client/lib we have GWT add-on libraries like “gwt-tk” or “gwt-login”. You can find a good list of third-party client-side GWT add-ons at the GWT Unofficial Wiki site (see Resources). The RPC-project does not need a lib directory in my case statudy, but the server-side project’s lib directory will contain only jar files needed by your servlet code.

Using this three-projects GWT structure, the Eclipse launch configuration for your application in the GWT hosted mode is a little bit more complicated than having only one project. Therefore, I provide a screenshot in figure 2 that shows the necessary settings for my case study.

Figure 1: Eclipse project directory structure for client, rpc, and server-part

Figure 1: Eclipse project directory structure for client, rpc, and server-part

Figure 2: Eclipse launch configuration for GWT hosted mode

Figure 2: Eclipse launch configuration for GWT hosted mode

You can do even more on structuring your code by establishing a separate Java “rpc” package inside the client and rpc project. In the rpc project it will just replace the “client” package still containing your transfer objects and the asynchronous interface definitions. In the client project the “rpc” package must be added, and it will include your synchronous interface definitions (without the callback parameter) moved from the client packag e. The only thing left is to make the new rpc package known to GWT by adding the <source path="client"/><source path="rpc"/> statements to your GWT module description.

Tip 2: Debugging and Error Reporting: there is more than Window.alert ()

It’s true: you can actually use your IDE’s full debugging capabilities when creating GWT applications. But before digging into where the error may have occurred, you need solid exception reporting on both the client and the server side of your code. This is typically done with try/catch blocks. Inside a client-side catch block you should pay attention to the fact that the default method call e.printStackTrace() is not a suitable solution in all situations. It will work when running your application in GWT hosted mode, printing text to your Eclipse console. However, for web mode ask yourself: “Where will my stacktrace and error messages that I have sent to stdout or stderr be displayed?” One possible solution is to use Mat Gessel’s debug utility classes (see Resources) but you need a browser JavaScript console to see the results in web mode.

One thing I recommend to do on the client-side is to provide your own exception handler for any uncaught exceptions using the GWT.setUncaughtExceptionHandler() method. After having caught these kind of exceptions you have several alternatives: GWT.log(message, caught), Debug.println (message_with_stacktrace), when working with Mat Gessel’s’ Debug class, or – coming closer to JavaScript – Window.alert(message_with_stacktrace), or your own customized error reporting. Figure 3 shows the GWT console output if you do not catch all exceptions.

Figure 3: GWT console output when not catching all exceptions on the client side

Figure 3: GWT console output when not catching all exceptions on the client side

Depending on the origin, you will either get “Failure to load module” (if the exception was thrown inside your onModuleLoad() method) or “Uncaught exception escaped”, if it has been thrown elsewhere in your client code. Of course, you can click on the error message in the GWT console to get the full stacktrace but I recommend to use the startup sequence shown in listing 1 as a template for your onModuleMoad() method. It makes use of a small DebugUtility class that provides a default client-side error handling that can easily be customized.

On the server-side you have the broad power of the java.util.logging API or of log4j, depending on your personal preferences or on the project’s constraints. However, if you do not patch GWT’s com.google.gwt.user.server.rpc.RemoteServiceServlet class, for uncaught unchecked exceptions you will only get a hint inside the stacktrace that points to your server-side class that produced the error; for checked exceptions that are covered and reported inside your catch() block, everything is fine.

To give you an idea of what I mean, please look at figure 4 that shows two screenshots of error messages caught in hosted mode with Window.alert (you will get the same messages on your Eclipse console):

  • The first alert window contains the original message from the RemoteServiceServlet class pointing to a proxy.

  • The second alert window shows the output with the patch mentioned above, pointing to the concrete line of servlet code that forced the error (marked with a blue border).

Figure 4: Server-side exception reporting enhanced

Figure 4: Server-side exception reporting enhanced

The code presented in listing 2, an addition to the processCall() method of the RemoteServiceServlet class does the magic (my statements are marked as bold).

The only thing to pay attention to is to incorporate the patched class so that it is searched before the original GWT code from gwt-user.jar in your classpath. But note: all this patching will not help you when you call a remote method with a wrong target in your setServiceEntryPoint(GWT.getModuleBaseURL() + "/wrongTarget") method. This can happen, e.g., with copy/paste actions when you tried to “reuse” server access code, but you forgot to adapt the target string constant.

Tip 3: Beware of the GWT Shell’s “Refresh” Button Pitfalls

When you start your application in hosted mode, you will see a “Refresh” button in your browser’s toolbar. When you press this button, GWT will recompile your modified Java client sources to Java bytecode (as .tmp files in the .gwt.-cache/bytecode directory) and reload your module. You can use this button to shorten your edit-compile-debug cycle, but there are some things you should keep in mind when using this nice feature:

  • Only modified sources are recompiled, i.e., no new bytecode is generated for files that depend on your modified code. So, when you change the value of a global constant, let’s say the value of a public final int field, you will not see this change in dependent files immediately.

  • Only modified sources are recompiled by GWT. That means that even a “Project clean” inside your Eclipse IDE does not help. You have to touch all dependent sources, e.g., by adding a new empty line.

    Because this would be quite a cumbersome procedure, my advice is to follow these 4 steps when modifying global constants:

    1. Change your public final constant value in the corresponding source file.
    2. Recompile the changed source.
    3. Delete the GWT cache by removing the complete -client/.get-cache/bytecode directory.
    4. Create a new GWT cache with recompiled bytecode by starting your application from scratch using “Run as” inside Eclipse. It’s better to ignore the Refresh button in such cases, although there are situations when deleting the complete -client/.get-cache/bytecode directory would allow you to use the Refresh button.
  • When modifying server-side code, the GWT bytecode cache is not affected. However, the embedded Tomcat instance will cache it, and therefore only your first code change after starting from scratch will be acknowledged when you use the Refresh button. So again, to be on the safe side, it’s better to start your application from scratch after changing server-side code.

Tip 4: Read Servlet Init Parameters in Hosted Mode too

When you are working with a database system, you generally do not want to have hard-coded database connection parameters in your servlet source. Typically you will read them from a property file, or even better, you will supply them as init parameters to your servlet (as a part of your application’s web.xml file). This works fine when running your application in web mode, but it fails in hosted mode. This is due to restrictions in the GWT hosted mode servlet processing. The good news is: you can overcome this obstacle by modifying the web.xml file that will be used by the embedded Tomcat instance. To do so, modify (or create if necessary) the web.xml file in the -client/tomcat/webapps/ROOT/WEB-INF directory: in addtion to the GWTShellServlet mapping for the embedded Tomcat add a context section with the init parameters as shown in listing 3 for my JUnit2MR example. Because the context information is “global”, and not targeted to a specific servlet, you have either only one section of init parameter information here, or you have to use a special naming scheme to relate parameters to different servlets. When using this new web.xml file, you can delete the old one in your src/web/WEB-INF folder.

In your servlet code you access the init parameters the same way they are read in web mode, e.g., final String host = getInitParameter("host")

The hack I used to make this happen is to modify GWT’s RemoteServiceServlet in the same way as was done in tip 2. Now you simply have to override GenericServlet's getInitParameter() method in order to use getServletContext() instead of getServletConfig(). That’s all!

                                   // new method added to RemoteServiceServlet
                                       public String getInitParameter(String name) {
                                       return getServletContext().getInitParameter(name);
                                       }
                                     

My classpath settings guarantee that the modified RemoteServiceServlet class is executed instead of the one in gwt-user.jar. For the GWT web mode, the modified class is not deployed. When deploying your application to Tomcat, an Ant script (or my Gant script) can help you to deal with the different web.xml versions.

An additional tip: When testing different server code behavior in hosted and web mode, I found it saved time to skip the GWT compile part in my Gant script and to copy the pre-compiled JavaScript code from a “temp” location instead. This made sense with compile times of up to 10 minutes when having more than trivial client-side code. With Gant it is very easy to incorporate a switch in the script that controls whether you are compiling or reusing older JavaScript files. The parameter for this switch is provided on the command-line, e.g., as “>gant -D ‘compileGWT=yes’ deploy”. For details on this Gant script see tip 8.

Tip 5: Display your PDF Files inside the Browser

Most real web applications give you a way to produce and read a PDF file. In our context, I assume that this PDF file will be produced by the servlet, e.g., by means of JasperReport. The created file can be read later inside the Browser by clicking a specific hyperlink. If you want to test such a feature in both the hosted mode and in web mode I propose you adopt the following procedure:

1. Design your RPC interface to accept a boolean parameter that tells the server whether we are running in hosted mode or in web mode. The interface method should return a string with the name (i.e., the last part of the filename) of your PDF file created by the servlet.

2. Implement the servlet code according to the code presented in listing 4, depending on the boolean parameter “isScript”.

3. On the client side: inside your widget code call the createXyzPDF() method with a GWT.isScript() parameter and create an external hyperlink containing the servlet result string.

Listing 4 shows an example where the interface method is called createSummaryPDF(). The string returned from the servlet is “summary.pdf”.

Consistent with the Perl philosophy of “There’s more than one way to do it”, I do not claim that this is the only way to handle this case, but it currently works fine for me. Please note that before starting your application in hosted mode, you have to create at least a dummy “summary.pdf” file (the filename returned from the servlet) in your -client project’s src/…/public folder; otherwise you will get “HTTP 404 – The webpage cannot be found” when the GWT tries to read your PDF when clicking on the hyperlink in your browser 😉

Tip 6: Aim for a Stateless Server

One crucial question when designing client/server web applications is “how to deal with session and state management?” In the Web 1.0 world the answer is obvious: session and state management is a server issue. But with GWT you have another choice. No longer does the server have to be a “web” service that only supplies HTML content. Using GWT RPC the server now supports services – implemented by servlets in our case – that only supply structured data.

So, “How does GWT influence session & state management?” At last years JAOO conference in Denmark, GWT Tech Lead Bruce Johnson pointed out that with GWT session management should now be a client issue. Figure 5 shows a slide that reviews the changes.

Figure 5: A stateless server with GWT

Figure 5: A stateless server with GWT

In my JUnit2MR GWT application I started with the traditional approach handling session state in my servlets. But it was a rather nasty job, and I looked for another option. So, after seeing Bruce’s presentation I decided to redesign the whole application to follow his mantra: “Session state? … All client…not a server issue”. But that step required changing all my RPC interfaces, my caching strategies, and last but not least, all my servlets. Therefore my recommendation is: Consider early on where to put session and state management, and give Bruce Johnson’s recipe a chance. It pays at the end.

Because of this decision I had more communication between client objects. So I used the well-known GoF mediator pattern. However, we have some JDK 1.4 and GWT runtime library restrictions on the client side. Therefore I re-implemented the PropertyChangeEvent class and the mediator support to handle listener registration and message broadcast. Please contact me, if you are interested in the mediator part of the system.

Tip 7: Automate your GWT Web Testing with Selenium

Selenium (see Resources) is an open source tool that enables you to easily test web applications that contain rich, client-side, interactive content. So it is an ideal candidate for testing an Ajax application like the one you have created with GWT. Of course, there is still JUnit and the JUnit support inside GWT, especially for the asynchronous parts of your system. That has been described in about every serious publication about GWT. Therefore I will draw your attention to Selenium because I found it easy to use (at least its IDE), powerful, and, last but not least, it has a lot in common with JUnit. You can work with the Selenium IDE to record GUI use-cases, then running the recorded actions with its “Play” feature. Every action may be followed by a JUnit-like “assert” command that verifies some text on a page etc. The IDE is a Firefox extension, but be sure to use the latest Selenium version “Selenium IDE 0 .8 .7” because it contains a major bug fix for the “waitFor …” commands. These commands — together with the “pause” commands — are substantial when it comes to testing Ajax applications. When you are satisfied with your tests you can even generate JUnit Java test classes out of Selenium’s proprietary test format. Figure 6 gives you an idea of Selenium’s IDE applied to my JUnit2MR example web app that runs with Firefox 2.0. In the IDE screenshot you can see “pause” and “waitFor” commands as well as assertions that could be verified in the test run (marked green) or that failed (marked red). Of course that’s only a small part of the tests you can do with Selenium, e.g., you can use XPath expressions to look for text in specific positions.

Figure 6: Example for a Selenium GUI test of a GWT web application

Figure 6: Example for a Selenium GUI test of a GWT web application
Tip 8: Deploy Your Application with a Groovy Gant Script

It’s truly great, trying out your application in GWT hosted mode, but the real power of GWT comes when deploying your application to an application server or servlet container like Tomcat. In this step you need to create a “war” file that should be copied automatically to your Tomcat “webapps” destination. Of course, you can do all the necessary preparation, compilation, copy and other tasks with Ant and ant-contrib, and, indeed, that was how I started. But with my Ant script getting more complicated, I found the ant-contrib control structures and property regex processing a little bit cumbersome. After reading “Groovy in Action” with a recommendation for “Gant”, I decided to have the best of both worlds: Groovy + Ant = Gant. Installing Groovy and Gant is just an matter of less than 10 minutes. Then you can customize the ‘build.gant’ script with ordinary properties coming from a ‘build.properties’ file.

As I mentioned before, I started with a familiar Ant script. It was based on the work of Gautam Shah (see Resources). If you prefer ordinary Ant over Gant, I suggest starting with Gautams’s Ant script. If you want the power and flexibility of a scripting language plus the capacity of Ant, then I propose you look at my Gant script, too.

Conclusion

The Google Web Toolkit is really an attractive way of producing Ajax applications in Java where the client part is compiled to JavaScript. Therefore, in general, you do not have to worry about the JavaScript side of things, including browser optimizations. With a solid AWT/Swing/SWT and servlet background I believe you are actually quickly up to speed with GWT, but if you will do more than rapid prototyping, some difficulties and problems remain. So, I hope that the tips and recipes presented in this article were useful to help you in preparing and implementing your GWT-based web applications. Perhaps, after leaving GWT hosted mode and deploying your modified solutions successfully many times to a servlet container, you will enjoy the power of GWT as I did.

Author bio

Klaus Berg has studied electrical engineering and informatics at the University of Karlsruhe in Germany. He was an architect and implementor in some projects at Siemens focused on Java GUI development with Swing and Java Web Start as well as acting on the server side creating Java based intranet applications. Now he works as a senior engineer in the area of functional and performance testing, mainly for J2EE software.

Code Listings

Listing 1: A template for your onModuleLoad() method with improved error handling

/**
                                       * This is a template for your GWT entry point method.
                                       */
                                       public void onModuleLoad() {
                                       DebugUtils.initDebugAndErrorHandling();
                                       Window.setTitle("<Your App Title>");
                                       Window.addWindowCloseListener(new WindowCloseListener() {
                                       /**
                                       * Fired after the browser window closes or navigates to a different site.
                                       * This event cannot be cancelled, and is used mainly to clean up your application
                                       * state and/or save the state to the server.
                                       */
                                       public void onWindowClosed() {
                                       Debug.println("App window closed.");
                                       }
                                       
                                       public String onWindowClosing() {
                                       return "Application closing...";
                                       }
                                       });
                                       // try/catch is necessary here because GWT.setUncaughtExceptionHandler()
                                       // will work not until onModuleLoad() has returned
                                       try {
                                       // create and init your startup widgets (e.g., Login Dialog) here
                                       createStartupWidgets(); 
                                       } catch (RuntimeException e) {
                                       GWT.log("Error in 'onModuleLoad()' method", e);
                                       e.printStackTrace();
                                       }
                                       }
                                       
                                       // DebugUtils class used by 'onModuleLoad()'
                                       //
                                       import asquare.gwt.debug.client.Debug;
                                       import asquare.gwt.debug.client.DebugConsole;
                                       
                                       import com.google.gwt.core.client.GWT;
                                       import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
                                       import com.google.gwt.user.client.Window;
                                       
                                       /**
                                       * Helper class that provides static utility methods to 
                                       * support GWT-client-side error reporting.
                                       */
                                       public class DebugUtils {
                                       
                                       private DebugUtils() {
                                       // hide constructor because we are a utility class with static methods only.
                                       }
                                       
                                       public static void initDebugAndErrorHandling() {
                                       Debug.enable();
                                       DebugConsole.getInstance().disable(); // can be enabled for web mode
                                       GWT.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
                                       public void onUncaughtException(final Throwable tracepoint) {
                                       performDefaultErrorHandling(tracepoint);
                                       }
                                       });
                                       }
                                       
                                       public static void performDefaultErrorHandling(final Throwable caught) {
                                       if (caught != null) {
                                       final String stacktrace = DebugUtils.getStacktraceAsString(caught);
                                       Debug.println(stacktrace);
                                       Window.alert(stacktrace);
                                       } else {
                                       final String message = "Error ocuured, but we have no further information
                                       about the cause";
                                       Debug.println(message);
                                       Window.alert(message);
                                       }
                                       }
                                       
                                       public static String getStacktraceAsString(final Throwable tracepoint) {
                                               final StackTraceElement[] trace = tracepoint.getStackTrace();
                                               final StringBuffer sbuf = new StringBuffer(2048);
                                               sbuf.append(tracepoint.toString());
                                               sbuf.append(": atn");
                                               // I cut the stacktrace at depth 7
                                               final int length = Math.min(7, trace.length);
                                               for (int i = 0; i <= length; i++) {
                                                   sbuf.append(trace[i].toString());
                                                   sbuf.append("n");
                                               }
                                               if (trace.length > 7) {
                                                   sbuf.append("...");
                                               }
                                               final String stacktrace = sbuf.toString();
                                               return stacktrace;
                                           }
                                       
                                       }
                                     

Listing 2: Patching RemoteServiceServlet class to get better error information

public String processCall(String
                                       payload) throws SerializationException {
                                           ...
                                       ... String myServletException = null; // added by KPB
                                       try {
                                       .....
                                               } catch (InvocationTargetException e) {
                                                   // Try to serialize the caught exception if the client is expecting it,
                                                   // otherwise log the exception server-side.
                                                   caught = e;
                                                   Throwable cause = e.getCause();
                                                   
                                                   // added by KPB
                                                   final StringWriter stringWriter = new StringWriter(1000);
                                                   e.printStackTrace(new PrintWriter(stringWriter));
                                                   final StringBuffer sbuf = stringWriter.getBuffer();
                                                   int startIndexCause = sbuf.indexOf("Caused by:");
                                                   if (startIndexCause != -1) {
                                                       myServletException = "n" + sbuf.substring(startIndexCause) + "n";
                                                   }
                                                   // end additional code
                                                   
                                                   ......
                                       
                                               // Let subclasses see the serialized response.
                                               //
                                               onAfterResponseSerialized(responsePayload);
                                       
                                        // added by KPB
                                               if (myServletException != null) {
                                                   responsePayload = myServletException + responsePayload;
                                               }
                                            // end additional code
                                       
                                               return responsePayload;
                                       }
                                     

Listing 3: Enhancing web.xml to access servlet init parameters in hosted mode

<?xml version="1.0"
                                       encoding="UTF-8"?>
                                       <web-app>
                                           <!-- GWTShellServlet mapping for embedded Tomccat -->
                                           <servlet>
                                               <servlet-name>shell</servlet-name>
                                               <servlet-class>com.google.gwt.dev.shell.GWTShellServlet</servlet-class>
                                           </servlet>
                                           <servlet-mapping>
                                               <servlet-name>shell</servlet-name>
                                               <url-pattern>/*</url-pattern>
                                           </servlet-mapping>
                                           
                                           <!-- Display name -->
                                           <display-name>JUnit2MR</display-name>
                                           
                                       <!-Context info for embedded Tomccat to allow access to servlet's init parameters in hosted mode, too-->
                                           <context-param>
                                               <param-name>host</param-name>
                                               <param-value>localhost</param-value>
                                           </context-param>
                                           <context-param>
                                               <param-name>port</param-name>
                                               <param-value>3306</param-value>
                                           </context-param>
                                           <context-param>
                                               <param-name>db</param-name>
                                               <param-value>fts_db</param-value>
                                           </context-param>
                                           <context-param>
                                               <param-name>cq_db</param-name>
                                               <param-value><![CDATA[SYM_API_7.0.0/SYM]]></param-value>
                                           </context-param>
                                       
                                           <servlet>
                                               ... your module servlet's information goes here...
                                       }
                                     

Listing 4: PDF file creation on the server and file usage on the client

// Client-Code (Widget)
                                       String pdfLink;
                                       dataService.createSummaryPDF(GWT.isScript() ,new AsyncCallback() {
                                          public void onFailure(final Throwable caught) {
                                             DebugUtils.performDefaultErrorHandling(caught);
                                          }
                                          public void onSuccess(final Object result) {
                                             pdfLink = (String)result;
                                             final String infoAsHtml = "..." + "<P/> + "<a href="" pdfLink target="_blank" rel="noopener">Summary (PDF)</A>";
                                             final HTML htmlText = new HTML(infoAsHtml);
                                             ...
                                          }
                                       });
                                       
                                       // Corresponding Server-Code (Servlet)
                                       private static final String SUMMARY_PDF_NAME = "summary.pdf";
                                       public String createSummaryPDF(final boolean isScript) {
                                         try {
                                           if (isScript) {
                                             FileCopy.copy(PDF_FILE_TO_COPY, getGwtServletContext().getRealPath(
                                              "/" + SUMMARY_PDF_NAME)); // FileCopy is a replacement for your real PDF creation
                                           } else {
                                             // the GWTShell servlet expects resources coming from the src.../public folder
                                             String destinationPathname = System.getProperty("user.dir")
                                                + "/src/" + getClass().getName().replace('.', '/').replace("server", "public");
                                             destinationPathname = destinationPathname.substring(0,
                                                destinationPathname.lastIndexOf('/')) + SUMMARY_PDF_NAME;
                                             generatePdfIinPath(destinationPathname);
                                           }
                                         } catch (final IOException e) {
                                             e.printStackTrace();
                                         }
                                         return SUMMARY_PDF_NAME;
                                       }
                                       
                                       public final getGwtServletContext() {
                                         final HttpServletRequest request = this.getThreadLocalRequest();
                                         return request.getSession().getServletContext();
                                       }