This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
MEAP Edition Manning Early Access Program
Copyright 2007 Manning Publications
For more information on this and other Manning titles go to www.manning.com
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Contents PART I: Getting started Chapter 1 - Introducing GWT Chapter 2 - Creating the Default Application Chapter 3 – Advancing your own Application
PART II: Building user interfaces Chapter 4 - Working with Widgets Chapter 5 - Working with Panels Chapter 6 - Handline Events Chapter 7 - Creating Composite Widgets Chapter 8 – Building JSNI Components Chapter 9 – Modularizing an Application
PART III: Advanced Techniques Chapter 10 - Communicating with GWT-RPC Chapter 11 - Examining Client-Side RPC Architecture Chapter 12 – Classic Ajax and HTML forms Chapter 13 – Achieving Interoperability with JSON Chapter 14 – Automatically generating new code Chapter 15 – Changing Applications based on GWT properties
PART IV: Completing the Understanding Chapter 16 - Testing and Deploying GWT Applications Chapter 17 – Peeking into how GWT works Appendix A – Introduction to Web technologies Appendix B – A Quick Java Primer (tentative) Appendix C – Overview of Other IDEs and How to Use GWT
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1 Introducing GWT In May of 2006, Google released the Google Web Toolkit (GWT), a set of development tools, programming utilities, and widgets, allowing you to create rich Internet applications differently than you might have done before. The difference between GWT and all of those other frameworks, is that with GWT you write your browser-side code in Java instead of JavaScript. For those of us that rely on Java as a trusted tool this is really a monumental difference over traditional JavaScript coding. It means that besides gaining all of the advantages of Java as a programming language, you also get immediate access to a gazillion Java development tools that are already available. Instead of trying to build a new tool to support the development of rich Internet applications in JavaScript, Google has altered the language that we use to write these applications to Java, allowing us to use the tools that already exist. The need for writing code in Java instead of JavaScript is rooted in the ever-increasing size and complexity of rich Internet applications. The fact is that large applications are difficult to manage, and Java was designed to make large application development manageable. While bringing all of Java’s benefits to rich Internet applications, GWT still allows you to interact with existing JavaScript code. So when you embrace GWT, it doesn’t mean that you need to throw away all of your old JavaScript code. In fact, GWT makes every attempt to be a glue language, allowing it to integrate with not only existing JavaScript code, but also your existing server-side services. At the core of GWT is a Java to JavaScript compiler that produces code capable of running on Internet Explorer, Firefox, Mozilla, Safari, and Opera. The compiler converts the Java syntax to JavaScript, utilizing JavaScript versions of commonly used Java libraries like Vector, HashMap, and Date. The compiler can then weave in JavaScript that you have referenced in your code, allowing you to utilize popular libraries like Scriptaculous, JSCalendar, and TinyMCE. Beyond the compiler, GWT also includes a large library of widgets and panels, making it effortless to build a web application that looks more like a desktop application. The widget library includes the usual suspects like text boxes, drop-down menus, and other form fields. In addition it includes complex widgets including a menu bar, tree control, dialog box, tab panel, stack panel, and others. When it comes to communication with the server GWT has a tool for every job. First is the inclusion of several wrappers of varying complexity and capability around the JavaScript XMLHttpRequest object, an object often associated with Ajax development. Another tool provided by GWT is a set of classes for supporting the JavaScript Object Notation (JSON) message format. JSON is a popular message format known for its simplicity and the widespread availability. Beyond this GWT provides some of its own special sauce in the form of an tool that allows you to send Java objects between the browser and server without the need to translate them into an intermediate message format. These tools for communication allow you access to server-side services, written in any language, and makes it possible to integrate with frameworks such as JSF, Spring, Struts, and EJB. This flexibility means that GWT doesn’t make more work for you, instead it allows you continue to use the same server-side tools you are using today. But, still, being able to write rich Internet applications in Java isn’t enough to make them easier to write. To this end, GWT provides support for the JUnit testing framework and a special hosted-mode browser that
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
lets you develop and debug in Java without ever needing to deploy your code to a server. This is a real timesaver! As you can see, GWT is a rich topic and we still haven’t mentioned half of what there is to cover. In this chapter we begin the exploration of GWT gently, spending some time enumerating each of GWT’s major features that will be covered throughout the book. Along the way, we provide some very short code snippets to help you better understand how you will use the feature in practice. After exploring the main features of GWT, we will compare GWT to some of the other toolkits. Our selection of frameworks for the comparisons is based on questions that we have seen posed by non-GWT developers. Our hope is that by making these comparisons we can better explain what GWT is, and what it is not. Again, we provide some code snippets for the purpose of comparison, but not quite a working application. At the end of chapter 1, we wrap up the tour by providing you with a complete example application. This example will provide you with your first working GWT application and start you in the process of developing rich Internet applications with GWT. So let’s get down to business and find out what GWT is all about, starting with an overview of the primary features that make this toolkit so useful to web application developers.
A Walk Through GWT GWT provides a rich set of tools focused on solving the problem of moving the desktop application into the browser, including not only a rich set of widgets, but many other tools as well. The GWT toolbox provides an XML parser, several tools for communicating with the server, internationalization and configuration tools, and a browser history management system. Figure 1.1 provides a visual map of the central aspects of GWT, each of which will be described in this section. In the figure you can see that the tools can be divided into those that are tied to the compiler, and the Java libraries that make up the GWT API.
Figure 1.1 GWT provides a comprehensive set of tools to meet the challenge of developing modern rich Internet applications. From UI components to configuration tools to server communication techniques, GWT’s tools help web apps look, act, and feel more like full-featured desktop apps.
We will cover each of this tools in turn beginning with the compiler, the most important piece of the puzzle, along with the accompanying Java emulation library. We then move on to provide information about the Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
rich widget library, and show you how GWT allows you to interface your new GWT code with your existing JavaScript libraries. We then hop over and examine GWT’s support for internationalization, and then see what RPC services GWT has to offer for communicating with server-sde services. Finally, we wrap up the examination by looking at the XML parser API, browser history management API, and close with a strong dose of JUnit integration. By the end of this section, you should have a good idea of what GWT is capable of, and hopefully will be as excited as we are about this new technology. Our tour of the features within GWT will mimic the ordering that you see in figure 1.1, from top to bottom, and left to right. We begin with the keystone of the diagram, the Java to JavaScript compiler.
Explaining GWT's Java to JavaScript Compiler The most obvious place to start looking at what GWT provides is the one tool that really defines it, and that is the compiler itself. The responsibility of the GWT compiler is to convert your Java code into JavaScript code, in much the same way the Java compiler compiles your Java code into bytecode. Compiling your project is done by running the Java program com.google.gwt.dev.GWTCompiler, passing it the location of your module definition file along with some other parameters. A module is a set of related Java classes and files accompanied by a single configuration file. The module definition typically includes an entry point, which is a class that executes when the application starts. The compiler starts with the entry point class, following dependencies required to compile the Java code. The GWT compiler works differently than the standard Java compiler because it doesn’t compile everything in the module; instead it only includes what is being used. This is useful in that it allows you to develop a large library of supporting components and tools, and the compiler will only include those classes and methods that are actually used by the entry point class. The compiler has three style modes that determine what the resulting JavaScript looks like. The default style is “obfuscate,” which makes the JavaScript look like alphabet soup. Everything is compressed and impossible to decipher. This isn’t being done to prevent it from being read, although that could be seen as a benefit for preventing code theft, but instead it is to help keep the resulting JavaScript file as small as possible. This is a real concern as your application gets larger and larger. This snippet of GWT code compiled to JavaScript using the obfuscated mode. You can see that it is as compressed as it can be, with no hint as to what the method is used for. 1.1
function b(){return this.c + '@' + this.d();}
The next style is “pretty,” which generates readable JavaScript. This code snippet is the same code as out obfuscated sample above. We can now see that the code is a toString() method, a common method for Java classes, but we still can’t tell what class this code is for. 1.2 1.3 1.4
function _toString(){ return this._typeName + '@' + this._hashCode(); }
The last style is “detailed,” which produces JavaScript code that looks like the “pretty” style with the addition of having the full class name as part of the JavaScript method name. This makes it easy to trace the JavaScript code back to the originating Java code. In this code sample compiled in detailed mode we can now easily see that this is the toString() method for java.lang.Object, the root of all Java classes. Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.5 1.6 1.7
function java_lang_Object_toString__(){ return this.java_lang_Object_typeName + '@' + this.hashCode__(); }
The pretty and detailed styles are typically used only during development so that those JavaScript errors in your browser will be a lot easier to track back to the source Java code. For production use obfuscated is favorable because it keeps the JavaScript file size down, and can hide trade secrets. Besides the different compiler styles, another important aspect of the compiler is that is compiles from Java source code, not compiled Java binaries. This means that the source for all of the Java classes you are using must be available. This plays a role when you want to distribute GWT code for reuse. When you build distributable jar files, you must include both the Java source and compiled Java class files. The GWT compiler also requires that the source code be compliant with the Java 1.4 syntax. This is expected to change eventually, but for now you can’t use generics, enums, and other Java 1.5 features in your application. Note that this restriction only applies to code that will be compiled to JavaScript; it does not limit what Java version you can use to write server components that will be communicating with the browser. One last feature to note is that when your code is compiled to JavaScript, it results in a single JavaScript file for each browser type and target locale. The supported browsers include Internet Explorer, Firefox, Mozilla, Opera, and Safari. So typically this means that your application will be compiled to a minimum of four or five separate JavaScript files. Each of these files is meant to run on a specific browser type, version, and locale. A bootstrap script, initially loaded by the browser, will automatically pull the correct file when the application is loaded. The benefit of this is that the code loaded by the browser won’t contain code that it can’t use. Typically this won’t result in a huge bandwidth savings, but in some cases, especially when providing the interface in multiple locale settings, the size can be reduced significantly. Following our diagram in figure 1.1, we next look at a core feature of the GWT compiler, that being it’s ability to allow Java code to interact with native JavaScript code via the JavaScript Native Interface.
Using JSNI to Execute JavaScript from Java Although GWT code is written in Java instead of JavaScript, there may be times when you need to write code that can make direct JavaScript calls. There are several reasons why you might need to do this. One reason might be that you need to make a call to the browser’s API for which no GWT equivalent exists. Another, perhaps more common reason, is that there is some super-fantastic JavaScript library that you want to use. The JavaScript Native Interface, or JSNI for short, allows you to execute JavaScript from Java, as well as execute Java from JavaScript. This is made possible by the GWT compiler that can merge natives JavaScript code with the JavaScript code that is generated from Java. We will get into the finer points of doing this in chapter 8, but here are some examples to give you an idea as to how this works. This first example is very basic but reveals how the mechanism works. public native int addTwoNumbers (int x, int y) /*-{ var result = x + y; return result; }-*/;
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
In Java, you can declare a method as “native,” altering the compiler that the actual implementation of the method will be written in some other language. Per the Java language specification, when you declare a method as being native, you are not allowed to specify a code block for the method. If you haven’t seen this before, this mechanism was built into Java to allow Java code to call methods in compiled libraries written in languages like C and C++. When you inspect this method, you will see that what appears to be a block of code is really all contained in a multi-line Java comment. Inside of this comment is the “native” JavaScript code that will be executed when the method is called. This satisfies the Java syntax requirement of not allowing a code block for native methods, yet provides the actual JavaScript that can be used by the GWT compiler to allow execution of this code. In this example, we are passing a Java List object to the method and are using JavaScript to add two items to it. public native void fillData (List data) /*-{ [email protected]::add(Ljava/lang/Object;)('item1'); [email protected]::add(Ljava/lang/Object;)('item2'); }-*/;
Because we are calling the add() method on a Java object, we need to use a special syntax to provide details on the object and method we are referencing. Here we let the GWT compiler know that the variable data is an instance of java.util.List, and that the add() method takes a single java.lang.Object argument. This mechanism is fairly easy to use once you understand the special syntax, and allows you to include any needed JavaScript code in the same source file as your Java code. Chapter 8 gets into the details of the syntax. Moving on to the next component of GWT from figure 1.1, and keeping in line with the relationship between Java and JavaScript in GWT, we need to visit the JRE Emulation Library, a mapping of Java Runtime Environment (JRE) classes to their JavaScript equivalents.
Accessing the JRE Emulation Library When we looked at the compiler, we mentioned that the GWT compiler needs access to the Java source code for any class you are using in your code. This requirement doesn’t stop with the use of just external libraries; it includes the Java Runtime Environment (JRE) as well. To provide developers with the ability to use some of the JRE classes GWT provides the JRE Emulation Library. This library contains the most commonly used parts of the full JRE, which may be used in your projects and can be compiled to JavaScript. The two lists below enumerate the available classes of the JRE that may be utilized in your GWT applications. The first list shows the classes from the java.lang package, followed by the classes from the java.util package. If you look through the lists carefully, you will likely see several missing classes that you consider important. For example, you may notice that the java.util.Date class is available, but not java.util.Calendar or any date formatting tools. Classes from java.lang.* that are available in GWT Classes Boolean Class Integer Number String Throwable
Byte Double Long Object StringBuffer
Character Float Math Short System
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Classes from java.util.* that are available in GWT Classes AbstractCollection AbstractSet Collections LinkedHashMap* SortedMap* Vector Exceptions / Errors EmptyStackException Interfaces Collection Iterator RandomAccess
Abstract List ArrayList
AbstractMap Arrays
Date ListIterator* Stack
HashMap HashSet TreeMap*
NoSuchElementException
TooManyListenersException
Comparator List Set
EventListener Map
* Targeted for inclusion in the 1.4 release of GWT
When you start using these classes, you will notice some additional differences. Some of the functionality of these classes differs from the JRE versions in subtle ways. As of this writing, the following restrictions apply: Double and Float should not be used as HashMap keys for performance reasons. For String.replaceAll, String.replaceFirst, and String.split, the regular expressions vary from the standard Java implementation. StringBuffer(int) behaves the same as StringBuffer(). System.out and System.err are available but have no functionality in web mode. Stack Traces in Throwable are currently not supported The implementation of the Vector class does not include any of the capacity and growth management functionality of the normal Java implementation, nor is there any checking of index validity. In general, this isn’t as limiting as we have made it sound. In many cases you can get around the problem by using other Java classes, writing your own code to perform a specific function, or making direct use of the JavaScript API. As GWT gains momentum, it is likely a lot of these holes will be filled by either the GWT library itself or by open source libraries. Now we want to switch gears. As we saw in figure 1.1, the components of GWT are roughly divided into those relating to the compiler, and those relating to the GWT API. In this next section we begin our tour of the GWT API- by taking a look at the GWT widget and panel library, the visual components that will be used to build your user interface.
Understanding GWT's Widget and Panel Library GWT ships with a large set of widgets and panels available for use. The distinction between widgets and panels is that a widget is some sort of control used by a user, and a panel is a container into which controls can be placed. For example, a button or text box is a control, and a table that displays the button and text Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
box left-to-right on the page is a panel. But panels in GWT aren’t just for layout, some panels offer interactivity as well. Generally speaking there are three types of components in GWT; widgets, panels for layout, and interactive panels. Figure 1.2 shows a small sampling of some of the available widgets and panels.
Figure 1.2 – GWT ships with a set of widgets and panels that allow you to quickly create a rich Internet application without needing to worry about the HTML and JavaScript details.
The MenuBar, a widget, is shown across the top of the page. The TabPanel, an interactive panel, appears in the middle of the page, acting as a container for a TextArea and Button widget. The MenuBar and TabPanel are then contained in an AbsulutePanel that allows for exact positioning of the components it contains. If you are familiar with Java’s Swing library, then you may be familiar with using layout managers to organize the components inside of a panel, but this is difficult to map to HTML elements, so GWT takes a different approach.. Instead of layout managers GWT provides a set of panels that display their children in a specific manner. For example, the HorizontalPanel displays its child widgets from left to right, FlowPanel displays its children using normal HTML flow rules, and the AbsolutePanel provides exact positioning of the components it contains.. The set of widgets provided by GWT generally map back to a specific HTML equivalent. This includes form fields like Button, TextBox, TextArea, Checkbox, RadioButton, and FormPanel. In addition to these, there are several variations of the HTML table element including the base class HTMLTable and two specialized sub-classes, Grid and FlexTable. GWT also comes with several rich components that are familiar in desktop applications, but not so much in web applications. The TabPanel allows you to place different widgets on different tabs, and the widgets displayed depend on the currently selected tab, like Firefox’s and Internet Explorer’s tabbed browsing. The
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
MenuBar provides an easy way to create a multi-level menu for your application. Then there is a PopupPanel, StackPanel, and others. Although there are over 30 widgets and panels included with GWT, it is likely that they won’t fill all of your needs. To fill this void open source projects have been making specialized widget, tools, and panels available to use in your own projects. The list of widgets includes calendars, sortable tables, calculators, drawing panels, tooltip panels, and others. There are also a number of widgets available that wrap existing JavaScript libraries, like the Google Maps API, Google Search API, and Scriptaculous effects. Besides HTML-based widgets, there are also widgets available for Scalar Vector Graphics (SVG), a markup language for creating extremely rich vector based graphics. We discuss how to use third-party libraries in your projects, as long as a few of our favorite libraries in chapter 9. When building your own widgets by extending those that come with GWT, it is often required that you access the browsers underlying JavaScript objects. It would be great it we could forget about the underlying JavaScript completely, but that isn’t always the case. Fortunately, GWT provides facilities for interfacing Java with the underlying JavaScript. Next we take a look at a different part of the GWT API. GWT provides a set of tools for internationalization and configuration, allowing you to present your newly developed user interface in several languages.
Examining GWT’s Internationalization and Configuration Tools Several techniques are provided by GWT that can aid with internationalization and configuration issues. This may seem like an odd pair, but they are similar in that you want the ability to store text strings or numeric values in a properties file and to access them from your application. GWT provides two primary mechanisms that should handle most needs: static inclusion at compile time, and dynamic inclusion at run time. The static method is done by implementing the Constants or Messages interface, while the dynamic method is done using the GWT Dictionary class. When including your settings statically at compile-time, you do so by implementing an interface, either Constants or Messages, and by creating a single method for each property that you will want to use. For example, perhaps we would want to use a properties file to store our welcome message along with the image path of the logo for our application, in which case we would provide a method for each in our interface. public interface MySettings extends Constants { String welcomeMessage(); String logoImage(); }
Once we have our interface, GWT can be used to dynamically create an instance of this interface, and it will automatically attach the properties file settings to it. We can also set up several properties to be used for different locales, for example, if we wanted the text to change based on the language of the reader. The Messages interface differs from Constants in that you may specify arguments to the methods, which are then used to fill placeholders in the property text. For example, we might want to alter the interface above to allow the person’s name to be included in the greeting message. Our properties file might look like the following. welcomeMessage = Welcome to my book {0} {1} logoImage = /images/logo.jpg
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The placeholder {0} is used to mark the place where the first variable should be inserted into the message and {1} for the second. The interface that we used for Constants would need to be modified to use the Messages interface instead, and we would add two arguments to our method. public interface MySettings extends Messages { String welcomeMessage(String fname, String lname); String logoImage(); }
One of the benefits of using these two interfaces is that the messages from your properties file are statically included in your compiled JavaScript. This means that the performance of using a value from a properties file is about the same as using a hard-coded string in the application. The compiler will also only include properties that are actually referenced. This makes it possible to use only a few properties from a very large properties file without having all of the properties embedded in your JavaScript code. If you are only using this mechanism for specifying settings that you don’t want to hard-code into your application, then you will likely only have a single properties file, but if you want to provide for localization you will have one properties file for each supported language. When compiling your code with multiple properties files, the GWT compiler creates a different set of JavaScript files for each locale supplied. If you are supporting a lot of locales, this will result in a lot of files, but the benefit is that each JavaScript file will only have a single set of properties embedded in it, making the file size smaller. On the flip side of the coin there will be occasions where the property values are generated dynamically, for example the details of a specific user. In this case you need a dynamic run-time mechanism to lookup property information. For this GWT provides a Dictionary class, which doesn’t use properties files at all. Instead, the Dictionary object can be used to grab settings that have been embedded in the HTML as JavaScript objects. var MySettings = { welcomeMessage: "Welcome to my book", logoImage: "/images/logo.jpg" };
In the GWT application, you would use the Dictionary class to load by specifying the JavaScript variable name. This is done by calling the getDictionary() method, which returns an instance of the Dictionary class. You can then use the methods of the object to get the individual settings. This mechanism is ideal when you want to pass data to your GWT application. Dictionary settings = Dictionary.getDictionary("MySettings"); String logo = settings.get("logoImage"); Table 1.1 – Configuration and internationalization properties can be included in your module either statically or dynamically. Both are briefly summarized in this table. There are advantages to each, depending on your circumstances. By providing these two methods, GWT ensures you can handle most configuration and internationalization issues.
Messages or Constants interface (static) Dictionary class (dynamic) Single property file for each supported Does not use properties; embeds settings in language HTML as JavaScript.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The configuration mechanisms provided by GWT, covered in detail in chapter 15, will handle most of your configuration and internationalization issues, and allows for both dynamic and static properties. So far we have looked at the compiler and it’s supporting tools for compiling our code, we have introduced you to widgets and panels for building the user-interface, and now internationalization tools so that we can make it available in a dozen different languages. Now we take it one step further and take a look at the tools that GWT provides for communicating with the server and making our applications truly interactive.
Calling Remote Procedures with GWT Most non-trivial GWT applications will need the ability to communicate information between the browser client and the server for one reason or another. For instance, perhaps the application will need to fetch data for display to the user, or perhaps to log the user into the application, or maybe to load some external data file Fortunately today’s browsers include a special JavaScript object called XMLHttpRequest that allows communication between the browser and server without forcing a page refresh like traditional HTML forms do. This special JavaScript object is the basis for making browser based Remote Procedure Calls (RPC). GWT provides two tools that sit on top of the XMLHttpRequest object. The first is the RequestBuilder class, which is essentially a wrapper around this object, although a bit more Java-like in its usage. The second tool, namely GWT-RPC, is a more elaborate and allows you to send and receive real Java objects between the client and server. We will begin with the RequestBuilder class, and get a feel for what RPC looks like in GWT.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Making RPC Requests with RequestBuilder The RequestBuilder class allows you to create a request to be submitted to the server, the ability to fire off the request, and provides access to the results sent back from the server. Let’s take a look at a short code example to get a feel for how it works. Listing 1.1 – An example of a RPC call using RequestBuilder
public void onResponseReceived (Request req, Response res) { // process here } public void onError (Request req, Throwable exception) { // handle error here } }); } catch (RequestException e) { // handle exception here }
If you aren’t used to using anonymous classes, then this example might look a little foreign, so let’s explain it a little. We begin by creating a new instance of the RequestBuilder [#1] by specifying the HTTP method and target URL to use. Once created we can use various methods of the RequestBuilder instance to set a timeout, add HTTP headers to the request, and even set the username and password for hitting URLs that require authentication. All of these options will be covered in detail in chapter 12. Next we fire off a request to the server by calling sendRequest() [#2] on the RequestBuilder instance. This method returns a handle to the request, which is of type Request. For long running requests you can use the returned Request to check on the status of a request or even cancel it. The sendRequest() method takes two arguments, a String that is sent to the server with the request, and a RequestCallback instance to handle the response and any error that might occur. In listing 1.1 we are using an anonymous class [#3] that implements the RequestCallback, essentially an inline class, for this second parameter to keep the example short, but the response handler will contain more than a few lines of code it is preferable to create a separate class. The reason for having using a handler class for handling the response, is that RPC calls from the browser occur asynchronously. The term asynchronous in this context means that the sendRequest() method will return immediately, not waiting for the server to respond, putting the server request and the execution of the JavaScript code out of sync. When the server does eventually respond the handler will be triggered, just like an event handler. There is a good reason for the call to happen asynchronously. For the browser to handle
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
the call synchronously it would need to stop handling events, and would appear to be frozen from the user’s perspective. This would definitely not be desirable. Besides the asynchronous nature, another notable attribute is that the response to a call made with RequestBuilder is text. This text can be an XML file, HTML code, simple plain text, or JSON code. If you haven’t heard the term JSON before, it is an acronym for JavaScript Object Notation. JSON is a simple message format for sending structured data, and is specifically geared for use in the browser. In chapter 13 we take a long look at how to use JSON, on both the client and server, but for now a brief example will suffice. 1.26 1.27 1.28 1.29 1.30
JSONObject obj = new JSONObject(); obj.put("title", new JSONString("GWT in Action")); obj.put("author", new JSONString("Hanson and Tacy")); obj.put("pages", new JSONNumber(600)); String serializedObj = obj.toString();
In this code sample we create a JSONObject, and populate it with several properties, just like we would with a Java HashMap. Calling the toString() method will return the serialized form of the object as a text string. The next step, although not shown, would be to use the RequestBuilder class to send this serialized data to the server for processing. The JSON implementation that comes with GWT is limited to use in the browser only, so you need to find your own JSON implementation for use on the server. Fortunately it is fairly easy to find a JSON implementation by visiting http://json.org, the home of the JSON format. One thing that you will notice when you look for an implementation is that you aren’t limited to just Java, in fact there are JSON libraries for dozens of languages. This makes JSON a truly universal format, allowing you to pair your GWT application with an application written in any language on the server. For those of us running only Java on the server, using JSON can feel very awkward. On the browser client you would need to copy the data from your Java object into a JSON object, then on the server copy the JSON object back into a Java object. When you are using Java on the server it just makes more sense to deal with Java objects directly without the JSON middle-man, and that is where GWT-RPC fits in.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Communicating with GWT-RPC The GWT-RPC mechanism allows you to send Java objects between the client and server with only a little additional work on both the client and server sides. We get into the finer points of using GWT-RPC, and show you some common usage patterns, in chapters 10 and 11, but for now we will stick to the basics. You first define a service interface that will be implemented by the server. For example, if you were going to create a password service it might look something like the following. 1.31 1.32 1.33
Nothing too complex here. The interface simply defines one method for changing the user’s password. The only requirement is that the interface must extend the RemoteService interface supplied by GWT. Next comes the implementation of the server. Again, this is about as easy as it gets. 1.34 1.35 1.36 1.37 1.38 1.39 1.40 1.41
public class PasswordServiceImpl extends RemoteServiceServlet implements PasswordService { public Boolean changePassword (String user, String old, String new) { // add code here } }
The implementation implements the interface that we defined for our service, and extends the RemoteServiceServlet. The RemoteServiceServlet is where all of the magic happens. This servlet receives the data from the server, which we already mentioned must be text due to how the underlying XMLHttpRequest object works, and deserializes the text data into Java objects. The Java objects are then passed to the implemented changePassword() method for processing. On the return trip the RemoteServiceServlet serialized the return value into text, which can then be sent back to the browser. So far so good. We created a service interface, and coded the implementation on the server in a specific way. Nothing too hard so far, and coding the client-side call is just as easy. service.changePassword("jdoe", "abc123", "m@tr1x", new AsyncCallback() { public void onSuccess (Object result) { Window.alert("password changed"); } public void onFailure (Throwable ex) { Window.alert("uh oh!"); } });
Here we call the changePassword() method, and in the call we include an additional AsyncCallback handler. This is just like the RequestCallback that we used with the RequestBuilder, it handles the result that is passed back from the server. Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
To be honest, we aren’t giving you the whole story, for example you may have noticed that we never showed you the code to create the service object on the client-side. We did this because we want to provide the flavor of what GWT-RPC provides without getting into some all of the details. For us, we found that GWT-RPC is very easy to use once you have gone through a few iterations of using it, and when you understand its limitations. We provide all of these details in chapters 10 and 11 so that you can take full advantage GWT-RPC. Following our diagram from figure 1.1, we move on to a topic that is somewhat related to RPC. Earlier in the section we mentioned that RequestBuilder received a text response from the server, and we included XML in the list of formats that the text might be in. Well, it would be difficult to use XML data without an XML parser, so it is a good thing that GWT has that too.
Investigating GWT’s XML Parser In the last five or so years XML has become a part of our daily lives as developers. The configuration of our Java server uses an XML format, the RSS feeds that we consume and supply are XML, and often so are the protocols that we use to communicate with remote services, like in the case of SOAP and XML-RPC. To make it as simple as possible to deal with these data formats on the client browser, GWT provides a DOM based XML parser. DOM based, or Document Object Model, XML parsers will consume the XML and create an object tree. You can then use the DOM API to traverse the tree to read its contents. GWT takes advantage of the fact that modern browsers have the ability to parse XML and create a DOM tree. Because the parsing is done by the browser and not by GWT, you get the performance benefit of native code execution. This code sample shows you how the XMLParser is used to create a Document object, through which the entire DOM tree can be traversed. Document doc = XMLParser.parse(responseText); Element root = doc.getDocumentElement(); NodeList children = root.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { doSomethingWithNode(children.item(i)); }
From the Document object we can grab the root element, and in an XML document there can be only one. From the root element we can iterate through the children, and their children, and so on. The GWT DOM implementation is based on the standard provided by the W3C, the same people who standardized HTML. In chapter 12 we use RequestBuilder with GWT’s XML parser to load a set of bookmarks from an XMl file sitting on the server and present them as a menu list. Next we continue with our exploration of the various GWT APIs and show you how GWT can manage your browser history.
Managing the Browser History One of the completely valid complaints of rich Internet applications is that it “breaks” the browser’s back button. What we mean by this is that if you replace a piece of content on the web page dynamically with JavaScript, the browser doesn’t count that as a page change. So what often happens is the user sees the content of the page change, so they just assume that they can hit the back button in the browser to get back to the previous content, but their assumption is false. This behavior is due to the fact that changing the content
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
of a page programmatically is a fairly new to many users, and they don’t understand it. In order to provide a good experience for the user, we need to find a way of handling this so that it is transparent to the user. The popular solution to this problem tends to be a little complicated to implement and use. It requires adding a hidden frame to your page, and some amount of scripting to get it to work. In the case of GWT, all of the hard work has been done for you. To access it, you will need to write an event handler that implements the HistoryListener interface and register it with the History object. Here we register an instance of a user defined class MyHistoryListener. 1.42
With the listener registered you can track a “page change” by creating a new history token. A token is a keyword that defines the content change. For example you might create the token “overview” for the page with information about your company, and “reports” for a different page showing reporting information. Note that when we say page, we don’t mean that the browser is loading different URL. This is GWT after all, so a page could be content loaded by selecting a tab on a TabPanel, or selecting an option from a MenuBar. 1.43 1.44
Creating a new token will do two things. The first thing it does, which is invisible to you, is that a hidden frame on the web page will be loaded with a new page. Because the hidden frame is loaded with a different page, the browser will count this as a new page view, and add it to the history for the browser. The second thing this does is call the onHistoryChanged() method of the HistoryListener to indicate a content change. 1.45 1.46
public class MyHistoryListener implements HistoryListener { public void onHistoryChanged (String historyToken) { if (historyToken.equals("overview")) { // display overview panel } else if (historyToken.equals("reports")) { // display reports panel }
} }
Hopefully you can see how this will work now. Because the hidden frame has changed, clicking the back button in the browser will change it back. This will in turn trigger another call to the HistoryListener with the previous token. And don’t forget the browser’s forward button. Now that we have gone back into the history, the forward button will move it back forward again. This behavior is exactly what the user expects. We discuss the history mechanism a little more in chapter 4. Now we move on to the last major API feature, and that is the testing framework. Being able to test JavaScript code properly has proven to be a fairly difficult task due to the lack of good testing tools, and often it is done poorly. GWT again makes our life easy by allowing you to test your GWT code with the Java developers favorite testing framework, JUnit.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Introducing GWT’s JUnit Integration It has always been a best practice to write automated tests for your code, and various frameworks have been made available over the years to make the process less painful. JUnit is the most popular tool of this variety used by Java developers and is integrated with many IDEs. Instead of creating a new framework for GWT from scratch, GWT provides support for JUnit so that you don’t need to learn yet another testing framework. To create a new test case, you create a class that extends GWTTestCase, which in turn extends JUnit’s own TestCase class. You then need to implement just one required method, getModuleName(), followed by any number of specific tests. The module name is used by GWT for locating the project configuration file. We will delve into the details in chapter 16, but for now let’s look at a short example. public class MathTest extends GWTTestCase { public String getModuleName () { return "org.mycompany.MyApplication"; } public void testAbsoluteValue () { int absVal = Math.abs(-5); assertEquals(5, absVal); } }
Here we only included one test, which verifies that the absolute value of -5 is 5. This is obviously true, but it if were false the assertEquals() method would trigger an exception that will be reported by the JUnit test runner. Besides the usual JUnit style testing the GWT version also allows you to test RPC calls to the server. Specifically GWT will start up its own version of Tomcat, execute your compiled GWT code, and test that when your client-side code calls the server-side application that it gets the expected results. In short, it is doing things that you couldn’t do otherwise without having someone sit in front of a browser to test your application. No offense to real user testing, but if you have a large suite tests that you want to run over and over, it is not realistic to always do this manually. With the end of this section on JUnit, so does our look at the major features of GWT. Next we want to show you how GWT compares to other technologies.
GWT vs. Other Solutions GWT isn’t the first tool trying to make it easy to build rich Internet applications, and as web technologies constantly evolve, it undoubtedly won’t be the last. In this section we take a look at several other technologies that are often lumped into the same category as GWT and explain where the differences lie. In each of the following sections, we provide a code example for creating a text box and a button. We then fill the text box with the word “clicked” when the button is clicked with the mouse. Finally, we compare the code example provided to our GWT reference implementation and discuss the important differences. Figure 1.3 shows what the GWT reference implementation looks like in a web browser when the application starts, and then after the action button is clicked. Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 1.3: Images of the GWT reference implementation of text box and button before and after the action button is clicked
To begin, we need to examine our GWT reference implementation. Listing 1.2 – An implementation of a simple Button event in GWT
final TextBox text = new TextBox(); text.setText("text box");
| #1 | #1
final Button button = new Button(); button.setText("Click Me");
This is the first example of GWT code that we have looked at, and it deserves a thorough explanation. #1 To start, we create a new text box, which, once displayed in the browser, will look like a normal text box that you might find in a form on the web. #2 Next we create a button and set the text to display on the button. Note that at this point neither the text box nor button has been “attached” to the web page. #3 We then add an event handler for the button. We do this by adding a click listener object, which will be called when a user clicks the button. You will notice that the object we add as a listener is an anonymous class. If you haven’t seen this type of Java construct before, it might seem a little unnatural at first, but all we are doing is creating an new object that implements the interface ClickListener. #4 Next we add a panel to our window, and then #5 add the text box and button to the panel. When we run this GWT code, it renders an HTML page with a text box and button, and, when the button is clicked, changes the text in the text box. In the following sections, we port this very simple code to a few other frameworks and discuss how they differ from GWT. Note that we are making every attempt to be unbiased as we describe the competing frameworks. We believe that each tool is different from the next, and each has its own strengths and weaknesses.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
With that in mind, we begin our tour with Swing, followed by Echo2, Java Server Faces, and Ruby on Rails.
GWT vs. Swing Swing is the standard toolkit for building GUI applications in Java. This might seem like a strange choice of tools to compare GWT to because Swing isn’t typically associated with web applications. The reason for the comparison to GWT is because the two frameworks are very similar in how you write code for them. Listing 1.3 - An implementation of a simple Button event in Swing
final JTextField text = new JTextField(); text.setText("text box");
| #1 | #1
final JButton button = new JButton(); button.setText("Click Me");
This Swing code should look vaguely familiar to the GWT reference example; in fact it should look nearly identical. There are a few name changes, for instance GWT’s ClickListener interface is called ActionListener in Swing. For Swing developers, there are a few important differences between GWT and Swing. The first is that the components that ship with GWT don’t follow the Model View Controller (MVC) pattern. That is to say that there isn’t a model object that can be shared by multiple components to keep them in sync. The second difference is that GWT doesn’t use layout managers for controlling the layout. Instead you use panels that have built-in layout styles. For example, the GWT HorizontalPanel arranges its child components left-toright across the page, while the DockPanel allows you to add widgets to the panel in a similar fashion to the Swing’s BorderLayout.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
That being said, these differences are fairly easy to work with, and GWT is a very friendly environment for Swing developers. Next we look at Echo2, which allows you to write applications in a similar manner to GWT, but takes a very different approach.
GWT vs. Echo2 Echo2 is another popular web toolkit in the same problem space as GWT, and is similar to GWT in how it is used to create the user interface. You use the API to create instances of components, then add them to the display. Below is the Echo2 version of the GWT reference example. You will see that the Echo2 version looks nearly identical to the GWT version. Listing 1.4 - An implementation of a simple Button event in Echo2
final TextField text = new TextField(); text.setText("text box");
| #1 | #1
final Button button = new Button(); button.setText("Click Me");
Although both frameworks use similar APIs, they work in an entirely different fashion. Applications written for Echo2 run on the server, not the client. With GWT, you compile your Java source to JavaScript, and run it on the browser. With Echo2, you compile your Java source to Java class files, and run them on the server. This also means that, when a client-side event is triggered, it may need to be handled on the server. The consequence of this is that an interface built with Echo2 will need to hit the server more often, but it doesn’t need to deal with an RPC API since the RPC just happens all by itself. It also means that Echo2 doesn’t need to send all of the JavaScript to the browser at once; it only sends what it needs to given the current state of the application. And, finally, this also means that you are tied to using a Java application server since this is required to host your Echo2 application. Next up is another Java based framework, called Java Server Faces.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
GWT vs. Java Server Faces Java Server Faces (JSF) is a web framework for Java-based web applications. It makes use of managed Java beans on the server, which represent the model, plus a set of tag libraries that can be placed in a JSP page to reference the properties of the model. In a standard JSF implementation, all of the processing is done on the server, and the web page reloads for each transaction. The fact that the page needs to reload for each transaction doesn’t really make JSF a viable rich client for built-in components, but with additional effort it is possible. For the sake of comparison, we will provide a standard non-Ajax JSF application that can perform the same action as our reference GWT application. The first step in creating a JSF application is to create a class to represent the model. For our example, our model is very simple; it contains only one property named “text”. In standard Java bean fashion, we make the property private to the class and provide accessors for getting and setting the value. To this we also need to add a method named changeText(), which is triggered when the command button is clicked. Listing 1.5 – A JSF managed bean that represents a simple model with a single property “text” and a single command
package org.gwtbook; public class SampleBean { private String text = "text box"; public String getText () { return text; } public void setText (String text) { this.text = text; } public void changeText () { this.text = "clicked"; } }
The next step is to register this class as a managed bean in the JSF configuration file. We provide the name “sampleBean” for our managed bean, and this name will be used to reference it in the JSP code to follow. Listing 1.6 – A snippet of the JSF configuration file that defines a managed bean
The JSP page will look similar to a standard JSP page. It uses two JSF tag libraries to specify the view and controls that we are using. For the value in the inputText tag, we reference the text property of our managed bean using the JSF expression language. In the actionButton tag we see it again, but this time it references our changeText() method.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Listing 1.7 – The HTML code and tag libraries used to render a button and link it to our managed bean
JSF Example
JSF is very different from GWT in the fact that JSF provides very little in the way of support for rich client-side functionality. It would be possible to build reusable client-side components, doing some of the work in JavaScript, but custom components would have little reuse value. Because JSF does integrate the client-side, it is in competition with GWT, but there is still some potential for integrating the two. Next we compare GWT to Ruby on Rails, a very popular non-Java framework for writing web applications.
GWT vs. Ruby on Rails The title of this section is a little misleading because GWT and Ruby on Rails don’t really compete, although they may overlap in some respects. Ruby on Rails is a rapid development framework using the Ruby language. It provides the server-side of the equation and is specifically designed to handle a lot of the backend work automatically for you. On the client-side, Ruby on Rails provides some support for Ajax, allowing you to use the Java equivalent of a tag library in your HTML code. The end result is that Ruby on Rails can send data to the server triggered by a user action, and display a response in the page. It is not designed, though, for complex interactions between the client and server. GWT is client-centric, and most of what GWT does is on the client-side of the picture. It allows you to develop and display widgets using Java and write Java handlers to trap user-triggered actions. GWT can communicate with the server, as needed, which could be driven by user interaction, or perhaps a timed event. GWT then allows you to compile all of the Java code to JavaScript so that the program can be run in the browser. On the server, GWT only provides a mechanism for serializing and desterilizing Java objects so that they can be received from the browser and sent back, and does not get involved in other aspects of the server. Instead of competition between GWT and Ruby on Rails, we find an opportunity for integration. This is in part driven by the fact that GWT provides several non-proprietary schemes for passing data between client and server. We are finding that many developers who are starting to use GWT are using non-Java technologies on the server, and are looking at GWT to provide only client-side functionality.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Building Your First GWT Application As most developers, the first thing we want to do when trying out a new technology is to build something with it. We could write pages and pages explaining how to use GWT, and we will, but that can’t replace having an actual running example in front of you. To that end, this section will be drastically light on the details, and we focus only on getting your first GWT application up and running. In this section we assume that you have downloaded and uncompressed the appropriate GWT distribution for your platform, and verified that you have a version of Java installed on your workstation that can be used with GWT. If you haven’t, you can download GWT from http://code.google.com/webtoolkit/ and Java from http://java.sun.com. So let’s get to it and build something.
Building and Running an Example Application GWT ships with an application creator tool that creates a directory structure and populates it with some sample code. This tool is your friend; you will likely use it to create the skeleton for every GWT application you write. We will cover this in much greater detail in the next chapter. To run it, you will need to open a command prompt or shell and navigate to the directory where you unpacked GWT. As arguments to the command, we specify an output directory named “Sample” and specify the Java class that we want the tool to create. 1.47
That’s it. You have now written your first fully functional GWT application. Granted, we haven’t actually seen it run yet, but rest assured that this little application will compile to JavaScript and execute. At this point, before you run it, we suggest that you explore each of the generated files. Table 1.2 provides a description of each file. Table 1.2 – The files that are generated when using the GWT default applicationCreator tool, which will be described in more detail as we work through additional examples.
App.gwt.xml
This is the configuration file for the “module”. This is used to define the entry point class, dependencies, and compiler directives. The entry point class is the class that gets executed when the module loads into your browser. Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
App.html
App.java
App-shell.cmd
App-compile.cmd
This is the HTML page that will load and execute the application. We don’t want to get into the specifics as to how the HTML loads the module, but it may be worth looking at. This file is well commented. This is a sample entry point class that is generated by the creator tool. This is where you put the Java code that you want to be compiled to JavaScript. This is a simple shell script that executes the hosted-browser that ships with GWT. The hosted-browser works like your web browser, but it is specifically tailored for GWT development. Executing this will launch the application. This is another simple shell script that executes the Java to JavaScript compiler. Just like the App-shell script, this script references the module configuration file which provides the details on what needs to be compiled. Running this will create a directory “www” and generate the JavaScript there.
The next step is to actually run the application, and, if you haven’t guessed already by looking at the generated source code, it is a “Hello World” application. You have two options for doing this. You may either (A) run the App-shell script to launch the hosted-browser, or (B) run the App-compile script, then open the App.html file in the “www” directory in your browser. We recommend that you do both. It is worth experimenting here a little to see how each of these works because you will use both of these frequently when you develop your own applications. In figure 1.4 we show what the application looks like when run in Firefox. It is a very basic application that presents a button that toggles the visibility of the “Hello World!” message. From browsing through the project files, you may have noticed that the text at the top of the page is in the HTML page, and only the button and “Hello World!” label are referenced in the Java file. This is a good example as to how GWT can mix HTML content with application logic, allowing you to leverage the skills of a designer without needing to teach them GWT.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 1.4 Sample generated Hello World application running in JavaScript. This is what you should see on your screen; if not, you may want to go back and make sure you followed along exactly. This example shows how GWT can mix HTML content with application logic, allowing you to leverage the skills of a designer without needing to teach them GWT.
That is a wonderful example, but, if you are like us, you have lost count of how many Hello World examples that you have seen. So let’s have some fun and alter the code a bit to do something a little more exciting.
Building Tic-Tac-Toe with GWT First things first; if you have a favorite IDE, now is the time to start it up. You will need to add the gwtuser.jar file that came with the GWT distribution to your classpath. It doesn’t matter too much what IDE you use at this point, but it is worth noting that GWT does provide some additional support for Eclipse users, as we will see in chapter 2. You should also set the project to use Java 1.4 compatibility. As of writing this, GWT does not yet support the new Java 5 syntax constructs for client-side code, so for now we need to limit ourselves to Java 1.4. The client-side code is considered to be any Java code that will be compiled into JavaScript and run in the browser. The next thing we need to do is decide what to build. This early in the book we don’t want to overwhelm you with advanced GWT concepts like internationalization or remote procedure calls, so we will stick to something simple. So what we will do is build a very simple rendition of Tic-Tac-Toe.
Designing theTic-Tac-Toe Game We will build a Tic-Tac-Toe game by starting with the Grid widget from the GWT library. The Grid control will render a HTML table with a specific number of columns and rows. In our example, we will use this to build the regulation 3x3 Tic-Tac-Toe grid, but you may alter this to use a more challenging 4x4 or even 5x5 grid. In each of the cells of the Grid control, we will put a blank Button control. The end result looks something like Figure 1.5. Unless you are using a visual designer to build your GWT application, it is usually a good idea to sketch out your application before you build it, like we did with this diagram.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 1.5 Design sketch of the GWT controls used in a Tic-Tac-Toe game (before coding). It is usually a good idea to sketch out your application before you build it.
The logic of the application is very simple. When the first player clicks a button, we change the text of the button to display an “X”. When the next player clicks a different button, we change the text of the button to display “O”. We toggle back and fourth between “X” and “O” as the game progresses. We also need to perform a check before changing the text of a button. If the button that was clicked by the player already contains a marker, then we display an alert message that tells the player they must select a different square. All of the changes we will be making are in the App.java file, so if you want to follow along with us you should open that file now. Now let’s move on and write the code to make this work.
Writing the Tic-Tac-Toe Game To build our Tic-Tac-Toe game, we need to import the needed Java classes for the project. In the App.java class, you need to include the following import lines for the project. 1.57 1.58 1.59
The EntryPoint interface is required to be implemented by the main class of the module. The EntryPoint interface requires the implementation of the method onModuleLoad(). This method has the same purpose as the main() method in a regular Java application, or the service() method in a Java servlet. This is the method that starts the application. The Window class is roughly equivalent to the window JavaScript object. It provides access to the browser window to do things, such as to determine the browser’s height and width or to display an alert message to the user.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The third import we use is to import the entire GWT user interface package. This includes dozens of widgets, listeners, and panels that we will see more of as you progress through the chapters. For now we won’t get into any of the details of what classes this includes, and we will just focus on the project at hand. Listing 1.8 A simple Tic-Tac-Toe implementation in GWT
public class App implements EntryPoint { private boolean playerToggle = true; private ClickListener listener = new TicTacClick();
| #1 | #1
public void onModuleLoad() { Grid grid = new Grid(3, 3);
| #2
for (int col = 0; col < 3; col++) { for (int row = 0; row < 3; row++) { Button button = new Button(); button.setPixelSize(30, 30); button.addClickListener(listener);
| #3 | #3 | #4
grid.setWidget(col, row, button);
| #5
} } RootPanel.get().add(grid);
| #6
} private class TicTacClick implements ClickListener { public void onClick(Widget sender) { // todo } }
| | | | |
#7 #7 #7 #7 #7
We begin by defining a boolean value [#1] to keep track of whose turn it is so that we know if we should display an “X” or “O”, and create an listener instance that will be used to handle click events. We will provide more information about this listener shortly. Next we create a 3x3 Grid control [#2]. At this point the control won’t be displayed on the page, but it is constructed in memory. As we loop through the 3x3 Grid, we create the Button control [#3] that will appear in that square. We also set the width and height to 30 pixels. Alternatively, we could have used CSS in the HTML page to specify the height and width, but for now this will suffice. After we create the Button control, we register the listener [#4] that we created earlier to receive click events for the Button. The listener is an instance of the private class TicTacClick, which will handle the click event for us, but for now we haven’t provided any code for this. Once the Button has been created, we need to add it to the Grid [#5]. Here we use the col and row loop variables to place it in the right cell. Once the Buttons are added to the Grid, we need to add the Grid to the page [#6]. The RootPanel class represents the top-level panel on the HTML page, and here we add the Grid to our RootPanel. By calling RootPanel.get() we are adding the Grid to the very bottom of the page. Alternatively you can pass a String argument to the get() method to specify the HTML element ID where the control should be added. This
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
allows you to add controls to not only the end of the page, but inside of any HTML element that has an ID attribute. [#7] This is our code that will handle the Button clicks and make the “X”s and “O”s appear. We didn’t want to try to put all of the code into a single listing, so we will add the look at this next. You should replace segment [#7] with the code from listing 1.9: Listing 1.9 A click listener class for our Tic-Tac-Toe implementation
Our TicTacClick class implements ClickListener [#1], which requires that we implement the method onClick(). This method is called whenever a click event is triggered, and, as a parameter to this method, we receive a reference to the object that was clicked. In our case we know it is a Button control that was clicked, so we can cast it as such. We need to test if this specific Button has been clicked before [#2], and, if it has, we need to alert the player that they need to select a different square. The getText() property of the Button will return the text that is displayed in this control. Remember the boolean toggle property we created in our class? We created it to toggle between player turns. If the value is true, we display an “X” [#3], and, if it is false, we display an “O”. We then toggle the playerToggle value so that it switches the displayed value the next time a Button is clicked. If the Button is already displaying text, we drop down to this else condition. Here we are using the Window object [#4], which is the equivalent of the JavaScript window object, to display a message to the user indicating that they need to select a different square. If you typed everything in exactly the same as we did, your Tic-Tac-Toe game should look like figure 1.6, which shows the game in the hosted-mode browser.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.108 Figure 1.6 Tic-Tac-Toe GWT application running in the hosted-mode browser. This is how your screen should appear if you are following along with the code.
1.109
What we have done with this example is pretty simple, but we hope that it has given you a small taste of what GWT is capable of. This example only scratches the surface, though, as there is much more to GWT than simple client-side applications. In any case, we need to wrap things up and start getting into the fine details. So let’s see if we can summarize what we have seen thus far and prepare ourselves for the rest of the GWT adventure.
Summary The Google Web Toolkit (GWT) adds a new tool to the web developer’s tool belt, helping to solve some of the hardships involved with developing complex rich Internet applications. GWT changes the way you write rich clients by allowing you to write web applications the same way you write desktop applications with Swing. With GWT, you write code in Java, using a plethora of fancy Java tools like Eclipse and JUnit, without the need to learn the intricacies of JavaScript. It provides an abstraction on top of the DOM, allowing you to use a single Java API without having to worry about differences in implementations across browsers. When you are done with your Java application, you can then compile your code to JavaScript, suitable for running on today’s popular browsers. Tools like Echo2 attempt to do this as well, but they require a Java application server to serve the application. GWT allows you to instead create an application that compiles completely to JavaScript and can be served by any web server. This allows GWT to be easily integrated with existing applications no matter what type of server you are running. An important part of what GWT has to offer is its toolset for making Remote Procedure Calls (RPC). It provides a simple RPC mechanism for passing Java objects between the client and server. It does so by serializing and deserializing the objects on both client and server, allowing you to pass custom beans without having to worry about serialization details. GWT also allows communication to non-Java applications via the
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
standard JavaScript Object Notation (JSON). JSON libraries are available for most languages, making integration a relatively simple task. GWT as a whole lets you develop web applications at a higher level of abstraction, and leverages the tools already available for the Java language. It provides an easier way to build rich Internet applications. In the chapters that follow, we will get into the details of doing this. We will show you how to set up your development environment, build an application with existing widgets, create some new widgets, communicate with the server, and much more. First, let's start by expanding upon the default application we began in Chapter 1, then applying that to a running Dashboard example we will be returning to throughout the book.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
2 Creating the Default Application Chapter 1 got us up and running with our first GWT application; it was a fairly quick and dirty approach, but it worked, and you quickly had a fully functioning Tic-Tac-Toe application. Now it is time to start shoring up our approach a little and take the first few steps towards building a full-scale application. Over the next two chapters we will create the first version of the Dashboard application, which is an example we will keep expanding and adding to as we progress through this book. You should really read this and the next chapter together, as they are both needed to create the first version of the Dashboard. We split it into two chapters, though, so that in this chapter we could first explain the tasks that are needed for every GWT application - these initial steps result in the production of an application which we call the GWT default application - and then in chapter 3, we go through those steps needed to specialize the default application in order to produce your own application (in our case, the GWT Dashboard). If you are using a wizard provided by an Integrated Development Environment, then it may perform the steps necessary to generate the default application for you. To help us explain how the contents of the two chapters go together, we first present a typical development lifecycle, which a real world programmer might apply when developing GWT applications. With one possible specific high-level web development process in mind, chapter 2 looks again at the tools we used in chapter 1, but this time we will consider them in some detail. We actively use those tools shown first in chapter 1, which we will refer to as the creation tools, to create the default directory structure and files used as the basis for our example Dashboard application. This step is performed for all GWT applications we build, and the output is the GWT default application that we first saw in figure 1.2 in chapter 1.3.1. Enough talk; let’s take the first step along the way of building the Dashboard’s directory and code structure by examining exactly how we build this GWT default application we have been talking about.
The GWT Application Development Lifecycle GWT applications can be created in one of three ways. The first way is to use the set of creation tools that are provided within the GWT download. These tools allow us to quickly create a directory and file structure suitable for our GWT applications, which is also in line with the structure the GWT compiler expects. We have already seen these tools in use when we created the TicTacToe application in chapter 1. If we use these creation tools then the result is, by default, independent of any IDE that we might be using. By providing the extra –eclipse flag to the creation tools, we direct them to generate an additional set of files that enable the whole structure to be easy imported into the Eclipse IDE. (Eclipse is not the only IDE that could be used; GWT just makes it really easy to integrate with Eclipse, without using an IDE specific wizard – it is also possible to import Eclipse projects into some of the other more common IDEs). As GWT matures, more and more 3rd party tools are coming to the market that hide the need to use the GWT creation tools. In some cases these 3rd party tools come in the form of wizards that IDEs support, or in other cases whole new IDEs are being built or tweaked specifically to support GWT application development. If you are using one of these tools, then generally there will be specific instructions on how to use them given with the tool, but quite often the result of running an IDE specific wizard will be a default application, similar to that produced by the GWT creation tools.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
If you don’t want to use the GWT provided tools, or a tool provided by your IDE, then it is possible to create the directory structure and basic files yourself. You might want to do this, for example, if you are working in an environment where system restrictions prevent you using the standard structure. Using this approach you are responsible for creating the directory structure and basic set of files. You may also have to deal in more detail with the application’s Module XML file (see chapter 9) in order to tell the compiler all of the paths to the necessary files. We can summarize the 3 different creation methods in one single diagram: Figure 2.1:
Figure 2.1 The three different ways of creating a GWT application: using the GWT tools, using a plug-in for an IDE, and creating the structure and files by hand. We use the GWT creation tools in chapter 2.
All three approaches lead to the production of the same set of basic files that represent the GWT default application, which is good, as all of those files are needed for your application to work. For simplicity at this stage in GWT maturity, in this book we will follow the first approach – using the set of creation tools provided by the GWT distribution. Looking at the other two approaches identified in Figure 2.1, we would say that probably the most error-prone way to create a GWT application’s structure and files is to do so by hand. We would advise against this approach, but it can be useful if your environment forces you to use a different directory structure than the default one. We will not discuss this “by hand” approach in this book, it is not difficult, but requires a lot of attention to detail making sure all the necessary files are in correct places and that the hosted mode and compile tools have the correct class-paths set etc; so we would risk spending more of our time explaining that than getting going with the development! We should say that even though we are using Eclipse in this book, GWT is not tied to Eclipse; the files produced can be imported into any IDE or your application can even be created from using the command line tools. Eclipse, like any IDE, has plus and minus points, but it is free and, probably more importantly, widely used. This book will not get into the pros and cons of any particular IDE; GWT provides great support at the creation tool level for Eclipse, so we will continue with that – don’t worry if you are not using Eclipse as you IDE, we will look briefly later in section 2.2.6 on how you could import the files into other IDEs.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The GWT creation tools we look at in this chapter -- together with the GWT Hosted Browser, GWT Compiler, and your choice of Web server and browsers -- provide your complete development and test environments (Table 2.1 explains where each tool is used and its benefit). In Figure 2.2 we show a simplistic view of the lifecycle stages in which the creation tools, and other tools just mentioned, are typically used (If we were to take one of the other approaches indicated in Figure 2.1, then that would happen in Figure 2.2’s Stage 1).
Figure 2.2 Generic lifecycle of GWT application development, showing along the top which tools are used and down the side the stage in which they are used. Some of the stages, for example Stage 2, can be repeated many times before moving onto the next stage. This is quite a waterfall approach, and you may follow a more rapid application development process cycling also between stages 2,3,4,5 and 6 as necessary.
Table 2.1 details each stage of this typical web application development lifecycle.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Table 2.1 Definition of the stages involved in the typical development of a GWT application – the stages are those shown in Figure 2.2. Stage Description 1 In Stage 1 the directory and code structure for the project is established. This can be performed using one of the three approaches suggested in Figure 2.1 – using the GWT creation tools, an IDE specific wizard or by hand. If you use the GWT creation tools then they create a default application (we saw this in chapter 1). Quite often, if you have used the GWT creation tools or the by hand method, you would now like to import your project into an Integrated Development Environment (IDE) before starting the development for real. If you are using the GWT creation tools and the Eclipse IDE, then by adding the –eclipse flag to the command line tools you will create all the necessary additional files required for easily importing your directory and code structure into Eclipse as an Eclipse project. 2
Once the directory and code structure is created then the application development can proceed. Typically you will now replace the default files created for you by the GWT creation tools or your IDE’s wizard as well as adding other files that you require for your application, for example additional Java classes, Cascading Style Sheets, images etc.
3
The development period will typically contain several cycles moving between writing your application and testing in Hosted mode, using the Hosted browser (Stage 3). The Hosted mode can be launched either directly from a shell window or from within your IDE. It acts as a managed environment executing your code as pure Java code and deploying any server side Java code you have developed into its own internal web server. Errors and exceptions that are raised in Hosted mode, as well as any output from the GWT logging you may have included in your code, are safely captured by this managed environment. Compare this to the Web mode where your application has become JavaScript code and is executed directly by a web browser – it therefore has no guaranteed safe management of exceptions and errors. Another benefit of Hosted Mode comes from the ability to link it to your IDE’s debugger for both client and server side Java code debugging.
4
When you are happy with the developed code in Hosted mode it is time to compile your Java code into JavaScript for use in Web mode (Stage 4). Compilation is started by invoking the GWT compiler: the resultant files are then ready for viewing in a web browser. If you only have client side code at this point then you can open up the application directly from the file system, but if you have server side code then you need to deploy the code to a web server. The compilation process produces a number of files that are required, and in chapter 15 we discuss this whole process.
5
A compiled application will typically be deployed to your standard test environments web server so that you can check the deployment process as well as ensuring that the code executes correctly. Deploying is largely dependent upon your web server, but if you have no server side code then you can always check Web mode directly from your file system by double clicking on your applications HTML file from the compiled directory.
6
To finish development, though, you should check your functionality in a number of browsers in Web mode before final production release. It is nice to trust GWT and Google when they say that you can write once and run in many browsers, but maybe you are like us and do not take everything at face value!
So, the above text wraps up the theory of creating a GWT application: now the remainder of this chapter and the whole of the next one are given over to showing that theory in practice by stepping through each of these lifecycle stages in turn to produce the first basic version of our Dashboard application, which we will introduce in chapter 3 – we will be using the creation tools approach for Stage 1. In chapter 3 we perform all the steps in Stage 1 -- using the GWT creation tools to create the directory and default code and loading the application into the Eclipse IDE (don’t worry if you do not use Eclipse: you can use the files in your own IDE or just via a text editor if you want – and if you want to use your IDE's plug-in instead to create the structure for you, then you could try running that according to its manual now and try and pick us up again in chapter 3 where we should have similar structures). In producing a GWT application, we will always start by creating the GWT default application in order to ensure that the file and directory structure is complete and correct, the correct. After producing the GWT default
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
application, we will need to take the files produced and turn them into our own application. We do this work in chapter 3, where we first look at what is produced as the default application and then look at the changes we need to make to produce our first version of the Dashboard. But as we mentioned at the start of this paragraph, our first task is to go through all of the Stage 1 steps to create the default directory and code structure.
Stage 1: Creating a GWT Application Looking into the GWT distribution you downloaded in chapter 1, you should be able to see four command line applications, all ending with the word “Creator”. The functionality of these tools is summarized in Table 2.2. They help us create the directory and file structure of a GWT application, and they additionally create a set of default files that go together to make the default application. In this stage, we will take each of the creation tools described in Table 2.2 in turn, and look at how they can be invoked and discuss the outputs that they produce. The result will be the GWT default application which can support internationalization as well as some basic unit tests. Table 2.2 GWT provides a number of creation tools that can be used to quickly develop the default GWT application. These tools are used at various stages in the typical development lifecycle, as shown in this table. Stage Tool Name Overview 1A projectCreator GWT provides tight integration with the Eclipse IDE, and if you are going to be using this IDE then you need to execute this tool first – if you are not using Eclipse, you can safely ignore this stage. This tool establishes the necessary files required to enable the directory and file structure to be easily loaded into the Eclipse IDE as an Eclipse project. This essentially means at this stage creating the necessary .project and .classpath files. 1B
applicationCreator
Performs the following three functions: Creates the Java package structure within a directory that will hold our GWT application. Creates default HTML and Java files together with a basic Module XML file that is used to tie the GWT application together. These created files are the default application that we saw in chapter 1. For most applications we will overwrite all of these files in Stage 2 of the lifecycle. Creates the command line scripts that can be used to launch the GWT application in Hosted mode and to compile it for Web mode.
1C
i18nCreator
I18n is an acronym for internationalization, and this tool performs the following two functions: Creates a very simple property file containing the key/value pairs that act as constants or messages in an application that uses the GWT i18n capabilities. Creates a new command line tool specifically for the GWT application being created which we will need to use in Stage 1.D.
1D
Appl-i18n
This command line application is created by the i18nCreator tool in stage 1C. Its purpose is to take the property file containing constants or messages and produce a corresponding Java interface file. The resulting interface file is used in our GWT application code when we need to access i18n constants and/or messages from the property files. In the compilation process, GWT binds together the interface file with the property file so that the functionality all works seamlessly – don’t worry, we explain this in more detail in chapter 15.
1E
junitCreator
If you are going to perform unit testing of your GWT application using the JUnit tool, then the junitCreator tool creates a suitable directory and file structure. Your unit
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
tests will be written into the file that is created by this tool. 1F
Import to IDE
The final optional step in Stage 1 is importing the directory and code structure created by the creation tools into an IDE. If you performed step 1.A and ensured that the -eclipse flag was used in the other tools, then your GWT application can easily be imported into the Eclipse IDE. Should you not be using Eclipse, then it is still possible to import the directory structure and files into other IDEs.
While Table 2.2 gives a good overview of each of the tools, it does not explain which tools are optional and in what order they should be used. When we, the authors, have developed GWT applications, we have followed the flow given in Figure 2.3. This figure helps us understand when and where to use each of the tools contained within Stage 1 of the development lifecycle.
Figure 2.3 How the various GWT creation tools are used together in the development of the structure and files for the GWT default
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
application (the application which we end up with at the end of this chapter).
If you have created GWT applications before you might be wondering where the commands for compiling and executing a project in the hosted mode are in Figure 2.3? Well, they are not provided as standard command line tools since they require some knowledge of your project name and the necessary classpaths. Do not worry though, GWT does not require you to have all this knowledge about classpaths, as the applicationCreator tool will create the necessary commands for compiling and hosted mode. So if we are using the creation tools we have been talking about to create our application, then the simplest application created by following the path through Figure 2.3 would be to only use the applicationCreator tool. This would result in just the plain box-standard GWT default application files being produced. . Taking all of the steps in Figure 2.3 would result in the generation of the GWT default application again, but this time it would include the files necessary to support internationalization and unit testing. If we are using Eclipse as the IDE, then we need to add the –eclipse flag when we execute these other creation commands to ensure that the required launch files for Eclipse are produced or appropriately updated. Don’t forget that, if we are using Eclipse, then the first tool we need to execute is the project creator tool so that a number of Eclipse project specific files are created. Since the project creator tool is executed first when using Eclipse that will be where we start our process of creating the GWT default application (which will in chapter 3 be turned into the Dashboard application). In the rest of this section we will be following all of the steps in Table 2.2 to create the GWT default application that supports internationalization and unit testing. We will start by assuming we will be using Eclipse as our IDE – however, if you are not using Eclipse, or an IDE that can import an Eclipse project, then you can skip right to Stage 1B (section 2.2.2) to create the GWT application.
Creating the Project When using the Eclipse editor as your IDE, you can quickly create the necessary Eclipse project structure and files by using the projectCreator command line tool. Doing this, and using the -eclipse flag in the other creator tools, makes it very easy to import your project directly into Eclipse since it produces certain project files that are Eclipse uses to describe classpaths and applications. If you are using Eclipse, then the projectCreator command should be executed as the first step in the development process and you should run the following command now to create the DashboardPrj Eclipse project in the DashboardDir directory: projectCreator –eclipse DashboardPrj –out DashboardDir
The projectCreator command will give the following response if it executes successfully, having produced a source directory, src, and Eclipse specific .project and .classpath files in the newly created Dashboard directory: Created directory DashboardDir\src Created file DashboardDir\.project Created file DashboardDir\.classpath The .project and .classpath files will be manipulated by some of the tools we apply in later stages of Stage 1, so if we do not create them first, then those tools will fail. If you do not get this output then the tool will try and tell you what was wrong, which is most likely to be issues with file permissions on directories at this stage. Note
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
If you forget to run projectCreator before applicationCreator, then it is not the end of the world. You can run it afterwards, just remember to use the –ignore parameter. If you do not do that, then projectCreator will delete any existing files in the src directory (which is a pain as the applicationCreator tool creates a number of folders and files there!).
The full format for the projectCreator command is:
The various flags used in the command are described in Table 2.3. Table 2.3 Listing the various arguments that are used in the GWT projectCreator command to create a GWT project capable of being imported directly into the Eclipse IDE (or another IDE that can import an Eclipse project). -ant AntFile Request the tool to produce an Ant build file to compile the source code of the project. The suffix .ant.xml will be added to the value provided as the parameter. (optional) -eclipse The eclipse project name. ProjName -out DirName The directory to write output files into (defaults to current). -overwrite Overwrite any existing files in the output directory. (optional) -ignore Ignore any existing files in the output directory; do not overwrite. (optional)
One of the arguments in Table 2.3 refers to an argument that will create an Ant file. Including such a flag in the arguments to the projectCreator tool directs the tool to produce a valid Ant file with actions for compiling, packaging and cleaning you GWT application (Ant is a popular tool used to ease the building and deployment process of applications). By default the extension ant.xml is appended to the name you provide with the ant flag. You will only use the Ant flag if you intend to use the Ant tool for building/deploying your projects. If you are considering using Ant, then Error! Reference source not found. shows the default contents of Dashboard.ant.xml that would be produced if we had used the –ant Dashboard flag in the creation step. So, at the end of this section, we have created the basic directory structure and files required for an Eclipse project – this makes it easy to import our project into the Eclipse IDE (or other IDE that can import Eclipse projects). In a short while we will show you how to actually load this project into the Eclipse editor. But first, we should note that we have only created the structure for an Eclipse project and not actually created any GWT specific files yet. Creating those GWT default application files, is the next step we take, as we rejoin the mainstream development path.
Creating an Application In this step we will be creating the directory structure and files that make up the GWT default application. The structure and files produced in this step are independent from any IDE you might be using, but if you are using the Eclipse IDE, then you will have just created an Eclipse project and this step will add the GWT default application’s files to that project, The application creator tool creates a GWT application that conforms to the GWT expected directory structure (more on this in chapter 9) as well as generating the hosted and web mode scripts. This is the tool used for creating a new GWT application (don’t forget that if you are using the Eclipse IDE then you should run the projectCreator tool first, which we discussed in chapter 2.3.1). To create the GWT default application, you need to execute one of the two command lines shown in Table 2.4. There is a non-Eclipse version for those of you who will not be using Eclipse as your IDE and an Eclipse one for those that will (the difference being the inclusion of the –eclipse DashboardPrj flag in the Eclipse version).
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Table 2.4 Two different versions of the applicationCreator tool in Action using the specific code to create our Dashboard application; one for and one not for the Eclipse IDE. If using the Eclipse version, you should have executed the projectCreator tool first. Version Command Line Non-Eclipse applicationCreator -out DashboardDir org.gwtbook.client.Dashboard Eclipse applicationCreator -eclipse DashboardPrj -out DashboardDir org.gwtbook.client.Dashboard
Running either of the command line versions listed in Table 2.4 produces the following output: Created directory DashboardDir\src\org\gwtbook Created directory DashboardDir\src\org\gwtbook \client Created directory DashboardDir\src\org\gwtbook \public Created file DashboardDir\src\org\gwtbook \Dashboard.gwt.xml Created file DashboardDir\src\org\gwtbook \public\Dashboard.html Created file DashboardDir\src\org\gwtbook \client\Dashboard.java Created file DashboardDir\Dashboard-shell.cmd Created file DashboardDir\Dashboard-compile.cmd The applicationCreator tool creates the expected Java package structure under the src directory; the module XML file Dashboard.gwt.xml, which we discuss in detail in chapter 9; the hosted and web mode command line tools; and it even creates the default application’s HTML and Java files. If you have used the Eclipse version, then the project name we used in creating the project is added as an extra parameter with the –eclipse flag. This tells the command to create an Eclipse launch configuration specifically for this project, and results in the additional output of Created file DashboardDir\Dashboard.launch If we were to examine the directory structure of the Dashboard application now, we would see the structure shown in Figure 2.4.
Figure 2.4 Examining the directory structure that is created by the GWT applicationCreator tool – in this case for our Dashboard application. It is possible to see the org.gwtbook.client package structure under the src directory, which is where our Java code will go, with the application’s basic HTML file being stored under the public directory.
Note If you cannot use the default directory and file layout for your application (perhaps you have coding standards that are in conflict, or your system setup prevents using it) then you can create the directory structure by hand. But, you will need to ensure the command tools have their paths set up correctly, and that the GWT compiler can find the source code and public folders by setting the source and public attributes in the Module XML file (see chapter 9).
As we saw in Figure 2.3 (which showed how the various GWT creation tools are used together to develop the directory and file structure of a GWT application), the applicationCreator tool is all we need for creating the structure
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
of the basic GWT default application. To just double-check that the applicationCreator tool has executed successfully, you could run the default application by executing the Dashboard-shell command. Doing this rewards us with the application shown in Figure 2.5.
Figure 2.5 The default application that is created by the GWT creation tools – the files that produce this application are created by executing the applicationCreator tool and are usually replaced by your own files when you develop your own application (we get to this in chapter 3). The default application is a useful tool to show that the creation tools have worked and that the basic dependencies are correct.
Definition The GWT default application is the application that is created by the applicationCreator tool. It is useful, as you can quickly check that the creation tools have executed correctly by running the application. Creating your own application means changing the files provided in the default application – something we cover in chapter 3.
But, where did this application come from? If you look in the src/org/gwtbook/client directory of DashboardDir, then you will find the Dashboard.java file, which created the application shown in Figure 2.5. The contents of this file are repeated in Listing 2.1. Listing 2.1 First view of the GWT code that adds a label and a button to the screen. This listing is created automatically for us as part of the default application by the applicationCreator tool (note that for brevity we do not show the import statements and comment lines that are produced – typically we will not show these in our code listings unless explicitly necessary). 1.110 public void onModuleLoad() { 1.111 final Button button = new Button("Click me"); #1 1.112 final Label label = new Label(); #2 1.113 1.114 button.addClickListener(new ClickListener() { #3 1.115 public void onClick(Widget sender) { 1.116 if (label.getText().equals("")) 1.117 label.setText("Hello World!"); 1.118 else 1.119 label.setText("");
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
It is this code, together with the Dasboard.html file you can find in the src/org/gwtbook/public directory, that generates the output you see in Figure 2.5. We will not go through what these file contain at the moment; we just wanted to let you see that this default application is not created magically by GWT. To then get the look and feel of our Dashboard application (or any other application for that matter), we need to replace these files with our functionality – but we leave that until Stage 2 of our development process, which we find in chapter 3. If we look at the command in full then we see that, to create a GWT application, we call applicationCreator from the command line using the following template:
The options that are available for this command are listed in Table 2.5. Table 2.5 Listing the arguments that can be used with the applicationCreator command (which is used to create a default GWT application). -out The directory to write output files into (defaults to current) -eclipse Name of the Eclipse project previously used in the projectCreator tool execution (optional) -overwrite Overwrite any existing files (optional) -ignore Ignore any existing files; do not overwrite (optional)
The command provides some flexibility over the output, allowing you to change the default directory (if you do not provide one then it writes all of its output to the current directory). It is also possible to tell the tool to overwrite any existing files or ignore them using the –overwrite and –ignore flags, respectively. If the application is being created with a view of importing it into Eclipse, then you should add the –eclipse flag as well as the project name you used in section 2.3.1. Note GWT requires the package name for your application’s main code to have the sub-package “client” at the end – not having it will cause the GWT compiler to raise an error.
The final input to the applicationCreator command is the class name. This class name needs to be a fully qualified class name constructed in a particular style, which is shown in Figure 2.6. It follows the Sun standard for fully qualified class names in Java but specifically requires the last nested package name be called client. This restriction is not required by Java, but is enforced by GWT.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 2.6 Breakdown of a GWT fully qualified Java class name for our application. This is standard Java syntax, but GWT requires that our user interface code is always under a sub-package called “client” (there are other special sub-package names such as “server” and “rebind” which we cover at various points in this book).
It is useful to follow this GWT specific format since later in this book, in chapter 9, we talk about Java package structure for an application, and later still we introduce our server code under the org.gwtbook.server package, our code for automatically generating new code under the org.gwtbook.rebind package. Up to this point, we have created our project and basic application, and if you wanted to, you could already have ran the Dashboard-shell command script to execute the application as we saw in Figure 2.5. For certain applications, those that have no internationalization and/or we have no desire to perform unit testing with JUnit, this is all you need to do. However, in our example we will be adding internationalization to our Dashboard; therefore, we go through the next two optional stages, Stage 1C and 1D.
Setting Up Internationalization Internationalization allows us to display different interfaces depending on the locale that the application is being viewed from (for example in the Dashboard we will have different languages in the menus). We discuss internationalization (also known as i18n) in detail in chapter 15, but as this is the next step in Stage 1 then we will set our application structure up now so that i18n is supported. It is the i18nCreator tool that allows us to set up this structure. It is useful to note that we do not need to set u our i18n approach at this point, we can defer it to later in our development lifecycle if we so wish – however, since we know we are going to use it, we will set it up now. The rationale behind GWT internationalization is to allow our application to replace specific constants and messages on the user interface with a locale specific version. For example, our Dashboard application will be using both English and Swedish a little later on. The generation of this functionality is really a two-step process; the first, which uses this i18nCreator tool, creates a sample property file and a new script. The second step then involves entering some constants or messages into the property file and then executing the script generated by this tool. The output from the second stage is a Java interface file that is used in your application. Trust us; it is a lot easier in practice than this might have just sounded! Note Internationalization (i18n) is really easy to set up and use in GWT. We just create some property files and a simple Java interface; at compile time, GWT creates all the necessary Java plumbing code for us!
To add i18n to our existing structure, you need to select the appropriate command line to execute from Table 2.6 – either the Eclipse or non-Eclipse version – and execute it now. Table 2.6 The different versions of the i18nCreator tool in Action used to create the framework for the Dashboard internationalization. Version Command Line Non-Eclipse I18nCreator –out DashboardDir org.gwtbook.client.DashboardConstants Eclipse I18nCreator –eclipse DashboardPrj –out DashboardDir org.gwtbook.client.DashboardConstants
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Successful output of executing either of the i18nCreator commands listed in Table 2.6 looks as follows: Created file DashboardDir\src\org\gwtbook\client\DashboardConstants.properties Created file DashboardDir\DashboardConstants-i18n.cmd This tells us that the tool has created a) a sample properties file called DashboardConstants.properties that sits where the main code is, and b) a new command line tool called DashboardConstants-i18n where the rest of the creator commands are. It is this new DashboardConstants-i18n command line tool that we will use in stage 1D. If you are using the Eclipse version then the command will also ask the tool to output the necessary Eclipse launch configurations for the second script: Created file DashboardDir\DashboardConstants-i18n.launch So you have seen the internationalization tool in action, and we have created a very simple structure into which to place our internationalization aspects for the Dashboard application (in this case we have created the structure necessary for constants – in chapter 15 we will also look at internationalizing messages). Let’s now take a moment to look at the command line for this tool in detail and see what other arguments we can pass to the tool in order to alter its behaviour. The tool is called using the following template:
The options that are available to the command are listed in Table 2.7. Table 2.7 Description of the various arguments that can be passed to the GWT i18nCreator command. -eclipse Name of the eclipse Project (optional) -out The directory to write output files into (defaults to current) -createMessages By default the tool will produce an interface that extends the GWT Constants interface, if you wish it to extend the GWT Messages interface instead, then add this flag. -overwrite Overwrite any existing files (optional) -ignore Ignore any existing files; do not overwrite (optional)
By default the tool produces output files that support the GWT i18n approach for constants, but adding the flag –createMessages alters the output to suit GWT i18n approach for messages instead (we explain the differences between constants and messages in chapter 15). We can also direct the tool to overwrite or ignore any existing files in the directory indicated by the value to the –out flag. This concludes the first part of setting up internationalization, we now have laid the foundations for the i18n of our application; the next stage is to get our i18n approach ready for use in our code – that is, to create the specific locale properties files that contain the locale specific constants or messages. Remember that for our Dashboard example, we will be changing the text in the menu system based on the locale, and so in the next section we will focus on creating the properties files that contain our menu text.
Implementing Internationalization In the previous stage the i18n creator tool created a dummy property file as well as a new DashboardConstantsi18n command line application, but we are not yet in a position to use these files in our application, since they contain no data. This stage, Stage 1D, is where we create the link between the property file and our application code. To
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
create that link, we must create some key/value pairs for constants in the property file and then execute the DashboardConstants-i18n command line application. This tool takes that property file and produces a Java interface class that contains a method for each key. It is these methods within the Java interface file that we use in our application. This stage is the part of Stage 1 that is performed possibly more than once and quite possible during the later stages of development. Each time new constants and/or messages are added to your properties file, this stage should be executed to ensure that the Java interface file is up to date. If we executed the DashboardConstants-i18n command line now, we would get a simple interface file reflecting the default property file. When we update our i18n property file in chapter 3, then we will need to run the DashboardConstants-18n command line again to make sure we take advantage of and gain access to the new constants. That concludes the creation of internationalization. The final step in using the creation tools (from Figure 2.3) is where we set the foundations for unit testing. Although it is an optional step, we feel that unit testing is an important part of development. It is not necessary to do this step at this stage, and you could jump forward to section 2.2.6 if you wish to start getting your framework into an IDE or further on to chapter 3 if you want to start building the Dashboard functionality without an IDE. So, onwards to the final step in using the creation tools: creating the unit test cases.
Creating Unit Test Cases JUnit is a powerful approach to testing, and it is truly beneficial that GWT includes a simple way of integrating unit tests into your development approach. Undoubtedly, JUnit deserves, and indeed has, a wealth of books written about how to use it, and we will not attempt to cover using JUnit here. If you are interested in knowing more about JUnit, we would recommend JUnit in Action by Vincent Massol and Ted Husted (in Chapter 16 we look at GWT testing in more detail). Note GWT makes writing and creating JUnit tests for your code a painless (maybe enjoyable is going too fair) process.
Before we can start with creating JUnit test case structure, we need to have the JUnit jar file somewhere on our development system that we can refer to (it can be downloaded from http://www.junit.org). To add JUnit tests to the Dashboard example, you should execute one of the two versions listed in Table 2.8 (for simplicity we are assuming that the JUnit jar is stored in the root of the C:\ directory, if you have it somewhere else, then you should replace the argument to the first parameter with your location). Table 2.8 The different versions of the junitCreator tool in Action used to create the framework for the Dashboard JUnit testing. Version Command Line NonjunitCreator –junit c:\junit.jar –module org.gwtbook.Dashboard –out DashboardDir Eclipse org.gwtbook.client.test.DashboardTest Eclipse junitCreator –junit c:\junit.jar –eclipse DashboardPrj –module org.gwtbook.Dashboard –out DashboardDir org.gwtbook.client.test.DashboardTest
Running either command shown in Table 2.8 creates a suitable directory and file structure ready to accept our unit test code for any unit testing we may wish to perform on our Dashboard example (the eclipse version just provides some more files for easy integration with Eclipse). We have asked for the test class to be called DashboardTest and to be in the package org.gwtbook.client.test. The script will create a new directory under DashboardDir called test and store our generated test class files under that. Since we are creating the tests within the same package as the code, there is no need for the junitCreator command to alter the Dashboard module.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Note It is the module name that is used, not the package name. If we are testing the Dashboard application, then the module name is org.gwtbook.Dashboard.gwt.xml, not the class org.gwtbook.client.Dashboard.
Successful output of both versions of the junitCreator command will be as follows: Created directory DashboaradDir\test\org\gwtbook\client\test Created file DashboardDir\test\org\gwtbook\client\test\DashboardTest.java Created file DashboardDir\DashboardTest-hosted.cmd Created file DashboardDir\DashboardTest-web.cmd If we are using the Eclipse version, then the output is appended with the following two lines, indicating the creation of the associated hosted and web mode launch scripts for Eclipse: Created file DashboardDir\DashboardTest-hosted.launch Created file DashboardDir\DashboardTest-web.launch The junitCreator tool creates the necessary classes with stubs in them, places the necessary links in the GWT application module, and if using Eclipse creates the appropriate launch configurations. The full template for the command is:
The options that are available to the command are listed in Table 2.9. Table 2.9 Listing the arguments for the GWT jUnitCreator command. -junit Path to the JUnit libraries -eclipse Name of the Eclipse project (optional) -module The GWT module of the application you wish to test. -out The directory to write output files into (defaults to current) -overwrite Overwrite any existing files (optional) -ignore Ignore any existing files; do not overwrite (optional)
The flags -eclipse, -out, -overwrite, and –ignore are the same as discussed in the previous two tools. There are two new flags, –junit and –module. The –junit flag is required, and its value must point to the installation of the JUnit classes in your system. The –module flag must indicate the GWT module you want to test. Running the JUnitCreator tool will not, unfortunately, create our unit tests for us; it does, however, manipulate the necessary files and create a sensible directory structure, which is shown in Figure 2.7. Additionally, it creates a simple Java class file in which we can place our unit tests.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 2.7 Examining the directory structure which exists after executing the GWT jUnit creator tool – notice that the strucutre for the application that we saw in Figure 2.4 is still intact and the new structure for testing sits under the test directory.
We look at using JUnit testing in more detail when we get to chapter 16. Let’s take a second to recap to see where we are with respect to creating the GWT default application. Right now we have created the directory and code structure for the GWT default application and we have added support for internationalization and, if you took the extra last step, unit testing. These steps are common to all GWT applications that you will build, and it is only by altering and adding to the default application code that you will start to create your own application. Just before we start building the Dashboard application in the next chapter, we will look at the final Stage 1 application development task of importing your project into an IDE, which is an optional step that we recommend, as it makes code development/debugging much easier and quicker.
Importing into Your Integrated Development Environment (IDE) We’re convinced that a great benefit of GWT is the ability to use familiar development environments in the development of our web applications (in fact, the GWT team members often say on the forums that Java was chosen not because they are Java addicts, but because at this moment in time it has really great tool support). Right now GWT integrates easily into Eclipse, and other IDEs are rapidly delivering easier integration approaches. There are even approaches to build GUI development tools for GWT in a similar manner to Matisse (the NetBeans IDE GUI Builder). In this section, we demonstrate the use of Eclipse as the IDE and also the generic steps you could take to use another IDE if you have created your application following the early part of this chapter.
Importing into Eclipse Importing the project into Eclipse after it has been created using the above project and application creator tools is simple and quick. In the Eclipse Project Explorer windowpane, right-click and select the Import… option. This step, together with the next step of choosing the “Existing Project into the Workspace” option, can be seen in Figure 2.8.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 2.8 Importing the newly created Dashboard project into the Eclipse IDE for development of the application by right-clicking the Eclipse Explorer window and selecting the Import menu option.
Clicking the next button presents the dialog shown in Figure 2.9 where we have already clicked the browse button next to the “Select Root Directory” option. You can see that we have navigated the file system to find our DashboardDir, which we have selected, so we will now click the OK button to load the project into Eclipse.
Figure 2.9 The second stage of importing the Dashboard Project into Eclipse – finding the project that you are importing in your directory structure.
After clicking OK, then click the Finish button to import the project. A short while later you should be presented with a view similar to that shown in Figure 2.10 as the project is now loaded into Eclipse.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 2.10 GWT Dashboard application loaded into the Eclipse project Explorer window, showing the directory structure, the Java and HTML files together with the associated Module XML file, and the range of command line applications and Eclipse launch configurations generated by the GWT creation tools.
Eclipse is not the only IDE that can be used to develop GWT projects, though it is the easiest to integrate straight out of the box using the tools provided in GWT. In case you choose not to use the “–eclipse” flag with the creation tools, or you wish to import your files into a different IDE, we will look at the steps to do that next.
Using Other IDEs With GWT you are not restricted to using the Eclipse IDE. Should you wish, you could simply use a text editor and command line execution of tools. If you do wish to use an IDE then you can still import the structure we have just created. We have already seen how the applicationCreator tool creates the correct file structure for a GWT application, so you are really up and running from that point. We suggest the following steps are considered if using another IDE for which there is no existing GWT support: Create the application using applicationCreator (and both i18nCreator and junitCreator if you want that functionality). Import the code into your IDE, preserving the file structure just produced. Add the gwt-user.jar file to your IDE’s classpath and any path required for auto-completion to operate correctly. Hook up your IDE in order to execute the application-shell and application-compile scripts. For debugging, hook your IDE debugger up to port 9000, which is the port that the hosted-mode allows connections for debugging upon. Using ant scripts will significantly increase the ease of development and testing, and you can find some references to scripts that others have built on the GWT forum. IDEs, such as NetBeans, can import Eclipse projects, so this might be a smoother way to ensure all the configuration needed for your application is imported. The alternative choice is to use an IDE that has a GWT wizard built for it, e.g. IntelliJ, which creates the necessary files and configuration for you for instant use in that IDE. Finally, some extensions to Eclipse exist, such as Googlipse and GWTDesigner which aim at allowing you to develop GWT applications graphically and thus create the necessary configurations for you at project creation time. So, one way or another, we have now arrived at the point where our project structure is in place and we have the ability to start building our new GWT application, either in text editors or in an IDE. We have executed the default
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
application to make sure everything has been created correctly, and now we are ready to change that default to our own application.
Summary This chapter has covered the first step in developing a GWT application created using the default GWT creation tools. What you now have in your hands is the directory and file structure for the default GWT application, which forms the basis for the Dashboard example we will be building on throughout the entire book. You may be left thinking that fifteen plus pages is a lot of text to get us to what is effectively the GWT HelloWorld example, but the GWT structure is not as simple as typing four lines of code and saying, “there you are.” However, we have now done the hard part; continuing to develop our application from this point is much more like writing a few lines of code and executing it. While it may have seemed a long path to get to this point, you have already seen in chapter 1 that, with a bit of practice and knowing what you are doing, completing Stage 1 in reality only takes a few minutes. The key benefit of using the GWT creation tools in the first stage is that they generate the directory structure and file contents in such a way that they correctly tie together. We can easily check that the structure is correct by executing the default application. This puts us in a safe position to now set off replacing the default files with the files needed for our own application, and that means heading on to chapter 3.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
3 Advancing to Your Own Application Generating the GWT default application is great, and if you follow the steps in the previous chapter time and time again, then you will consistently get the default application. We doubt, though, that this is all you will be using GWT for; it would be a pretty dull technology if that were all it did! Stage 1 of our lifecycle is generally performed for all GWT applications. It is not wasted work, since it provides the solid foundation of files and directories that enable us to confidently build our own applications. However, going through the remaining stages in the lifecycle is the key to creating our own applications. By the end of this chapter, we will have covered the remaining stages in the lifecycle in the context of our own specific example, which we introduced briefly in chapter 2: a Dashboard application.
Describing Our Specific Application Example Throughout the rest of this book we go through the remaining steps of the lifecycle needed to transform the default application into the application you want to build. In our case that happens to be the Dashboard application you can see in Figure 3.11.
Figure 3.11 GWT in Action Dashboard running in Hosted Mode showing a number of Component Applications, an About Box automatically created by GWT Generators that have introspected the application’s class and a couple of open menu bars, one showing options for the Component Application in Focus and the other listing the component applications that could be executed.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Although this could look a little intimidating, the purpose is to show what you can quite simply do with GWT, and throughout this book and the code, you will see how this is all achieved.
The key functionality of the Dashboard application, where a number of component applications will sit, can be summed up as: Providing a simple windowing system allowing component applications to be dragged and minimized. Implementing drop functionality for windows, where if dropped upon the trash icon they are removed from the display. Supporting a menu system that displays option menu bar for in focused windows (it is the responsibility of the windowed application to place and remove its options menu). Allowing for the menu system to be extended by a Bookmark listing that loads bookmarks from an XML file. Using GWT Generators to automatically generate an About menu item whose display contains information gleamed from introspecting the component application’s Java class. Supporting internationalization using constants for the menu bar and messages for the initial name of the dashboard. Component application is the name we give to the individual applications that can be created within the Dashboard, for example the Calculator, Google Search, Server Status components. We will not show how to build these applications in detail in this book, rather we will point out parts of them to demonstrate key GWT concepts (the source code for the complete Dashboard application and a large number of component applications can be retrieved from http://www.manning.com/hanson ). But this application is a little too ambitious to build in one short chapter, so, to show how the remaining lifecycle stages are used, we will take the default application from chapter 2 and build a simple basic version of the Dashboard, which you can see in Figure 3.12.
Figure 3.12 Visual view of the part of the Dashboard application that we will build in this chapter by taking the default application created at the end of chapter 2 and altering the files to become our application. Comparing this figure to the screen shot of the full Dashboard application shown in Figure 3.1. we can see that it is much simpler, but that is what we want for this chapter.
Stage 1 of our lifecycle got us to the start of this chapter and is generally performed for all GWT applications. As we promised, the rest of chapter 3 steps through the remaining stages in the web application development lifecycle. If you are following along with us as we build the Dashboard application, then there will be some hands-on work for you in this chapter as we alter the default files created for the default application, starting with Stage 2 where we build our application.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Stage 2: Developing Your Application Stage 1 laid the solid foundations for our GWT application. In Stage 2 we move on to alter the files provided by default to build our own GWT application (this stage is required for all GWT applications built using the GWT creation tools and most of those built using IDE specific plug-ins). When we build small applications, it is possible, though not always advisable since it I surprisingly often that little prototypes try and head their way towards production systems, to get away with just sitting down and writing some code, hoping that it meets our needs. As we step up to building larger and larger applications, we must take a more structured approach in order to keep track of where we are, enable multiple developers to work on the code, and generally make an application that is maintainable in the future. The approach we have typically taken to building our applications, and that we have done in the background for the Dashboard, is shown in Figure 3.13. We start, not surprisingly, by defining the functionality we wish to implement, then designing the application to meet that functionality by identifying components and events we will use as well as how we will use GWT’s modularization capabilities. We create the design of the application, and end up with a construction step that involves building the various components that make up our application.
Figure 3.13 Typical steps in Stage 2 (Develop Application) from our Application Development Lifecycle Process introduced in Chapter 2 for developing a new GWT application (but not showing the testing tasks that would normally be present).
Each of the five steps takes us nearer the end goal of a completed application. For our simple Dashboard, we can define the functionality as a simple menu system that implements internationalization text for the English and Swedish languages, with a simple icon for the trash can. Why did we pick the Swedish language? Well, one of the authors lives in Sweden and it also gives a good opportunity to use extended characters such as ä, ö and å in our internationalization files. We have no new widgets or panels to build, and our modularization is negligible (we discuss modularization in much more detail in chapter 9). The next step in stage 2 is to address the internationalization aspects.
Implementing Internationalization When we executed the i18n tools in chapter 2, it created a default property file (DashboardConstants.properties) and a DashboardConstants-i18n command line tool. You may have executed this new command line tool at that point, and it would have produced a simple Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
DashboardConstants.java file, which would have been fairly empty since we had not yet defined any constants in the DashboardConstants.properties file. Now we will go through the process of adding internationalization aspects for our Dashboard, which in this case means creating the text that will appear in the menu for both the English and Swedish locales, examples of which we can see in Figure 3.14 and Figure 3.15.
Figure 3.14 The Dashboard when the locale is left as the default locale – the menu bar is written in English.
Figure 3.15 The Dashboard when the locale is set to Swedish – the menu bar is now written in Swedish. (the locale was changed by adding the parameter ?locale=sv to the end of the URL)
We mentioned in chapter 2 that the second part of the internationalization step was the only Stage 1 stage that would possibly be used again in later stages to ensure that the DashboardConstants.java file matched the available keys. This is now demonstrated as, following the update to the properties files that we are about to perform, we will need to execute the Dashboard-i18n creator tool again. First we update the key/value pairs that we require for the Dashboard example by replacing the DashboardConstants.properties file with the constants we will use for the default locale (the locale that is used if the requested one is not available). So, you should now replace the contents of DashboardConstants.properties with those shown in Listing 3.2.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Listing 3.2 The Key/Value pairs used for menu constants in the default locale (these values should replace the current contents of the Dashboardconstants.properties file) 1.126 AboutMenuItemName: About 1.127 CalculatorMenuItemName: Calculator 1.128 ClockMenuItemName: Clock 1.129 CreateMenuName: Create 1.130 HelpMenuName: Help
Now when we use the key HelpMenuName in our Java code, we get the constant value Help. The format of these properties files follows the traditional Java properties file format (with the slight exception that the file can be encoded as UTF-8 format and therefore include Unicode characters directly). In our example, we have used a colon as the separator between key and values, but it is equally valid to use an equals sign, =, or whitespace character, e.g space or tab but not a new line marker (as that will indicate the start of a new key and imply the last key had an empty string as its value). If you wish to use the colon or equals signs in your key then you must ‘escape’ it using a backslash, i.e \: or \=. There is no need to do this in the value since all characters up to the line feed are treated as part of the value. That set of key/pairs is great for the default locale, but we also want to deal with the Swedish locale. To do so, we need to add a new properties file that contains the same keys as in Listing 3.2 but with the values in Swedish. In fact, we would have to do this for all the specific locales we would want to make available in the application (though as we see later in the book, these properties files exist in a hierarchy and where entries are missing, the value from a file higher in the hierarchy is used instead – if no such value exists then the GWT compiler raises an error). These new files must be named according to a well-defined naming convention, which we discuss in more detail in chapter 15, to ensure that GWT can deal with them. For now, we need to create a property file for the Swedish locale, and so you should create a new file called DashboardConstants_sv.properties in the same location as our default properties file, and fill it with the contents shown in Listing 3.3 (don’t worry if your keyboard doesn’t have these characters, as you can always just use the electronic copies of the files available at http://www.manning.com/hanson). Listing 3.3 The Key/Value pairs used for menu constants in the Swedish locale. First we need to create a file whose name follows a predefined syntax to indicate it is not the default constants file. In this case, it is called DashboardConstants_sv.properties and should contain the following contents. 1.131 AboutMenuItemName: Om 1.132 CalculatorMenuItemName: Kalkylator 1.133 ClockMenuItemName: Klocka 1.134 CreateMenuName: Nya 1.135 HelpMenuName: Hjälp
Note You should set the encoding of your properties files to be UTF-8; otherwise, GWT will not be able to cope with any special characters – in the case of Swedish, this means the Ä, Ö, and Å characters, but other languages have their own specifics, too! Tip To set the encoding of your properties files to be UTF-8 in Eclipse, select the file in the project explorer, right click on it and select Properties. In the dialog box that opens, select Other for the file encoding, and in the drop down box, pick the UTF-8 option. For other IDE’s, see the appropriate IDE manual
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
We now create a simple interface file which allows us to refer to constants in our code. We create this file, or update it if we have previously done this task, by executing the DashboardConstants-18n tool. This tool takes the default properties file and creates items in the interface file for each key (notice that if the locale specific files have more keys than the default file then these will be ignored; if the locale specific files have keys missing then the default values are used). Since we have changed the default properties file, we should execute the DashboardConstants-i18n command now to refresh our interface file. Doing so now would create (or replace, if it already existed) the DashboardConstants.java file in the client directory. This new file will contain the contents shown in Listing 3.4 where we can see one method declaration for each of the keys in our properties file. Listing 3.4 The generated Java interface file, created by running the DashboardConstants-18n application, allowing the GWT application Java code to defer binding to internationalization constants and messages until compile time. Notice how the method names are the same as the keys used in the properties files. 1.136 package org.gwtbook.client; 1.137 1.138 import com.google.gwt.i18n.client.Constants; 1.139 1.140 public interface DashboardConstants extends Constants { 1.141 String AboutMenuItemName(); 1.142 String CalculatorMenuItemName(); 1.143 String ClockMenuItemName(); 1.144 String CreateMenuName(); 1.145 String HelpMenuName(); 1.146 }
That is all we need to implement the back-end aspects of internationalization -- relatively easy, we felt at least, compared to doing this ourselves in JavaScript! To use the locales in code, we use the GWT.create() method as shown in Listing 3.8 to get the correct set of constants for the locale the application is told it is in. We can change locales in one of two ways. The simplest is to add the following text to the URL used to view the application: ?locale=sv. The other is to set it in the Module XML file as discussed in section 3.2.2. (Chapter 16 discusses how to use the dynamic string i18n approach GWT provides when dealing with legacy code) At first glance, the interface and property files seem to sit in useless isolation – and that is actually true, except for when we execute or compile our code. When that occurs, the GWT system steps in and generates new Java classes binding together our properties files and this interface to produce new Java classes that are used by our code (this is performed by something called GWT Generators, and there is more on this topic in chapter 14). To use these constants, we need to use a GWT concept called deferred binding in the upgraded Dashboard.java file. Deferred binding allows us to write code that is syntactically correct at development time, but where the exact semantics/functionality are deferred until run-time (though technically this occurs at compile-time as that is where GWT resolves these deferred choices, but conceptually they are resolved at run-time).
Constructing the Application In this section we examine and alter the contents of the default files; in particular, we will be altering the HTML page (Dashboard.html) and the Java file (Dashboard.java) as well as adding a Style Sheet (Dashboard.css). When developing applications using the creation tools, it is very typical that you need to alter the files shown in Table 3.10. Table 3.10 Overview of the files usually replaced when moving from the default GWT application to developing your own application. File Description HTML This is the HTML document into which the GWT application will be loaded. The default file is very
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
File
simple and is unlikely to meet your needs for a real application, so we will almost by default end up replacing it. What it is replaced with is dependant upon how you will be using your GWT application. In the case where your application will be placed into brand new page you have a lot of flexibility, but sometimes your application will be going into an existing web page, in which case, you should use a HTML file representative of the that page.
Module XML File
A file that indicates a number of aspects regarding the current GWT application (module) to the GWT compiler. Normally each application will have 1 Module XML file, in our example Dashboard.gwt.xml, unless you start making your applications modular, in which case there will be a number of XML Module files (we cover this in chapter 8 when we discuss how the Dashboard application will be built).
Java Code Files
The set of Java classes and interfaces that go together to make up the application. The default application always provides one file, in our case the Dashboard.java file, which provides the basic interface between our application and the GWT loading mechanism. For our Dashboard example we have also implemented internationalization, so we have a DashboardConstants.java file too. In practice we will end up replacing these files to reflect the needs of our application as well as adding other Java files to represent other aspects of our application. For example, as we progress through this book we will build some new widgets and panels as well as applications that sit within the Dashboard. Whilst it is physically possible to place the details of all these components within one massive Dashboard.java file, it is considered bad programming practice and certainly a support nightmare! In practice each of these components will consist of one or more Java class files which all need to be created from scratch.
For each of the default GWT application files we will be replacing, we will look at what the original file contains, how it works, and then show the replacement file. In a GWT application, the HTML file acts as the container into with the application will be delivered; it makes sense then that this is the first file we examine and change for our needs.
Constructing the HTML File There is nothing unusual in the default Dashboard.html file created by the tools. It is simple HTML consisting of a standard head and body block, with a title and some style information. When viewed in a web browser, it produces an output similar to that shown in Figure 3.16, with a header, some text, and an HTML table that contains two cells named slot1 and slot2. (We have used the Web Developer extension for Firefox to allow us to easily see the DOM elements and their names on the actual web page.)
Figure 3.16 HelloWorld HTML file displayed in Firefox using the Web Developer extension tool to highlight the DOM elements – here you can see the GWT History frame, title, some text and the two “slots” where the default application will place its widgets.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The important part of the HTML file, in a GWT application sense, are the table cells named slot1 and slot2. These are important because, when the default application loads, this is where the Button and the Label we saw in chapter 1 are placed. The actual HTML that renders Figure 3.16 is shown in Listing 3.5.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Listing 3.5 The Dashboard.html file provided for the default GWT application – which creates a web page similar to that shown in Figure 3.6 1.147 1.148 1.149 Wrapper HTML for Dashboard 1.150 <meta name='gwt:module' content='org.gwtbook.Dashboard'> |#1 1.151 1.152 1.153 <script language="javascript" src="gwt.js"> |#2 1.154 <iframe id="__gwt_historyFrame" |#3 1.155 style="width:0;height:0;border:0"> |#3 1.156 |#3 1.157 Dashboard 1.158
1.159 This is an example of a host page for the Dashboard application. 1.160 You can attach a Web Toolkit module to any HTML page you like, 1.161 making it easy to add bits of AJAX functionality to existing pages 1.162 without starting from scratch. 1.163
The GWT specific parts of the HTML come at points [#1], [#2], [#3], and [#4] in Listing 3.5. Points [#1] and [#2] are mandatory, and your application cannot work without them being present. The next two points are less mandatory -- the third point [#3] only being required if you are going to use GWT history functions. It is normally required, but not mandatory, to have some form of named element(s) in the HTML file (point [#4]) in order for your application to know where to position itself once loaded. What is important for this example is that there are some named DOM elements, which can be found in the web page, not the fact that they are called slot1 and slot2 or that they are table cells. Let’s examine the default HTML file in a little more detail. A meta-tag is introduced in the head section [#1] for each GWT application to be included in the web page. In the case of the default example, there is only one application, so only one gwt:module meta-tag is required. We set the content attribute of the meta tag to be the class name of our application, org.mycompany.Dashboard, which informs the GWT loading mechanism where to look to initialize the application (we cover the GWT loading mechanism in chapter 17). As well as setting meta tags for the module, there are three other meta tags that the GWT loading mechanism will recognize; these are (with the definitions taken directly from the GWT code) shown in Table 3.11. Table 3.11 Describing the various meta tags that can be entered into an application’s HTML file. Each meta-tag controls a various aspect of the GWT application Meta Tag Description Example gwt:module <meta name=”gwt:module” content=”qualified_class_name”> gwt:property This defines a deferred binding client property. It could cover <meta name="gwt:property" many aspects, for example the locale of the application (which content="_name_=_value_">
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
gwt:onPropertyErrorFn
gwt-onLoadErrorFn
would drive the loading of other locale specific constant files if we had defined them). Specifies the name of a function to call if a client property is set to an invalid value (meaning that no matching compilation will be found). This specifies the name of a function to call if an exception happens during bootstrapping or if a module throws an exception out of onModuleLoad(); the function should take a message parameter.
At point [#2] in Listing 3.5, we include the main GWT JavaScript file, gwt.js, which is provided in the GWT distribution. The purpose of this file is to initiate and manage the application loading mechanism, which is discussed in chapter 17, as well as some other administrative tasks. Google indicate that this tag could be placed in the header block, but there is a gain in startup speed if it is included in the body – we will trust them on that point. The default application’s HTML includes an IFRAME [#3], which is an optional inclusion but the creation tools include it by default. It is used by the GWT history functions, and you should only remove it if you know for sure you will not be using these functions. It is particularly useful, as poor design and development of Ajax solutions run the risk of breaking the user’s expected behavior of how browser history works – since it is no longer necessary to refresh the browser page every time a screen update is required. GWT solves this problem by using an IFRAME in which history can be stored and retrieved. The final element of the HTML that we will discuss is at [#4] in Listing 3.4, where we can find the named DOM elements that we will place our application components within. In the default application, there are two table cells called slot1 and slot2 into which our Java code places the button and the text, which is displayed when the button is pressed. It is worth noting here that you do not need to place GWT components in a table; any named DOM container will do, e.g. a span or a div. Also, it is not always necessary to have a separate element for each GWT component that you will place in the web page. The Google example could easily have been implemented using a GWT panel in which the button and message were placed. The panel itself would then be placed as one element on the screen, but now we are straying into composite widgets and layout design territory, which is a subject for later in this book in chapter 7. When we develop the Dashboard, we will see that we need no named DOM element. So that is what the default application’s HTML file looks like. In the case of our Dashboard application, we (and we mean you now) will replace the contents of the Dashboard.html file with that shown in Listing 3.6. Listing 3.6 The Dashboard.html file used for the first version of our Dashboard example application. This replaces the code shown in Listing 3.4 and is one of the first steps we take on turning the default application into our own application. Wrapper HTML for Dashboard <meta name='gwt:module' content='org.gwtbook.Dashboard'> #1 <script language="javascript" src="gwt.js"> #2 <iframe id="__gwt_historyFrame" #3 style="width:0;height:0;border:0"> (annotation) (annotation) (annotation)
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
In this new file, you can see that the two mandatory elements are still there ([#1] and [#2]), and we have also kept the history capability ([#3]), since that will be used by our Dashboard components later. The big difference in this HTML is that we have deliberately not included any named DOM elements, such as slot1 or slot2, from the default application. The main benefit of named DOM elements is to allow our GWT applications to seamlessly fit into existing web pages at specific locations; our Dashboard application will take over the whole real estate of the web page, so named slots are relatively meaningless in this case. So that is the new Dashboard.HTML file -- nothing really out of the ordinary, and you can see how, if you have a GWT application, to go into any existing web pages you just need to identify a named DOM container for it and include the relevant meta tag and gwt.js script. But how does the GWT application bind itself to that named container (or just display in the browser in the case of our Dashboard) and actually provide the necessary functionality? Well, that comes in the Java Code.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Altering the Application Files Along with the HTML file, altering the application’s java file, Dashboard.java in our case, is a key step in the process of moving away from the default application. The code that is provided as default in the Dashboard.java file is shown in Listing 3.7. Listing 3.7 Looking at the default application’s Dashboard.java Java code 1.171 package org.gwtbook.client; 1.172 1.173 import com.google.gwt.core.client.EntryPoint; 1.174 import com.google.gwt.user.client.ui.Button; 1.175 import com.google.gwt.user.client.ui.ClickListener; 1.176 import com.google.gwt.user.client.ui.Label; 1.177 import com.google.gwt.user.client.ui.RootPanel; 1.178 import com.google.gwt.user.client.ui.Widget 1.179 1.180 public class Dashboard implements EntryPoint { 1.181 1.182 public void onModuleLoad() { 1.183 final Button button = new Button("Click me"); 1.184 final Label label = new Label(""); 1.185 button.addClickListener(new ClickListener() { 1.186 public void onClick(Widget sender) { 1.187 if (label.getText().equals("")) 1.188 label.setText(“Hello World”); 1.189 else 1.190 label.setText(""); 1.191 } 1.192 }); 1.193 RootPanel.get("slot1").add(button); |#6 1.194 RootPanel.get("slot2").add(label); |#6 1.195 } 1.196 }
This is very simple Java code, which breaks down as follows. The Java classname must be the same as the filename, and in this case it is called Dashboard ([#1]). For GWT applications, there must be one class in your application known as the entry point class, which provides an onModuleLoad() method (this method is called by the GWT loading mechanism to start your application). This class is identified to GWT by implementing the EntryPoint interface, as this class does. As this class implements the EntryPoint interface, then it must provide the onModuleLoad() method ([#2]). For the default application, this method simply creates the necessary GWT widgets (a button and a label), and adds a click listener to the button. This click listener contains an onClick() method, which is called when the button is clicked. In segment [#3] of the code, we create a GWT Widget, in this case a Button widget. Within the Java code, this is treated as any other object in that it is created and we can perform a number of actions on it through the Button class’ member functions. When executing in Hosted or Web modes, this object becomes a direct representation of a DOM Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
button and is so displayed in the web page. We look at all the widgets that GWT provides in more detail in the next chapter. Similar to the button in step 3, at point [#4] in the listing we create a GWT Java Label object, which in Java code acts as a simple object, but in Hosted or Web mode becomes a direct representation of a DOM div element whose inner text can be altered. Most functionality on the user interface aspects of a GWT application will be driven by events -- a user clicks on something, or something is dragged or changed in some way. GWT manages these events through listeners added to GWT widget objects. In step [#5] we can see a simple click listener being added to the button widget. When the button is clicked, the onClick() method is called to update the text value of the Label (causing it either to be blank or to say Hello World). Event Handling is covered in more detail in chapter 6. Now that we know how the default application Java code works, let’s introduce the Java code for our application (you should replace the existing Dashboard.java contents in your project with that shown in Listing 3.8). Listing 3.8 The Dashboard.java Java code for the first version of the Dashboard application. Just like the HTML file, this version of the code replaces that from the default application shown in Listing 3.6 1.197 package org.gwtbook.client; 1.198 1.199 import com.google.gwt.core.client.EntryPoint; 1.200 import com.google.gwt.core.client.GWT; 1.201 import com.google.gwt.user.client.Command; 1.202 import com.google.gwt.user.client.Window; 1.203 import com.google.gwt.user.client.ui.MenuBar; 1.204 import com.google.gwt.user.client.ui.RootPanel; 1.205 1.206 public class Dashboard implements EntryPoint{ #1 1.207 public void onModuleLoad(){ #2 1.208 MenuBar menu = new MenuBar(); |#3 1.209 MenuBar menuCreate = new MenuBar(true); |#3 1.210 MenuBar menuHelp = new MenuBar(true); |#3 1.211 MenuBar login = new MenuBar(true); |#3 1.212 Image trash = new Image(”trash.png”); |#3 1.213 DashboardConstants constants = |#4 1.214 (DashboardConstants) GWT.create(DashboardConstants.class); |#4 1.215 1.216 menuHelp.addItem(constants.AboutMenuItemName(), |#5 1.217 new DummyCommand()); |#5 1.218 menuCreate.addItem(constants.ClockMenuItemName(), 1.219 new DummyCommand()); 1.220 menuCreate.addItem(constants.CalculatorMenuItemName(), 1.221 new DummyCommand()); 1.222 menu.addItem(constants.HelpMenuName(), menuHelp); |#6 1.223 menu.addItem(constants.CreateMenuName(), menuCreate); |#6 1.224 RootPanel.get().add(menu); |#7 1.225 RootPanel.get().add(trash); |#7 1.226 menuCreate.addStyleName("submenu"); |#8 1.227 menuHelp.addStyleName("submenu"); |#8 1.228 trash.setStyleName("trash"); |#8 1.229 } 1.230 1.231 public class DummyCommand implements Command{ #9 1.232 public void execute() { 1.233 Window.alert("Menu Item Clicked");
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
As we walk through this code, we will see a number of similarities and differences to the default application. The class is still the EntryPoint to the application and is still called Dashboard ([#1]). As this is the EntryPoint class, it must still implement the onModuleLoad() method ([#2]). In the default application, we created a Button and a Label widget. For the first version of the Dashboard, we create some MenuBar widgets instead ([#3]). As we mentioned previously, we will cover more on widgets in chapter 4. When creating the Dashboard directory and code structure using the tools, we said we were going to implement Internationalization, and we created a DashboardConstants interface file using the tools. We also said that we access these constants through a GWT concept called deferred binding. Definition Deferred binding allows us to write code that is syntactically correct at development time, but where the exact semantics/functionality are deferred until run-time (though technically this occurs at compile-time as that is where GWT resolves these deferred choices, but conceptually they are resolved at run-time).
At point [#4] we show the code used within the GWT Java application that requests the correct binding between interface file and locale specific property files (we will create these in a short while) to ensure the right locale constants are available. The GWT.create() method is a special method that works differently in hosted and web mode, but all we need to worry about here is that the correct set of locale specific constants or messages is selected by this method. (We cover internationalization in much more detail in chapter 15). Step [#5] is similar to where we added a ClickListener to the Button in the default application, except here we are adding MenuItems to MenuBars and indicating that when these MenuItems are clicked that we should perform the execute() method of the new DummyCommand object we are creating for each MenuItem. We set the text to be shown for each MenuItem to be the value returned from constant object created in step [#4]. This is how we show different constant values (effectively language in this case) for different locales. To finish off the menu system we add two different MenuBars to our “root” MenuBar which will be displayed at the top of the screen. In the default application we used this RootPanel.get() method with specific named elements on the HTML page as a parameter to insert our widgets at specific locations. Within the Dashboard we did not define any named elements in the HTML file, instead, we are content to allow the RootPanel to place the widgets “in-line” from the top of the page ([#7]). So, on our screen we put the menu first and then the image for the trash icon. This approach works well for the Dashboard – different approaches work better for different applications. If you are quickly checking back in the book to see what this looks like, you might be wondering how the trash icon appears directly underneath the menu bar and on the right hand side of the page – since this is not really how
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
placing elements in-line works. Well, the answer to that is the use of cascading style sheets, which are covered in the next point. The final step of our application setup is adding some names from a Cascading Style Sheet (CSS) to the various objects that we have created ([#8]). We explicitly set the style for the sub menus and the trash icon. In a corresponding CSS definition, we enter the stylistic aspects for our components. The styling for the trash icon includes the statement float: right, which is how the icon is made to float to the right hand side of the page. When we created the MenuItems we told them that if they are clicked then they should call the execute() method of an object of type DummyCommand and so if they are clicked, the text “Menu Item Clicked” appears on the screen. It is at point [#9] in the code listing that we provide a simple implementation of the Command interface, which simply creates a JavaScript alert box on the screen with the text “Menu Item Clicked” in it. Don’t Forget (1) Currently the Java that you use in your client-side code must comply to Java 1.4 standards, i.e., no generics, etc. Your IDE is probably set to the Java 5.0 standard by default, so, even though your IDE says you have valid code, it might fail in the GWT compiler (if you are new to Java, do not worry, Java 5.0 is the next version after 1.4, GWT is not that far behind!). The easiest thing to do for simple projects is set your IDE to use the 1.4 standard – in Eclipse, right-click the project in the project explorer and select the properties option, then under the Java Compiler tree, set the compliance to Java 1.4 mode. If you are using server side code, then there is no such restriction on the Java used for that code. Don’t Forget (2) Any Java packages that you include in your client side code must have the source code visible to the GWT compiler, and must follow the same restrictions as for code you write – i.e., be compliant to Java 1.4. Don’t Forget (3) Your client-side Java code becomes compiled as JavaScript – therefore, do not expect your Java code to do anything that JavaScript cannot do. In particular, this means you cannot access the user’s file system (except through the standard file upload browser functionality), and, from client-side code, you cannot directly access a database on your server (to do this, you need to get your client-side code to talk to some server-side code).
All GWT applications are glued together with one or more corresponding Module XML files – in the case of simple applications we only have one of these files. When we build the full Dashboard application later we will see that there is a Module XML file for the Dashboard application as well as one each for most of the component applications.
Examining the Module XML File Under the src/org/gwtbook folder, we find the Dashboard.gwt.xml module file. This file will always be named YourApplicationName.gwt.xml and is used by GWT to control the compilation (generation of permutations) of your application as well as GWT module inheritance. The file itself can contain many elements, all of which are described in glorious detail in chapter 9, but right now it still describes the view of the default application and looks as shown in : Listing 3.9 The contents of the default application’s Dashboard.gwt.xml file. 1.237 <module> 1.238
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.239 <entry-point /> 1.240
We need to replace this content with that shown in Listing 3.10. Listing 3.10 Replacing the default application’s XML Module file with new contents to represent the actual Dashboard application. 1.241 <module> 1.242 #1 1.243 #2 1.244 <extend-property name="locale" values="sv" /> #3 1.245 <entry-point /> #4 1.246 <stylesheet src="Dashboard.css" /> #5 1.247 (annotation) (annotation) (annotation) (annotation) (annotation)
This module file is slightly more than that provided with the default application – let’s look at what it contains. At point [#1] we indicate that this application will inherit the functionality in the User GWT Module – almost all applications will include this entry. Next, [#2], we say that we will also inherit the functionality in the I18N GWT Module – unfortunately i18n functionality is not part of the default include mentioned in step [#1], and so we need to specifically inherit this module if we have i18n aspects in our code. Step [#3] indicates to the application that it needs to manage the Swedish locale in addition to the default one. If we did not include this entry then even if we have code for the Swedish locale it will never be accessible (additional locales require additional entries in this file). We talked when looking at the Java code about the need for a class to implement the EntryPoint interface and act as the starting point of the application. It is here at step [#4] in the Module XML file that we tell the compiler which class implements this EntryPoint interface. Finally, at [#5] we inject the style sheet as we discussed in the last chapter. With the completion of this Module XML file, our first version of the Dashboard application is now functionally complete, but it is still missing the styling needed to make it look like Figure 3.12. Remember A GWT Module XML file and the Java classpath are only very loosely related. The XML file points out GWT dependencies, the classpath Java dependencies. If a particular module is mentioned in Module XML file but its source code is not on the Java classpath, then compilation will fail.
Applying Styling The most obvious instance of styling applied to our application (obvious if you are running the code rather than perhaps looking at the black and white picture in the book!) is the fading blue background given to the Dashboard. Without the styling, the Dashboard would appear as shown in Figure 3.17.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 3.17 This is what the Dashboard application looks like if we forget to apply any styling – not quite the view of the Dashboard from Figure 3.2
Setting a background image on a web page is an easy task to accomplish when using normal HTML and a Cascading Style Sheet (CSS); we just set an entry for the body element in the style sheet, for example 1.248 body { 1.249 margin: 0; 1.250 padding: 0; 1.251 background: #111fff; 1.252 background-image:url("DashboardBackground.jpg"); 1.253 }
We then link the style sheet into our HTML. How do we do this in GWT? Well, after compilation GWT is just simple HTML and JavaScript, so the same CSS approach works perfectly well. We (and at this moment in time this still means you) should create a Dashboard.css file and store it in the src/org/gwtbook/public directory. In that file you can place the contents shown in Listing 3.11 Listing 3.11 Part of the contents of the Cascading Style Sheet for the first version of the Dashboard application. body { #1 margin: 0; padding: 0; background: #111fff; background-image:url("DashboardBackground.jpg"); } .trash { float: right; margin: 5px; }
There are a couple of graphics files mentioned in this style sheet, which we use to give the actual look – you can either grab them from the electronic part of this book, or supply your own. Either way, they need to be stored in the same directory as the style sheet. This style sheet shows three different ways of styling GWT component. We can style standard HTML elements using the normal way of defining styling for that element. In Listing 3.11 at point [#1] we see the styling for the body element, which amongst other things sets the background image of the body element to be DashboardBackground.gif. When the application loads, it expects to find that image file in the src/org/gwtbook/Public folder in hosted mode (it is possible to change this default location as we discuss in chapter 9) or the public path in web mode. It is entirely possible to set the style name of GWT components using the setStyleName() method as we saw in point [#8] of the Java code in Listing 3.8. There we set the style name (actually we are setting the CSS class name for the DOM element) of the trash object as follows: 1.254 trash.setStyleName("trash");
At point [#2] in this style sheet, we can see the corresponding CSS that is used to actually style that element. Note that in the CSS you need to start the name with a period (‘.’), which is not present in the name used in your Java code – if you do not do so, then the styling is not applied (that has caught us out a few times!). It is possible to give an element multiple CSS class names by using the addStyleName() method (the setStyleName() method removes all previous CSS class names associated with the element first and then applies the provided CSS class name) Finally, all GWT widgets come with a set of default style names already applied, and we can use those in the style sheet without having to actually name them in the code. To get the names of the styles set as default, it is necessary to go to the GWT Web site and navigate through the class hierarchy to the UI object you are interested in. We have styled the root MenuBar a lovely shade of brushed steel in the Dashboard application. To do this, we navigated to the MenuBar object on the GWT web site to see what the standard GWT style names are for a MenuBar (at the time of writing, that website could be found at the following URL: http://code.google.com/webtoolkit/documentation/com.google.gwt.user.client.ui.MenuBar.html). When we look at the website, we see the image given in Figure 3.18.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 3.18 Identifying the default style names for standard GWT widgets from the GWT Web site – in this case, we are looking at the MenuBar definition from the GWT web page and we can see the CSS Style Rules section
Under the CSS Style Rules section we can see three entries for the MenuItem, which match those given at point [#3] in Listing 3.11. To complete the linkage between application and style sheet we will need to decide where to place the link to the style sheet. There are two choices. We can either edit the Dashboard.html file adding the following in the HTML’s HEAD section (just as you normally would in a HTML file that you were linking a style sheet to). 1.255 or we can create an entry in the Module XML file to inject the CSS resource into the application. The benefit of this second approach is that we can keep style specific information logically with our application, thus if we embedded the application in another HTML file we do not need to alter that HTML file. To use the injection method we place the following into our Module XML file. 1.256 <stylesheet src="Dashboard.css"/>
So if you just add that line to the Dashboard.gwt.xml file we altered earlier, then we are ready to get on with testing and debugging our application in the Hosted Mode.
Stage 3: Testing and Debugging in Hosted Mode Hosted mode is a managed environment provided as part of the GWT distribution. We saw in the process diagram in chapter 2 that Hosted Mode is the environment where we perform the majority of our system testing and debugging. In the Windows operating system Hosted mode uses the version of Internet Explorer that is installed on your machine as the display technology; whereas under the other GWT distribution there comes a pre-built Mozilla instance (unfortunately you cannot change these defaults). When running your code in Hosted mode your Java code is not compiled into JavaScript rather it is interpreted. This allows us the distinct advantage of being able to link the execution of the application in Hosted mode to the debugging capabilities of your IDE.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
In this section we will look at the process of running our example in hosted mode both from the command line and from within the Eclipse IDE’s program launch capability. We will also look at how you can perform debugging of your application at run-time in the Eclipse IDE (though this will be similar for other IDEs). Let’s prepare ourselves for Hosted Mode.
Preparing for Hosted Mode In Hosted mode, our application is executed through a managed environment that is provided by the GWT distribution. Our client side code executes in a managed web browser, where the Java code is effectively interpreted and any errors or issues are captured and reported in a controller window. Hosted mode also includes an embedded version of the Tomcat Servlet engine into which any server side Java code is deployed automatically. When we executed the application creation tool in chapter 2, it created a script for us called Dashboardshell, in the DashboardDir directory. It is this script that is used to launch the hosted mode. If you haven’t used the creation tools or are just interested in the contents of the default script, then we have listed it in Listing 3.12 (at least for the Windows environment – the Mac and Linux versions are similar). Listing 3.12 Looking at the default contents of the Dashboard-shell command line script, which shows the basic arguments necessary to launch hosted mode 1.257 @java -cp "%~dp0\src;%~dp0\bin;gwt-user.jar;gwt-dev-windows.jar" 1.258 com.google.gwt.dev.GWTShell -out "%~dp0\www" %* 1.259 org.gwtbook.Dashboard/Dashboard.html
The hosted mode script can take a number of arguments, the default set are shown in Listing 3.12, where Table 3.12 lists all the possible arguments and what they mean. Table 3.12 Listing the arguments that can be supplied to the Hosted mode launch script. Argument Description -noserver This argument will prevent the embedded Tomcat server from starting up. It is useful to do this if you have deployed your server side code to your own Servlet engine. By default this value is false, meaning that the embedded Tomcat server is used. -whitelist “list” By default Hosted mode prevents the user/code from navigating to URLs outside of the applications scope, that is to say the whitelist is empty. If you wish to allow the user/code to do so, then the URLs that will be navigated to need to be added to a whitelist using a regular expression approach. For example, if we want the application to navigate to the Swedish Google search site, and we do not add the URL to the whitelist, then the error message in is show.
Figure 3.19 Looking at the security warning that GWT Hosted mode raises if the user or code tries to navigate to a URL that is not specifically included on the whitelist.
To add http://www.google.se to the whitelist, we add the following entry into the Dashboard-shell command script: -whitelist " ^http[:][/][/]www[.]google[.]se"
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
-blacklist “list”
Now when we or the code attempts to navigate to this Google site, no security warning is presented, but the browser navigates to the requested location. Similar to the whitelist approach, Hosted mode can explicitly bar URLs if they are placed on the blacklist, and by default the blacklist is empty. To add http://www.google.se to the blacklist, we add the following entry into the Dashboard-shell command script: -blacklist " ^http[:][/][/]www[.]google[.]se" Now when we or the code attempts to navigate to this Google site, no security warning is presented to the user, but the browser is not navigated to the requested location. If we look at the Hosted mode console after we have tried to navigate to a location on the blacklist, then we see the output given in Figure 3.20
Figure 3.20 Looking at the output on the Hosted mode console if the user or code tries to navigate to a URL that is specifically included on the blacklist. -logLevel level
Running GTW Hosted mode with default settings means that logging level is set to the INFO level. This level can be altered by providing the –logLevel argument to the Hosted mode browser. There are seven levels of logging that are available: ERROR WARN INFO TRACE DEBUG SPAM ALL The levels live in a hierarchy the same as that in Log4J (see section 3.7.1). This means that if we set/leave the logging level at INFO, then all INFO, WARN and ERROR messages are displayed; if set to DEBUG then all DEBUG, TRACE, INFO, WARN and ERROR messages are shown. This logging can be quite extensive – Figure 3.21 shows logging set to ALL for the Dashboard application.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 3.21 Examining the very detailed output when the full Dashboard project starts and logging level is set to ALL.
-gen “location” -out “location” -style value
Setting the log level to ALL produces an extremely detailed log of exactly what the system is producing – it is not likely that you will use this level all the time, if at all! However, section 3.7.1 we discuss how we hook into this logging mechanism for our own code. Indicate the directory into which files produced by any GWT generators that are in your project are placed (so that they can be reviewed at a later date if required). We cover GWT Generators in detail in chapter 14. Including the –out argument indicates to the Hosted mode that the files produced for the application should be stored at a particular directory (rather than the default location which is the current user directory). This argument alters the way in which the code is created. There are three options: OBFUSCATED, PRETTY and DETAILED; each one alters the readability of any produced JavaScript code. Obfuscated code has the small size footprint but is effectively not readable by you or I, whereas detailed output contains as much information as the compiler can stuff in there.
Don’t Forget If you are including any Java libraries in your development, then they need to be added to the classpath defined on the first line of the hosted mode execution script; otherwise they will not be available to your code (this includes any libraries that you are using for client and server sides). Tip Whilst the arguments are normally added to the application’s shell command script, if you are using an IDE such as Eclipse, then you will need to add them to the launch configuration. In Eclipse that is done by right clicking on the project, selecting the “Run As -> Run…” option and then adding the arguments to the arguments tab (see Figure 3.23).
When this script is executed, the Hosted mode for our application is invoked as we will see in the next section.
Running the Dashboard in Hosted Mode Firing up the application in hosted mode from the command line is as simple as typing in the following command in the root directory of the application; in our case DashboardDir. Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Dashboard-shell
After a short delay, two new windows open; the first is the Hosted development controller, where you will be able to examine any error messages etc, and the second is the application itself; in the case of our Dashboard you should see something resembling Figure 3.22.
Figure 3.22 Dashboard application running in Hosted Mode (the left window is the hosted console, and the right the hosted browser with the Dashboard application running).
If you are using the Eclipse editor then it is possible to launch the program from within Eclipse. To do so, you just run the project as a simple Java application. Right click on the project and select “Run As…” and then Java Application, the projectCreator tool has created all the details Eclipse needs to know to run the program. If you want to see this information in more detail, either look at the Dashboard.launch file, or right click and select Run… and the view in Figure 3.23 is shown. Clicking on the Run button then launches the Hosted Mode from Eclipse and the same view as in Figure 3.22 should be seen after a short startup delay.
Figure 3.23 Running Hosted Mode from Eclipse by clicking on the Run As… option; this presents the dialog box shown here where we can see that the Java class to be executed is com.google.gwt.dev.GWTShell, which is part of the GWT system. It is also in this
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
dialog box that we would add any libraries to the classpath that we are using (in our case, we are not using any at this stage).
There is not a lot of testing we can do for our Dashboard as at this time it does not do too much. You could try the menus out to confirm that you get back the message “Menu Item Clicked” or change the locale to see the difference. Changing the locale is as simple as adding the text ?locale=sv to the end of the URL and clicking Refresh. Doing this, and assuming you made all the changes along with us, should now show the Swedish version of the page we saw in Figure 3.15. The other task that we can perform in Stage 3 of our development is debugging the application; we will do this through Eclipse in the next little section.
Debugging the Dashboard in Hosted Mode Through Eclipse Debugging in Eclipse works in exactly the same way as debugging any Java application (and this applies to both client and server (RPC) side code, although we haven’t got any server-side code yet). Table 3.13 shows the steps we take to debug an application.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Table 3.13 Going through the steps involved in debugging a sample application in Eclipse (though the steps are the same in other IDEs using their specific user interface). Step Description 1 Setting the Breakpoints in the Code. The first thing to do is set breakpoints in your code where you wish the debugger to step in. In Figure 3.24 we have set a breakpoint at the point where we display an alert to the screen when a user selects a menu option. This is seen as the little blue ball in the left hand side bar.
Figure 3.24 Setting a breakpoint in Eclipse IDE at the start of the changeLabel() method. We have clicked in the gray bar next to the Window.alert() command and Eclipse is showing a little blue circle indicating that a breakpoint has been set. 2
3
Execute the application in debug mode. The next step is to execute the project in debug mode. In Eclipse this is performed in the same manner as we did when launching Hosted mode, except we select “Debug As…” rather than “Run As” - right clicking on the project in Project Explorer and selecting “Debug As…” option and then Java Application. Invoke the breakpoint Now when we execute the application in debug mode the hosted browser is launched and everything works as normal until we click the button. Upon clicking the button the normal execution is suspended and the Eclipse debugger traps the execution point and presents the screen shown in Figure 3.25.
Figure 3.25 This is the view shown in the Eclipse Debugger when the code reaches the breakpoint that we previously set in Figure 3.11 4
Step through the code.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
It is now possible to step through and into the code using the navigation buttons at the top of the debug window (and shown in Figure 3.26).
Figure 3.26 We can use the various buttons on the debugger’s toolbar to navigate through out code in debug mode. In order of left to right we can see circled: Step into, Step over, Step out and Drop to Frame.
The debugger is immensely helpful when it comes to understanding the null pointer errors that seem to inevitably appear when building complicated applications (so far we’ve experienced it when we have forgotten to create objects (widgets and panels) before trying to use them – school boy errors, yes, but with the debugger they are very quickly found; we could not begin to imagine how difficult this would be just by looking at the JavaScript code, not to mention ensuring it was consistent across all browser versions we would have to maintain in a non-GWT approach). It is also a very useful technique when using the more advanced techniques of remote procedure calling (RPC), where there is Java code deployed onto a server. When executing such applications through hosted mode, it is possible to hook your debugger up to both client and server side code. OK, at this point we are almost at the end of our development lifecycle for the first version of the Dashboard example. Up to this point we have created the project structure, replaced or amended the necessary files and filled them with appropriate content. Then we launched the application in Hosted mode and tried a little bit of debugging as well as setting up the basis for unit testing. Now it is time to take the final step towards a complete GWT application and exercise it in Web mode, where it runs for real.
Stage 4: Compiling the Code Whereas Hosted mode provided us with a protected environment in which to perform system testing since our Java code was interpreted and could be debugged, Web mode is where our application runs for real. In Web mode our application has been compiled into the web technologies we discussed in chapter 1. No longer is our application Java code, what we now have is a number of JavaScript files (permutations as they are called in GWT) for each browser and locale combination. As we create more complex applications, including and managing different GWT properties, then the number of permutations will increase – the full Dashboard, for example, will come with an intranet and Internet version selected by setting an externalvisibility property. This means the permutation matrix space will cover browsers, locales, and external visibilities – luckily GWT takes care of all that for us, as well as selecting the correct permutation to show the user. The first step to executing our application in Web mode is to compile our Java code. In chapter 17 we take a more detailed look at the functionality and outputs from the compilation process, but let’s take a moment now to look at how we invoke the compiler and see what the resulting files are.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Compiling the Code/Preparing for Web Mode In Web mode, an application is executed through a normal web browser and therefore the GWT application needs to be compiled from Java to JavaScript before this happens. Compilation is performed by executing the compilation script, which has also thoughtfully been created by the application creator command. It will be in the DashboardDir directory and will be called Dashboard-compile. The contents of this default command look like: 1.260 @java -cp "%~dp0\src;%~dp0\bin;gwt-user.jar;gwt-dev-windows.jar" 1.261 com.google.gwt.dev.GWTCompiler -out "%~dp0\www" %* 1.262 org.gwtbook.Dashboard
Table 3.14 lists all the possible arguments to the compiler and what they mean. Table 3.14 Listing the arguments that can be supplied to the GWT compiler script. Argument Description -logLevel level As with Hosted mode, we can set the logging level that is used during the compilation process by providing the –logLevel argument to the compiler. There are the same seven levels of logging available: ERROR WARN INFO TRACE DEBUG SPAM ALL
-treeLogger
-gen “location”
The levels live in the same hierarchy as for Hosted mode. Passing the compiler the –treeLogger flag directs the compiler to produce its output in a standard GWT TreeLogger format in a new window (as can be seen in Figure 3.27).
Figure 3.27 Setting the treeLogger flag for the GWT compiler causes it to produce its output in a new window in a tree style, as shown in this figure. We can then easily navigate and check the output of the compilation process. Indicate the directory into which files produced by any GWT generators that are in your project are placed (so
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
-out “location” -style value
that they can be reviewed at a later date if required). We cover GWT Generators in detail in chapter 14. Including the –out argument indicates to the compiler the output files produced for the application should be stored at a particular directory (rather than the default location which is the current user directory). This argument alters the way in which the code is created. There are three options: OBFUSCATED, PRETTY and DETAILED; each one alters the readability of any produced JavaScript code. Obfuscated code has the small size footprint but is effectively not readable by you or I, whereas detailed output contains as much information as the compiler can stuff in there. It is likely for production ready code you would prefer the small footprint size created by setting the style to obfuscated, but in development you might like to use pretty or detailed if you wish to look at the produced JavaScript code.
When the compiler is invoked, it will take our Java classes and translate them into a number of JavaScript files. Each JavaScript file (a permutation in GWT parlance) represents a version of our application for each the property values the application is concerned with. Normally those properties are restricted to browser types and any internationalization that might be going on. In the case of the full Dashboard, we get 18 different permutations – one for each of the browsers, locales, and external visibility options that could be found. Let’s take a look at the files that are produced. Don’t Forget Again, if you are including any Java libraries in your development, then they need to be added to the classpath defined on the first line of the compiler script; otherwise they will not be available to the compiler and you will get compilation errors. Note The default values for heap size of the Java Virtual Machine (JVM) used for compilation are not optimal, and you should consider changing them. In the authors development environment we add the arguments Xms256m and -Xmx512m just before the –cp argument in the compiler scripts. This has the benefit of substantially speeding up compilation (on our machines from around 15 minutes to just over 40 seconds!) and avoiding the potential for heap size errors generated by the compilation process.
Viewing the Compilation Results Compilation is performed by executing the following command: Dashboard-compile
When compilation has finished, your application will be compiled into an org.mycompany.Dashboard directory under the DashboardDir/www/ directory (which will be created if it does not already exist). Figure 3.28 shows the typical set of files produced for our Dashboard application.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 3.28 Looking at the output of compiling the Dashboard GWT application for web mmode. We can see the basic gwt.js JavaScript file, three HTML files that form the framework for our application followed by eight more HTML files that contain the JavaScript permutations of our application (each permutation contains JavaScript specifically relating to browser/locale values). Following this there are a number of XML files whose names match the previous HTML files and they detail the choices made by the compiler for each permutation. The remaining files are our image and style cheet resources.
We discuss these files in more detail in later chapters, but for now we can say that the XML files correspond directly to the HTML files with the same name – they list the decisions taken by the GWT compiler when generating the permutations of code. For example, the XML file named 127D3E5E8741F096957AE332A38198D0.xml contains the rebind decisions shown below that indicates the 127D3E5E8741F096957AE332A38198D0.nocache.html file is specifically the version for the Opera browser when the application is using the Swedish locale. 1.263 1.265
(The file names are based on MD5 hashing and so it is unlikely that you will have exactly the same named files as we did, but the conceptual link between the xml and html files will still exist and be valid!) We cover the whole process of compilation in chapter 17, but for now we have a compiled application, and it is time to deploy that code.
Stage 5: Deploying the Code Once the application has been compiled, we can then take the next step to executing it in Web mode, by deploying it to your favorite web server. We cover deployment in some detail in chapter 16 since the more complicated your application becomes, the more complicated deployment can be. For now we can perform a simple deployment since we have no server side code. Note When you have server side code (in Java) then you need to ensure that you create a web.xml file that details the services you are deploying to the Servlet engine (in Hosted mode this is all automatically done for you). Just
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
because you have included your service endpoints (see chapters 10-14) in the GWT Module XML file (see chapter 9) you still need to create a web.xml file for them to be visible to your web mode deployment.
Deploying to a Web Server We developed our examples for this book with the Apache Web server as our target, and in that case deploying this example was as simple as copying across the files in the compilation directory to the web server’s root directory (or if you prefer, a specific folder under the root directory). If you really want to save space on the deployment then you do not need all the XML files we previously discussed, as they are only used by the compiler to keep track of where it is. It might be the case that you do not have web server, in which case we would recommend you get one – Apache is free, runs on most systems, and is quite simple to use in a basic way. On those occasions where your application only uses client-side code then you can “deploy” to the file system.
Deploying to a File System If you do not have a web server, or want to make a quick check of functionality in browsers, then you can still “deploy” this version of the Dashboard as most operating systems allow you to double-click on Dashboard.html file in the compilation folder. The limitations of this approach are that you cannot execute any server related code – for example, if you try this for the full Dashboard example in the downloadable code then you will receive an error notification since the code cannot call the server to retrieve the XML file necessary for the Bookmark menu. On the whole, this limitation is not too great - especially if you quickly want to check some functionality. If you want to always deploy to a server it is worth taking the extra effort and using Maven or Ant to build automatic packaging and deployment tasks that greatly reduce your effort of deploying to a server. This is a really basic deployment; in chapter 16 we go into detail on the subject of deployment. After the application has been deployed, it is time to run it.
Stage 6: Running in Web Mode Running the application in web mode is a case of navigating to the correct URL on your web server. The final result of the Dashboard application that we have been building in this chapter can be seen in Figure 3.29 (running on the Opera browser).
Figure 3.29 Looking at the Dashboard application in Web Mode in the Opera browser. Any of the supported browsers can be used
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
for web mode, we chose Opera here as we normally use Firefox and wanted to see our application in another browser.
If you have errors at this phase, it is likely to be with the deployment you have just performed, and so you would normally take a good look at the deployment you have performed to identify where it has gone wrong. To round off our discussion on our application, we will take a little look at adding some logging capability to check on the progress of our application as it executes.
Implementing Application Logging Logging information is useful both for tracing errors and for understanding workloads of services. Modern logging frameworks make for a flexible, and often light, approach to capturing in process information for applications. However, logging for a web application is an interesting topic; for a start, what do you log, and more importantly, where do you store logs? With a non-Ajax approach, such as Struts used in a pure way, every action on the screen results in server calls and a redrawing of the web page, which means you can log every action the user makes -- but you are hoping to get many thousands of visitors per hour, and logging all of their activities could generate substantial logs that are unwieldy. Ajax complicates the last approach slightly, since a lot more functionality occurs on the user’s machine, meaning there is potentially not as much client server communication. However, as a web application, you do not get access to the file system of the user for security reasons, so you cannot store log files there. How is this logging issue solved in GWT?
Logging Information on the Client-Side If you are a JavaScript developer coming to GWT, used to the extensive logging needed in that language to understand errors, then chances are you will be looking for, and perhaps become frustrated with, the client-side logging capabilities offered by GWT. But, and this is a key point, you should move your mindset towards using the much more powerful Java debuggers available to you now instead of relying on output messages and log statements. Typically you should rely heavily on the GWT compiler, but there are, in the rare case, issues that occur in the JavaScript code. If you want to debug these, then your best chance is to ensure that the code is compiled in detailed or pretty mode (by supplying –DETAILED or –PRETTY flag to the compiler) and then adding some debug statements to the human readable JavaScript code (it will be a very rare case when you need to do this). If you are a Java developer and used to frameworks such as Log4J, then you too might be slightly disappointed. In GWT, there is limited capability for client-side logging: the only built in client-side logging capability for web mode is the Window.alert() method, which really is not that user friendly, and in hosted mode this is only slightly improved by the addition of a log method in the GWT class. However, as we discussed in the introductory section to Logging, if you are logging for information purposes there is limited benefit in client side logging for an AJAX application (inability to access those logs). If you are logging for the purposes of debugging, then you GWT.log() method can help identify where issues might be occurring, but the real power comes from the debugging capability of your IDE. Other frameworks/toolsets provide more comprehensive JavaScript logging on the client side – but this is because they are unable to be debugged like GWT is in Java. One we have used, for example, is that provided by the Yahoo UI Widgets. Figure 3.16 shows the logger from this toolkit in use on its demonstration page.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 3.30 Client-side logging capabilities that are provided by the Yahoo UI widget framework (GWT does not provide such extensive client side logging capabilities – but that is related to the philosophy of being able to use Java IDE and debuggers rather than trying to fix issues by reading logs from running programs)
Many times you wish to use logging in other frameworks/toolkits because it is useful to debug errors, identify uninstantiated objects, and confirm that you are calling valid methods. Remember that when you are writing GWT programs you are writing your code in Java, and as such you can use the power of your IDE’s debugger to capture all of these issues. Sometimes you may just wish to get notification that your code is following particular paths whilst you are developing code and may not want to always invoke the IDE’s debugger (or you may even be developing without an IDE). If this is the case, then GWT does provide a basic logging capability (but is it only valid in hosted mode). We can use the GWT.log() method to output messages to the Hosted mode console. In your code you place the logging messages where you wish, and GWT duly prints them to the hosted browser window as required. For example, if we could add some logging to our Dashboard example’s onModuleLoad() method, then when you execute the Dashboard in Hosted mode, you would see the view shown in Figure 3.31.
Figure 3.31 Using GWT’s basic log capability, GWT.log(), to display some log messages in the Hosted Browser Window when the Dashboard Application Executes.
Adding this logging capability to the onModuleLoad method is accomplished by the code shown in Listing 9.13. Listing 9.13 Updating the Dashboard’s onModuleLoad method to log some basic run time information. 1.267 public void onModuleLoad(){ 1.268 GWT.log("Starting GWT In Action Dashboard",null); 1.269 1.270 // Create Dashboard Name EditableLabel
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
// Create Menu System // Create Trash Icon // Set up window event handling GWT.log("Window Event Handling Initialised",null); // Add components to the browser GWT.log("Dashboard Components Added to Browser",null); //Start API listening GWT.log("Dashboard API Initialised",null); GWT.log("Dashboard Started",null);
Using the GWT.log() method means deciding which of the two arguments you will pass in (you can only pass one argument; the other must be null). The first argument is plain text, and is the approach that we use in the Dashboard. Using the second argument means passing in an object that is an instance of the Throwable object, i.e. a GWT exception. Finally, if you really want to implement a logging system on the client side for web mode, then you could wrap an existing JavaScript logging framework, for example Log4JavaScript (http://log4javascript.sourceforge.net/), with some JavaScript Native Interface (JSNI) and then access it through that (we talk about wrapping JavaScript libraries more in chapter 8). However if you have followed a development lifecycle that only uses web mode as the last stage, then there is only limited benefit from client-side logging a GWT application. It is of course possible to use a very basic approach to client side logging in web-mode and put your messages to the screen using the JavaScript alert functionality, but again, this seems only useful if you have an error that appears in your compiled code and not Java code – which should be very very rare. Of much more use and interest is logging on the server side.
Logging Information on the Server-Side Logging on the server side is much more open than client side, and you are free to choose the most familiar/useful framework available? If your server-side code is Java, then a good recommendation would be to base your logging on an approach such as Log4J from the Apache Foundation. As there are no restrictions on the Java used on server side code, this recommendation fits well for RPC and J2EE based approaches. If you are using other languages, then you can find implementations of Log4J for those languages, too, such as Log4PHP (http://logging.apache.org/log4php/) or Log4Perl (http://log4perl.sourceforge.net/). The scope of these logging packages is worth chapters on their own, but we focus on GWT rather than logging in this book. For more information on this type of logging we would recommend starting with the Log4J documentation (http://logging.apache.org/log4j/docs/manual.html). We have seen that it is possible to perform logging on both client and server side, but that, in a web application, true logging is best performed server-side. Logging on the client-side to debug code is not necessary with GWT since we should develop in Java in an IDE that will reduce errors in client-side to functional misunderstandings. Congratulations -- that is it. We have taken our first run through the whole development lifecycle of a real GWT application, and you have just run the first version of the GWT in Action Dashboard application! Over the coming chapters we will expand on all the concepts we have talked about over the last two chapters to look at how the full Dashboard example was built. Figure 3.32 shows a schematic of the Dashboard identifying the components that we
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
will be building over the next four chapters, including the ToggleMenuItem, the EditableLabel, DashboardComposite, and the DashboardPanel (in which DashboardComposites sit).
Figure 3.32 Looking at the design of the Dashboard application and the various components that are used – most of these components we will be building or looking at over the next few chapters.
For now, it is time to take a well deserved break, grab a coffee, and then we can set off looking at the real basics of GWT while building up to the more complicated version of the Dashboard.
Summary If you’ve not seen GWT before, then these last two chapters have taken you right through the development lifecycle to being in possession of a working GWT application that includes some aspects of styling and internationalization. The foundations laid down in this chapter will be built upon throughout this book as we continue to construct our Dashboard application. Two key points should be taken away from these two chapters (chapters 2 and 3). First, GWT is flexible in terms of how you create and build an application. We feel that at the moment the smoothest way to develop a GWT application is to use the GWT command line creation tools with the –eclipse parameter and then import the application into Eclipse for development. As time progresses, the plug-ins for the other IDEs will improve and they should become the preferred way of creating GWT applications, but we are not quite there yet. Second, the GWT creation tools will always create the default application, and if you do not at a minimum change the applications Java file, then you will get the default application displayed in Hosted and/or Web mode. Just adding your own Java files will achieve nothing, unless you alter the main Java file for the application. Creating applications themselves is a straight-forward process as long as you remember several key points: 1. Java code used on the client side must comply with the requirements of Java 1.4 (not the later versions). 2. Any libraries that you wish to include on the client side must also comply with the GWT Java requirements, and you must have the source code available since that is what the GWT compiler uses to create the JavaScript.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
3. If you use any Java libraries, then these need to be added to the hosted and web modes class-paths in the respective command line scripts. From a journey perspective, we have now reached the conclusion of Part I of our trail. If we look back over our shoulder, in the distance we see the land of Ajax with all of its cross-browser scripting problems and issues over maintaining several versions of JavaScript for the same applications. In front of us lies the world of GWT where all those issues are significantly reduced. There is still a tricky trail to complete our Dashboard, but we have taken the first steps by defining the basic structure. In the next part of this book, we will learn the basics of GWT: the core user interface components. We look at widgets, panels, events, composite widgets, and JSNI components as we ready ourselves for building the user interface parts of the full Dashboard.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
4 Working with Widgets Now that we have wet our toes a bit by building the first version of our Dashboard application, we are going to take the next few steps towards building the full version, where we will use many different types of widgets and panels as well as handling different types of events. To get there, we first need to tell you exactly what these components are! This chapter deals specifically with widgets, which are the visible components of a GWT application a user sees on the browser page; for example buttons, labels, images, or the menu system. Imagine buying a new plasma television and then finding out that the manufacturer hasn’t provided any control buttons or a remote – this is what your application would be like without widgets: useless. Definition Widgets are the visible components of a GWT application that a user can see on the browser page.
Over this and the next four chapters we cover some of the basics of GWT to get us into a position to fully understand how the Dashboard is constructed, and along the way we will be building a few of the necessary components for the Dashboard. The chapters cover the concepts shown in Table 4.15. Table 4.15 Short summary of the five chapters involved in covering the GWT basics of widgets, panels, events, composite widgets, and using JavaScript Native Interface (JSNI). Chapter Name Details 4 Widgets Widgets are the visible components of a GWT application that the user sees on the screen, for example Buttons, Labels, Menu Bars, etc. We will build the following two widgets used in the Dashboard -- the PNGImage and ToggleMenuItem widgets. 5
Panels
Panels help us structure the view on the screen; they can be used to position (with panels such as VerticalPanel) and manage the visibility of widgets (with panels such as DeckPanel). We will construct the DashboardContainer panel, which is used to hold all of the Dashboard components applications we build later in the book.
6
Events
Functionality within GWT is driven by events, for example when a user clicks on a button, when a form submission returns, or when the user drops a component they have been dragging. We will extend our DashboardContainer from chapter 5 to handle double-click and focus events.
7
Composite Widgets
8
JavaScript Native Interface (JSNI)
Combining all of the power of the last three chapters, we finally come to Composite Widgets. These are widgets that are made up of other widgets, usually placed within one or more different panels, and are really the most powerful form of component we can create ourselves. We will build the EditableLabel composite as well as the DashboardComposite object, which are used in our Dashboard example. JavaScript Native Interface (JSNI) affords us access to native JavaScript. It can be thought of in a similar manner as using assembly language code in a C program. Chapter 8 discusses the appropriate places to use JSNI and how we can wrap existing 3rd party JavaScript libraries for use within our GWT programs.
As we have said, this chapter covers widgets, and we will start by looking at exactly what these things we have called widgets actually are. Once we have a good idea of what a widget is, we will take a quick look at those widgets that come as standard with GWT, including how we will use them in the various components of the Dashboard. In the second part of this chapter, we will see how we can create our own widgets just in case those provided as standard are not enough or do not quite meet our needs. Within that discussion, we will build a PNGImage widget
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
to allow us to use PNG images in our Dashboard, and we will extend the MenuItem widget so that it meets our Dashboard application’s needs. If you are ready, then we will jump right in and define what a widget is.
What is a Widget? Widgets are one of four fundamental building block of GWT applications -- the others being panels and events and server communication (including RPC, Form, JSON and XML handling as well as the traditional AJAX HTTPRequest). When a user fires up your GWT application, they are looking at a set of widgets that have been positioned by panels and which react to events. Widgets, just like the buttons on the plasma television remote control we mentioned earlier, are the components the user interacts with. Luckily, GWT provides many different widgets for free, and these include the usual suspects of buttons (such as the one shown in Figure 4.33), textboxes, and menus. . Figure 4.33 Button widget shown as rendered HTML in the Firefox browser
Most of our applications will be built using multiple widgets, which we will put inside panels to provide some structure -- this is quickly obvious if we look at the Dashboard’s Calculator component application (see Figure 4.34).
Figure 4.34 Calculator application from the Dashboard, showing how a number of widgets can be put together to create a complete application
Widgets, as well as panels, which we look at in the next chapter, have a dual existence in GWT – they can be thought of as both Java objects and DOM elements. The Java object view is the one that we use in our day to day programming to create our application. The DOM view is the representation the Java objects, that we used in programming, have in the web browser used for Hosted and Web mode. We will look at both of these views in the next two sections, starting with the Java object view of widgets.
Using Widgets as Java Objects The purpose of GWT is to develop rich Internet applications once, in Java, and then have the GWT compiler generate the HTML and JavaScript necessary for the application to work in a variety of different browsers. To achieve this, we must have a way of representing various browser objects, which GWT calls widgets, in our Java programs. We will literally take advantage of the object oriented programming world’s ability to model objects and concepts as programming objects. For example, in a GWT program we will quite happily use a Java object called Button. This Button object will model various properties that we expect a Button to have, such as the ability to set the
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
visible text and the ability to click it. In fact, we can model all the components we would wish to see in a browser – the widgets – as Java objects with methods and properties. So, our everyday programming use of GWT will see us considering all widgets in their natural Java object form. The button we just mentioned is created by calling the constructor of the GWT Java Button class as follows: 1.286 Button theButton = new Button(“Click Me”);
This code creates a new GWT button Java object that we can then use execute various class methods on. The tasks shown in Table 4.16 are typical operations we can perform on a GWT Button widget. Table 4.16 Applying some of the Java Button class methods to the Java Button object and describing what functionality happens Code Description 1.287 theButton.setStyleName(“buttonStyle”); Set the Cascading Style Sheet (CSS) style name for the Button – a corresponding entry should be found in the CSS style sheet attached to the web document, though its name must be prefixed with a period, i.e. .buttonStyle{…}. Add a ClickListener (an event listener that specifically listens for 1.288 thebutton.addClickListener(new mouse click events) to the Button. When the button is clicked ClickListener(){ then the code within the onClick() method defined in the 1.289 public void onClick(Widget sender){ ClickListener will be executed.
Change the text that is shown on the button from the original “Click Me” to “Go on, click me”. Hide the button on the web browser so that it is no longer visible.
The Java view of widgets is pretty straightforward to anyone familiar with writing Java programs or any other similar high-level object based language; we create objects from classes and call methods that those classes have. What the Java view does not give us is an understanding of how these widgets are displayed on a web page. That is given by the widget’s alternative DOM representation.
Considering Widgets as DOM Elements The Java representation of the widget we have just seen works great in our Java code and allows us to build GWT applications as we want, using any number of widgets and using their associated methods to build our functionality. However, we are not able to display these Java objects in a web browser, so we do not yet have an Ajax application. This is where the alternative DOM representation of widgets comes in. All GWT widgets have an alternative DOM representation that is built in parallel with the Java object. The Java constructor is generally responsible for creating this DOM representation, so, if we look at the detail of the constructor for the Button class, we can see the following definition: 1.294 public Button() { 1.295 super(DOM.createButton()); #1 1.296 adjustType(getElement()); 1.297 setStyleName("gwt-Button"); 1.298 }
The call to DOM.createButton() ([#1]) creates the DOM element through the GWT DOM classes (there is much more detail on the use of this DOM class in appendix A). Also, within the constructor of the parent, the setElement() method is called to set the widgets DOM representation to this new element. It is using this value set by the setElement() method that we get access to the DOM representation from the Java
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
view (to do so, we use the getElement() method). If we were to look at the DOM representation of a GWT Java Button we would see that it looks like this: 1.299 1.304 Click me 1.305
This is the standard DOM representation of a object with a couple of additional GWT specific attributes. These additional attributes (eventbits, onload, etc.) are set by the object’s Java constructor when this DOM representation is created. Don’t be confused by the name of the first attribute, class, as this refers to the CSS styling that can be applied to a widget, rather than the Java class name of the widget. The next attribute, eventBits, is one that we will see occurring in many widgets and indicates to GWT what type of events are listened to (“sunk”) by that particular widget – we will go into this in more detail in chapter 6, but it will keep popping up in examples until we get there. Essentially a widget sinks browser events that it is interested in handling. One golden rule that applies to this DOM representation is that we should not rely on the fact that a particular widget is implemented as a particular DOM element, as there is nothing to stop future implementations of widgets being represented using different DOM elements than previous versions. By focusing our programming efforts on the Java code view using the methods provided by each GWT widget class to perform functionality, we protect ourselves against any possible future changes at the DOM level. So now that we know what a widget is and how it is represented both in Java and the DOM, we will take a quick tour of the widgets that are available as part of the GWT distribution.
The Standard GWT Widgets The standard GWT distribution comes with a wide range of widgets for use in our applications. These widgets cover the types of areas you would expect, with buttons and text boxes and other such widgets. There are, however, some widgets you might expect that are missing, for example progress bars and sliders, though we will build one of those later in chapter 7. Within the set of widgets, the designers of GWT have implemented a strong hierarchy of Java classes in order to provide an element of consistency across widgets where that consistency naturally exists. Take the TextBox, TextArea, and PasswordTextBox widgets; it is not unreasonable to expect them to share certain properties. GWT recognizes this and captures the common properties in a TextBoxBase class, which these three widgets inherit. To get a snapshot of this hierarchy, cast your eye over Figure 4.35.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 4.35 GWT widget class hierarchy, also indicating the types of event listeners that can be registered against each widget.
We can see in this hierarchy that all widgets ultimately inherit from the UIObject class, which contains a number of essential housekeeping and property aspects. It is within the UIObject class you will find the setElement() method, which we discussed previously when it is used to set the physical link between the widgets Java object and DOM views. Subclasses of UIObject must call this method as the first thing they do before any other methods are called to ensure that the link to a Browser element is established. Note All GWT widgets inherit from the UIObject class, which provides a common set of methods and attributes for all widgets, including setting size, visibility, and style names, as well as providing the link between the Java and DOM representations.
We will not go through all the methods in the UIObject class, but we will highlight the typical functionality you can expect all widgets to inherit. UIObject class allows access to a wide range of DOM functionality without us having to access the DOM directly. For example it is possible to set the height of a GWT UIObject using the setHeight() method, which in turn uses the setStyleAttribute() method from the DOM class. 1.306 public void setHeight(String height) { 1.307 DOM.setStyleAttribute(element, "height", height); 1.308 }
The other methods written in this style include the ability to set the width, title (what is displayed when a mouse hovers over an element) and both width and height at the same time through the setSize() method. All of these methods take Strings as parameters, such as setSize(“100px”,”200px”). The setPixelSize() method allows integers, e.g. setPixelSize(100,200). Whilst these methods for setting stylistic attributes are available, we would recommend that styling should generally be performed by using Cascading Style Sheets.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
After UIObject all widgets, except TreeItem and MenuItem, must inherit from the Widget class, which provides widgets with their widget-ness, including the methods that are called when a widget is attached or detached from a panel. This class also contains the default implementation of the onBrowserEvent() method, which allows a widget to manage any events that it has sunk (we see this in action in chapter 6). In the next section we look briefly at the widgets we get for free with the GWT distribution as well as how we will be using them in the Dashboard application. We can summarize the widgets as shown in Figure 4.36.
Figure 4.36 Summary of widgets included within GWT indicating how they will be grouped and discussed in this chapter
As we discuss the widgets during this chapter, we will try and show some simple code showing their use and pointing out where in the Dashboard application we use them. This code might start looking a little alien in some places; don’t worry, we have not introduced most of the concepts used in the code yet, as they come in the next few chapters, but in most cases you should be able to see what is generally happening. There are a couple of principles to bear in mind as you read these next few sections. First, it is not intended to be a complete walk through of the GWT API for widgets; to do that would border on the excessively boring. Instead we try and show some of the key methods and pitfalls, as well as where we have used the methods in the Dashboard application so you can look in the code yourself. In general, where we define a getter method, e.g. getText(), the GWT API usually provides the appropriate setter method too, e.g. setText(). Finally, to keep focused, we often do not include names or number of parameters for a method, unless it is really necessary; this allows us to write a book focused on what can be done without getting bogged down in details – the online GWT API reference (http://code.google.com/webtoolkit/documentation/gwt.html) is an invaluable source of help for the details, as is the use of a good IDE. We break our discussion of widgets down into the following five main categories of widgets that were shown in Figure 4.36 -- Basic, Label, Focus, Button Base, and Text Box Base widgets. Lets start by looking at the class of widgets we have called Basic widgets.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Interacting with the Basic Widgets We will define the basic widgets as those that inherit directly from the Widget class and have nothing else getting in the way. There are five of these widgets, which we briefly consider next in the context of the Dashboard application that we will be building, but sometimes we show a little code sample in isolation to emphasize a particular point or property.
Uploading Files with the FileUpload Widget The file upload widget acts as a GWT wrapper to the standard Browser FileUpload text box – the one that you need to use when the user wants to upload a file from their machine to your server.
Figure 4.37 The FileUpload widget
You must remember that this is only the client side component -- clicking the Browse button allows the user to select a file on his own computer that presents the standard File Search dialog box but does not allow you to save a file to your server – that takes a little more work. This widget should be embedded within a form that has its encoding set to multi-part and when ready you should submit the form to your server to process the uploading of the file selected (an example of this functionality is provided in the FileUploadServlet class in the server package of the Dashboard). This widget does not provide any server side code to handle file upload; we have to provide that ourselves, but we are free to do that in our favorite server side language. We look at such a FileUpload in more detail when we discuss Server Side components in chapter 12, but we also consider it briefly later in this chapter where we look at how we build our own widgets. The FileUpload widget is perhaps one of the most inconsistently implemented widgets across browsers, with different browsers allowing differing security restrictions and ability to style it. Most browsers, for example, will not allow you to set the default value of the text box since that would allow a web application to go fishing for files. As with all widgets, the thing to remember is that, if you cannot do something with the widget in HTML and JavaScript, then you cannot do it in GWT. To that end, GWT only provides a getFilename() method that retrieves the filename selected by a user. This method should not be confused with the getName() and setName() methods, which are used to set the DOM name of the FileUpload widget. But enough of file uploading until chapter 12; we will now continue looking at some of the other basic widgets that GWT provides.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Navigating Your Application with Hyperlinks The Hyperlink widget acts as an internal hyperlink within your GWT application. To the user it appears exactly as a normal hyperlink on the web page and when clicked they would expect some navigation within the application to occur. In your code, this action is coded as manipulating the GWT History object to change the application state – you can see how this works in the Dashboard’s Slideshow application (org.gwtbook.client.ui.slideshow.Slideshow). The application has two hyperlinks, as shown in Figure 4.38, placed at the bottom that enable the user to move the slideshow to the start or the end.
Figure 4.38 Two Hyperlink widgets (Start and End) in Action at the bottom of the Slideshow Dashboard application.
Any component that uses a Hyperlink widget should also extend the HistoryListener interface and implement the onHistoryChange() method to catch and manage clicks on the hyperlinks. As you can see the Dashboard’s Slideshow component implements two Hyperlinks, which can be found in the code as: 1.309 Hyperlink startLink = new Hyperlink("Start","0"); #1 1.310 Hyperlink endLink = new Hyperlink("End",""+(maxNumberImages-1));
#2
Each Hyperlink widget constructor comprises of two elements, the text that is to be displayed on the screen and a history token (which can be any arbitrary string). In the Slideshow’s case, all the history tokens represent numbers of a picture, starting at 0 for the first and ending at the maximum number of images in the slideshow, minus one. It is easy then to have a hyperlink to the start and end of the slideshow by using the appropriate values in the Hyperlink constructor – “0” for the start (as shown in [#1]) and the string representation of the largest image number (as shown in [#2]). When using GWT History, you must remember to include the code shown in Listing 4.14 in the body of your HTML page. Listing 4.14 Implement the GWT History sub-system by including the following line of text in your application’s HTML file 1.311 <iframe id="__gwt_historyFrame" style="width:0;height:0;border:0">
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Failure to do so will result in errors, which in Hosted Mode are visible by errors in the Hosted Mode console, as shown in Figure 4.39.
Figure 4.39 Error raised when trying to use the GWT History sub-system in Hosted mode without it being properly initialized.
(The rebinding message you can see in Figure 4.39 is a result of some GWT manipulation we perform later in the book using GWT Generators to take the basic component application and automatically generate some new code to show an About menu item.) Using methods in the Hyperlink class, it is easy to get the history token of a particular link (getTargetHistoryToken()) or even update the token if you were to so wish (setTargetHistoryToken()). Similarly, you can set the hyperlink’s text or get it through the setText() and getText() methods respectively (or the HTML using setHTML() and getHTML() if we have created the hyperlink so that the text is treated as HTML, using the Hyperlink(String, boolean, String) constructor instead of the more simple Hyperlink(String, String) version) which treats the hyperlink as simple text. Treating a Hyperlink text as HTML really means that the any mark-up code, such as text in bold, underlines or images are displayed. If you want a normal hyperlink, to say another HTML page, then you should use the HTML widget, which we look at a little later, rather than a Hyperlink with text set to HTML. Hyperlinks are one way to navigate through an application; another is to use a menu system.
Navigating Your Application Using Menus The menu system provided by GWT is based on the MenuBar and MenuItem widgets. MenuItems are added into MenuBars and MenuBars are added to other MenuBars in order to create your applications menu system. Figure 4.40 shows this system in place for the Dashboard menu system, where Clock, Calculator and Slideshow MenuItems are added to a Create MenuBar, this Create MenuBar and a Help MenuBar are then added to another MenuBar, which is then displayed on the browser page.
Figure 4.40 MenuBar and MenuItem widgets being used in the Dashboard application. In this example there are three MenuItems placed in one MenuBar, which is itself then added to an overall MenuBar
In the Dashboard (org.gwtbook.client.Dashboard) we define one global MenuBar using the following code: 1.312 MenuBar menu = new MenuBar();
This simple line of code creates a horizontal menu bar (which could have just as easily been created using the alternative constructor that takes a Boolean parameter whose value is false to create a horizontal menu bar, e.g.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
new MenuBar(false)). At present, the Dashboard will have two standard menu bars; we create two additional ones later in this book, and further down the Dashboard code we find two methods used to build the create and help menu bars. The method buildHelpMenu(), shown in Listing 4.15, builds the initial create menu bar using vertical menu bars. Creating vertical menu bars is performed by passing the Boolean value true as a parameter in the constructor (see [#1]). Listing 4.15 Building the Dashboard’s Create menu bar and menu items, together with the nested Locale menu bar. 1.313 protected MenuBar buildHelpMenu(){ 1.314 MenuBar menuHelp = new MenuBar(true); |#1 1.315 MenuBar menuLocale = new MenuBar(true); |#1 1.316 1.317 menuLocale.addItem(constants.EnglishLocale(), 1.318 new ChangeLocaleToEnglishCommand()); #2 1.319 menuLocale.addItem(constants.SwedishLocale(), 1.320 new ChangeLocaleToSwedishCommand()); 1.321 menuHelp.addItem(constants.LocaleMenuItemName(), menuLocale); #3 1.322 return menuHelp; 1.323 } (annotation) (annotation) (annotation)
Within a MenuBar, you will find one or more MenuItems or other MenuBars that go together to give the visual structure you saw in Figure 4.40. It is possible to create MenuItems inline in the code, which is exactly what we do at step [#2] where the first parameter to the addItem() method is a new MenuItem. Each MenuItem is bound to a segment of code, the second parameter to the addItem() method, which is executed when that menu item is clicked upon. We use something that is called the command pattern to describe the command that will be executed upon a menu item click. In practice, this means that we create a new instance of either the GWT Command class, or a subclass, which contains an execute() method where our code is stored. For the Dashboard, we decided to define a number of Command subclasses as inner classes to the Dashboard, since this best met our needs. An example of one of these classes is shown in Listing 4.16 (this is the command that is attached to the MenuItems defined in [#2] of Listing 4.15). Listing 4.16 Providing a subclass of the Command class to change the locale of the application to the English (default) locale. 1.324 class ChangeLocaleToEnglishCommand implements Command{ 1.325 public void execute(){ 1.326 Window.removeWindowCloseListener(dashboardWindowClostListener); 1.327 changeLocale(""); 1.328 } 1.329 }
Using the command pattern allows the GWT application to turn a request for some future functionality into an object that can be passed around the code, and the defined functionality can then be invoked at a later point in time. In the case of the menu items, GWT provides the plumbing so that when a menu item is clicked the associated Command’s execute() method is invoked. In this example, when the user clicks a menu item whose command is set to an instance of the ChangeLocaleToEnglishCommand class, then this execute() method is invoked, and a
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
WindowCloseListener is removed from the application before the changeLocale() method is called (otherwise we invoke both of the window closing events we set up in chapter 6 -- in the case of the Dashboard, these event handlers display two alert boxes, which is something we do not want to happen if we are just changing the locale). We can interrogate a MenuItem to find out what its parent menu is through its getParentMenu() method, as well as finding out if it opens a sub menu through its getSubMenu() method. Similarly you can set the sub menu of a MenuItem through the setSubMenu() method, but not its parent menu. In some applications we might need to change the command that is associated with a particular MenuItem (setCommand()) or even find out what that command is, which we can do using the getCommand() method. The final implementation aspect of a MenuItem we want to discuss relates to the text shown as the item itself. This text can either be treated as pure text or as HTML depending on whether a Boolean parameter is provided in the constructor of the MenuItem. As with any widget that can allow HTML to be set we should always take care that we do not expose the application up to script-based security issues. Creating a menu item using new MenuItem(String, true, Command) treats the String as HTML and using the MenuItem(String, Command) constructor treats it as plain text. A MenuBar widget allows us to define if its child menus should open automatically, or wait for the user to click on them to open. The setAutoOpen() method is used to achieve this, and in the Dashboard we do this in our onModuleLoad() method where we create the whole menu system using the code shown in Listing 4.17. Listing 4.17 Creating the Dashboard’s menu system. 1.330 MenuBar menuCreate = buildCreateMenu(); |#1 1.331 MenuBar menuHelp = buildHelpMenu(); |#1 1.332 menu.addItem(constants.HelpMenuName(),menuHelp); 1.333 menu.addItem(constants.CreateMenuName(),menuCreate); 1.334 menu.setAutoOpen(true); #3
|#2 |#2
(annotation) (annotation) (annotation)
At the top of the screen is the Dashboard’s menu system, as seen in Figure 4.41, which is involved a number of times in the code. The Help and Create menu bars are created as simple implementations for an Internet view (in the Dashboard class) and then overridden to provide a more functional intranet version (in the Dashboard_intranet class). The version, intranet or Internet, which is used, is chosen by using GWT user defined properties and setting the externalvisibility property in the Dashboard.html file – we describe all of this in chapter 15.
Figure 4.41 The Dashboard menu system, showing the four possible menu bars: the Help and Create menu bar are always present, the Bookmarks menu is loaded as XML from the server, and the application’s Option menu bar is shown when a component application gains focus.
The text for these two menu bars is created using GWT internationalization constants set up for a default, English, locale and an alternative Swedish locale. This internationalization approach, which we looked at briefly in chapter 3, is expanded upon in the chapter 15.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
We also make use of the two new MenuItem widgets we will create later in this chapter in our Dashboard’s menu system. When running in the intranet mode the Help menu bar provides the user the ability to turn on/off the confirmation requested when they delete a component application, and they do this through a TwoComponentMenuItem. In both modes, the user can change the locale through two TwoComponentMenuItems; one for each locale supported by our application. You can see the intranet view of the Help menu in action in Figure 4.42 where both the new widgets are in use.
Figure 4.42 Examining the Dashboard’s menu system and showing-off the two new MenuItem widgets we build in this chapter. The English and Swedish locale MenuItems are instances of the TwoComponentMenuItem that we build, and the Confirm Delete MenuItem is an instance of the ToggleMenuItem given in this chapter (it is shown in the ON state)
We also manipulate the menu bar in two further ways. First, we create a bookmark menu bar whose contents are loaded from an external XML file by using the GWT implementation of the HTTPRequest object – this is covered in chapter 12. Secondly, each component application can register an options menu when it gains focus, which is shown in the main menu when the application gains focus (we use a GWT Generator, see chapter 14, to automatically generate an About item in the options menu for each application which lists all the methods and fields included in the application). Figure 4.42 shows the options menu for the Google Search component application. These component applications also have some generic functional requirements placed on them. The final point to note about the MenuBar is that it implements the PopupListener interface, which allows functionality to be fired when the MenuBar is closed. If you wish to use different functionality when the MenuBar closes then you can override the existing class and implement your own onPopupClosed() method. We don’t use this functionality in the Dashboard, but that is how you would use it if you wished to do so.
Managing the View of Data Using Trees We have now nearly completed our look at most of the basic widgets GWT, there are two left – the first of these is the Tree widget. This widget provides our applications with a standard hierarchical tree widget comprising of TreeItem widgets.
Figure 4.43 The Dashboard’s Book application showing the Tree widget on the left-hand side. In chapter 6 we will see how events
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
are used when TreeItems are selected or expanded
A Tree is built in a similar manner to how we built a menu a little earlier on. In that case we added a number of MenuItems to a MenuBar, here we will be adding TreeItems to a Tree. The constructors for a TreeItem are very flexible and allow us to create an empty TreeItem or TreeItems from Strings or other Widgets (which could mean a standard widget or a composite widget). In Listing 4.18, which comes from the Dashboard’s Book application (org.gwtbook.client.ui.book.Book), we build as simple tree to represent the top-level structure of our book. Listing 4.18 Creating the Dashboard’s Book tree system. 1.335 private Tree buildTOC(){ 1.336 TreeItem chapter1 = new TreeItem("1: Introducing GWT"); |#1 1.337 chapter1.addItem("1.1: A Walk Through GWT"); |#1 1.338 chapter1.addItem("1.2: GWT Versus Other Solutions"); |#1 1.339 : 1.340 TreeItem chapter2 = new TreeItem("2: Exercising the GWT Tools"); |#2 1.341 chapter2.addItem("2.1: Setting up Dashboard Version 1"); |#2 1.342 : 1.343 TreeItem chapter3 = new TreeItem("3: Creating the Dashboard"); |#3 1.344 chapter3.addItem("3.1: Stage 2 - Developing the Application"); |#3 1.345 : 1.346 Tree t = new Tree(); |#4 1.347 t.addItem(chapter1); t.addItem(chapter2); t.addItem(chapter3); |#4 1.348 return t; 1.349 } (annotation) (annotation) (annotation) (annotation)
Unlike the menu system we do not add commands to MenuItems in order to implement functionality when they are clicked, or expanded, instead we implement a TreeListener. This requires us to implement two methods: an onTreeItemSelected() method, which is fired when a MenuItem is selected, and the onTreeItemStateChanged() method, which gets invoked if the state (opened or closed) of a TreeItem changes. For the Dashboard Book application we implement the TreeListener as shown in Listing 4.19. Listing 4.19 Adding event handling to the Dashboard’s Book tree to change the display text . 1.350 Tree theTree = buildTOC(); 1.351 theTree.addTreeListener(new TreeListener(){ 1.352 public void onTreeItemSelected(TreeItem item) { #1 1.353 changeText(item.getText()); #2 1.354 } 1.355 1.356 public void onTreeItemStateChanged(TreeItem item) { #3 1.357 if (item.getState()){ #4 1.358 currChapter.setText(item.getText()); #5 1.359 } else { 1.360 currChapter.setText(”----------”); 1.361 } 1.362 } 1.363 });
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
This listener will call the Dashboard application’s changeText() method ([#2]) to fill in the text box on the right of the Book application with some text when a TreeItem is selected ([#1]). When the state of a TreeItem changes, then the onTreeItemStateChanged() method ([#3]) is called. This method retrieves the state of the MenuItem that was changed ([#4]) and if this item is now open then it places the text of the time at the bottom of the widget, by retrieving it using the getText() method ([#5]) – otherwise it places some dashes as the text. A TreeItem comes with a host of helper methods that can be invoked to learn more about or change existing properties of the item. We can find out the child at a particular index (getChild(int index)), count the number of children (getChildCount()), or get the index of a particular child (getChildIndex(TreeItem)). Additionally, we can get the Tree a particular TreeItem is within (getTree()), find out if an items children are currently displayed (getState()), its parent item (getParentItem()) and whether it is currently selected (isSelected()). TreeItems may have a widget associated with them, though if it does it cannot directly have text associated, unless set up as a composite widget. Quite often we might associate a CheckBox, for example, with TreeItems. We associate a widget with a TreeItem through either the setWidget() method or by using the TreeItem(Widget) constructor.
Viewing Images Finally, for the basic widgets, if we wish to display an image in a GWT application then the Image widget is the basic widget that could be used. This widget allows images to be loaded and displayed, as we can see in Figure 4.44.
Figure 4.44 Looking at the Image widget in Action in the Dashboard’s Slideshow application
An interesting aspect of the Image widget is the ability to add a LoadListener to it so that a particular action can be performed when the image has completed loading (onLoad()), or another action performed if there is an error loading the image (onError()). Tip
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The LoadListener will only work if the Image is actually added to the browser page, usually via a panel, which is itself added to the browser. Just creating the Java object and adding a LoadListener is not enough to catch the events due to the way in which the GWT event handling system works (see chapter 6).
In Listing 4.20 we show the code from the Dashboard’s Slideshow component (org.gwt.client.ui.slideshow.Slideshow) that could be used to preload images into an application. The preloadImages object is actually a HorizontalPanel, which we have added to our application specifically for the future use of pre-loading images. Due to the way the GWT event mechanism works, we need to have our images loading into a component that is added to the browser page (if they are not, then there is no hook into the event mechanism, and the LoadListener is ignored). However, there is no requirement for the component to be visible, and so in [#1] we set it to be invisible to avoid an unsightly mess! Listing 4.20 Preloading Slideshow Images making use of a LoadListener (in the Dashboard example we have explicitly set it up so that one image is missing and therefore the onError code will also be executed) 1.364 Image[] testLoading = new Image[maxNumberImages]; 1.365 preloadImages.setVisible(false); #1 1.366 for(int loop=0;loop<maxNumberImages;loop++){ 1.367 testLoading[loop] = new Image(theImages[loop][1]); 1.368 testLoading[loop].addLoadListener(new LoadListener(){ #2 1.369 public void onError(Widget sender) { #3 1.370 Window.alert("Expected Error - onError() Method works."); 1.371 } 1.372 public void onLoad(Widget sender) { #4 1.373 Window.setTitle("Loaded Image: "+imageName); 1.374 } 1.375 }); 1.376 preloadImages.add(testLoading[loop]); 1.377 } (annotation) (annotation) (annotation) (annotation)
We add a new LoadListener at [#2] and define the onError() method to just put a JavaScript alert on the screen if there is an error ([#3]), or if the image loads, then we change the title bar of the browser window to say the image name using the Window.setTitle() method ([#4]). Once we have an Image object, it is possible to use the prefetch()or setUrl() methods to load a new image rather than creating new objects as we did in our example – but either way is valid and you will make you choice as to which way you set it up in your own applications. The only other thing about images that you should be aware of is that there are some issues when using a transparent PNG image over another image in Internet Explorer 5.5 and 6 where ugly backgrounds start to get applied, but we will build a new widget a little later in this chapter that overcomes these problems.
Displaying Text on the Application The Image widget we looked at just now was very useful for displaying pictures and images on the web browser. However, a lot of our applications will need to show text either as some sort of passive information or sometimes more actively or funkily. As we dig down into the hierarchy of widgets we find two widgets that allow us to present text on the screen -- the Label and HTML widgets.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Showing Text as a Label A Label widget contains arbitrary text, which is displayed exactly as written. This means that the Label created by the code new Label(“Hi there”) would appear on the browser page exactly as “Hi there”, i.e. the word “there” in not interpreted as HTML and not shown in bold text.
Figure 4.45 The Dashboard’s Server Status application showing GWT Label widgets in Action.
It is possible to control the horizontal alignment of labels, though by default the size of a Label is the size of the text it encloses. So right aligning, using the following command: 1.378 theLabel.setHorizontalAlignment(HorizontalAlignmentConstant.ALIGN_RIGHT)
would have little visible affect unless you use a style sheet (or less preferably the theLabel.setWidth() method) to set the width of the label to be longer than the text. The alignment that you can see visible within the Dashboard’s ServerStatus application (org.gwtbook.client.ui.serverstatus.ServerStatus), as can be seen in Figure 4.45 is achieved by aligning Labels in a Grid panel (see chapter 5). A Label may also word-wrap if the setWordWrap() method is called, which takes a Boolean variable which is set to true if the Label should word wrap and false otherwise. GWT allows us to add ClickListener and MouseListener to a standard Label widget offering the possibility to capture the user trying to interact with the Label. By default no action occurs, we have to add click or mouse listeners ourselves. In the EditabelLabel widget we build in chapter 7 (org.gwtbook.client.ui.EditableLabel) when a user clicks on the label we want to present a text box instead of the label that they can use to change the text of the label. We add the click listener as shown in Listing 4.21 at point [#1]. Listing 4.21 Adding a ClickListener to the GWT in Action EditabelLabel widget. 1.379 text = new Label(labelText); 1.380 text.setStyleName("editableLabel-label"); 1.381 text.addClickListener(new ClickListener() #1 1.382 { 1.383 public void onClick (Widget sender){ 1.384 changeTextLabel(); 1.385 } 1.386 }); (annotation)
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
What the ClickListener is to act in a similar way to the Command we used in the MenuItem widget. It registers an onClick() method that GWT will execute when the user of the application clicks on the Label. Once we have a Label it is possible to change the text programmatically using the setText() method (as well as getting the current text using the getText() method). Listing 4.22 Changing the Label text in the Clock application through the setText() method 1.387 Date d = new Date(); 1.388 if (! local) { 1.389 d = new Date(d.getTime() - (d.getTimezoneOffset() * 60 * 1000)); 1.390 } 1.391 clockLabel.setText(d.getHours() + #1 1.392 ":" + twoDigit(d.getMinutes()) + 1.393 ":" + twoDigit(d.getSeconds())); (annotation)
However, we can have a slightly more active text presenting widget if we wish – the HTML widget.
Making Text Active Using the HTML Widget If we want to provide some more funkiness to the presentation of text then the HTML widget could be exactly the component we are looking for. It acts in the same way as a Label widget, but importantly, it interprets any text as arbitrary HTML. Whereas in the Label the text “Hi there” was written as is, if we write the code new HTML(“Hi there”) it is displayed as “Hi there”. The HTML widget is also useful if we wish to provide a true hyperlink. When we looked at the Hyperlink widget we saw that we could present what looked like a hyperlink to the user, but when clicked it only changed the historical aspect of the application. If we use a HTML widget instead then we can provide proper links, as we do in the About application to link to the book’s web pages (see Figure 4.46) e.g.: 1.394 new HTML(“Manning”);
However, you must be careful with this widget as allowing arbitrary HTML can expose security issues for your application if maliciously constructed HTML is used. The other consideration you should have when looking at the HTML widget, is if the HTML Panel that we discuss in chapter 5 is more appropriate for your needs. Labels and HTML are a useful way of presenting information to a user, but there is another half of interacting with the user – capturing their input.
Grabbing the User’s Interaction Using Focus Widgets We can grab user input using one of a number of widgets within GWT, all of which fall under the FocusWidget hierarchy shown in Figure 4.35. These widgets inherit from the FocusWidget class, but before we look at those widgets in detail, we should understand that FocusWidget is not a widget in the normal sense – you cannot create an instance of it and display it on the browser. Yes, it does extend the Widget class, but its only purpose is to provide a class that handles focus, click and keyboard events that can be extended to provide the necessary common functionality across its children, so it is defined as an abstract class. Although an abstract class, FocusWidget provides the common functionality for all focus widgets, and this includes setting the widgets position in the tab index of the browser, through the setTabIndex() method. The tab index of a widget indicates the ordering it will be highlighted in when the user uses the tab key to move through web page elements – setting tab indexes is particularly useful to the user if widgets are being used in a form, and correspondingly, the standard form components are subclasses of FocusWidget. Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
We can also assign a special key combination, using the setAccessKey(char key) method, that when pressed gives the widget focus – or we can do this programmatically through the setFocus() method. In the EditableLabel widget we build in chapter 7, we change a Label widget into TextArea for editing. Once the widget swapping has taken place we want to make sure that the TextArea immediately has focus, and so call the setFocus() method on it (as can be seen at [#3] in Listing 4.23). Listing 4.23 Setting the TextArea widget to have the focus in the EditableLabel widget. 1.395 changeTextArea.setText(originalText); #1 1.396 changeTextArea.setVisible(true); #2 1.397 changeTextArea.setEnabled(true); #3 1.398 changeTextArea.setFocus(true); #4 (annotation) (annotation) (annotation) (annotation)
In GWT there are two widgets that extend FocusWidget directly to produce new widgets (Frame and ListBox) which we will look at next, as well as two additional abstract subclasses of focus widgets (TextBoxBase and ButtonBase), which in turn have more widgets as their children.
Framing an Area of Interest In the Dashboard we wanted to introduce some flexibility into the About component application (org.gwtbook.client.ui.about.About), and rather than hard code the about message we decided that it should be loaded from a separate HTML file. To support that functionality we use the Frame widget, into which we can easily load a new HTML page. We do this either through the constructor, such as new Frame(“resource-to-load”), or through the widget’s frame.setURL(“resource-to-load”) method.
Figure 4.46 Using the Frame widget in the Dashboard About dialog - the contents for this display are loaded from another HTML file into a frame.
Figure 4.46 shows the basic About component application in the Dashboard where the contents are loaded from another HTML file (the About.html if you have downloaded the example code). Since the widget represents an IFRAME DOM element, this content can easily be created from a static HTML file, JSP, Servlets, PHP script, .NET applications etc.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
A subclass of the Frame widget is the NamedFrame widget, which allows a name to be associated with it. These NamedFrames are typically used as targets for the FormPanel – and we will see this in action in chapter 12 when we deal with forms.
Listing Options The ListBox widget is used quite extensively in the Dashboard’s AddressBook application (org.gwtbook.client.ui.addressbook.AddressBook), as shown in Figure 4.47. It presents a list of choices to the user, either as a drop down or as a standard list-box. In the AddressBook application we use it in the list-box form for selecting names and in the drop down form for selecting countries.
Figure 4.47 The Dashboard AddressBook application uses both forms of the ListBox Widgit, as can be seen in this figure where names are stored in a normal format ListBox and countries are selected from a ListBox in its drop-down style.
To create a normal format ListBox we simply use the standard constructor new ListBox(), as shown in step [#1] of Listing 4.24. Listing 4.24 Creating a standard ListBox and adding a ChangeListener Dashboard’s AddressBook application. 1.399 addressLinks = new ListBox(); #1 1.400 addressLinks.setVisibleItemCount(10); #2 1.401 super.add(addressLinks); 1.402 addressLinks.addChangeListener(new ChangeListener(){ #3 1.403 public void onChange(Widget sender) { 1.404 int val = addressLinks.getSelectedIndex(); #4 1.405 String text = addressLinks.getItemText(val); #5 1.406 showAddress(text); 1.407 } 1.408 }); (annotation) (annotation) (annotation) (annotation) (annotation)
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
After creating the ListBox object ([#1]) we set the maximum number of visible items that will be shown ([#2]), this is effectively the height of the ListBox, through the setVisibleItemCount(number) method – for the AddressBook, we will display 10 items as standard, if there are more then the widget acquires as scroll bar. Having a ListBox provides a way of presenting choices to the user, to understand which item the user has selected requires us to add a ChangeListener to the ListBox ([#3]). When the selected value in the ListBox is changed the onChange() method is called. We use this change listener to show the address of the new person by first finding out the index of the option the user as selected (using the getSelectedIndex() method) and then passing that index value as the parameter to the getItemText() method to get the text representation of the selected option. This returned text is then used as a parameter in the AddressBook’s showAddress() method. The alternative view of ListBox as a drop down box of options is created by setting the visible item count to be 1. For variety in the AddressBook we subclass the ListBox widget in order to provide a country choice drop down widget, as shown in Listing 4.29. Listing 4.25 Creating a Drop-Down ListBox for the Dashboard’s AddressBook application. 1.409 private class CountryChoiceBox extends ListBox{ 1.410 public CountryChoiceBox(){ 1.411 super(); #1 1.412 this.setVisibleItemCount(1); #2 1.413 this.addItem("France"); |#3 1.414 this.addItem("Finland"); |#3 1.415 this.addItem("Norway"); |#3 1.416 this.addItem("Sweden"); |#3 1.417 this.addItem("United Kingdom"); |#3 1.418 this.addItem("United States of America"); |#3 1.419 } 1.420 } (annotation) (annotation) (annotation)
Selecting an option programmatically is performed using the setSelectedIndex(value) method. This method works best, and makes more sense, when the ListBox is in its drop down view and we use it in the Dashboard to ensure the correct country option is shown for each address. One thing we do not do in any of the Dashboard component applications is to allow the ListBox to have multiple selections. To do so is just a case of using the setMultipleSelect() method. If we do that, then we need to be a little more intelligent when getting the selected item since the getSelectedItem() method will only return the first selected item. To get all the selected items we need to iterate over all of the items, calling the isItemSelected() method for each item to determine if it is selected or not.
Clicking Your Way Through an Application Using a ListBox gives the user quite a wide range of choice, if we wish to be a little more careful over the range of choices offered to the user, then we should look at using one of the widgets that extends the ButtonBase widgets. These widgets include the Button itself, along with the RadioButton and CheckBox. ButtonBase provides the standard methods common to all its three children, for example setting and getting the text associated with them through the setText() and getText() methods or the similar methods for the HTML (setHTML() and getHTML()). Let’s look at all these three children and how we use them in the Dashboard. Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Pushing the Button The Button widget is one we have already seen in this chapter, and it is a push button in the style normally found on web pages. It is simply created by calling the constructor as follows: 1.421 new Button("7");
You can see the Dashboard’s calculator buttons all lined up in Figure 4.48 where we have added them to a Grid panel and also applied some CSS styling to change the normal gray button/black text to a light blue button/green text – although that might not be so visible in the book!.
Figure 4.48 A range of Button widgets set out to form the keypad of the Calculator Dashboard application
Normally we register a ClickListener with the Button in order to perform some functionality when the button is clicked. For the keys that represent the operation buttons, plus, etc in the Dashboard’s Calculator application (org.gwtbook.client.ui.calculator.Calculator) we add click listeners to the button: Listing 4.26 Adding a ClickListener to a Button 1.422 Button w = new Button(“+”); 1.423 w.addClickListener(new ClickListener(){ 1.424 public void onClick(Widget sender) { 1.425 performCalculation(op); 1.426 } 1.427 });
#1
(annotation)
When we press keyboard buttons whilst focused in the dashboard Calculator application we wish to perform the same functionality as if we had clicked on the buttons themselves. We achieve this by programmatically executing the clicking of a button by calling the button.click() method. Buttons are one subclass of the ButtonBase widget, but there are two more and we will look at the first of those next, the CheckBox.
Checking the Box The CheckBox widget implements the standard browser check box functionality. In normal pre-Ajax applications, this widget would usually be found in a Form and the values submitted to the server when the form is submitted. In the Ajax world, this is not a constraint anymore, and, yes, it is still found in forms, but can also function as a standalone component. If using this as standalone, it would not be surprising to see a ClickListener added to it, so that some segment of code could be executed when the element is check or unchecked. This is in fact what we do in chapter 5 for the Dashboard application.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 4.49 CheckBox components in action
Making the Choice The final widget we look at that is provided by the GWT distribution is the RadioButton widget, which provides the implementation of a set of mutually exclusive selections. As with the CheckBox it is common to see this either in a form or standing alone. Unlike CheckBox, we need a way to indicate which set of RadioButtons are included within a particular group – and we do this in the constructor, which has three forms, all of which require the definition of a group name. Listing 4.27 shows the construction of two groups of radio buttons. Listing 4.27 Creating two groups of a RadioButtons, and seeing that managing clicks is unfortunately not as efficient as it could be. 1.428 RadioButton rb1 = new RadioButton(“Grp1”); |#1 1.429 RadioButton rb2 = new RadioButton(“Grp1”, ”Second Choice”); |#1 1.430 RadioButton rb3 = new RadioButton(“Grp1”, ”Third Choice”,true); |#1 1.431 RadioButton rb10 = new RadioButton(“Grp2”,”Other”); |#2 1.432 RadioButton rb11 = new RadioButton(“Grp2”, ”Other 2”); |#2 1.433 1.434 rb1.addClickListener(new ClickListener(){ |#3 1.435 public void onClick(Widget sender) { |#3 1.436 performAction1(op); |#3 1.437 } |#3 1.438 }); |#3 1.439 rb2.addClickListener(new ClickListener(){ |#3 1.440 public void onClick(Widget sender) { |#3 1.441 performAction2(op); |#3 1.442 } |#3 1.443 }); |#3 (annotation) (annotation) (annotation)
1.444
If management of choices is not the purpose of your application and you need a freer way of letting the user express themselves, then the next set of widgets in the TextBoxBase family are probably the ones your are after.
Getting User Input Through Text Input Extending the FocusWidget is the TextBoxBase widget, which again is not a widget but an abstract class providing standard functionality for the PasswordTextBox, TextArea and TextBox widgets. TextBoxBase class provides the type of functionality you would expect for an editable text element on screen, for example canceling key presses, setting and getting the actual visible text, setting the cursor position and even the ability to trap a key press and replace it in the textbox with a different character. TextBoxBase provides a number of methods that one could reasonably expect a widget that extend it to implement. We can get all of the text in the widget with the getText() method or just get the text that the user has selected using the getSelectedText() method. In addition, we can get the length of the selected text using getSelectionLength() and the cursor position with getCursorPos()). It is also possible to “set” all these properties using the opposite methods, such as setText() and setCursorPos(), or select all of the text using the selectAll() method. Let’s have a look the three children widgets of TextBoxBase and how we’ve used them in the Dashboard.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Securing Password Entry This widget represents the standard browser text box that masks its input when the user types in values. It is very commonly used to allow application users to enter in passwords. We have a simple security application within the Dashboard, called the Login component (org.gwtbook.client.ui.login.Login), which uses the PasswordTextBox as shown in Figure 4.50.
Figure 4.50 PasswordTextBox in action in the Dashboard Security application to hide the characters being types for the password.
It is easily created using the PasswordTextBox() constructor, but includes no other methods except for those inherited from the TextBoxBase widget.
Entering Multiple Lines of Text The TextArea widget allows application users to enter text that has multiple lines. In the Dashboard we use this widget in our EditableLabel composite widget (see chapter 7) as shown in action in Figure 4.51.
Figure 4.51 Using the TextArea in the EditableLabel composite widget built for the Dashboard (we look at this in more detail in chapter 7)
Creating a TextArea is simple, case of using the TextArea() constructor, and from there it is possible to set the “width” and “height” of the TextArea through either CSS or through using the provided setCharacterWidth() and setVisibleLines() methods. Sometimes we do not need multiple lines of text, and in that case the TextBox widget is more appropriate.
Entering a Single Line of Text If we do not need multiple lines of text that can be edited, then the TextBox is probably a more appropriate widget. In the Dashboard we use this in a number of places, including in the Calculator Dashboard application where a user can enter numbers directly in the screen area as well as by clicking buttons (see Figure 4.52).
Figure 4.52 Seeing the TextBox widget in action in the Dashboard Calculator application.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
One aspect of TextBoxBase widgets we have not discussed yet is the ability to trap key presses, and either change or cancel them through using a KeyboardListener. In the calculator we do not want users to have the ability to enter anything except for numerical characters into the text box. We achieve this using the code given in Listing 4.28. Listing 4.28 Cancelling Key Presses in the Calculator display through a KeyboardListener – this is from the Calculator application to stop the user typing anything but numbers into the Calculator’s display 1.445 theDisplay.addKeyboardListener(new KeyboardListenerAdapter(){ #1 1.446 public void onKeyPress(Widget sender, char keyCode, int modifiers) { 1.447 if (!Character.isDigit(keyCode)){ #2 1.448 ((TextBox)sender).cancelKey(); #3 1.449 } 1.450 } 1.451 }); (annotation) (annotation) (annotation)
First we associate a KeyboardListenerAdapter ([#1]) to the text box (theDisplay) and then override its onKeyPress() method. In that method we check to see if the keyCode is a digit by using the Java Character.isDigit() method. If it is not a digit then we cancel the key press – which means that it does not reach the widget and therefore is never displayed on the browser. The syntax to do this might be a little strange, we have to cast the sender object into a TextBox object before we can call the cancelKey() method as the listener method only deals with pure Widget class objects. In the same way that we could alter some of the stylistic aspects of the TextArea either via the preferred approach of CSS or programmatically, we can do the same with TextBox. Programmatically we would use the setMaxLength() and setVisibleLength() methods. The first method defined the maximum number of characters that can be typed into the TextBox, the second the maximum number of characters that are visible at any one time. We hope this discussion has shown that there are many widgets available for you to use in your applications and indicated just where we will be using them in our future development in this book. However, there will be occasions where this set of simple widgets is not enough, or they do not fulfill the needs of your application. In these cases you will be looking at either creating your own widget or extending one of the existing ones.
Creating New Widgets Whilst there is quite an extensive set of widgets, it can be the case that you have not got the functionality you need for your application. This might be because the provided widgets are not quite right, or there is a basic widget missing. There are three ways of creating new widgets, as shown in Figure 4.53.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 4.53 The three options available for creating a new widget within GWT - in this section we look at following two methods: directly from the DOM and extending an existing widget (chapter 7 covers Composite in some detail).
In this section we will look at the first two approaches – creating new widgets directly from the DOM and by extending an existing widget. Remember that when we say a basic widget we are referring to a widget that represents some basic functionality a browser can provide – if we are thinking of something more complicated then we are discussing the third mentioned way: creating composite widgets. We discuss composites in detail in chapter 7 and these are used if you need to make a widget that is made up of two or more existing widgets. For the first approach we struggled to find an example to include in this book as the current GWT distribution has been comprehensive enough for our purposes, but earlier distributions were missing a FileUpload widget, so we built one then and will look at how we did that in this section. When it comes to extending existing widgets, we will build three new widgets used for the Dashboard. The first of these two widgets is the PNGImage widget, which extends the standard Image widget to cope with some IE5.5 and IE6 problems displaying PNG Image transparency (we will use this for the trash can icon to allow PNG images to be used there). Our second new widget is created by extending the MenuItem widget to create the Dashboard’s ToggleMenuItem widget. GWT provides a MenuItem widget that is used in Menus, but it consists just of text. The ToggleMenuItem will consist of text and one of two images indicating the toggled state of the menu item (you could imagine that these images consist of a check or a blank image indicating the functionality represented by the menu item is on or off). Let’s assume that we do really need to create a new widget, and first off we shall see how we do that through manipulation of the DOM.
Creating New Widgets By Manipulating the DOM The first approach we look into when we need to create a new widget is how we do so by manipulating the DOM directly (this is generally the way that the standard GWT widgets are constructed). We do not expect to have to do this very often since most widgets are already built, and when we do it should be reserved for those occasions where a basic browser widget is missing. When writing this book, we could not think of a widget that needed to be built this way, but we do recall that, when we started using GWT, there was a basic widget missing. This basic widget was called the FileUpload widget, for which there is now a standard GWT version, so we will look at how we built that.
Developing the FileUpload Widget We wanted our version of the FileUpload widget to look like exactly like the standard browser file upload shown in Figure 4.54.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 4.54 Standard browser file upload GWT widget (this is how we want our widget to appear)
The first step when creating a new widget is to decide which DOM element our widget will implement. In this case it is a simple choice, we want a FileUpload widget then we need to use a DOM element whose type attribute is set to file (this is the only way in a browser to create the widget shown in Figure 4.54). Having completed step one, we turn to step two where we decide where the new widget will sit within the hierarchy that already exists. The approach to this is sometimes a little bit of trial and error since the hierarchy is quite large, but taking a common sense approach we would like the widget to be notified of focus events and clicks, so we will choose for it to sit under the FocusWidget class. However, the widget is rather like a text box and a user might expect it to behave in such a way, so perhaps TextBoxBase is also a good candidate, but there are a number of issues with this. For a start, it is not possible to set the text of the underlying input DOM element due to valid security restrictions implemented by most browsers. Plus the notion of text alignment is not that valid for this type of widget. So we choose for this widget to extend FocusWidget as well as implementing the HasText interface (which indicates that there is text we can set and get in this widget). Our widget then becomes the code shown in Listing 4.29. Listing 4.29 Our attempt at a FileUpload widget that was included in the GWT Widget Library before an official FileUpload widget became available in the GWT distribution. 1.452 package org.gwtbook.client; 1.453 1.454 import com.google.gwt.user.client.DOM; 1.455 import com.google.gwt.user.client.ui.FocusWidget; 1.456 import com.google.gwt.user.client.ui.HasText; 1.457 1.458 public class FileUpload extends FocusWidget implements HasText{ #1 1.459 1.460 public FileUpload(String name){ #2 1.461 super(DOM.createElement("input")); 1.462 DOM.setAttribute(getElement(), "type", "file"); 1.463 DOM.setAttribute(getElement(), "name", name); 1.464 } 1.465 1.466 public String getText(){ #3 1.467 return DOM.getAttribute(getElement(), "value"); 1.468 } 1.469 1.470 public void setText(String text){ #3 1.471 throw new RuntimeException("Not possible to set Text"); 1.472 } 1.473 } (annotation) (annotation) (annotation)
At step [#1] in the code we state the name of our new widget and say that it will extend the FocusWidget class as we have just discussed so we will inherit a number of methods from there. We also say that this widget will implement the HasText interface, which means we will have to provide setText() and a getText() methods. Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Next, in [#2], we define the constructor of this widget. It is here that we introduce the direct DOM manipulation we have discussed in this section so far. Initially we need to create the actual DOM input element used and do so with the following code: 1.474 DOM.createElement("input")
This creates the element, which we then use in the FocusWidget’s constructor, which is accessed through the call to super() to establish the widget. This also sets the element field of the widget to be this new element. To complete the widget, we use some more DOM manipulation approaches to set the type of the element to be file and its name. 1.475 DOM.setAttribute(getElement(), "type", "file"); 1.476 DOM.setAttribute(getElement(), "name", name);
The getElement() method retrieves the widgets underlying element that was just set in the call to the FocusWidget’s constructor. We use this returned element in the two setAttribute() calls to set both the type and name attributes. After the constructor has completed the element then we have the following DOM element held as our Java object: 1.477
Here we can see the eventBits has been set although we have not asked for it. These values have come since we are extending the FocusWidget that has these values set. Since we have implemented the HasText interface we need to provide the two methods that this interface requires. The first of these two methods is the getText() method. For the FileUpload widget it makes sense that the value returned is the value of the filename selected by the user. To get this value we must perform some more DOM manipulation, and this time we use the getAttribute() method as follows to retrieve the value of the file input box ([#3]). 1.478 return DOM.getAttribute(getElement(), "value");
Another consequence of implementing the HasText interface is that we must provide the setText() method ([#4]). However the majority of browser place security restrictions on setting the text of a browser file upload input and so we choose to raise an exception if this method is called: 1.479 throw new RuntimeException("Not possible to set Text"); 1.480
We can now use our new widget as any other widget by creating a new instance of it in our Java code, such as: 1.481 FileUpload myUpload = new FileUpload();
As it turns out, our version wasn’t too far off the version that now ships with GWT, although that is a little bit fancier and more flexible! The steps then for creating a new widget by manipulating the DOM are shown in Table 4.17.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Table 4.17 Steps required when creating a new widget through DOM manipulation. Step Action Description 1 Identify the DOM Identify which DOM element(s) the widget will be wrapping 2 Locate on Hierarchy Locate whereabouts on the hierarchy this new widget should sit, should it be directly under the Widget class itself or can it take advantage of inheriting from an widget further down the hierarchy? 3 Identify Interfaces to Once it is established where in the hierarchy the new widget will sit, we implement should identify what interfaces (in addition to those it inherits) it should implement. We cover event interfaces later in chapter 6. 4 Implement Constructor Create the constructor using the appropriate DOM methods to create the element and add necessary attributes to the DOM element.
5
Implement the required methods
If the widget is to sink events that are not included in the hierarchy, then use the sinkEvents() method at this point to indicate this, and don’t forget to override the onBrowserEvent() method in the next step. There are three types of methods that we now need to implement. 1. 2.
3.
Those methods required by the interfaces that we have said in step 2 the widget will implement. Override the onBrowserEvent() method if we sink additional events not performed by other widgets above us in the hierarchy. Those methods we wish to create ourselves.
That is all there is to it to building a very simple new widget from scratch. As we have said, though, it is not that easy to see where this situation will arise anymore given the increasing completeness of the provided GWT widgets. Most of the time, new widgets will come from extending existing ones or creating composite widgets (as we will see in chapter 7). The next section looks at how we extend existing widgets, and we will build three concrete examples for use in the Dashboard later on.
Creating New Widgets By Extending Existing Widgets Instead of creating a widget directly from scratch there are occasions where you can create a new widget by extending an existing one where it almost has the functionality we need. In this section we will see three examples. The first is extending the Image widget to allow it to cope with displaying transparency within the IE5.5 and IE6 browsers, which we need for the trash icon in our Dashboard example. Secondly we will build a widget that extends the standard MenuItem widget by allowing us to display another widget after the menu text; we then extend that widget to create a new one that allows us to toggle the widget shown after the text between two different widgets upon clicking on the MenuItem. Both new widgets will follow the development steps shown in Table 4.18 which is comfortingly similar to the steps taken in developing a widget from scratch, except we no longer need to identify DOM elements and where in the hierarchy the widget will sit, since these are given by the widget we choose to extend. Table 4.18 Steps required when extending an existing widget to create a new one. Step Action Description 1 Identify the Functionality The first step requires us to define exactly what we wish the new widget to do. 2 Identify the Widget to With the functionality defined we identify which widget provides the closest Extend match to that functionality we require.
3
Identify Interfaces to implement
Sometimes we will find a widget that provides not enough functionality in one area but too much in another. This is easy to fix as we can override methods to provide both more and less functionality (sometimes this over-functionality is in the event handling area, in which case we can unsinkEvents() and/or alter the onBrowserEvent() methods) Are there any new interfaces that we need to implement for this new widget? We cover event interfaces later in chapter 6.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
4
Implement Constructor
5
Implement the required methods
Create the constructor using the appropriate calls to super() to invoke the parent widgets constructor and implement any additional code required for this new widget. If the widget is to sink events that are not included in the parent then use the sinkEvents() method at this point to indicate this. Similarly if you wish your new widget to not manage events that the parent widget does then use the unsinkEvents() method now. (and don’t forget to override the onBrowserEvent() method in the next step). There are three types of methods that we now need to implement. 4.
5.
6.
Those methods required by the interfaces that we have said in step 2 the widget will implement. Or we override those methods required by the parents interface that we wish to implement differently. Override the onBrowserEvent() method if we sink additional events not managed by the parent, or if we unsink events that are managed by the parent. Those methods we wish to create ourselves.
So let us now create the first of three new widgets we will be using in the Dashboard: the PNGImage Widget.
Developing the Dashboard’s PNGImage Widget In our Dashboard application we display a trash icon image that sits in the top right of the screen, and back in chapter 3 we implemented this as a simple Image widget. However, the Image widget that comes with GWT is not that successful in displaying a transparent PNG image over another when using Internet Explorer 5.5 or 6. This is exactly what we could end up doing in the Dashboard where the trash icon sits on top of a background image. Also, when we build our slider widget later in chapter 7 we will be sliding a thumbnail image over a background scale image, if we just use the standard Image widget for this and the programmer provides two PNG images, then it will look messy. The problem in IE5.5 is that the transparent PNG images acquires a halo effect (see Figure 4.55), and in IE6 a horrible background (see Figure 4.56).
Figure 4.55 PNG image transparency problem in IE 5.5 showing white “halo” around the star using the GWT Image widget.
Figure 4.56 PNG image transparency problem in IE 6 showing a gray background around the star using the GWT Image widget.
As a solution, the 3rd party GWT Widget Library includes a PNGImage widget that overcomes this problem and in this section we will walk through the steps we took to develop this widget (to avoid importing the GWT Widget Library into our project just yet, we will create the PNGImage widget under the org.gwtbook.client.ui package). Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Rather than try and create a completely new widget, we decided that PNGImage should behave as much as possible as the standard GWT Image widget, and therefore inheritance from the standard Image widget was the way forward. The widget is made from three separate classes, a main widget class and two implementation classes (one for Internet Explorer and one for other browsers). The full code for the implementation class of the PNGImage is shown in Listing 4.30. Listing 4.30 The main GWT PNGImage class – the complete widget needs two other classes (included in the download) which provide the browser specific implementations 1.482 package org.gwtbook.client.ui; 1.483 1.484 import org.gwtbook.client.ui.impl.PNGImageImpl; 1.485 import com.google.gwt.core.client.GWT; 1.486 import com.google.gwt.user.client.Event; 1.487 import com.google.gwt.user.client.ui.Image; 1.488 1.489 public class PNGImage extends Image #1 1.490 { 1.491 private PNGImageImpl impl; 1.492 1.493 public PNGImage (String url, int width, int height){ 1.494 impl = (PNGImageImpl) GWT.create(PNGImageImpl.class); #2 1.495 1.496 setElement(impl.createElement(url, width, height)); #3 1.497 sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS | #4 1.498 Event.ONLOAD | Event.ONERROR); 1.499 } 1.500 1.501 public String getUrl (){ #5 1.502 return impl.getUrl(this); 1.503 } 1.504 1.505 public void setUrl (String url){ #6 1.506 throw new RuntimeException("Not allowed for a PNG image"); 1.507 } 1.508 } (annotation) (annotation) (annotation) (annotation) (annotation) (annotation)
Having identified the functionality we required for the PNGImage to be exactly the same as the Image widget, except to be able to handle PNG transparency, we indicate that the definition of the PNGImage class just extends the Image widget ([#1]). Additionally, there are no new interfaces that we wish PNGImage to implement, since we wish PNGImage to behave the same as Image. At point [#2] in the code we are using code that is similar to that which we looked at in internationalization. Although in this case, instead of deferring picking the right Java class until we know the locale we are deferring, we defer the binding until we know the browser that the code is being executed within (this is known technically as deferred binding, which we cover in chapter 15 -- it is used here since we need to implement different code depending on whether the browser is IE5.5/IE6 or not). If the browser is not IE5.5 or 6, then the deferred binding will pick a class called PNGImageImpl if not then it will pick a class called PNGImageImplIE6. Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
In step [#3] we create the DOM object that represents the PNG image. This is performed by calling the create method on whichever version of the PNGImageImpl class has been selected by the compiler under the deferred binding rules. In the case where the browser is not IE5.5 or IE6 the create method contains the following code to create a standard DOM element and set some attributes. 1.509 1.510 1.511 1.512 1.513
Element result = DOM.createImg(); DOM.setAttribute(result, "src", url); DOM.setIntAttribute(result, "width", width); DOM.setIntAttribute(result, "height", height); return result;
Where the browser is IE5.5 or IE6 then the create method in the PNGImageImplIE6 class is used. First it determines if the image is a PNG image or not (by crudely looking at the extension). If the file is not a PNG image then the normal constructor is used creating a simple element. Otherwise we need to apply a specific IE AlphaImageLoader filter to the image. We do this by creating a element and then set the innerHTML of that DIV to include the PNGImage plus a specific Internet Explorer AlphaImageLoader filter, as follows: 1.514 1.515 Element div = DOM.createDiv(); 1.516 DOM.setInnerHTML(div, "<span style=\"display:inline-block;width:" + 1.517 width + "px;height:" + height + 1.518 "px;filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + 1.519 url + "', sizingMethod='scale')\">");
In all of the cases, a DOM element is returned back to the constructor in the PNGImage class. This element is then set as the element of the PNGImage widget. The full code for the implementation classes can be found in the electronic downloads for the book (http://www.manning.com/hanson). The events that this widget must sink are the same as the Image widget, so normally we would not need to include any new sinkEvents() call. However, in this case we do not call the Image constructor using (super()) and so must explicitly identify the events we will sink ([#4]). We do not need to override the onBrowserEvent() method since that is directly inherited from the parent Image widget. To finalize the widget we ensure that the methods from the Image class that we inherit are handled appropriately. The getURL() method ([#5]) of the Image class is no longer valid in this implementation so we override it and make it call the particular browser specific implementation we are using to return the correct value. Finally, for PNG images it is not possible to set the URL as it is for an Image widget. This is a case of where the parent widget provides more functionality than we can support in our extended widget. It is simply solved in this case by raising a RuntimeException if someone wishes to try and call this method ([#6]). Our corrected PNGImage will now display correctly in IE 5.5 and 6, as shown in Figure 4.57, where the halo effect from IE 5.5 and background from IE 6 are gone and therefore PNGImage displays a PNG image correctly in all browsers.
Figure 4.57 Corrected IE PNG handling with no "halo" or background using the new PNGImage widget, which extends the GWT Image widget.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
We create our new PNGImage object using the standard Java call to its constructor: 1.520 PNGImage myImage = new PNGImage(“star.png”,100,100);
Now we have the PNGImage class we can represent the trash icon in our Dashboard application and use safely when building our sliders – both of which we cover later in this book. Next we will look at developing another widget that we need for the Dashboard – the ToggleMenuItem widget.
Developing the Dashboard’s ToggleMenuItem Widget GWT provides a very useful widget called the MenuItem. One or more of these MenuItems are placed within a MenuBar to create the GWT menu system, and in chapter 9 we will build the Dashboard menu system. Useful as it is, it is not immediately obvious how to use MenuItem to display menu items that has two components, for example the menu item and a shortcut key, or a menu item followed by an image. In this section we will first build a TwoComponentMenuItem widget that allows us to place some text followed by another widget (which could be a Label representing a shortcut key combination, or an Image). The content of a MenuItem is generally text, though it does allow the option of that text being interpreted as HTML, and this is the hook that we will use to build this first of our new widgets. In the Dashboard we will use this widget to indicate the locales, as shown in Figure 4.58.
Figure 4.58 Example of the TwoComponent MenuItem in Action in the Dashboard used to display an animated GIF of a country flag related to the selectable locale.
Once we have a widget that allows us to place two components in a menu item we can then extend that to get to the widget we actually need for the Dashboard, which shows one of two images after the text depending on an internal state and when clicked the state and image change, as shown in Figure 4.59, this widget will be called the ToggleMenuItem.
Figure 4.59 Various states of the Toggled MenuItem in Action in the Dashboard example’s Help MenuBar (here we show the Confirm Delete functionality as either enabled or disabled).
For the Dashboard we use this widget to indicate to the user whether they will be asked to confirm that a component should be deleted or not. In the left hand side of Figure 4.59 we indicate that the user will be asked for
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
confirmation, if the user clicks on the MenuItem then the right hand side of Figure 4.59 is visible. Let’s build the TwoComponentMenuItem first, and then we can look at the ToggleMenuItem.
Building the TwoComponentMenuItem The concept of the TwoComponentMenuItem is to provide a widget that displays the text in a normal MenuItem follows by a second widget, as shown in Figure 4.60. This second widget could be a simple text showing which shortcut key combination can be used, for example Ctrl + O, to fire the command behind the menu or perhaps an image.
Figure 4.60 Schematic of the TwoComponentMenuItem
We implement TwoComponentMenuItem, as shown in Listing 4.31. Listing 4.31 The TwoComponentMenuItem class. 1.521 package org.gwtbook.client; 1.522 1.523 import com.google.gwt.user.client.Command; 1.524 import com.google.gwt.user.client.ui.HTML; 1.525 import com.google.gwt.user.client.ui.HorizontalPanel; 1.526 import com.google.gwt.user.client.ui.Label; 1.527 import com.google.gwt.user.client.ui.MenuItem; 1.528 import com.google.gwt.user.client.ui.Widget; 1.529 1.530 public class TwoComponentMenuItem extends MenuItem{ #1 1.531 1.532 protected HorizontalPanel theMenuItem = new HorizontalPanel(); #2 1.533 1.534 public TwoComponentMenuItem(String theText, 1.535 Widget secondComponent, 1.536 Command theCommand){ 1.537 super(theText,true,theCommand); #3 1.538 theMenuItem.add(new Label(theText+" ")); #4 1.539 theMenuItem.add(secondComponent); #4 1.540 this.setSecondComponent(secondComponent); #5 1.541 } 1.542 1.543 public void setSecondComponent(Widget newComponent){ #6 1.544 theMenuItem.remove(1); 1.545 theMenuItem.add(newComponent); 1.546 SimplePanel dummyContainer = new SimplePanel(); 1.547 dummyContainer.add(theMenuItem); 1.548 String test = DOM.getInnerHTML(dummyContainer.getElement()); 1.549 this.setHTML(test); 1.550 } 1.551 1.552 public void setFirstComponent(String newComponent){ #7
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The TwoComponentMenuItem is extending the MenuItem class ([#1]) so everything there is available to us in the class and we can use this class everywhere we would use a MenuItem. To provide the ability to handle two components, we create a HorizontalPanel ([#2]) that will hold the two components, the first element will be a Label that contains the text and the second one will be whatever widget is passed in to the class. In the constructor of the widget we first call the MenuItem constructor ([#3]) to make sure we are creating a valid MenuItem, but let it know that we will be treating the text component as a piece of HTML text. Next we create a Label ([#4]) using the text passed in for the menu and then add it to the HorizontalPanel as well as adding the second component directly to the same HorizontalPanel. Then we call our setSecondComponent() method – we had to add the second component prior to calling the method to set it; otherwise the method would break since it first removes the second component as we see next. To create the view of the two components on screen we perform some GWT and DOM magic ([#6]). First we remove any existing second component from the HorizontalPanel, and then we add the new second component. So far, this is simple GWT. Next we want to get access to the HTML of the HorizontalPanel and we do so by juggling with the DOM. We put the HorizontalPanel into a SimplePanel and then retrieve the inner HTML of the SimplePanel. A similar process is shown to set the first element in [#7], but note that the code now removes the first component (index 0), which makes the second component become the first. The implication of this is that when adding a new first component we must insert it before the existing first component and so use the insert() method compared to the add() method. Finalizing the component is a case of overriding the setText() method since we need to put any text within the HorizontalPanel instead of straight into the widget. Changing the text is a case of really setting a new first element and so the override method shown at [#8] does just that. As with the PNGImage and FileUpload we create our new widget using its constructor: 1.565 TwoComponentMenuItem myMenuItem = 1.566 new TwoComponentMenuItem(“MenuItem”, 1.567 new Image(“myMenuImage.jpg”), 1.568 new Command(){ 1.569 public void execute(){ 1.570 Window.alert(“Menu Clicked”); 1.571 }
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.572
});
With the TwoComponentMenuItem in place, we can look at specializing it by extending the class to create our Dashboard’s ToggleMenuItem widget.
Building the ToggleMenuItem With the TwoComponentMenuItem widget built, we can extend it to provide the ToggleMenuItem as shown in Listing 4.32. The purpose of this widget is to show one of two different images depending upon an internal status. For the Dashboard, we wish to show a check image if a certain piece of functionality is enabled and a cross image if it is not (though you could easily envisage this as showing a check and no image as the disabled view in other scenarios). Listing 4.32 The ToggleMenuItem class. This widget allows us to show one of two different widgets after nd a MenuItem depending upon the widgets status. We can also toggle the status which changes the 2 nd widget shown (normally the widgets chosen for the 2 place would be Images, as used in the Dashboard) 1.573 package org.gwtbook.client; 1.574 1.575 import com.google.gwt.user.client.Command; 1.576 import com.google.gwt.user.client.ui.Image; 1.577 1.578 public class ToggleMenuItem extends TwoComponentMenuItem{ #1 1.579 1.580 public class ToggleMenuItemStates{ |#2 1.581 static public final boolean ON = true; |#2 1.582 static public final boolean OFF = false; |#2 1.583 } 1.584 1.585 Widget[] states; #3 1.586 boolean state = true; #4 1.587 1.588 public ToggleMenuItem(String theText, 1.589 Widget onState, 1.590 Widget offState, 1.591 Command command){ 1.592 super(theText, onState, command); #5 1.593 states = new Widget[2]; |#6 1.594 states[0] = onState; |#6 1.595 states[1] = offState; |#6 1.596 } 1.597 1.598 public void toggle(){ #7 1.599 setSecondComponent(states[state?1:0]); 1.600 state = !state; 1.601 } 1.602 1.603 public boolean getState(){ #8 1.604 return state; 1.605 } 1.606 } (annotation) (annotation) (annotation)
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
For the ToggleMenuItem we are extending the TwoComponentMenuItem, so in the definition of the class we say that ([#1]). Through the inheritance hierarchy this widget inherits MenuItem and so can be used anywhere a MenuItem could. The second component of this widget will be one of two different widgets (in the Dashboard we use Images), which we hold in the array of Widgets we create at ([#3]), and to determine which widget we will show by default, we set the state value of this ToggleMenuItem widget to be true ([#4]). Creating the widget is performed by the constructor, which first calls the parent constructor to ensure that this ToggleMenuItem will act in a similar manner to a TwoComponentMenuItem ([#5]) as well as setting the second component as the image representing the first state. It then creates an array and stores the two Widgets passed in as parameters as the alternative two states widgets ([#6]). We have talked a little so far on states in this widget, and at [#2] we define an inner class that contains the available states (an ON and an OFF state). Toggling the state of the menu item is performed through the toggle() method, defined as [#7], which it is expected will be called by the user’s code. This method simply sets the second component of the ToggleMenuItem widget to be the widget representing the state we are currently not in, and then changes the internal representation of the state. The current state of the widget can be returned by calling the getState() method defined at [#8]. We could continue our hierarchy of MenuItems, building many different types if we so wished (to build the many different types of menu items you are familiar with from desktop applications), but we will leave it with just these two. With this class developed, we now have the set of basic widgets that we need to develop our Dashboard example, but we still need to look at panels and events.
Summary That concludes the first part of our journey through the GWT basics, covering Widgets. Hopefully you have seen that there are quite a few widgets provided as standard by GWT, but, where the functionality you need is not present, then it is relatively simple to create your own widgets. You can do this either by starting from scratch or by extending an existing widget (where we need more complicated functionality, which is better implemented as two or more simple widgets put together, then we should consider composite widgets, which are discussed in chapter 7). The fact that widgets have two alternative views, the Java object and the DOM representation, can be a little strange at first. However, 99% of the time you only have to consider the Java object view. One of the few times you need to think of the widget in its DOM view is if you are creating new widgets. In fact, it can be dangerous to rely on the DOM view of the widget, as there is no guarantee that future versions of GWT will use the same DOM construct for a particular widget. The second half of the chapter saw us constructing three widgets that will be used in our running Dashboard application – the PNGImage together with the TwoComponent and Toggle MenuItems -- and discussing how they were created. To keep going with our Dashboard, the next area we need to look at is how we visually arrange widgets in our application, and we do that in chapter 5 by using panels.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
5 Working with Panels If widgets can be said to provide user interface functionality, then panels provide the application’s layout and visual organization. Think again of our new Plasma television we bought in Chapter 4 – thankfully the manufacturer did provide a remote control with lots of buttons (widgets) on it, but what if the labels did not match the buttons, and the buttons themselves were all over the place? Then it would not be that useful a remote control!! Panels allow us to control the visual structure (ensure the right labels are with the right buttons, and they are all in the right place) of our GWT applications. Definition Panels provide the means to visually organize and lay out a GWT application.
In this chapter we define what panels are, and we then take a tour looking at the panels that are provided in the standard GWT distribution and how we will use them in the Dashboard. As we move into the second part of this chapter, we shall begin building our own panels. Like widgets, there will probably not be that many times where you need to build a panel from scratch, as there is quite a range of panels pre-built, but we will look at the steps that are involved in doing so. We will also look at creating new panels by extending existing panels, which is a much more common situation. We end the chapter by building the first version of the DashboardPanel into which all of the component Dashboard applications (such as the Calculator, Server Status, and Clock) we build as part of the larger Dashboard example will all sit. Let’s get straight down to business and start by defining GWT Panels.
What is a Panel? Panels are the building blocks of a GWT application’s structure, and sometimes functional, make up. They allow us to position widgets where we need them to make the application’s functionality make sense – Labels next to the right Buttons, making sure particular components are hidden until needed, etc. This notion starts off with the RootPanel, which is a direct interface into the browser’s current web page. We saw this in action in Chapter 3 where we looked at a) how the GWT default application inserted a Button and a Label into two named table cells on the screen, and b) when we discussed how Dashboard components will be inserted into the Dashboard example. But RootPanel is a special case since it captures the browser page directly; all other panels have no knowledge of the browser until they are added through methods in the RootPanel class. Outside of the RootPanel, panels exist to be the containers into which widgets (and often other panels) are placed, sometimes recursively, to give the necessary structure to your application. GWT provides a number of different panels ranging from a simple FlowPanel, where components are laid out flowing from top left to bottom right, through to more complicated panels, such as DeckPanel where child widgets are held like a deck of cards with only one widget being visible at a time. There is almost a panel for your every need! Panels, just like the widgets we looked at in the last chapter, have a dual existence in GWT – they can be thought of as Java objects and DOM elements.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Using Panels as Java Objects Just like the widgets we saw in chapter 4, our every day programming view of Panels will also be their Java object view. For example, when we create a FlowPanel (a panel that lets any widgets added to it flow within the constraints of the panel size), we use the following Java code: 1.607 1.608 FlowPanel theFlowPanel = new FlowPanel();
This code creates a new FlowPanel Java object, which we can then execute a number of class methods on, some of which are listed in Table 4.16. Table 5.19 Applying some of the Java Button class methods to the Java Button object. Code Description 1.609 theFlowPanel.setStyleName(“buttonStyle”); Set the Cascading Style Sheet (CSS) style name for the FlowPanel – a corresponding entry should be found in the CSS style sheet attached to the web document.
1.610 theFlowPanel.add(new Label(”Test”));
Add a new Label widget to the FlowPanel.
1.611 theFlowPanel.iterator();
Retrieve a Java iterator object to allow us to iterate over all of the Widgets that have been added to the FlowPanel.
All panels are created in such a way, even the more complicated VerticalPanel, which is a panel that places widgets on top of each other in a column. This is created in Java code as simply as: 1.612 VerticalPanel theVerticalPanel = new VerticalPanel();
The Java object view of panels allows us to treat the panel as a pure Java object, and, while methods such as add() allow us a peek of how the panel behaves and might appear on the browser, they do not let us fully understand how they behave. For that we need to consider the Panel in its alternative view, as a DOM element.
Considering Panels as DOM Elements The DOM Element view of panels is the view that is displayed in the Web browser. When a panel is created using the Java constructor, that constructor is responsible for creating the necessary DOM element for the panel. For example, the FlowPanel we looked at in the previous section has a Java constructor defined as follows: 1.613 public FlowPanel() { 1.614 setElement(DOM.createDiv()); 1.615 }
Therefore, when we create a FlowPanel Java object, we are also creating a DOM element. If we were to look directly at the DOM element of this FlowPanel, we would see the following: 1.616 1.617
Not the most exciting of Panels, but it works as described since, if we were to add two other FlowPanels to it, we would see they get added in directly inside it with no direction on structure, i.e., they flow:
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Listing 5.33 DOM element result of adding two FlowPanels to an existing FlowPanel. 1.618 |#1 1.619 |#1 #2 1.620 |#1 #3 1.621 |#1 (Annotation) (Annotation) (Annotation)
[#2] and [#3] represent the two new FlowPanels added to the original FlowPanel. Unlike widgets, where functionality of the component was pretty much determined by the type of widget – a Button is a button, an Image an image – panels prove a little harder to understand since they are a little more abstract in concept. If we were just to look at the DOM element from the FlowPanel, it would be hard to tell what type of panel it was. The defining property of a panel is really how it deals with other user interface components that are added to it. We can see this easier if we take a quick look at the VerticalPanel. It has a DOM element representation of: 1.622
1.623 1.624 1.625
If we add the same two FlowPanels we added to the FlowPanel just now to this VerticalPanel, then the DOM element becomes: Listing 5.34 DOM element resulting from adding two FlowPanels to a VerticalPanel. 1.626
When we add a new widget to the VerticalPanel, its add() method creates a new
DOM element into which it places the new widget, and then this new DOM element is added at the end of the existing table. We can see this in Listing 5.34 where the two FlowPanels are captured within separate cells in the table structure ([#1] and [#2]), and each cell is held on a separate row.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Each panel defines its own add() and insert() methods, amongst others, and it is these that give the panel its layout properties. Each panel can have widgets or other panels added to it, and this can be done recursively to build the structure you need for your application, such that a panel may contain other panels that contain widgets. At the heart of the whole GWT system is the RootPanel – the panel which links the application to the browser Window. It provides references to particular DOM elements on the page, rather than being a panel itself. There are two ways to use the object, the first is to pass a DOM name that you know exists, e.g. RootPanel.get(“MyDomArea”), in which case a DOM Element reference to MyDomArea will be returned. The second approach is just to call RootPanel.get() – the lack of parameters means that the result will be a reference to the page’s Body element. As we mentioned previously for widgets, we should not rely on the fact that a particular DOM element is used for implementing any particular panel as it is not guaranteed that it will always remain that way in future GWT releases. The only exception here is that there are three panels that are set up specifically to be HTML tables and used in that way, but we will come to those when we look at the different classes of panels in the next section.
The Standard GWT Panels The standard GWT distribution comes with a wide range of panels that cover many different circumstances. To make sense of the panels, it is useful to split them up into four different families, shown in Table 5.20, which correspond to the breakdown shown in the class hierarchy in Figure 5.61. Table 5.20 Definition of the four different Panel family types in GWT. Panel Family Description Simple Panels These panels are based on a div element and may contain only one Widget. (though this is a semantic distinction rather than a practical one since this one widget may be a panel containing other widgets/panels, or a composite widget) Complex Complex panels allow any number of widgets and are usually based on a div or table elements. However, when you use Panels these widgets you should not rely on knowing their internal structure since that could change in the future. HTML Table These types of panels are based on HTML tables and should be expected to behave in that manner. Panels Composite The final family is the composite panel family, which are those panels that are a combination of other panels put together to Panels provide some new functionality.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 5.61 Class hierarchy of panels provided with the GWT framework showing the different types of panels in each of the four main panel families.
We can see in this hierarchy that all panels, within GWT, subclass the Panel abstract class. This Panel class actually subclasses the Widget class (and hence UIObject) so in a sense a Panel is a Widget. As such we can make Panels sink events and override the onBrowserEvent() method as we will see we can for Widgets in the next chapter. The defining difference between panels and widgets comes in the panel’s implementation of the HasWidgets interface. This interface requires panels to provide the ability to add and remove widgets from the component that implements it. It is, therefore, possible to add widgets and panels to a panel, but it is not possible to add panels or widgets to a widget (since it does not implement the HasWidgets interface). Additionally, when a panel is added to the browser, the panel’s onAttach() method is called; this iterates over all the widgets the panel contains, calling their onAttach() methods (when we just add a widget to the browser on its own, the same mechanism occurs since we are adding the widget to the RootPanel; it is just less obvious). In the next section we take a quick tour through the panels that come as standard with GWT and look at how they are used in the Dashboard application. Our starting point for exploring the panels will be the SimplePanel family.
Interacting with Simple Panels The SimplePanel family provides panels that restrict the number of widgets that can be added to them to one. If we strip it down to its bare bones we can see that the SimplePanel class extends the abstract Panel class and has a single variable for holding details of its one widget. We can see from Figure 5.61 that there are the following four SimplePanel panels:
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
PopupPanel DialogBox FormPanel FocusPanel ScrollPanel Let’s take a quick look at each of them, discussing any particular peculiarities or points of interest, and where we use it in our Dashboard application.
Popping Up Displays A PopupPanel “pops” up over other widgets on your page – you could use it for example in implementing tooltip functionality (which is exactly what we do in the Dashboard’s ServerStatus component application, as can be seen in Figure 5.62).
Figure 5.62 Displaying a Dashboard ToolTip when clicking on a Server Status attribute name (we created the ToolTip by subclassing the PopupPanel).
Google are particularly proud of this component since it even pops up over list boxes, which is a normal a problem for this type of functionality. The panel can be set to auto hide if the user clicks outside of it, and you should be aware that it previews browser events, so if a particular widget you are developing does not get the events you are expecting, then make sure that there are no unexpectedly open PopupPanels that are grabbing and deleting that event! This caused a particular problem with our ToolTip class, but we discuss that in more detail in chapter 6. A subclass of the PopupPanel is the DialogBox panel, which our Dashboard component applications heavily use.
Communicating in a Dialog The DialogBox panel is a particular instance of a PopupPanel that can contain a single widget and a caption at the top – which can be readily seen in Figure 5.63.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 5.63 GWT DialogBox is used as the basis for the panels in which the component Dashboard applications sit within
The user can drag the panel around by clicking on the caption and dragging the mouse, although the panel does not contain any close button. When you look at Figure 5.63, you might start questioning whether this really is a simple panel since there appears to be clearly more than one widget in the dialog box. While it is true that DialogBox appears to have many widgets associated to it, it does not (really). What we get into here is an argument of semantics that many politicians would be proud of -- since a panel itself is a widget, then, if we add any type of panel to a simple panel, we are still adding only one widget (the panel we add might be a more complicated panel with many widgets added to it, but we still are adding only one “widget” to the dialog box). To create a DialogBox, we use one of the two constructors that GWT provides. The first takes no parameters, and the second takes a boolean parameter to specify the auto-hide capability (true to hide the dialog if a user clicks somewhere else, false otherwise). Once a DialogBox is created, we need to set the two component parts – the caption and the widget. The former is set using the setText() or setHTML() methods, whereas the later is set using the setWidget() method. Generally you would create a DialogBox as shown in Listing 5.35. Listing 5.35 Creating a Dialog Using the DialogBox Class 1.640 final DialogBox theDialog = new DialogBox(true); #1 1.641 theDialog.setText("A Dialog"); #2 1.642 VerticalPanel thePanel = new VerticalPanel(); |#3 1.643 Label theMessage = new Label(”Some Dialog Text”); |#3 1.644 Button okButton = new Button("OK"); |#3 1.645 okButton.addClickListener(new ClickListener() { |#3 1.646 public void onClick(Widget sender) { |#3 1.647 theDialog.hide(); |#3 1.648 } |#3 1.649 }); |#3 1.650 thePanel.add(theMessage); |#3 1.651 thePanel.add(okButton); |#3 1.652 theDialog.setWidget(thePanel); #4 (Annotation) (Annotation) (Annotation) (Annotation)
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
We will create our own subclass of DialogBox later in this chapter for use in our Dashboard example. Let’s look at another panel we use in our Dashboard, though as part of an application.
Forming an Opinion The FormPanel was introduced into GWT in version 1.1 to allow the management of Forms and is probably the most functionally active panel in the set. We can set all sorts of attributes, such as the encoding of the form, the method of sending to the server (post or get) and the action (what happens when the form is submitted). In chapter 13 we show a complete example of this panel together with how it is submitted to the server and how we handle the events related to submission and return of results.
Figure 5.64 Example of the FormPanel in Action.
While a simple panel, it also performs the same trick we have just seen in DialogBox in that the form components – the text boxes, labels, radio buttons, etc. – need to be added to a particular panel, and then that panel is added to the FormPanel. Submission of a form occurs programmatically by calling the FormPanel’s submit() method as the result of some other user interaction – most usually by adding a ClickListener to a submit Button added to the form. We look a little more at ClickListeners in our next chapter on events, but there is one panel that manages many different types of events, the FocusPanel.
Focusing User Actions A FocusPanel acts in very much a similar way to the FocusWidget that we saw in the previous chapter. It lets its contents become focusable as well as adds the ability to capture mouse and keyboard events that occur anywhere on the panel.
Figure 5.65 The Calculator Dashboard application we will build in this book places its keypad inside a focus panel, which enables us to capture physical computer key presses and pretend the user has mouse-clicked on one of the calculator's buttons.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Unlike the FocusWidget, where we could not create instances of it, we can create instances of FocusPanel. Although, for it to have any real function, we need to place some content within it (while you could just use it on its own, giving it some dimensions through CSS or the setWidth()/Height() methods, we’re not too sure what purpose an empty focus area is – though that is not to say at some point one will be needed). In the Dashboard’s Calculator application, we place our calculator keys inside a FocusPanel so that we can capture physical keyboard presses. Listing 5.36 Using a FocusPanel in the Dashboard’s Calculator by adding a set of buttons and a KeyboardListener to it. 1.653 FocusPanel theKeypad = new FocusPanel(); 1.654 : 1.655 theKeyPad.add(theKeys); #1 1.656 theKeyPad.addKeyboardListener(new KeyboardListenerAdapter(){ #2 1.657 public void onKeyPress(Widget sender, char keyCode, int modifiers) { #3 1.658 : 1.659 } 1.660 }); 1.661 theKeyPad.setTabIndex(0); #4 1.662 theKeyPad.setAccessKey(’+’); (Annotation) (Annotation) (Annotation) (Annotation) (Annotation)
Now, by adding the calculator’s grid of buttons, theKeys, to theKeyPad FocusPanel ([#1]) then we can add Click, Focus, Mouse and Keyboard Listeners. We chose to add a KeyboardListener ([#2]) to process any keyboard key presses whilst the panel has focus. In this way, the user can use the physical keyboard to enter numbers and operations in the calculator as if they were using clicking directly on the buttons. The FocusPanel, as with the FocusWidget, allows us to set its position in the tab index of the browser through the setTabIndex() method ([#4]). If the tab index is shared between more than one FocusWidgets or FocusPanels then the ordering given by the browser when the user presses the tab key is arbitrary, however if the FocusPanel is in sequence with the FocusWidget then that ordering is upheld. Finally, we can set-up a special access key ([#5]) for the focus area such that if the user presses the browser’s modifier key and the special key then the widget automatically receives focus. In Firefox and Internet Explorer the special modifier key is the Alt key, in Opera it is the Shift-Escape key combination. Or we can set the focus programmatically by using the setFocus() method with a Boolean variable as the parameter. As well as focusing on a Panel, the user may wish to scroll up and down (or even across) a panel.
Scrolling Through Information A ScrollPanel allows a user to scroll around a widget that is larger than the dimensions of the ScrollPanel. By default the panel will always show both scroll-bars though it is possible to set it such that they are shown only when necessary. It is also the panel that we have the most problems with as unless you set particular dimensions for it (either preferably through CSS or otherwise programmatically using the setWidth()/setHeight() methods) then it quite often expands to the size of the widget you place within it – and therefore you get no scrolling.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
It is possible to set a particular widget to always be visible within the scroll panel, and we use that functionality in the Dashboard’s Address Book application to quickly show the relevant address details when a name is selected from the list of names.
Figure 5.66 The Dashboard’s Address Book application uses a ScrollPanel when displaying a list of addresses
Our AddressBook ScrollPanel uses the code shown in Listing 5.37 to create and use a ScrollPanel. Listing 5.37 Using a ScrollPanel in the Dashboard’s AddressBook. 1.663 visibleAddresses = new ScrollPanel(); #1 1.664 visibleAddresses.setStyleName("addressBook-visibleAddress"); #2 1.665 visibleAddresses.add(theAddresses); #3 1.666 visibleAddresses.addScrollListener(new ScrollListener(){ #4 1.667 public void onScroll(Widget widget, int scrollLeft, int scrollTop) { 1.668 int widgetCount = theAddresses.getWidgetCount(); #5 1.669 int panelHeight = theAddresses.getOffsetHeight(); 1.670 int widgetHeight = panelHeight/widgetCount; 1.671 int widgetNumber = scrollTop/widgetHeight; 1.672 addressLinks.setSelectedIndex(widgetNumber); 1.673 } 1.674 }); (Annotation) (Annotation) (Annotation) (Annotation) (Annotation)
If the panel/widget added to the ScrollPanel has dimensions less than the ScrollPanel then by default no scroll bars will be displayed. Scroll bars are added automatically once dimensions of the component added to the ScrollPanel grow out of the ScrollPanel’s dimensions. To have scroll bars always present then we use the setAlwaysShowScrollBars() method on the panel. We can programmatically set the position of the scroll bars for the panel using two methods the panel provides. Vertical position can be set using the setScrollPosition() method and the horizontal scroll bar position can be altered with the setHorizontalPosition() method. Alternatively, we can set the position of the bars relative to a widget that we want visible using the ensureVisible() method. This last method is used in the AddressBook to show the address selected by the user using the code in Listing 5.38. Listing 5.38 Setting the Scroll position to an element using the ensureVisible() method of the ScrollPanel.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
These panels were all restricted to only adding one widget – although while that was mainly a semantic restriction, should we wish to use a panel that explicitly allows the addition of multiple widgets, then you need to consider either a Complex or HTMLTable panel family panel. In the next section we look at the Complex panel family and after that the HTMLTable one.
Considering More Complex Panels The ComplexPanel family of panels allows a user to add one or more widgets to them, which makes them more advance compared to the SimplePanel family we have just looked at which only allowed one widget per panel. Presentational aspects of complex panels are delegated to the subclasses, which must decide what DOM elements they will use to provide their visual behavior. Some of the subclasses use div elements, for example the DeckPanel, and others use table elements, for example the HorizontalPanel and VerticalPanel. This difference is driven by the ease as to which visual behavior can be implemented. In the case of the DeckPanel only one widget is visible at any given time, as such it is easier to place each widget in separate div elements and then manipulate the visibility style property of the individual divs. Horizontal and vertical panels are easier to construct as tables in HTML – but we should be clear here that you should not rely on the fact that they are implemented as tables as that could change in future releases (if you really need to rely on a table structure, then you should use the HTMLTable family of panels we discuss later). A complex panel allows us to retrieve an iterator to access its child widgets (to get the iterator we call the iterator() method on the panel). If wish to get a list of a panels child widgets instead of an iterator, then we would use the getChildren() method, which returns a WidgetCollection object. Adding widgets is performed through the add() method and an insert() method is provided which allows new widgets to be inserted before a particular index in the widget collection. There are nine complex panels provided in GWT: AbsolutePanel RootPanel DeckPanel StackPanel DockPanel FlowPanel HorizontalPanel VerticalPanel HTMLPanel Let’s now take a quick look at the salient points of each of them in turn.
Positioning Components Absolutely In an absolute panel widgets can be positioned wherever the programmer wishes. Widgets are added by specifying the particular x and y co-ordinates at which they should appear in the panel. This is achieved by using either the Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
constructor’s add(Widget, x, y) method or if the widget is already added then the position can be changed through the setPosition(Widget,x,y) method. It is certainly possible that widgets can overlap if they are so arranged, but it is not possible to directly alter the zindex of an AbsolutePanel with the standard implementation. If you are interested in finding the position of a widget in an AbsolutePanel, we can use the getWidgetTop() and getWidgetLeft() methods, which retrieve values of the top and left of the widget relative to the panel it is sat within. One thing to really note about an absolute panel is that it will not resize itself to make room for widgets that you have added; all the other panels generally do. So, if you add an extra widget to a HorizontalPanel then it will grow to the right to make room, but not so for the AbsolutePanel. If you add a widget in a position that is outside of the existing visible area for an AbsolutePanel then it will happily place it there. Should you then wish for it to be displayed, then you must either move the widget, or explicitly resize the panel. This panel is also the parent of RootPanel, which we will look at next.
Interfacing to the Browser Through the Root Panel A special case of the AbsolutePanel is the RootPanel, which gives your application direct access to the browser page. RootPanel is mainly used to add your application’s components to the view the user gets, as we saw way back in Chapter 3.1.2. It provides functionality to get specific named DOM elements from the DOM representation of the current browser page using the get(elementName) method, or just a reference to the DOM page using the get() method. We have seen both these techniques used; the first in the default application to place the Button and Label in desired locations on the screen and the second in the Dashboard where we just place widgets as they come. Figure 5.67 shows what happens when we add a number of widgets to the browser using the second approach.
Figure 5.67 The Dashboard showing how we use the RootPanel as an AbsolutePanel for positioning Dashboard applications where we want.
Slightly more controlled, but still flexible, is the HTML Panel.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Adding New HTML Areas The HTMLPanel panel allows standard HTML code to be included within it. The constructor is used to add HTML string to the panel, and widgets are added to named elements within that HTML. This is performed in a similar manner to how widgets are added to the RootPanel, but using the add(Widget,String) method. We do not use this panel in the Dashboard – even we couldn’t manage to use every single GWT component in the Dashboard! These three panels are extremely relaxed on laying out components, giving us total control over positioning. Now we start to move onto panels that are a little more controlling on the structure. Let’s ease ourselves into the control step by step by first looking at the FlowPanel.
Flowing Components A FlowPanel allows the widgets that are added to it to flow in a left to right, top to bottom manner, as if we were adding widgets directly to the browser page. This is the same manner that they would if they were just added to the Browser screen. Figure 5.68 shows this flowing, from left to right and then top to bottom, in action as we have added four widgets to the panel in sequential order.
Figure 5.68 Result of adding four widgets to a FlowPanel – the widgets were added in sequential number order and have flowed left to right, top to bottom.
The panel also implements the IndexedPanel interface, which enforces an explicit ordering on its children. This means that we can get a widget with a particular number using the getWidget(int) and always be guaranteed it will be the same widget. We can also get the index of a widget, through the getWidgetIndex(Widget) method and a count of the widgets in a panel with the getWidgetCount() method. Finally, a widget can be removed using the remove(int widgetIndex) method as well as the standard panel remove(Widget) method. In terms of layout FlowPanel is the panel that allows the most flexibility for styling through CSS after the AbsolutePanel. We make use of this in the EditableLabel component we build in chapter 7 where a text area and two buttons are placed in a FlowPanel, by varying the width of the container we can move the buttons from being on the right of the text to underneath them (or we can achieve the same effect using CSS commands) – see Figure 5.69.
Figure 5.69 Using the FlowPanel in the EditableLabel component of the Dashboard. In this example, three components (TextArea and two Buttons) flow from left to right and top to bottom into the shape of the outer container.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
If we want to let the application have even more control over positioning, and perhaps not showing parts of our application, then the DeckPanel is a great choice.
Decking Out an Application Numerous widgets can be added to a DeckPanel, but only one widget is visible at any given time. The panel acts effectively as a pack of cards. In the GWT implementation, this panel is used by the TabPanel implementation where clicking on a tab makes the widget for that tab visible, as we will see later. We also use it in the Dashboard for our simple security application, the three decks of which are shown in Figure 5.70. Only one of these decks is visible at a time, and we default with Deck 1 to allow the user to log in – if they are unsuccessful, then they get shown deck 2; however, if they succeed then they get shown deck 3.
Figure 5.70 The three decks of the Dashboard’s Login functionality, showing 1) default view, 2) failed login, and 3) successful login. Only one of these decks is visible at a time.
To create our Login widget, we have the code shown in Listing 5.39 in the constructor of the composite widget. Listing 5.39 Creating the Deck Panel for the Dashboard’s login application. 1.679 application = new DeckPanel(); #1 1.680 application.add(createLoginPanel()); |#2 1.681 application.add(createLoggedInPanel()); |#2 1.682 application.add(createErrorPanel()); |#2 1.683 application.setStyleName("login-visiblePanel"); 1.684 application.showWidget(LOGIN_PANEL); #3 (Annotation) (Annotation) (Annotation)
This panel also implements the IndexedPanel interface, which means that when widgets are added they are ordered and can be indexed – it is this ordering that gives us the confidence that the correct deck is displayed in our security application. On top of this, we can set the current visible widget through the showWidget(int) method as well as getting the current visible widget through the getVisibleWidget() method. If we wish to have a panel that acts in a similar way to DeckPanel, but which allows the user to choose which panel is visible, then we would use the StackPanel.
Stacking Components Together StackPanel and DeckPanel share similar functionality, in that they both only show one widget at a time, but in a StackPanel a header for each widget is visible and can be selected by the user to show the widget.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
In Figure 5.71 we take a quick peek at the Dashboard’s Search Comparison application. There are two panels (which are two different versions of the Google search widget we will cover later in the book), but only panel 2, the video search, is currently visible. Clicking on header 1 for the blog search would display that panel – as can be seen on the right-hand side of the figure.
Figure 5.71 StackPanel, which is similar to DeckPanel but allows you to select a label to show each panel (only one of which is visible at a time, depending on whether you click “Blog Search” or “Video Search”).
To create the StackPanel in Figure 5.71 we use the code shown in Listing 5.40. Listing 5.40 Creating the Deck Panel for the Dashboard’s login application. 1.685 StackPanel theStack = new StackPanel(); #1 1.686 theStack.add(createGoogleBlogSearch(),"Blog Search"); |#2 1.687 theStack.add(createGoogleVideoSearch(),"Video Search"); |#2 (Annotation) (Annotation)
The stack is created using the constructor shown in [#1]. We then add new widgets to the stack panel using the add() method which should be given a widget and some header text as parameters. The header text can also be HTML text if a third parameter, a Boolean, is given and set to true. This third way allows us to add images to the header text. We can also change the text associated with a widget through the setStackText() method for a particular widget index - which means we need to find the index of the widget we wish to set the text for. If that happens to be the currently selected widget then we can use the getSelectedIndex() method. To programmatically show a new stack we use the showStack() method.
Docking Components The DockPanel is very reminiscent of the Swing BorderLayout. It allows multiple widgets to be added to the North, West, East and South, but only one widget can be added to the center space, which is allowed to take up any remaining space on the widget. They layout of the DockPanel can be seen in Figure 5.72.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 5.72 Schematic of the DockPanel showing the five areas where widgets can be placed within it.
If no widget is added to a particular area, then that area’s space is “lost”. We make use of this property in the Dashboard’s Slideshow application where we use two DockPanels, one inside the other as shown in Figure 5.73.
Figure 5.73 Use of the DockPanel in the Slideshow application. The outer DockPanel does not use the East and West areas, whereas the DockPanel placed in the Southern area of the first only uses the East and West areas to position the Start and End hyperlinks.
Adding widgets must explicitly set where the widget will reside in the DockPanel, using static constants from that class, e.g. DockPanel.NORTH. Only one widget can sit in the DockPanel.CENTER location – trying to place more than one widget there will raise an exception, although it is perfectly possible to add more than one widget to the other areas, as you can see in Listing 5.41. Listing 5.41 Creating the Dock Panel for the Dashboard’s Slideshow application. 1.688 DockPanel thePanel = new DockPanel(); #1 1.689 thePanel.add(theTitle,DockPanel.NORTH); #2 1.690 thePanel.add(theImage,DockPanel.CENTER); 1.691 thePanel.add(theName,DockPanel.SOUTH); |#3 1.692 thePanel.add(navPanel,DockPanel.SOUTH); |#3 1.693 thePanel.add(preloadImages, DockPanel.SOUTH); |#3 (Annotation) (Annotation) (Annotation)
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
DockPanel extends the CellPanel, as do the horizontal and vertical panels we will look at next. This CellPanel allows widgets to be to be contained within cells of an HTML table, and as such we can set horizontal and vertical layouts using constants from the HasVerticalAlignment and HasHorizontalAlignment classes – but, using CSS styling is preferred. If we want to guarantee that widgets are always kept in a horizontal line, then we should use the HorizontalPanel.
Laying Out Horizontally A HorizontalPanel adds widgets in a horizontal line from left to right. There really is not much more to say about this panel than that; it is very good at doing its one job. We used this panel when creating the TwoComponentMenuItem widget in chapter 4 and shown in Figure 5.74.
Figure 5.74 HorizontalPanel is used in the TwoComponentMenuItem widget built in chapter 4 to position both components horizontally across the page
For this component we create a HorizontalPanel that contains two widgets. Listing 5.42 shows where we use the horizontal panel. Listing 5.42 Creating the Horizontal Panel for the Dashboard’s menu. 1.694 private HorizontalPanel theMenuItem = new HorizontalPanel(); #1 1.695 theMenuItem.remove(1); #2 1.696 theMenuItem.add(newComponent); #3 1.697 SimplePanel dummyContainer = new SimplePanel(); 1.698 dummyContainer.add(theMenuItem); 1.699 String test = DOM.getInnerHTML(dummyContainer.getElement()); 1.700 this.setHTML(test); (Annotation) (Annotation) (Annotation)
With the horizontal panel in place, Listing 5.42 shows how the second component is changed. We first remove the image ([#1]) - it is at index 1 as the widget collection is zero indexed – and then add the new image using the standard add() method. The remaining part of this code is all about extracting the HTML relating to the panel so it can be added to the menu item and we covered that functionality in chapter 4.4.1. If you want the widgets to go down instead of across, then you need the VerticalPanel.
Laying Out Vertically Diametrically opposite the HorizontalPanel is the VerticalPanel, which does an excellent job in keeping your widgets stacked on top of each other. We use it, for example, in the Dashboard security application to keep the components lined up as shown in Figure 5.75.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 5.75 Using the VerticalPanel to position all the components used in the first deck of the Dashboard’s login application.
It is simply created using the code shown in Listing 5.43. Listing 5.43 Creating the VerticalPanel for the Dashboard’s login component application. 1.701 loginPanel = new VerticalPanel(); 1.702 loginPanel.add(appImage); 1.703 loginPanel.add(userName); 1.704 loginPanel.add(password); 1.705 loginPanel.add(loginButton); (Annotation) (Annotation)
Whether the complex panel uses a div or table DOM elements is transparent to us as users. It is nice to be aware of the underlying implementation, but we should not rely on it, since amongst other things, it may change in the future. If we need to manage a panel as a table, to insert for example widgets in the fourth column and third row, then we need to consider the final family of panels – HTMLTable panels.
Considering HTML Tables There are three panels that explicitly act as if they have a table structure; these are the HTMLTable, FlexTable and the Grid. We can insert GWT widgets at determined column/row locations, and manipulate the style name of specific cells. The three GWT provided panels are known as: HTMLTable, FlexTable, and Grid The first panel is the just a basic HTML table which is then specialized by the second two panels. The key difference between the second two panels is that Grid is of a fixed size and needs to be explicitly resized if columns/rows outside the initially set size are to be accessed. The FlexTable panel, on the other hand, will create new grid cells as and when needed.
Implementing an HTMLTable The HTMLTable panel provides all the basics for building HTML tables within GWT, it can create an empty table, add or remove cells, rows and columns to it and check if particular row, columns or cells exist. It also allows widgets to be set or removed from particular cells as well as set things such as cell padding and spacing.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
It is possible to add a TableListener to an HTMLTablePanel, which is fired when cells in the table are clicked. For example the code shown in Listing 5.44 could be used to provide functionality that sorts a table based on a column, when the header of that column is clicked. Listing 5.44 Adding a TableListener to a table to allow the user to re-order data by clicking on elements in the header row 1.706 int HEADERROW = 0; 1.707 HTMLTable myTable = new HTMLTable(); #1 1.708 myTable.addTableListener(new TableListener(){ #2 1.709 public void onCellClicked(SourcesTableEvent sender, int row, int col){ #3 1.710 if (row==HEADERROW){ #4 1.711 orderTableOn(col); #5 1.712 } 1.713 } 1.714 }); (Annotation) (Annotation) (Annotation) (Annotation) (Annotation)
Individual cells can be assigned their own Cascading Style Sheet class name and have certain stylistic attributes altered programmatically. This is achieved through the use of the CellFormatter object from the HTMLTable class, which is sub-classed by Grid and FlexTable. In Table 5.21 we show how to set the format of both a cell and a whole row. Table 5.21 Looking at the different ways of formatting a cell in a Grid Panel Code Formatting a Cell Formatting a Row
The key aspect of the HTMLTable is that it provides the basis for the next two panels.
Flexing a Table Layout A FlexTable is essentially a table where rows and columns are created implicitly to match the size needed when the program adds widgets. For example, if the FlexTable is initially dimensioned as a 4x4 grid and the program adds a widget to column 10, row 15 then the FlexTable automatically grows to be a 10x15 grid. Additionally, rows and columns can span a number of other columns or rows, which is not possible using a Grid.
Figure 5.76 Example of a FlexTable with some row and column spanning going on
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
FlexTable provides an additional class to allow rows and columns to span a number of cells. This is the FlexCellFormatter class, and we can set some row and column spanning as simply as in Listing 5.45. Listing 5.45 Using a FlexCellFormatter on a FlexTable
In the onMouseMove method we first check to see if this slider is a horizontal slider ([#1]). If it is then in [#2] we make sure that the x-index never goes outside of the left and right of the control. If the control is not a horizontal one, then we make sure the x-index never changes. Similar code for the y-index starts at step [#4] in the code. Once we have recalculated the new position, within the constraints, we more the thumbnail popup ([#5]). The rest of the Slider class is responsible for trying to set up the mathematics for dealing with translating screen positions to slider values (and vice versa) as well as dealing with one of the problems with using the popup panel for this functionality. If we resize the browser window, our background image will move as it flows within the new spacing of the browser page, but unfortunately, the thumbnail does not. Luckily, we can get GWT to tell our code whenever the browser window is resized, by using a WindowResizeListener. This is similar to all the EventListeners we discussed in chapter 6 and lets us fire code when the particular event occurs, and we set up ours as shown in Listing 7.88. Listing 7.88 The WindowResizeListener used in the Slider composite widget. 1.1558 Window.addWindowResizeListener(new WindowResizeListener(){ 1.1559 public void onWindowResized(int width, int height) { 1.1560 theSliderThumbnail.setVisible(false); #1 1.1561 Timer reinit = new Timer(){ #2 1.1562 public void run() { 1.1563 redimensionalise(); #3 1.1564 } 1.1565 }; 1.1566 reinit.schedule(10); #4 1.1567 } 1.1568 }); (Annotation) (Annotation) (Annotation) (Annotation)
Now when the browser window gets resized, our listener’s methods get fired. This means we will hide the thumbnail ([#1]) and then call the method that moves the thumbnail to the needed position ([#2]). We put this functionality inside a timer ([#2]) since it is not guaranteed that the browser will have completely resized before we
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
start trying to reposition it, so repositioning may fail. In ([#4]), we schedule our repositioning code to execute in 10ms time, which should be more than enough for the browser to resize the window, and small enough for the user not to notice the delay. The final point we want to discuss in relation to the code is how we manage events. When the user drags the slider, it updates certain values including the X and Y values of the slider index. As those values are updated we fire an onChange event to any ChangeListeners that are registered with the widget. To accomplish that, we implement the techniques shown in chapter 6, and start off with making our widget implement the SourcesChangeEvents interface which allows us to register ChangeListeners into the ChangeListenerCollection’s changeListener variable. Then whenever a change is made to the slider’s X and Y values, then we call the changeListener.fireChange() method. We create sliders using constructors, and register ChangeListeners in the normal way. 1.1569 1.1570 1.1571 1.1572 1.1573 1.1574
Slider slider = new Slider() Slider.addChangeListener(new ChangeListener(){ Public void onChange(Widget sender){ Window.alert(”Slider has changed”); } });
With the basics of the slider in place, we then generate 3 subclasses that constrain the thumbnail in its directions of movement as shown in Table 7.35. Table 7.35 How various constraints are applied to the x and y plane to create various sub classes of our Slider Name Constrain X? Constrain Y? HorizontalSlider N Y VerticalSlider Y N GridSlider N N
The VerticalSlider used in the ColorPicker widget is simply created from a stripy colored background representing all the hues that could be selected and an image appropriate for the thumbnail. The ColorPicker widget is a special form of the GridSlider. A GridSlider allows the thumbnail to move in the X and the Y plane, and the ColorPicker widget uses this to present the user with the swathe of colors.
Constructing the ColorPicker Composite To create the ColorPicker composite used by the user uses to select the actual color, we use a GridSlider with a twist. First we create a GridSlider whose background is set to the graduated transparent PNG image shown in Figure 7.121.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 7.121 Background image used in the color picker widget
The clever part comes when we start playing with the slider thumbnails. As we move the slider on the VerticalPanel, we select a different hue for the color. As this hue changes, we fire a change listener that sets the background color of the ColorPicker widget using the following code: 1.1575 1.1576 1.1577 1.1578 1.1579 1.1580 1.1581
To determine the exact color selected by the user, we take the hue value from the vertical slider and the saturation and brightness are given by the X and Y positions of the thumbnail on the GridSlider that sits behind the ColourPicker widget. Values that are displayed in the text boxes are also calculated from these three values. Putting the whole composite widget together is a simple case of creating new instances of each composite and widgets, and then placing them within a HorizontalPanel. We can see that composite widgets are very powerful although quite easy to use if we plan and set them out in advance. For our Dashboard project, we will also be using composites to develop the component applications; however, we want to also provide some consistent functionality across all component applications, so we will extend the standard composite class to ensure this – Dashboard component applications will then extend this new DashboardComposite class.
Creating the Dashboard Composite The components created as Dashboard applications (those components that are dragged around such as a Calculator, a server monitor, etc.) will undoubtedly be created using composites. They also need a way of registering option menu components into the Dashboard and of setting their name (since that will be displayed in the header of the DashboardPanel we created in chapter 5). We can again harness the power of writing our applications in Java by extending the standard Composite class to provide some functions and fields that will help our components site within the Dashboard happily -- Listing 7.89 shows such a class, that we call the DashboardComposite.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Listing 7.89 Framework code for Dashboard components that provide the ability to add Option menus and change the name of the components window 1.1582 public class DashboardComposite 1.1583 extends Composite #1 1.1584 implements FocusListener{ #2 1.1585 1.1586 private String name = "Default Name"; #3 1.1587 private MenuBar parentMenu; #4 1.1588 protected MenuBar optionsMenuBar = new MenuBar(true); |#5 1.1589 private MenuItem optionsMenu ; |#5 1.1590 1.1591 public DashboardComposite(MenuBar parentMenu){ 1.1592 this.parentMenu = parentMenu; 1.1593 } 1.1594 1.1595 protected MenuBar getOptionsMenuBar(){ return optionsMenuBar; } 1.1596 public void setName(String newName){ this.name = newName; } 1.1597 public String getName(){return name; } 1.1598 1.1599 public void onFocus(Widget sender){ addMenu(); } #6 1.1600 public void addMenu(){ 1.1601 if (parentMenu!=null){ 1.1602 optionsMenu = new MenuItem(name, optionsMenuBar); 1.1603 parentMenu.addItem(optionsMenu); 1.1604 } 1.1605 } 1.1606 1.1607 public void onLostFocus(Widget sender){ removeMenu();} #7 1.1608 public void removeMenu(){ 1.1609 if (parentMenu!=null){ 1.1610 parentMenu.removeItem(optionsMenu); 1.1611 } 1.1612 } 1.1613 } (Annotation) (Annotation) (Annotation) (Annotation) (Annotation) (Annotation) (Annotation)
We will be extending the normal Composite class ([#1]) as well as implementing the FocusListener interface ([#2]). This new interface requires us to provide onFocus() and onLostFocus() methods. These methods will be called when the Composite is given and looses focus on the screen (usually by the user clicking on them). We give our DashboardComposite a default name ([#3]) – this field will be picked up by the DashboardPanel that we created in chapter 5 and displayed at the top of that panel. A link is also provided back to the parent’s menu bar ([#4]). Next, [#5], we create the basis for the Dashboard components Options menu. Each Dashboard component has the ability to create an options menu, which is embedded into the Dashboard when that component gains focus, and is removed again when it looses focus. When focus is gained the onFocus() method, required since we implement
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
the FocusListener interface, is invoked ([#6]). It simply calls the addMenu() method to add the options menu to the Dashboard. The converse of the onFocus() method is the onLostFocus() method ([#7]), which is called when the composite looses focus and it in turn calls the removeMenu() method to remove the options menu from the Dashboard. With all this code in place we are now almost ready to start developing the functional components of the Dashboard. These components are now simply created as extend this DashboardComposite class and will adhere to the following template: 1.1614 1.1615 1.1616 1.1617 1.1618 1.1619 1.1620 1.1621 1.1622 1.1623
public myDashboardComponent(MenuBar menu){ super(menu); setName(”MyName”); MenuBar optionsMenu = getOptionsMenuBar(); optionsMenu.addItem(…); MyWidget theWidget = new MyWidget(); initWidget(theWidget); }
You can see all of this in action in the code, downloadable from http://www.manning.com/hanson, which also includes the JSNI code for the examples we will see in the next chapters.
Summary This has been quite a tour through the key aspects of GWT in the last four chapters – believe us, it took a long time to write, and we can imagine that as a reader it has taken you through quite a journey. We hope that, by tying everything back to the Dashboard, you are still with us!! In your hands now you should have code for: PNGImage ToggleMenuItem DashboardPanel EditableLabel Range of Sliders ColorPicker DashboardComposite All of these will be used in the Dashboard as we go forwards. In this chapter we saw how composite widgets could be created and that the development of them should be treated in a similar manner to how we would develop a GWT application (though without the overheads of the application creation process). A composite is created as a single class extending the Composite object. These four chapters have also been the closest that this book will come to being a reference to the components within GWT. We discussed all the standard widgets, panels, and event handling objects and have indicated where in this book we will be using them in action. With that in mind, it is now time to introduce the final user interface aspect of GWT by looking at how we can interact directly with JavaScript on those occasions that we need to.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
8 Building JSNI Components GWT’s key benefit is the ability to abstract away from JavaScript, which frees the developer (you!) from concerns over browser differences and developing in an un-typed programming language. But, just like normal application code occasionally uses assembly language segments for special needs, so client-side GWT applications can interact directly with JavaScript. In a normal application, we might use assembly code to get speed advantage or access hardware in the only way it can; for GWT applications, in our experience, there are four possible situations when it may be sensible to use JSNI: To enable communication between a main application and component applications using a JavaScript variable (though only when they cannot be within the same variable scope). To access browser functionality that has not been included directly with GWT. To expose an API of your GWT application to other applications on your web page that can execute JavaScript (useful if you are replacing legacy applications in an existing site). To access some very useful JavaScript libraries that you either do not have the patience or ability to translate into GWT (you may, for example, be restricted by a license associated with the library). For the Dashboard application, we will enable communication between the main application and the component applications by using a JavaScript variable, which could be set by the menu system and was read by the component applications when they were being deleted. The slider we discussed in chapter 7 used the JavaScript Native Interface (JSNI) to find out the scroll position of the browser window to ensure that the thumbnail was moved to the correct location, regardless of how far the window had been scrolled – this functionality is not available directly within GWT (at present). In this chapter we look at the general syntax of JSNI, how we can use JSNI to communicate to the browser and other GWT applications. We round the chapter off by showing how to load and access 3rd party JavaScript libraries, in particular some JavaScript search libraries from Google, which allow us to produce Dashboard applications such as those shown in Figure 8.122.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 8.122 The GWT in Action Dashboard showing the Google Video Search application on the left and the Google Ajax Search application on the right. Both applications are JavaScript libraries wrapped as GWT widgets using the JavaScript Native Interface.
There is a strong caveat here with all of this talk about JSNI: really ask yourself if you need to use JSNI since a lot of the time the capability you are after may exist within the standard GWT. One key thing to remember about JSNI is that it can only be included in client-side code – so no JSNI can appear in code destined for the server, which makes sense if you step back and remember that JSNI is JavaScript code, which needs to execute within a web browser! Before we get ourselves into the position of being able to build our two widgets for this chapter, we need to introduce and explore some of the attributes of JSNI -- the basic syntax and operation.
Introducing JavaScript Native Interface (JSNI) JSNI, or JavaScript Native Interface, is GWT’s mechanism to allow us as programmers to embed JavaScript within our Java code. We cannot over-emphasize the point that JSNI is almost a last resort approach and that many issues that look like they need JSNI can be solved at the GWT Java level if time is spent looking. In a way, it is possible to view the relationship between GWT Java and JSNI as we do the relationship between a high level programming language and assembly code: yes, you can do it, but it is only sensible if there is a clear need to do so. It is possible to carry this analogy further, since JSNI, just like assembly language, is less portable across systems. If we write something in JSNI that works in one browser, there is a risk that it may not work at all, or perhaps in a different way, in other browsers. As an example, it is possible to count the number of children for a DOM element using the simple Java GWT DOM.countChildren() method in Java. If we were to write that method in JavaScript, we would have to, as GWT does for us, write several versions to cope with DOM differences between Internet Explorer and the other browsers (check out the DOMImplStandard and DOMImplIE6 classes in GWT gwtuser.jar file to see the GWT definitions for this method in the different browsers). In JSNI we can write only one of these methods, or we would have to add our own browser detection JavaScript as well, which is not in the spirit of GWT -- which advocates writing once and running in many different browsers. There is also the risk that writing our own JSNI code could introduce memory leaks, unless you are an expert at those matters.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Note JSNI is only applicable to client-side aspects of your application – since JavaScript does not run on the server side. It is therefore not possible to use JSNI code in any server-side code, or to pass an object over RPC to the server and expect to be able to execute any included JSNI code server-side.
However, let’s be a little more positive about JSNI. In the cases where you do have to use it, it can be very powerful. JSNI lets us interface between Java and JavaScript in a type safe way; we can use JavaScript objects in our Java code and rely on Java’s strong typing to protect us against various programming errors. Though again, the more functionality you include in a single JSNI block, the less you can rely on Java’s strong typing to minimize errors. JSNI also provides a seamless way of moving between Java and JavaScript, allowing us to also pass objects and exceptions across the boundary in both directions. Through JSNI, we manage JavaScript objects from Java, and we can call back to our Java code from within the JavaScript code we write. As we mentioned in the chapter introduction, one use is to wrap a 3rd party JavaScript library where we will create JavaScript objects from the library and pass them around our GWT Java code before perhaps sending them back to the JavaScript library again. There is one word of warning about JSNI; it is potentially a moving target during the early stages of GWT adoption. The existing models works well in most cases, as you will see later in this chapter, but there are already several requests for reviews published on changing functionality of certain aspects. A guiding principle of JSNI coding is to spend as little time as possible in “the dark side”; each JSNI method really should be at an atomic level (i.e. only perform one clear function) so that you isolate issues and keep as much control in the strongly types Java realm. Let’s move on and assume that we are in a situation where JSNI is the approach we need. The first thing we need to understand is how to use the syntax.
Understanding JSNI If you are familiar with writing native methods in Java for other languages, then JSNI will seem relatively familiar to you. If you’ve never written native methods before, don’t worry, their syntax is a little strange but not frightening. Java Native Interface (JNI) is the Java approach that allows Java code to interface with components written in other languages, for example C or C++ or assembly. JSNI is the GWT Java equivalent for interfacing with JavaScript components, and it follows a similar syntax to JNI. In the next few sections we look at the syntax used to cross the boundary both ways between Java and JavaScript and how the various objects we pass over that boundary are treated – a simple JSNI call can be seen in Listing 8.90. Listing 8.90 Sample JavaScript Native Interface (JSNI) method call 1.1624 public static native void method_name(ObjectTyp someData) 1.1625 /*-{ 1.1626 [email protected]::data1 == “GWT In Action” 1.1627 }-*/; (annotation) (annotation)
Hopefully that didn’t look too scary, even with the @ and :: symbols sprinkled in their. To start to understand why these are there, we will first look at how we call JavaScript functionality from within our GWT Java program.
Crossing the Boundary from Java to JavaScript To include some JavaScript code in our GWT application, we must write it in a specific way so that the both the syntax checkers of Java and the GWT Java compiler can recognize it and deal with it appropriately. For syntax checkers, that means ignoring the code since it is not Java, and, for the GWT compiler, it means merging it in a structured way into the JavaScript output of compilation.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
A basic JSNI method is defined as shown in Listing 8.91. Listing 8.91 Template code used in creating a JavaScript Native Interface (JSNI) method 1.1628 public static native void method_name() #1 1.1629 /*-{ |#2 1.1630 }-*/; |#2 (annotation) (annotation)
Within the template, we define the method in a normal Java way, but must include the keyword native as one of the modifiers ([#1]); this identifies the code to the Java compiler as some JNI code and to the GWT compiler as some JSNI code. To help any syntax checkers know that they should avoid parsing the JavaScript code, we wrap it up as a comment by using a modified standard comment, which starts with the characters /*- (a forward slash, an asterix and a dash) and ends with -*/ (a dash, an asterix and a forward slash). It is also important not to forget the trailing semi-colon at the end of the definition; otherwise your code will not compile! Crossing the boundary from Java to JavaScript can come in two forms, either by writing JavaScript code in our Java application that performs some dedicated functionality, or by writing some JavaScript in our Java application that calls some functionality in a JavaScript library already loaded into the web browser. Both methods follow the same syntax of extending the template from Listing 8.91 to provide parameters, a return type if necessary (otherwise the return type must be defined as void), and the native code. We can consider this crossing of the boundary diagrammatically as shown in Figure 8.123.
Figure 8.123 Examining the interaction between Java and JavaScript code when crossing the Java to JavaScript boundary in a JSNI call from a GWT application.
Listing 8.92 shows some typical code required to cross the boundary going from Java to JavaScript. Listing 8.92 Basic code outline used for crossing the boundary in the direction of Java to JavaScript 1.1631 public static native 1.1632 return_java_type #1 1.1633 method(java_type parameter, …, java_type parameter) #2 1.1634 /*-{ 1.1635 JavaScript code goes here and |#3
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.1636 1.1637 1.1638
can use the passed in Java parameters return return_java_object; #4
Overall the definition is the same as a normal Java method; we provide a list of parameters that can be passed in and a possible return object type. If we provide a return type, rather than just a void, then the JavaScript must return an object of the correct type (you should be aware that JSNI code cannot create new Java objects, it can manipulate ones passed in or create new JavaScript objects, but not new Java objects). Remember It is not possible to create new Java objects within a JSNI code block – return types must either be primitive types, manipulated Java objects that have been passed in as input parameters, or a reference to a newly created JavaScript object (a JavaScriptObject type).
It is important to understand how the objects we provide as parameters are handled across the Java to JavaScript boundary, as we will see in the next section.
Passing Java Objects Across the Java to JavaScript Boundary We have mentioned before that one of the benefits of using Java to develop our AJAX/rich Internet applications is the strong typing provided by the Java language, which is not present in JavaScript. This missing typing model could cause problems as we start crossing the boundary from Java to JavaScript. Unfortunately, there is not much we can do about the typing capabilities of JavaScript. Once our objects have passed into the realm of JavaScript they are sadly on their own. Which brings us back to the point we mentioned earlier – the shorter amount of time we can spend in this potentially “lawless” world of JavaScript, the better. If we have to spend a long time there, it is preferable that this is done only to interact with stable 3rd party JavaScript libraries. What we can do, though, is ensure that there is a clearly defined and repeatable mapping between Java typed objects and JavaScript un-typed objects, which is exactly what GWT provides. Primitive Java numeric types, such as byte, short, char, int, long, float, or double become simple objects in JavaScript whose value is that of the original object. For example, if we have the Java object char keyPress = ‘a’ then that will become the JavaScript variable var k = ‘a’. Similarly, the Java object int val = 10 would become the JavaScript variable var v = 10. A Java String is translated across into JavaScript as a simple variable to which the original text is assigned. Therefore the Java object String name = “GWT In Action” becomes the JavaScript variable var s = “GWT In Action”. The final simple JavaScript object is the Boolean, whose Java value becomes another simple JavaScript variable; the Java boolean b = true becomes the JavaScript variable var b = true. If we move on to looking at the more complicated Java objects that can be passed across boundary, then we have the Java array, a Java object, and a new object to GWT called the JavaScriptObject. We’ll discuss the last object more in the next section, but for now we should think of it as just a reference to a JavaScript object created somewhere else that can be passed around our Java code and on to other JavaScript methods, but we cannot look into its contents from within the GWT Java code. This might sound a bit strange, but we will explain it in a little more detail later in the section on this object. Passing an array across the Java to JavaScript boundary also gets treated in a similar opaque way; we know we have an array object, but we cannot look into its contents. This means that if the JavaScript code needs to use values in the array, then we should move them out of the array before we cross the boundary, either into separate parameters, or
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
into another type of user defined Java object since we can manage Java objects in JavaScript through the JSNI interface as we will discuss next. We can also pass our own defined Java objects across the Java to JavaScript boundary and GWT provides a special syntax allowing us to access to the fields and methods of that Java object. This is going to take a little bit of explaining, so, please, stick with us. Let’s say we have an object of the type Data, which is defined by the class shown in Listing 8.93 to contain a simple String and an integer. Listing 8.93 The example class that we will use to demonstrate the concept of accessing Java objects from JavaScript through the Java to JavaScript boundary. 1.1639 public class org.gwtbook.client.Data{ 1.1640 String data1 = “GWT In Action”; 1.1641 int version = 1; 1.1642 }
We will use an instance of this type in a new class we are about to define in Listing 8.94, called DataManip. Within the DataManip class we will have a couple of additional class variables, one called correct ([#1]) and then other a static variable called checked ([#2]) (it doesn’t really matter what they stand for in this example). Listing 8.94 The example class that we will use to demonstrate the concept of accessing Java objects from JavaScript through the Java to JavaScript boundary. 1.1643 public class org.gwtbook.client.DataManip{ 1.1644 boolean correct = false; #1 1.1645 static boolean checked = false; #2 1.1646 1.1647 public native void doSomething(Data someData)/*-{ #3 1.1648 if([email protected]::correct){ #4 1.1649 if([email protected]::checked){ #5 1.1650 if([email protected]::data1 == “GWT In Action” #6 1.1651 && 1.1652 [email protected]::version == 1){ 1.1653 //Do something else. 1.1654 } 1.1655 } 1.1656 } 1.1657 }-*/; 1.1658 } (annotation) (annotation) (annotation) (annotation) (annotation) (annotation)
Within the class we also define a JSNI method ([#3]) called doSomething(), which will take as a parameter an instance of Data class but returns nothing (we will cover going from the JavaScript to Java boundary in a short while). When doSomething() is called, we wish it to look at the DataManip class’ correct value ([#4]), if that is false, then we want to check the static field checked ([#5]). If that too is false, then we confirm if the Data instance passed in as a parameter has its data1 field set to “GWT In Action” and the version value set to 1 ([#6]); if they are then we do something else in the code.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
To perform this functionality we need to access an instance field, a static field and a field in a Java object passed in as a parameter in that ordering. In each case we need to use the JSNI specific syntax for accessing Java objects, the template for which is shown in Figure 8.124.
Figure 8.124 Explanation of the JSNI method to access a field in a Java object. The first part comprises of the name of the instance (an object name, the keyword this, or blank for a static field. Next are the fully qualified class name, and then the field name.
When we access the instance field in [#4] then the name of the instance is this, the class name of this instance is org.gwtbook.client.DataManip and the field we are after is called correct. Accessing this field is, therefore, performed by writing the following in our JSNI code: 1.1659
Accessing the static field ([#5]) requires us to access a field where there is no defined objectName (since the field is static across all instances). In JSNI we simply write: 1.1660
@org.gwtbook.client.DataManip::checked
(Notice that the period is missing and only the @ symbol is still required if there is no object name to reference.) Finally, when we want to reference the data1 field in the parameter that we have passed in, then the parameter name, someData, is the objectName, and we, therefore, write: 1.1661
We can also access the methods from the Java object passed across the boundary in a similar way, and we will discuss how that works once we have seen how we return objects back across the boundary to the Java code.
Sending Objects Back Across the JavaScript to Java Boundary When we have completed the functionality we need in JSNI, it will be very common for us to pass an object back in return. As with all Java methods, we need to define the return type of the JSNI method, which will either be a Java primitive, a Java object, a GWT JavaScriptObject or void. Diagrammatically, we are performing the action shown in Figure 8.125, and once complete, the program control is back in the hands of our GWT Java code.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 8.125 Examining the interaction between Java and JavaScript code when returning values across the JavaScript to Java boundary in a JSNI call from a GWT application.
Just as there was a defined mapping between Java and JavaScript objects for the parameters, so such a mapping exists when going from JavaScript to Java for the return values. To get a Java primitive numeric coming out of the method call the value that must be returned from the JavaScript must be a numeric value. We need to be careful with returning primitive numerics since GWT does not determine whether the type is correct. This means that if we define the Java method to return a Java int and the JavaScript returns the value 1.5 then the result passed out of the Java method will be unpredictable. Returning Strings and booleans is much simpler since they translate directly to their Java equivalents. Java objects can also be returned from the JavaScript; however, we cannot just simply create Java objects within the JavaScript. If we wish to return a Java object then it must have been passed in as one of the parameters. Care must be taken if we are returning null objects since the JavaScript undefined value is not treated by JSNI as null and, if used, unpredictable results can occur (you must always use the null value). You may even wish to ensure that any JavaScript variable you pass back is checked to ensure that it is not the undefined value before it is returned. We mentioned briefly when passing in parameters that there is also a special object called the JavaScriptObject provided by GWT. This type of object can be returned by the JSNI method if a new JavaScript object is created as part of the call. We will see this in use quite often in the later part of this chapter where we use JSNI methods to create new JavaScript objects from a 3rd party library. We need a reference to these 3rd party objects in our Java code since we will later be calling methods on them, and they are therefore passed back as subclasses to the JavaScriptObject. This JavaScriptObject is opaque to our Java code - we cannot use Java code to call the methods within it or even to see its fields; we need to pass them back into new JSNI methods for that. But we’ll see this in action a little later. First we need to finish off our journey through the JSNI syntax by looking at how we can call Java methods from JavaScript.
Crossing the Boundary from JavaScript to Java As well as being able to access fields on Java objects from JavaScript, we can execute methods defined in those objects in a similar manner, as shown in Figure 8.126.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 8.126 Examining the interaction between JavaScript and a Java object when crossing the JavaScript to Java boundary in JSNI.
To refer to a method, we use a very similar syntax to the syntax we used to refer to fields. There are, however, some slight differences. This time, the generic template for calls to methods is shown in Figure 8.127.
Figure 8.127 Explanation of the JSNI method to access a method in a Java object. First is the instance name, followed by the fully qualified class name. The method name is followed by the parameter signature and then the actual arguments.
The first part of this should be familiar from when we were accessing fields, and the same discussion over there being no objectName if we are accessing a static method, using this if we accessing a method in the current instance and using the parameter name if we are accessing a passed in object to a method. The difference occurs in the second half of the template, where we see the values param-signature and arguments. The arguments part is just a list of the various arguments whose types match the parameter signature. JSNI uses the internal Java method signature to define the parameter signature, but does not require a definition of the return type to be included. Parameter type signatures are defined in Table 8.36. Table 8.36 Definition of the Java type signature for various Java types Type Signature Java Type Z Boolean B Byte C Char S short I int J long F float D double L fully-qualified class ; Fully-qualified class [ type type[] (an array)
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Let’s have a look at a simple example, which is shown in Listing 8.95. Listing 8.95 Some sample attempts to call methods in a Java class from JavaScript. 1.1662 public class org.gwtbook.client.JSNIMethodExample{ 1.1663 1.1664 public void m1(String s){ 1.1665 // Do something with the String s 1.1666 } 1.1667 1.1668 Public void m2(int num){ 1.1669 // Do something with integer num 1.1670 } 1.1671 1.1672 public native void doSomething(String s, int num)/*-{ 1.1673 [email protected]::m1(Ljava/lang/String;)(s); #1 1.1674 [email protected]::m2(I)(num); #2 1.1675 }-*/; 1.1676 1.1677 } (annotation) (annotation)
In [#1] we are calling the m1() method in this class, which takes a Java String as its parameter, so we must define the parameter signature as Ljava/lang/String;. Then we call the m2() method ([#2]), which expects an integer, and so the method signature is simply I. The final type of object that can come out of our JSNI call are exceptions.
Handling Exceptions It is strongly recommended that JavaScript exceptions are handled within the JavaScript segments of the JSNI method, and that Java methods are handled within Java code. The reason for this is that when JavaScript exceptions creep over the JavaScript boundary into the Java then it becomes, and can only become, an object of type JavaScriptException. We do not necessarily lose information about the JavaScript exception, since we can use the getName() and getDescription() methods to return String values about the original JavaScript exception. But relying on these methods will lead to messy code requiring the use of String comparison to handle the exceptions, whereas they can be handled more gracefully within the JavaScript code. The only exception to this rule is where an exception is raised in some Java code called by a JSNI method, which is then handed back to some more Java code. In this case the original Java exception typing is preserved through the boundaries. With all these basics in place, let’s take a look at the occasions when we said we might need to use JSNI, starting with how we perform some different types of communication between components (browser, GWT applications, and legacy code).
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Communicating Using JSNI We can use JSNI to allow our applications to talk to the browser, to allow GWT applications to message each other (on the client side), and to allow legacy applications to talk to our GWT application. This chapter looks at each of these types of communication in turn. Due to the process GWT uses to load GWT applications, which we look at in chapter 17, we have to subtly alter the way in which we access the standard JavaScript variables window and document. The way that GWT loads our applications means that we do not have direct visibility of these JavaScript variables; however, GWT fixes that in one of two different ways – either we can talk to the Browser using a methods in the Window Java class or through a newly provided JavaScript variables: $wnd and $doc. The Window class is the preferred way, which we look at next, as that keeps us in the realm of Java.
Chatting to the Browser via GWT Java If you’re thinking of using functionality normally associated with the JavaScript window variable, you should first double-check the GWT Window class (which can be found in the com.google.gwt.user.client package in gwt-user.jar) does not provide the access you are looking for. It does provide access to a number of functions normally accessed through the JavaScript window variable (such as raising alert and confirmation windows, opening new browser windows, setting the browser title and enabling/disabling scrolling). We use this class in the Dashboard to display our confirmation message when a user deletes a component, as you can see in Figure 8.128.
Figure 8.128 Dropping the Clock widget on the Trash icon pops up a JavaScript confirm message, but, rather than using JavaScript to get the Message, we use the GWT Window class’ confirm() method.
When the user attempts to delete an application, we simply create a confirmation box using the code shown in Listing 8.96 (if we wanted purely an alert window then we could use the Window.alert() method). Listing 8.96 Simply using the confirm() method in Window class to present the user with the confirmation window for closing one of the Dashboard's applications 1.1678 Window.confirm(messages.ConfirmDeleteMessage(this.parkComponent.getName( )));
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The majority of our communication with the browser should be performed through the provided GWT classes, but there may be occasions where perhaps GWT has not provided a mechanism as yet, where we have to dig down to the native JavaScript to do that.
Chatting to the Browser via JavaScript On rare occasions, the methods provided by the GWT Java classes for communicating with the browser will not provide the functionality you need. In these cases, you need to use JavaScript (through JSNI), but, because of the way the GWT loading mechanism works, we cannot use the normal JavaScript window and document variables (see Chapter 17 for more on the loading mechanism). Instead, GWT provides the variables $wnd and $doc respectively. An example of using these variables is given in the Slider class we built in Chapter 7. When a user clicks on the slider’s background, we want the thumbnail to move to the appropriate location. This works fine using the standard methods, until someone scrolls the screen. Once that happens, the x and y positions the onMouseDown event provides us with no longer correlate with the locations on the slider’s background. We need a way of obtaining the scroll off sets and then using those in our positioning calculations. The code we use to implement that functionality is shown in Listing 8.97. Listing 8.97 JSNI code used to get the Y offset of scrolling in the browser window, which is then used to position the Slider’s thumbnail. 1.1679 private static native int getScrollYOffset () /*-{ 1.1680 var scrOfY = 0; 1.1681 1.1682 if(typeof($wnd.pageYOffset) == 'number' ){ 1.1683 scrOfY = $wnd.pageYOffset; #1 1.1684 }else if($doc.body && 1.1685 ($doc.body.scrollLeft || $doc.body.scrollTop)){ 1.1686 scrOfY = $doc.body.scrollTop; #2 1.1687 }else{ 1.1688 scrOfY = $doc.documentElement.scrollTop; 1.1689 } 1.1690 return scrOfY; 1.1691 }-*/; (annotation) (annotation)
In Listing 8.97, we use both the $wnd and the $doc variables to access Y offset properties of the browser. At point [#1] we retrieve the pageYOffset value for non-IE browsers and at [#2] we need to use the $doc variable to retrieve the body.scrollTop variable for IE. Whichever value we eventually retrieve is the one that is returned to the caller of the method. This is also a great example to point out the limitation of JSNI we spoke of earlier – managing browser differences. The code tries to identify browser differences using a number of conditional statements checking for the existence of particular variables to understand what type of browser it is dealing with. Not exactly elegant, and it could be solved using class replacement based on the browser property technique we discuss in chapter 15, but that would make it harder to explain the point of this example! If we are confident that there are no browser differences, then we can proceed in the manner shown in this section. Through these $doc and $wnd variables, all the usual window and document browser functions are possible. Another place in the Dashboard where we use these variables is to provide the Dashboard user the ability to change locale through the menu system. When the user clicks on a locale in the menu system, we call the function shown in Listing 8.98 with the appropriate locale parameter (actually, the GWT Command that is called when the Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
menu is clicked first removes the onWindowCloseListener(), which we added in the events chapter, chapter 6, so that the user is not bombarded with warning messages, then this code is called by that Command). Listing 8.98 JSNI code used to change the locale (by taking the current location, splitting it to retrieve the base URL, appending the GWT ?locale=xx_YY specific coding to the end and then setting the windows new href location). 1.1692 private native void changeLocale(String newLocale)/*-{ 1.1693 var currLocation = $wnd.location.toString(); #1 1.1694 var noHistoryCurrLocArray = currLocation.split("#"); |#2 1.1695 var noHistoryCurrLoc = noHistoryCurrLocArray[0]; |#2 1.1696 var locArray = noHistoryCurrLoc.split("?"); |#2 1.1697 $wnd.location.href = locArray[0]+"?locale="+newLocale; #3 1.1698 }-*/; (Annotation) (Annotation) (Annotation)
We use the $wnd variable often when interfacing between GWT applications and any JavaScript library that may have been loaded by the application, as well as when exposing an API into our GWT application.
Talking to a GWT Application via a JavaScript API GWT applications come in many shapes and sizes. Some, like our Dashboard, take up all of the browser’s space; many others fit in as a small segment of a web site. As we saw in chapter 1, there will be times when our GWT application may be replacing an existing application. In all of these cases, we might need to talk to our application from an external entity -- which might be another GWT application or some legacy code. In order to do this, whilst preserving the standard GWT loading mechanism, we need to expose the GWT application through a JavaScript API, something that GWT can easily manage with a little bit of JSNI magic. To expose an API, we need to expose JavaScript methods outside of our application that have the following features: Can call parts of our application’s code. Have names that do not get obfuscated by the compiler so we can reference them. We can achieve the first part of this through using JSNI to call back to static methods in our code. The second part is achieved by carefully constructing new methods in the browser by using JavaScript definitions. We can access the main browser page through the $wnd object, so, to create new JavaScript methods at that level, we just create definitions along the following lines: 1.1699 1.1700 1.1701
$wnd.newMethodName = function(parameters){ // Some code }
If we make the internal part of this definition a JSNI call back to static methods in our GWT application, then we have now exposed an API to external applications. Let’s look at how we do this for the Dashboard application. There is not really much of an API that we can expose for the Dashboard we are building, but for the sake of an example, we will expose two methods for setting and getting the Dashboard’s name (which is usually changed by the user clicking on it and typing in a new name. In the application download, we provide another HTML file than the one we have been using so far, it is called Dashboard_APITest.html. Using that file provides two new buttons, a Get
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
and a Set button (at the top of the web browser page), which are not part of the GWT application, but will call the API methods. If we look at Figure 8.129 then we can see the result of clicking the Get button is to display an alert window with the current name of the Dashboard as its contents.
Figure 8.129 Clicking the Get button on the web page to activate the Dashboard’s API getDashboardName() method.
Then, when we press the Set button, then our JavaScript in the HTML page will call the Dashboards __setDashboardName() API method, which will callback through our defined API into our Dashboard application and change the name, as shown in Figure 8.130.
Figure 8.130 The result of clicking the Set button on the web page that activated the Dashboard’s API setDashboardName() method.
In the Dashboard.java file you will find the segment of code that accomplishes this given in Listing 8.99. Listing 8.99 Definition of the methods involved in providing the Dashboard’s External API. 1.1702 public static void setDashboardName(String s){ #1 1.1703 dashboardName.setText(s); 1.1704 } 1.1705 1.1706 public static String getDashboardName(){ #2 1.1707 return dashboardName.getText(); 1.1708 } 1.1709 1.1710 public native void setUpAPI()/*-{ #3 1.1711 $wnd.__setDashboardName = function(s){ #4 1.1712 @org.gwtbook.client.Dashboard::setDashboardName(Ljava/lang/String;)(s); 1.1713 } 1.1714 $wnd.__getDashboardName = function(){ #5 1.1715 return @org.gwtbook.client.Dashboard::getDashboardName()(); 1.1716 } 1.1717 }-*/; (annotation)
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The first two methods, [#1] and [#2], provide the functionality for setting and getting the Dashboard’s name in normal Java code, which is held on the EditableLabel widget called dashboardName. The third method, [#3], is the JSNI method that sets up the external methods for our API. Within this third method you can see the creation of two new JavaScript methods, one for setting the Dashboard name ([#4]) and the other for getting the name ([#5]). It works by adding two new methods to the browser’s window object, which then means those methods, are available to anyone else that can see the browser’s window object. Within the new method definitions we use JSNI to refer to the static methods ([#1] and [#2]) in the Java class. Thus, if an external entity calls the __getDashboardName() JavaScript method accessible from the browser’s window object, then the GWT Java getDashboardName() ([#2]) method is invoked. To test the API we add two new buttons to our Dashboard_APITest.html file, which call the set and get methods as follows: 1.1718 1.1719 1.1720 1.1721 1.1722 1.1723
Set Get
Each button calls the appropriate JavaScript method. We can use this API approach to provide access to whatever functions we require in our GWT applications by just adding the necessary code. As we mentioned at the start of this section, this approach can be very useful if we are migrating an existing web site component by component, as we can still allow legacy JavaScript code to access new GWT applications in the way they accessed the old JavaScript code the application has replaced. Using an API is a way of calling into our GWT applications, and we could use the same approach to communicate between two separate GWT applications on a web page. Another way to do this inter-application communication is to use a variable stored in the browser page and pass values through that variable.
Talking Between GWT Applications When we drag a Dashboard component application over the trash icon we wish for it to be removed from the screen. Before it is removed though, we need to check if the user should be asked for confirmation as to whether it should be deleted or not. The mechanism we will use for this is for the component application to check a JavaScript Boolean variable, which is set by the main Dashboard application. This is a useful approach when we need to pass data between two distinct GWT applications, but should be limited to that situation. The normal way of passing information between components in your application is to ensure that both components are within the same variable scope within your GWT code, as shown in the left-hand side of Figure 8.131.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 8.131 On the left, we can that the variable is within the scope of both components and, therefore, can be manipulated directly in the code. On the right, the variable is out of the scope of both components, so another mechanism is needed to alter the variable’s value: for the Dashboard, we manipulate the variable using some JSNI code.
To understand this idea of the scope of a variable we can check out the code shown in Listing 8.100. In this code, the variable confirmDelete is visible by both objects. Listing 8.100 Showing how a variable has scope over a couple of objects. 1.1724 public class Test{ 1.1725 boolean confirmDelete = true; 1.1726 1.1727 public class Obj1{ 1.1728 public Obj1(){ 1.1729 confirmDelete = false; 1.1730 } 1.1731 } 1.1732 1.1733 public class Obj2{ 1.1734 public Obj2(){ 1.1735 confirmDelete = true; 1.1736 } 1.1737 } 1.1738 }
Most of the time we can make sure the variables needed are in scope of all components, either by having a class wide variable as we saw in Listing 8.100, by having setters and getters in the sub classes, or perhaps a static variable. Typically, components that need to share variables will be within the same panel, or composite widget, or even application. However, there will be the rare occasion where we cannot keep a variable in scope. In our Dashboard application, we have a variable called confirmDelete that needs to be shared with the menu system and all the DashboardPanel. If the variable is set to true and the panel dragged over the trash icon, then the panel will confirm with the user if they really want to delete it. Whilst it is actually true that we can keep this in scope of the component applications by using a static variable in the Dashboard class, we will break our rules slightly and implement it as a JavaScript variable outside the scope of our GWT to allow us to explain the concept shown in the right-hand side of Figure 8.131. In a model such as that shown in Figure 8.131, we make a JavaScript variable within the HTML page and then use JSNI code within our Java GWT applications to access it. The code we use in the Dashboard.java file to “get” and “set” this JavaScript value is shown in Listing 8.101. Listing 8.101 Exploring the code that is used from the GWT Java code to set and get a JavaScript value set in the Dashboard.HTML file. 1.1739 private native void setConfirmDelete(boolean confirmDelete)/*-{ 1.1740 $wnd.confirmDelete = confirmDelete;
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
At its basic level, the getConfirmDelete() method returns the value of the JavaScript variable confirmDelete, and the setConfirmDelete() method sets it. Similar code for getting the value can be found in our implementation of the DashboardPanel in chapter 5, which completes the link between the main Dashboard application and the component applications. As well as interacting with our own GWT components using JSNI, we can also use it to interact with existing JavaScript libraries.
Loading a JavaScript Library To use an existing JavaScript library, such as the Google Video Search capability, we need to load the necessary JavaScript code into our application. In the case of the Google Video Search, this means loading in two separate JavaScript libraries, both of them downloaded from Google at run time. The first is the Google Ajax Search library (which is common across both the Search and Video Search applications), and the second is the specific JavaScript library for the video searching. As there are two different libraries needed, we will explore each of the two different GWT ways of loading libraries: using the HTML file, and using the module XML file. For the Google Ajax Search library, we will use the HTML.
Using HTML to Load a JavaScript Library The first of the two ways we will look at for loading a JavaScript library is through the HTML file that the application lives within. We do so by using the normal way of linking JavaScript files to a web page, by using the <script> tag. In the head section of the Dashboard.html file we place the following text 1.1746 <script src="http://www.google.com/uds/api?file=uds.js&v=1.0&key=XXX" 1.1747 type="text/javascript"> 1.1748
You need to replace the XXX by a Google Ajax Search API key, which you can obtained from http://code.google.com/apis/ajaxsearch/ (when using the API in Hosted mode, you should get a key that is linked to the http://localhost:8888 domain; if and when you deploy your code to your server, you will need to change this key to one reflecting your URL at that point). That’s it for loading a JavaScript library via the HTML. As you can see, it is nothing different from the way we would normally load a JavaScript file for an HTML page. If we do not have access to the main HTML page, or just want to be more modular, we would add the JavaScript through the Module XML file. For the video searching JavaScript library, we will use this module XML file method.
Using the Module XML to Load a JavaScript Library In Chapter 9 we will talk about using the ability to load JavaScript libraries for applications through the Module XML file. This is a really useful approach if we wish to modularize our applications and ensure that everything
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
required by the application is kept with the application. It made sense for us to load the Google AJAX search JavaScript library in the HTML page as we just saw since it is used by more than one application. The Google Video Search JavaScript library on the other hand is only used by Google Video Search Dashboard application, so we will look at loading this code through that application’s Module XML file. In the GoogleVideoSearch.gwt.xml file we define the following: 1.1749 <script src="http://www.google.com/uds/solutions/videobar/gsvideobar.js"> 1.1750 1.1753
Here we are saying load the gsvideobar.js JavaScript file, but do not wait to tell the application that the file has loaded. We know this last part because in the CDATA part we simply just return the true value. If we wished to wait for the JavaScript code to have downloaded before our application continued, then we could have tried creating an object or calling a method from the loaded JavaScript class returning false if we could not or true otherwise (remembering that the objects would have to be accessed through the $wnd variable). GWT would the keep executing that code until a true value was returned. With the JavaScript library loaded, it is time to start using it. There are probably many different patterns of code we could use to achieve this, but we will look next at the one we have become comfortable with.
Wrapping a Simple JavaScript Library One powerful aspect of GWT is the ability to wrap any existing JavaScript library (your own or 3rd party) as a GWT Widget that can then be used as normal code within our GWT applications. We will need to do this if we cannot translate the library into Java, either because it is too much effort or because we do not have the ability (perhaps due to licensing or visibility of code). Google provide some powerful JavaScript libraries for use in our own web sites. There exists JavaScript libraries for Google Maps and for Google’s search capabilities, all provided by Google, and all falling into our category of JavaScript code that we do not have the ability to write ourselves as Java code, if only for the effort required in doing so! There already exists a very useful GWT Widget that wraps the Google Maps functionality (http://sourceforge.net/projects/gwt/), so we will focus on Google’s Video and Ajax search libraries. Google’s Video Search capability (http://www.google.com/uds/solutions/videobar/index.html) provides us a nice video searching application as shown in Figure 8.132.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 8.132 The Google Video Search application running in a browser searching for videos of the Taj Mahal in India. The whole coding for this is a JavaScript library provided by Google, which is then wrapped in JSNI for use in GWT.
The wrapping of this is relatively simple and a little limited as we wrap one simple JavaScript object and expose only two functions: create() and execute(), but it gives us a good opportunity to examine the concepts in detail and get an understanding of how the flow between the various objects we need to create works. To take us further into JSNI we will then wrap the Google Ajax Search functionality (http://code.google.com/apis/ajaxsearch/), which provides five different searchers in one search control, three of which are shown in action in Figure 8.133.
Figure 8.133 Three views of the Google Search Dashboard application showing the Web, Video, and Blog Search capabilities on the phrase “Kitesurf”.
The key to implementing both of these Dashboard component applications is, of course, JSNI and the use all of the techniques we have discussed so far in this chapter. In the general case we perform the following three steps: 1. Loading the JavaScript Library 2. Accessing the loaded JavaScript library and creating instances of the necessary objects 3. Using the objects
The first of these, loading the JavaScript library, allows us to use a particular capability we need for our application (for example, to use the Google Video Search capability, we need to lead two JavaScript libraries into our application). We have already seen how we can load libraries, so let’s take the next step and begin to look at how we access the library.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Accessing the Loaded JavaScript Library Our journey to create and use objects in our newly loaded JavaScript library follows the hierarchy shown in Figure 8.134.
Figure 8.134 The Object hierarchy involved with implementing a GWT widget that wraps a JavaScript library
First we will create a simple Java class that contains the JSNI code needed to interact with the JavaScript library, we call this the GWT Impl Class. Next, we create a Java class that extends the basic GWT JavaScriptObject class. This class is an opaque object that wraps the JavaScript object obtained by creating a new JavaScript object from the JavaScript library. It also contains an instance of our Impl class to enable us to call the JavaScript objects methods. Finally, we use the JavaScriptObject class inside our GWT widget class to provide a widget. Let’s take these three steps in a little more detail, beginning with the GWT Impl class.
Developing the Implementation Class This class is the first class we build when wrapping JavaScript libraries, and contains all of the JSNI code required for interfacing with the particular JavaScript object we will be wrapping. It is useful to look at JavaScript examples provided by the library’s documentation to see how we should create the implementation class and then identify the methods we wish to expose from that. For the Google Video Search, Google provides the code repeated in Listing 8.102 as an example. Listing 8.102 JavaScript example from the Google Video Search API web site showinghHow to create a simple example. 1.1754 <script type="text/javascript"> 1.1755 function OnLoad() { 1.1756 var vbr; 1.1757 1.1758 var options = { #1 1.1759 largeResultSet : true 1.1760 } 1.1761 vbr = new GSvideoBar( #2 1.1762 document.getElementById("videoBar"), 1.1763 document.getElementById("videoPlayer"), 1.1764 options 1.1765 ); 1.1766 vbr.execute("VW GTI"); #3 1.1767 }
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.1768
(annotation) (annotation) (annotation)
1.1769
In this Google example, an options object is created ([#1]) followed by a new GSvideoBar JavaScript object with two DOM elements representing where the two components will be displayed as well as the previously created options object as parameters ([#2]). Finally, the code executes a search ([#3]) on the newly created GSvideoBar. Our GWT Java implementation class will have to emulate all of this, but we will want to break out the functionality out into different methods to make it more modular. This allows us to include the methods in our GWT approach to create new widgets, such as that in Figure 8.135.
Figure 8.135 Breakdown of the Video Search Widget showing the three components that will be used.
For the GWT Google Video Search functionality we define the implementation class shown in Listing 8.103, which closely follows the original GWT example. Listing 8.103 Implementing the Java class that encapsulates the JSNI calls to an underlying already loaded JavaScript library. 1.1770 import org.gwtbook.client.jswrap.googleVideoSearch.GSvideoBar; 1.1771 1.1772 import com.google.gwt.user.client.Element; 1.1773 1.1774 public class GSvideoBarImpl { 1.1775 1.1776 public native GSvideoBar create(Element bar, #1 1.1777 Element player) /*-{ 1.1778 var options = { #2 1.1779 largeResultSet : true, 1.1780 horizontal : true, 1.1781 thumbnailSize : $wnd.GSvideoBar.THUMBNAILS_SMALL 1.1782 } 1.1783 var theGSvideoBar = new $wnd.GSvideoBar(bar,player,options); #3
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
In our GWT approach we provide a create() method ([#1]) which creates the GSvideoBar JavaScript object we are after. This method is written in JSNI and demonstrates quite nicely how we cross the Java-JavaScript boundary. The method takes two GWT Element objects that represent the browser elements where the search-bar and video player will be displayed. These parameters are used directly within the JavaScript call ([#3]) where GWT compiler will have made sure that the Java objects have become valid JavaScript objects. Notice as well that the JavaScript object is created by accessing it through the $wnd object. After creating the new JavaScript GSvideoBar object in the JavaScript code, we return it as the result of the method call. In this case we now have a JavaScript object, so we must return an object whose type is a GWT Java JavaScriptObject object (we will see how we handle that in the next section – the GSvideoBar Java object is a subclass of the JavaScriptObject). The second method defined in our implementation class is the execute() method ([#5]), which performs the main work in the class by executing a search on the Google video library. We pass in as the first parameter our GSvideoBar object, which was created previously. As it crosses the boundary back into JavaScript through the JSNI interface, the opaque object turns back into a fully visible JavaScript object whose methods we can call in JavaScript – which in this case is the execute() method ([#6]). Let’s look at this mysterious GSvideoBar object, which in our JavaScript code is the true GSVideoBar JavaScript object but in our Java code becomes an instance of the JSObject class.
Adding the JavaScriptObject Class Each JavaScript library will essentially create a JavaScript object on which we will subsequently call methods to provide the functionality we are after. In our GWT approach we wish to keep as much of our code in Java as possible and make use of the strong typing of the language and benefits of the compiler. As we saw in the last section, we try and make our GWT access to the JavaScript library as modular as possible, which results in us having to manage the initially created JavaScript object as a Java object. To do this, we use the GWT JavaScriptObject class. When we pass a JavaScript object out of a JSNI segment of code and into our Java then the GWT compiler will automatically treat it as an object defined by the Java method’s return type. If we define that return type to be a JavaScriptObject, or a subclass of it, then we will receive an instance of an opaque Java object. It is called opaque since we cannot look into or execute methods on it whilst in the Java code. The real use of the standard JavaScriptObject is just to act as a handle to a JavaScript object as we pass it around in our Java code. What we do with the JavaScriptObject class is to extend it hold a reference to our implementation class and contain methods that match those we wish to execute in the implementation. We can see this in action if we look
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
at the Java GSvideoBar class shown in Listing 8.104, which extends the GWT JavaScriptObject class and represents the GSvideoBar JavaScript object. Listing 8.104 Implementing the Java class that embodies a JavaScript object that is treated as an opaque object in the GWT code (we know it is there, but cannot see into it). 1.1792 public class GSvideoBar extends JavaScriptObject{ #1 1.1793 1.1794 private static GSvideoBarImpl impl = new GSvideoBarImpl(); #2 1.1795 1.1796 public static GSvideoBar create(Element bar, Element player) { #3 1.1797 return impl.create(bar,player); 1.1798 } 1.1799 1.1800 public void execute(String searchString){ #4 1.1801 impl.execute(this, searchString); 1.1802 } 1.1803 1.1804 protected GSvideoBar(int opaque) { #5 1.1805 super(opaque); 1.1806 } 1.1807 } (Annotation) (Annotation) (Annotation) (Annotation) (Annotation)
At step [#2] we create a static instance of the implementation class that we will use to create our new JavaScript object. There is a bit of a circular action going on here, we will call the static create() method ([#3]) which in turn calls the implementation class’ create() method, which returns a new GSvideoBar object (created by calling the compiler calling this class’ constructor ([#5]) – the constructor needs to take an int as a parameter for GWT reasons; don’t worry too much about this, you will never use it and there is a potential that this need will be removed in later versions of the toolkit). The result being that the code that calls the initial method now receives a fully created new GSvideoBar object. There are two key benefits in overriding the standard JavaScriptObject class. First, we get to provide a name that is more meaningful (and this is necessary when we start wrapping more complicated JavaScript libraries that require us to manage more than one JavaScript object). Secondly, we can add the JavaScript object’s methods into the new class to make it act more like the opaque JavaScript object. An example of this second point is the execute() method ([#4]). In this class we create a definition that just requires the user to pass in the search string as a parameter. Inside the method we call the implementation classes’ execute() method, passing in this JavaScriptObject as the first parameter followed by the search string. This means that we are always addressing the correct instance of the JavaScript object. The final step to take is to create the class that will present itself to our GWT application as the widget.
Creating the Widget Creating the widget class is an easy task. We need to create two GWT DOM Elements where the video bar and player will reside (we choose to use just some simple Labels ([#1] and [#2])) and then add them to a vertical panel to provide some structure. With the visual structure in place, we call our create method in the GSvideoBar class using the DOM elements representing the Labels ([#3]). We cannot directly pass the Labels, since the code is expecting some DOM Elements, but a simple call to the Label’s getElement() method resolves that.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Once completed, the create method returns back a JavaScriptObject, which we store in the class field gsvideoBar. 1.1808 Listing 8.105 Wrapping our JSNI and JavaScriptObject as a simple GWT widget. 1.1809 public class GVSWidget extends Composite{ 1.1810 1.1811 VerticalPanel theArea = new VerticalPanel(); 1.1812 Label bar2 = new Label("Loading Bar"); #1 1.1813 Label player2 = new Label("Loading Player"); #2 1.1814 static GSvideoBar gsvideoBar = null; 1.1815 1.1816 public GSvideoBar getGSvideoBar(Element bar, Element player){ 1.1817 if (gsvideoBar == null){ 1.1818 gsvideoBar = GSvideoBar.create(bar,player); 1.1819 } 1.1820 return gsvideoBar; 1.1821 } 1.1822 1.1823 public GVSWidget(){ 1.1824 theArea.add(bar); 1.1825 theArea.add(player); 1.1826 initWidget(theArea); 1.1827 getGSvideoBar(bar.getElement(), |#3 1.1828 player.getElement()); |#3 1.1829 } 1.1830 1.1831 1.1832 public void execute(String searchText) { #4 1.1833 gsvideoBar.execute(searchText); 1.1834 } 1.1835 } (annotation) (annotation) (annotation) (annotation)
We complete the widget by defining a widget level method for each of the methods defined in the JavaScriptObject class. In this case, this means creating an execute() method, which we do at point [#4]. This method will call the JavaScriptObject’s execute() method which in turn calls the implementation class using itself as a parameter to perform the video search. That’s all we need to do to perform the simple wrapping of a JavaScript library with one JavaScript object – our widget is now ready to use.
Using the Widget in an Application All of this work has created a brand new GWT widget that we use in exactly the same manner as any other GWT widget. We can see this in the Google Video Search Dashboard application code shown in Listing 8.106. Listing 8.106 Creating a Dashboard application that uses the new Google Video Search widget. 1.1836 public class GoogleVideoSearch extends DashboardComposite{ 1.1837 1.1838 VerticalPanel theArea = new VerticalPanel(); 1.1839 HorizontalPanel theSearchInput = new HorizontalPanel();
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
In the Dashboard’s video search application, we create a new GSVWidget ([#1]) and place it in a new composite that also provides the user with the ability to type in a search phrase and click a search button. When a phrase is entered and the button is clicked, we call the widget’s execute() method, and get presented with a number of videos to select from. In the next section, we look at how we can take this wrapping strategy a little further to look at libraries where there are multiple objects and discuss that it is not always efficient to wrap every single JavaScript object.
Wrapping a Complex JavaScript Library The previous section wrapped a simple JavaScript library where there was only one JavaScript object. As we saw, this is a real life example, but nowadays it is often the case that the JavaScript libraries we might wish to wrap contain more than one object that we need to manage. Let’s look at the Google Ajax Search API, which we use to present the user a Dashboard search component similar to that shown in action in Figure 8.136.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 8.136 Google Ajax API Search in Action in the Dashboard’s Google Search application (showing four of the five available searchers).
We will not go into so much detail in this section on the wrapping, since we did that in the last section. What we do look at here are the main differences between this component and the previous Video Search. First we consider the classes that need to be created.
Generating the Classes This component has a number of different JavaScript objects that could be wrapped, as shown in Figure 8.137 (which is taken direct from the API documentation at http://code.google.com/apis/ajaxsearch/documentation/reference.html ).
Figure 8.137 Identifying the Google Ajax Search API JavaScript objects – the majority of which we need to manage in Java code, making this a complex library to wrap.
For our widget we will wrap the GSearchControl and all of the searchers, and we do so using exactly the same pattern as we saw in the previous section. For example, the GnewsSearch class, becomes the code given in Listing 8.107. Listing 8.107 Looking at the GnewsSearch class, which is part of the Google Ajax Search implementation. 1.1867 public class GnewsSearch extends GSearch{ 1.1868 1.1869 protected GnewsSearch(int opaque) { 1.1870 super(opaque);
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
} private static GnewsSearchImpl impl = new GnewsSearchImpl(); public static GnewsSearch create(){ return impl.create(); } }
The only difference between this and the previous widget is in the GseaachControl we have some more complicated methods such as adding new searchers to the control. Since we are working at a Java level, we have simple methods such as: 1.1879 1.1880 1.1881
public void addSearcher(GwebSearch theWebSearcher) { impl.addSearcher(this, theWebSearcher); }
Where we add a JavaScriptObject to another JavaScriptObject. As we mentioned before, these are opaque object, so at this point we are just playing with objects and it is not until we get to the implementation class that the real work of addition occurs. At this point we cross the Java to JavaScript boundary and then call the JavaScript libraries addSearcher() method on the GSearchControl JavaScript object to add the GwebSearch JavaScript object. 1.1882 1.1883 1.1884 1.1885
public native void addSearcher(GSearchControl searchControl, GwebSearch theWebSearcher) /*-{ searchControl.addSearcher(theWebSearcher); }-*/;
We make a conscious choice not to wrap the GsearcherOptions and GdrawOptions JavaScript objects, and now we will see why and what the impact is of keeping them as Java objects.
Keeping JavaScript Objects as Java Objects When we start dealing with more than one object to wrap, we face a decision. Do we try and wrap every single object that is available, or do we take a more relaxed strategy and wrap only those that it is really necessary to do? Sometimes we do not wish to go through the process of creating full Java classes for all JavaScript objects, either due to time constraints or for other common sense reasons. In the Google Search functionality, there are two objects that perform no function except to hold values of options. While we could create all the implementation and JavaScriptObject classes for the GdrawOptions and GsearchOptions JavaScript objects, we would end up writing vast amounts of code for little benefit. It is easier to keep these objects as simple Java classes and then convert them only when necessary, which, in this example case, is when we draw the control or add searchers. When we draw the search control we call the draw() method in the SearchControlImpl, which takes as parameters a JavaScriptObject (the search control), a DOM element (where the control will be drawn) and a Java object representing the options. Unfortunately, GWT will not translate our options object from Java to the required JavaScript object itself, so we must do that, but it is relatively simple to achieve, as shown in Listing 8.108. Listing 8.108 Examining the Google Search Draw method where the Options objects become JavaScript objects.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.1886 public native void draw(GSearchControl searchControl, 1.1887 Element div, 1.1888 GdrawOptions options) /*-{ 1.1889 var theOptions = new $wnd.GdrawOptions(); #1 1.1890 if ([email protected]::isInputAttached()()) { #2 1.1891 theOptions.setInput( 1.1892 [email protected]::getInputElement()()); #3 1.1893 } 1.1894 if ([email protected]. GdrawOptions::isDrawModeSet()()) { 1.1895 if ([email protected]::getDrawMode()() == 1.1896 @org.gwtbook.GSearchControl::DRAW_MODE_LINEAR) { 1.1897 theOptions.setDrawMode($wnd.GSearchControl.DRAW_MODE_LINEAR); 1.1898 } else { 1.1899 theOptions.setDrawMode($wnd.GSearchControl.DRAW_MODE_TABBED); 1.1900 } 1.1901 } 1.1902 searchControl.draw(div,theOptions); #4 1.1903 }-*/; (annotation) (annotation) (annotation) (annotation)
We simply create a new JavaScript object of the GdrawOptions by writing the code at point [#1] (remember that we need to get the new object through the $wnd object). Once we have the JavaScript GdrawOptions object we walk through the various fields in the Java object checking for values and then setting up the appropriate equivalent in the JavaScript object (for example at [#2] we set up an alternative input area if one is provided). Finally, we use the new options object in the call to draw the search control ([#3]). The Google Search Control also provides the functionality for the user to save particular search results through a call back mechanism, so we should check out how we could implement and support such functionality.
Calling User-Defined Code from a Library It is not unusual for a 3rd party JavaScript library to provide the ability to call some user defined JavaScript code if an event happens in the library. In the case of the Google Search Library, we can tell it to display a label after each search result that if clicked upon some user defined action will be performed (typically the result of the search can be stored when clicking on the link). We show this in action in Figure 8.138.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 8.138 Implementing the feedback mechanism of Google Search to display an Alert when the Keep label is clicked.
As this is GWT we will try and implement this in a GWT style, the first step of which is to try and emulate the GWT Event Handling mechanisms we saw in chapter 6. We will create a new KeepListener interface, which requires a simple onKeep() method to be implemented that will be executed when the user clicks on the label in the widget. The listener is simply defined as the interface shown in Listing 8.109. Listing 8.109 Defining the KeepListener class. 1.1904 public interface KeepListener { 1.1905 1.1906 public void onKeep(); 1.1907 }
We will use this listener as shown in Listing 8.110 by adding it as an anonymous class to a search control and implementing the onKeep() method. Listing 8.110 Using the KeepListener class. 1.1908 searchWidget = new GSearchWidget(); 1.1909 GSearchControl sc = searchWidget.getGSearch(); 1.1910 sc.addOnKeepListener(new KeepListener(){ 1.1911 public void onKeep() { 1.1912 Window.alert("Tried to save a Search Result Element"); 1.1913 } 1.1914 });
In the implementation of GSearchControl we place the code that ties the listener to the call back functionality of the library. The way this is performed is usually dependant on library, in the case of the Google Search we need to call the setOnKeepCallback method. Google define this method as follows: “For instance, if this method is called as .setOnKeepCallback(foo, MyObject.prototype.myKeephandler), when a user clicks on the keep label, a call to foo.myKeephandler() is called.”
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
This is not so easy for us to implement in GWT! However, we can use the trick that we employed when creating an API again and implement the callback as shown in Listing 8.111. Listing 8.111 Implementing the Feedback. 1.1915 public native void setOnKeepCallback(GSearchControl searchControl, 1.1916 KeepListener theListener) /*-{ 1.1917 $wnd.__callbackMethod = function(){ #1 1.1918 [email protected]::onKeep()(); 1.1919 } 1.1920 searchControl.setOnKeepCallback(null, #2 1.1921 $wnd.__callbackMethod, 1.1922 $wnd.GSearchControl.KEEP_LABEL_KEEP); 1.1923 }-*/; (annotation) (annotation)
At [#1] we register a new JavaScript method that will call the onKeep() method of the listener passed in as the parameter. To tie this together we then register our new method with the search control at point [#2] to be the method called if the user clicks on the keep label after a search result. The final step in completing the generation of the Google search widget is to actually create the widget.
Using a Complex Wrapped Widget in an Application When used in the Dashboard application, we simply add the Widget as a single component in a DashboardComposite, resulting in the application shown in Figure 8.139 (where we have searched for one of the best contributions Finland has given the world for a cold winter’s night – the sauna).
Figure 8.139 The Google Search application in full
Creating a widget from a 3rd party JavaScript library means that we must adhere to any rules that the underlying JavaScript library may place on us. In the case of the Google Search library, there is a specific order in which tasks must be performed, as provided in their documentation, and that is:
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
6. 7. 8. 9.
Create the Search Control. Add the Searchers. Create and Options. Finally, draw the Control.
Listing 8.112 shows how we achieve this to create the widget shown in Figure 8.139. Listing 8.112 Creating a complicated Google Search application using only a few lines of Java code. 1.1924 searchWidget = new GSearchWidget(); #1 1.1925 GSearchControl sc = searchWidget.getGSearch(); |#2 1.1926 webSearch = GwebSearch.create(); |#3 1.1927 webSearch.setSiteRestriction("www.manning.com"); |#3 1.1928 sc.addSearcher(webSearch); |#3 1.1929 videoSearch = GvideoSearch.create(); 1.1930 sc.addSearcher(videoSearch); 1.1931 newsSearch = GnewsSearch.create(); 1.1932 sc.addSearcher(newsSearch); 1.1933 blogSearch = GblogSearch.create(); 1.1934 sc.addSearcher(blogSearch); 1.1935 localSearch = GlocalSearch.create(); 1.1936 sc.addSearcher(localSearch); 1.1937 GdrawOptions options = new GdrawOptions(); |#4 1.1938 options.setDrawMode(GSearchControl.DRAW_MODE_TABBED); |#4 1.1939 searchWidget.draw(options); |#5 (Annotation) (Annotation) (Annotation) (Annotation) (Annotation)
First we create a new instance of the search widget ([#1]) then we use the getGSearch() method on the widget to create the JavaScript instance from the library. Next we add a few searchers to the widget, namely a web, video and news searcher ([#3], [#4], [#5], [#6] and [#7]). With the searchers added according to the Google Ajax Search specification, then we create a new set of drawing options to tell the widget it should draw itself in tabbed mode ([#8]). Finally, we draw the search control in [#9].
Summary JSNI is an extremely powerful way of interfacing with existing JavaScript libraries and for filling in the gaps where GWT might not yet have the functionality you need. It allows us to apply some of the good control and typing aspects of Java to our JavaScript interfaces; however, we should keep the use of JavaScript as a minimum. View using JSNI as you would writing assembly language code segments in a high-level language project – it is for specialized tasks, not for every day use. Indeed, the main reason for restricting the use of JavaScript is due to having to deal with cross-browser issues, just as assembly language coding restricts your ability to move projects to other machines. If you are going to use JSNI, remember that it is only valid in your client-side components, and you will need to use the $wnd and $doc variables instead of the standard JavaScript window and document variables because of the way GWT loads your application. You should also remember that, when executing JavaScript code, you do not have the protection of GWT when it comes to browser differences (e.g., the JavaScript getElementById() method is not as robust as the GWT’s cross-browser DOM.getElementByID() method). Finally, GWT and Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
JSNI do not allow you to get around any normal restrictions of JavaScript coding; for example, you still cannot subvert the security mechanisms browsers employ. We have now reached the end of our journey through the more client-side related aspects of GWT, and it is time to consider the flexibility that GWT provides when building a real-sized application – using GWT’s modularization aspects.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
9 Modularizing an Application Now that we have built all of our user interface components, it is time to start looking at how we modularize our application. A principle of software design, which has now been with us since the 1960s, is the ability to develop code in a modular fashion in order to increase re-use and reliability. GWT supports modular development in two ways. First, since we are building a Java application, we have access to all the benefits of Java’s package structure. Second, and this is new to GWT, is the XML Module concept. Definition A module is a logical collection of classes and resource definitions that it makes sense to manage as its own entity.
So far you have probably been thinking of our Dashboard application as a single chunk of software, which is not incorrect, but it is also true that an application can be constructed out of a number of modules, each module defined in a module XML file where we also define a number of resource related items. Our Dashboard lends itself naturally to modularization since we have a main application and a number of component applications.
Creating a Modularization Structure We mentioned previously that there is quite a synergy between GWT modules and Java’s package structure. They are similar, but different things. A Java package consists of a set of classes organized together for convenience into the same directory. A GWT module consists of a set of configuration information, relating to a particular GWT project – including entry points (if there is one), style sheet references, other modules this module depends on, etc. We will discuss all of this in more detail in chapter 9.3 when we show how the Dashboard is modularized. One word of caution: it is not unusual to see a set of Java packages relating to a particular project and, therefore, a GWT module -- it is possible to get confused that a GWT module equals a set of Java packages, when it does not. You should also be aware that, just because you reference a module, does not mean that the Java packages loosely associated with that module are available to your code; you still need to add them to your class-paths (and that means to both the hosted and web mode tools class-paths). Failure to do this will result in your Java compiler (or IDE) informing you it has no idea where to find the necessary code – this can be a frustrating error if you forget that GWT Modules and Java packages are not the same thing!
Modularization within GWT Is modularization really used in practice? Well, yes, the GWT distribution itself is made up of a number of modules, including the DOM, TextBox, History, JUnit, i18n, JSON, etc. We can see these modules in their hierarchy in Figure 9.140.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 9.140 Hierarchy of modules supplied with the standard installation of GWT
If we were to look at the module XML file for i18n, we would see that it defines the default locale property, provides the JavaScript code for determining which web browser is being used, and also identifies a special Java class called a generator that should be executed on all of the i18n interfaces in the module to produce Java classes that bind the interface to the various property files. Modules can be very powerful. In this section we look at two things. First, we examine all the elements that can be included inside a module’s XML module, which could resemble the very comprehensive version shown in Listing 9.113. It is quite a busy listing, as it lists all the aspects, but we will go into more detail in the remainder of this section. Listing 9.113 Comprehensive Module XML File demonstrating the tags that are available 1.1940 <module> 1.1941 #1 1.1942 #2 1.1943 #3 1.1944 #4 1.1945 <servlet path='/upload' 1.1946 /> #5 1.1947 <define-property name=”externalvisible” #6 1.1948 values=”internet,intranet” /> 1.1949 <property-provider name=”externalvisible”> #7 1.1950 Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The second thing we look at in this section involves defining how we will break our application up into modules and provide the definitive module structure for the Dashboard. So, let’s get started with looking at the various parts that may be included in a module XML file by looking at how we include other modules into an application.
Including Other Modules in an Application In this section we discuss how we indicate that this module will include other modules. We saw in the introduction to this section that a module can inherit the GWT User module by simply writing the following: 1.1988 1.1989 1.1990 1.1991 1.1992
<module>
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
If we look at the definition of the User module, we see that it directs the compiler to include the majority of the GWT system. How do we know that? Well, we can look at the User.gwt.xml file included in the com.google.gwt.user package (see gwt.user.jar). In that module definition, we can see the following: 1.1993 1.1994 1.1995 1.1996 1.1997 1.1998 1.1999 1.2000 1.2001 1.2002 1.2003 1.2004
<module>
The inherit tag indicates that the module should inherit all the contents from the specified module. As you can see, we can have multiple inherit tags in a module definition, and in this case we link to all of the basic functionality of GWT (XML, JSON processing, HTTPRequest (traditional AJAX) and internationalization are not included by default). Most modules will inherit the User module (User.gwt.xml), since this contains the “inherits” for the majority of GWT functionality. Thus it would be expected to find as a minimum the following entry in your module file: 1.2005 1.2006 1.2007
If you are planning on using some of the extended functionality provided with GWT, such as the XML processing, the ability to handle JSON responses, internationalization, or the AJAX style interaction then you will need to include the appropriate inherits tags in your module XML file. Following are the four tags that represent what we have just mentioned: 1.2008 1.2009 1.2010 1.2011 1.2012 1.2013
Tip Basic applications just need to inherit the com.google.gwt.user.User module. It can be easy to forget that if you user i18n, XML, JSON or HTTPRequest you need to inherit other GWT modules. 1.2014
One thing to note is that GWT modules should not be confused with the Java class-path – the two are complimentary but not dependent upon each other. The class-path is used by the Java compiler and hosted mode to find code that is required. The module XML file explains to the GWT compiler what GWT modules it needs to find and use in the application. You must remember that the Module file can contain many different aspects of the application, not just more includes! As well as inheriting aspects of the standard distribution, we can inherit our own, or 3rd party, components in exactly the same way. If we do so, then care must be taken to set the qualified name of the module correctly, for example using the GWT Widget Library, which we discuss near the end of this chapter, requires the following inherits tag: 1.2015
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.2016 1.2017
And it is at this point where the confusion might start to begin between setting Java classpaths and inheriting modules. By inheriting a module we are telling the compiler certain things about a module, but we are not saying where the actual code is – that needs to be done by setting the classpath to include the code location (this classpath needs to be set both in the web and hosted mode commands, i.e. for the Dashboard we need to set it in Dashboardshell and Dashboard-compile. If you’re using a IDE then you might need to set the classpath in that too – e.g. adding the code as an external JAR in Eclipse). For the Dashboard we will create the module structure shown in Figure 9.141. This contains one module for the main Dashboard application, which inherits the modules for each of the individual component applications.
Figure 9.141 The module structure that will be used in developing the complete Dashboard application. Here, it can be seen that the component application modules will all be inherited by the main Dashboard module.
The benefit of creating a structure like this is that we can treat each component application in isolation, allowing it to have, for example, its own style sheet and resources. If these applications need to change in the future, we have now isolated those changes to just that component. Our Dashboard application’s Module XML file will, therefore, be as shown in Listing 9.114. Listing 9.114 The initial definition of the Dashboard module XML file 1.2018 <module> 1.2019 |#1 1.2020 |#2 1.2021 |#3 1.2022 |#4 1.2023 1.2024 |#6 1.2025 |#6 1.2026 |#6 1.2027 |#6
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Why have we included the standard modules that we have (i18n and XML)? Well, we mentioned in the design phase that the Dashboard will be using GWT’s internationalization capabilities, so we need to include that module. Similarly, the Bookmark menu bar will be populated by retrieving an XML file from the server and then processing its contents, so we also need the XML module. If you’ve peeked ahead in the book, you know that we have other components that, for example, process JSON data. So, you might be expecting to see GWT’s JSON functionality included in the Dashboard’s XML Module file. But, we do not need to since we can include that in the XML Module file that relates directly to that component. This is the reusability benefit in action – by putting the JSON reference into the component application, as opposed to at the Dashboard level, we can easily reuse the component elsewhere, safe in the knowledge that all it needs is kept with it. As we have said previously, there is no direct link between the GWT Module file and the Java package structure we choose, but it is often pragmatic to tie the two together. To some extent you can see this in Listing 9.114 where each component applications Module definition is inside a new Java package reference. It therefore makes sense to place the actual Java code for each application in the same named package – actually, this is an assumption that the GWT compiler makes as a default. For example, the XML Module file for the Calculator component application is stored as Calculator.gwt.xml in the package/directory org.gwtbook.client.ui.calculator (according to the module definition shown in Listing 9.114) – we will also store the necessary Java code in the same package. With the basic module inheritance out of the way, it is time to look at the other configuration aspects that can be stored within a module definition, starting with setting source and other resource paths.
Setting Source and Other Resource Paths By default, the GWT compiler uses the client directory relative to the module file to look for the source files of your GWT application/module. That is to say that, if your module file’s fully qualified name is org.mycompany.MyApp.gwt.xml, then it will be stored in the org/mycompany directory, and the compiler will assume that the source code is in the org/mycompany/client directory. If you have used the GWT applicationCreator tool, then this structure will have been automatically decided for you. Sometimes you may decide that this location is not best for you, and then you can set the source path to a different location. Placing the following entry into your module XML alters the default path that code is searched for: 1.2030 1.2031 1.2032
<source path="path-to-code"/>
Where path-to-code must be a package name, and any classes found in that package or sub-packages must follow the same rules as other classes that are to be translated by the compiler into JavaScript (typically that the source code must be available and that it must conform to Java 1.4 syntax). If this entry is not included, the value <source path="client"/> is assumed and this is why all of our package names have the package name client inserted into them. Similarly, the compilation process assumes that any files in the public sub-package relative to the module XML file are publicly accessible resources and so will be copied into the compilation output as is (in the Dashboard, the public folder is used to store all of our trash icon images, for example). To alter this value, the following tag can be used:
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.2033 1.2034 1.2035
The path must again be a package name, but there are no restrictions on the content of that package or any subpackages as those contents are just copied to the output folder by the compilation process. It is possible to filter the files that will be cpoied from the development structure to the run stucture as public resources using pattern-based filtering. This places fine-grained control over which resources get copied to the output directory and the control is based on the FileSet notion from Apache Ant implementation. using the FileSet tag. Not all aspects of FileSet are supported - identifies those attributes and tags that are. Table 9.37 Attributes and tags used in pattern-based filtering of GWT public resources Attributes Tags includes Include excludes Exclude defaultexcludes casesensitive
By default the defaultexcludes attribute of the public tag is set to true, and excludes quite a comprehensive set of file patterns, for example **/*~ , **/.#*, **/CVS and **/vssver.scc. To enable any of these patterns, set the attribute to false, e.g., 1.2036 1.2037 1.2038
If we wish to explicitly include or exclude files, then we can specify them using the includes or excludes element, placed within the definition of the public tag. As an example, to exclude a file called test.html, we would write the following: 1.2039 1.2040 1.2041 1.2042 1.2043
<exclude name="test.hmtl"/>
As with the code path, if no entry for the public tag is provided, then it is assumed that it defaults to . Another path-related aspect that is definable in the module XML file is the path of Servlets, or server resources used in hosted mode execution.
Defining an Application’s Server Resources When running in hosted mode, we need to be able to deploy any GWT RPC code we have as Servlets into the hosted mode web server. The first step towards doing this is to register the servlet path and class in the module XML file using the servlet tag. We will do this for a few of the applications given in chapters 10 through 13. Let’s take one of those as an example now (remember we will be adding what we see next into the appropriate component application’s module file and not the Dashboard’s). For the Server Status component application, we will require our application to talk to a servlet which will return some status details of the server. Within the Java code we will refer to this servelt as having a path of /server-status. In the Server Status application’s module file we will bind that path to the actual Java class which provides the functionality using the <servlet> tag. In this examples case, the Java class that provides the functionality is called
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
ServerStatusImpl and is found in the org.gwtbook.server package. To use this servlet in our code, we define a servlet entry in the module XML file as follows: 1.2044 <servlet path="/server-status" />
We will see later in chapter 10 that your application will use this path in the code when it sets the Entry Point for the Service, using the setServiceEntryPoint(String) method. We will also see in chapter 16 that this is not the final step required to set up servlets if we are deploying outside of hosted mode (in that case we need to create a web.xml file for use on our servlet server). Now we move on to look at a few tags that start driving the number of permutations of JavaScript the compiler will be required to produce – namely the tags associated with properties.
Managing an Application’s GWT Properties We discuss properties in detail in chapter 15, but since they are set and manipulated in the Module XML file, we need to talk about them here too. The most obvious impact of involving and managing properties for a GWT application that you will have seen to this point in the book, is that they drive the number of JavaScript permutations that are produced. The most obvious example where we can see properties at use within GWT is in the generation of a separate piece of JavaScript code per browser that is supported. You may not have noticed this yet, since the processing happens in the background and requires no interaction by ourselves. In fact, we do not even touch the properties involved in these decisions regarding browser choice. Where we have been a little more hands on with properties is when we looked at internationalization. Earlier in this section we saw that we extended the locale property to include the Swedish locale. Adding locales to be managed tells the GWT compiler that it needs to include additional permutations for those new locales. Even if we have included all of the code new locales, if we forget to extend the property in the XML file, that new code will not be used. We should note that GWT properties are not directly accessible to your application; they are used to drive permutations. In this section we shall look at the following: Defining and extending properties. Handling properties. So on to the first item on our list; defining properties.
Defining and Extending Properties Properties are defined using the simple define-property tag. 1.2045 1.2046 1.2047
If we wish to define our own properties then we use the define-property tag to set the property name and optionally a set of initial values. We do so as follow: 1.2055 1.2056 1.2057 1.2058
We also saw in chapter 3, where we introduced internationalization, that we can add new values to the already defined locales property. This was done using the extend-property tag where, for example, adding the ISO language code for Swedish was performed with the following tag: <extend-property name="locale" values="sv"/>
We use the extension idea where we are inheriting a set of properties from an existing module and we wish to add new values. In general we can extend already defined properties using the <extend-property> tag, as follows: 1.2059 1.2060 1.2061 1.2062
If you are wondering where these values appear in your code after compilation then for the Dashboard application you should look at the org.gwtbook.Dashboard.nocache.html file in the compiled output. In there you find some automatically generated JavaScript representing values for the User Agent property similar to the following: 1.2063 1.2064 1.2065 1.2066 1.2067 1.2068 1.2069 1.2070 1.2071
As well as some similar for JavScript the locales, in this case we have two the default locale that comes with the i18n module we have inherited and the Swedish locale property that our Dashboard XML module has extended: 1.2072 1.2073 1.2074 1.2075
For the Dashboard, we will define some new properties in Chapter 15 that allows us to restrict the component applications that are available based on whether the application is being accessed from an intranet or the Internet. In your own applications, if you define new properties, then GWT takes care of producing the additional JavaScript to representing them values.However, we still need to define just how we determine which property is the one selected – in other words, how to handle the properties.
Handling Properties and Managing Differences Once properties are provided then we need to provide a mechanism to manage the differences when one is selected over another. The compilation process will produce a number of permutations of JavaScript covering all of
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
the possible property value permutations, and subsequently the application loading mechanism will select the appropriate one. The simplest way of determining the appropriate property is to define it directly in the application’s HTML file (similar to how the locale property is set). We can alternatively provide some code which allows the application’s loading mechanism to determine the correct property (which is how the browser selection works). To take this second approach, we need to provide some JavaScript code in a <property-provider> tag within the module file. The generic pattern for providing such code is: 1.2076 1.2077 <property-provider name=”property-name”> 1.2078 1.2081 1.2082
Let’s look at the relatively simple JavaScript code shown in Listing 9.115. This is the code used by GWT to determine the user agent property (aka which browser is the application about to be executed within). 1.2083 Listing 9.115 Examining the GWT provided JavaScript code, which determines the browser into which the application is being loaded. 1.2084 var ua = navigator.userAgent.toLowerCase(); 1.2085 if (ua.indexOf("opera") != -1) { 1.2086 return "opera"; 1.2087 } 1.2088 else if (ua.indexOf("safari") != -1) { 1.2089 return "safari"; 1.2090 } 1.2091 else if ((ua.indexOf("msie 6.0") != -1) || 1.2092 (ua.indexOf("msie 7.0") != -1)) { 1.2093 return "ie6"; 1.2094 } 1.2095 else if (ua.indexOf("gecko") != -1) { 1.2096 var result = /rv:([0-9]+)\.([0-9]+)/.exec(ua); 1.2097 if (result && result.length == 3) { 1.2098 var version = (parseInt(result[1]) * 10) + parseInt(result[2]); 1.2099 if (version >= 18) 1.2100 return "gecko1_8"; 1.2101 } 1.2102 return "gecko"; 1.2103 } 1.2104 return "unknown"; 1.2105
This code is wrapped in the property-provider tag in the UserAgent module XML. After compilation of the Dashboard application, the org.gwtbook.Dashboard.nocache.html file will include this code wrapped as a function in the window array: 1.2106 1.2107 1.2108 1.2109 1.2110 1.2111 1.2112
window["provider$user.agent"] = function() { var ua = navigator.userAgent.toLowerCase(); if (ua.indexOf("opera") != -1) { return "opera"; : :
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.2113
}
If other properties are present then a similar process of creating JavaScript object for their values and copying the property-provider code into the window array. It is then left to the selectScript() method, which is generated by the GWT compilation process, in org.mycompany.client.Dashboard.nocache.html to determine which permutation needs to be selected as the correct one (for the browser, locale and externalvisibility property in the Dashboard’s case). This method uses a number of helper functions to extract the correct JavaScript permutation. We show a simple extraction from one of the helper methods here: 1.2114 1.2115 1.2116 1.2117
We can see that, ignoring the first parameter, both these entries will match to the User Agent being “opera”. The first entry matches to the locale being the default locale, and the second to the locale being set to sv (for Sweden). The final parameter refers to the MD5 file name that the compiler has given to this particular JavaScript permutation and is the one that will be loaded (if you look in your compiled output of an application you will see a number of these types of named files – one for each permutation. The more different families of properties you wish your application to manage, the more permutations there will be, by default there are five representing each of the browser types). In Chapter 15 we will write some JavaScript code that determines the particular role that a user accessing our application has, and that will be used to restrict the number of component Dashboard applications that the user can access. That is how property files drive the generation of numerous permutations of JavaScript code and how the link is made for loading the correct permutation. In addition to permutations being driven by properties, their generation can also be driven by the need to replace class files, which is also property based.
Replacing Classes Based on Property Values One GWT technique that we have not explored yet is the replacement of Java files with others based on the value of a property. We have actually used this functionality already, when creating the PNGImage widget, and also GWT itself uses it quite a bit to select appropriate browser specific behaviour, e.g. the DOM manipulation class. But, as that happens in the compiler, you have probably been unaware of it. The technique is not restricted to the user agent property (browser type), and we will use it later in this chapter to show how complete application components can be altered based on the locale property. The most common place class replacement occurs is when dealing with the different ways browsers deal with the DOM. GWT copes with these differences by providing a simple DOM class we use in our Java code which is implemented by a number of increasingly specific classes that inherit the original class which provide browser specific methods. We can see this hiearchy in Figure 9.142.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 9.142 The various DOM implementation classes shown in their hierarchy. This allows GWT to cope with all the browser differences in DOM implementation.
When the code is compiled or executed in Hosted mode, we need to be able to select the correct implementation for the browser. Luckily, GWT will do all of this work for us in the background, as long as we tell it the rules in out Module XML file. To tell GWT which particular class it should use for a particular property value, we use the replace-with tag inside the appropriate module XML file. The generic format is 1.2118 1.2119 <when-type-is class=”old-class-name”/> 1.2120 <when-property-is name=”property-name” value=”property-value”/> 1.2121
Let’s look briefly at a concrete example of this from the DOM module XML. In this example we are replacing the generic DOMImpl class with the DOMImplOpera class if the User Agent property is set to the value “opera”. 1.2122 1.2123 1.2124 1.2125 1.2126 1.2127
Replacing classes is one mechanism that GWT has to manipulate your basic code based on directions given in the module XML file (or one it inherits from). This functionality relies on the various different classes existing in the first place. We can additionally create code on the fly using something called Generators, the existence of which are also defined in the module XML file.
Registering Generators in the XML Module File Generators are used to create new code based on the existence of an existing class or interface. This is used in the i18n approach where GWT takes the interface we use in our Java code and the properties files we have defined and then at compile time produces a number of Java classes that implement the interface and bind method names to values in the properties file. Generators are registered using the generate-with tag, whose generic format is: 1.2128 1.2129 1.2130 1.2131
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.2132
There are several examples of Generators provided with GWT where they are used for generating i18n Java classes from the interfaces and properties files we provide. In chapter 14 we build our own Generator that takes our DashboardComposite Dashboard applications and extends them by adding a default About menu item in the options menu bar. This is done by a class we will write called DashboardCompositeGenerator which is executed for every instance of the DashboardComposite class the compiler finds. The compiler knows to do this because we place the following entry in the Dashboard Module XML file: 1.2133 1.2134 1.2135 <when-type-assignable /> 1.2136 1.2137
Within the generator we can perform introspection on the java class, and we show this by creating an About dialog box that lists the internal methods and fields of the class under scrutiny. It is important to note that this introspection is happening at compile time and is therefore no use to us if we are looking for a way to perform introspection at run time (something that is currently not possible within GWT). We are nearly there; two more entries in the Module XML to go, and the first of these is the ability to inject resources (JavaScript library or a Cascading Style Sheet) into an application.
Injecting Resources into an Application at Run Time GWT provides the ability to inject into applications JavaScript and CSS resources at run time (as opposed to defining them in the applications HTML file). You may wish to do this, for example, if you are embedding a GWT application into an existing web page and you wish to keep the application's CSS separate from the web page HTML. We will look at both types of injection in turn: first, injecting JavaScript resources into applications and second, injecting style sheet resources into applications at run time. Both of these types of injection again demonstrate the power of GWT’s modularization approach. In the Dashboard we inject a CSS style file for the Dashboard itself in the Dashboards module file. Each component application then made responsible for injecting any CSS or JavaScript code it requires. This way, if we reuse a component application in another application, then it comes already bundled with the necessary references to make it work. Injecting JavaScript and CSS is a useful technique in supporting reusability of our code and so we will examing them both now, starting with injecting JavaScript code.
Injecting JavaScript Resources into an Application To automatically inject a previously written JavaScript library or code into the web page for use by the GWT application, we use the script tag. This is generically written as: 1.2138 1.2139 1.2140
<script src="js-url">script ready-function body
The js-url is the URL of the JavaScript file we wish to be injected. Inside the body of the tag, we can insert some code that returns true when we are sure that the JavaScript code has loaded and false otherwise. It is often enough just to check if a particular function exists in the Browser’s model in order to return the true value. However, when injecting the JavaScript it is evaluated such that any JavaScript code required to be executed is. So when picking a particular the function that you wish to see has loaded care must be taken. Consider the simple JavaScript segment that we may wish to inject that includes two functions and some executable script: 1.2141 1.2142
function fn1() {
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
// some functionality } doSomething(); function fn2() { // something other functionality }
When we inject this code, the doSomething() function might take some time to execute so it is wise check for the availability of the fn2 function, for example: <script src="InjectedScript.js">
Once the injected JavaScript is loaded into the page, it is safely available for access through GWT’s JavaScript Native Inteface (JSNI). It is also perfectly possible to load JavaScript this way and not check for objects to exist – but if we wish to do that we still need to provide some code that returns the value true, otherwise GWT will wait forever. We do this in the GoogleVideoSearch Dashboard application we build later in chapter 15. In this case, our injection command looks like this: 1.2152 <script src="http://www.google.com/uds/solutions/videobar/gsvideobar.js"> 1.2153 1.2156
The second type of injection possible is the injection of Style Sheets.
Injecting Style Sheets into an Application While Style sheets can be added to your application by adding the appropriate links into the applications HTML page, there may be occasions where an application will sit within existing pages that we do not wish to affect. For the Dashboard application, we wish to keep style sheets for the component applications together with the application. Rather than having to put a link to all the style sheets in the Dashboard.html file, we can use each applications module XML file to inject the appropriate style sheet as needed. This is performed by the simple <stylesheet> tag: 1.2157 1.2158
<stylesheet src="css-url"/>
where css-url is the URL to the CSS file we wish to inject. It is possible to indicate a number of CSS files to be injected into the application (however GWT sometimes gets a little hung up in Hosted Mode when there is more than one style sheet to inject – this is nothing to worry about, since clickin on the Hosted browser screen gives GWT the kick it needs to continue, and this doesn’t happen in web mode). Figure 9.143 shows the Dashboard where we have not bothered to inject any styles in via the module XML files except for the Slideshow application.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 9.143 Dashboard application where styles have only been injected for the Slideshow component application (picture also shows the unstyled Calculator and Clock applications in an un-styled Dashboard)
The order in which those files are injected is in the order in which they are listed in the module XML. So, if you include the same name style rule in several style sheets, only the last used one will be used. We make use of this ordering in the GoogleVideoSearch Dashboard application where we need to first load a style sheet provided by Google, and then wish to alter some of those aspects, such as the video size. Our GoogleVideoSearch.gwt.xml file contains the following two entries in this exact order to achieve this: 1.2159 1.2160 1.2161
We have now looked at all but one of the aspects that could be placed within an module XML file. We have seen how to inject code and style sheets, how to tell the compile to replace code with generators or property specific classes and how to inherit other modules. Now we look at the tag that turns a simple module into an application.
Setting an Application's Entry Point If the Module XML relates to a GWT application, then it is necessary to place an entry-point tag into its associated module XML file. The value of this tag is used by the compilation process to identify what code needs to be executed upon loading the application. The generic template for the entry-point tag is: 1.2162 1.2163 1.2164
<entry-point />
This section is really short and sweet, as there is not much to say about the entry point apart from the class mentioned must itself extend the EntryPoint class. Only our main application needs an entry point, so you will find this tag in our Dashboard’s module XML: 1.2165
<entry-point />
but there will be no correpsonding entries in the component applications, since they are not entry points to the Dashboard application. So now we have listed all the theoretical aspects that could be included in Module XML file, let’s look at thi in practice by considering a version of the Module XML file for the Dashboard application.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The Dashboard’s Module XML File If you look at Dashboard’s Module XML file, then you will see that we have used almost all of these techniques 1.2166 <module> 1.2167 |#1 1.2168 |#1 1.2169 |#1 1.2170 1.2171 |#2 1.2172 1.2173 |#3 1.2174 |#3 1.2175 |#3 1.2176 |#3 1.2178 |#3 1.2179 |#3 1.2180 |#3 1.2181 |#3 1.2182 |#3 1.2183 |#3 1.2184 |#3 1.2186 <stylesheet src="CSS/Dashboard.css"/> #4 1.2187 <entry-point /> #5 1.2188 <extend-property name="locale" values="sv"/> |#6 1.2189 <extend-property name="locale" values="en_US"/> |#6 1.2190 <define-property name="externalvisibility" |#7 1.2191 values="intranet,internet"/> #7 1.2192 <property-provider name="externalvisibility"> |#8 1.2193 |#8 1.2205 |#8 1.2206 |#9 1.2207 <when-type-assignable |#9 1.2208 /> |#9 1.2209 |#9 1.2210 |#10 1.2211 <when-type-is /> |#10
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
This module XML description covers that majority of the areas we have talked about so far. At [#1] we introduce the standard GWT functionality that we will be using in the Dashboard – the standard user modules, as well as the XML handling and internationalization. If you’ve looked further forward in the book already, then you will know that we will be using GWT’s JSON capability but we do not list that here as the Dashboard itself does not use JSON – the module XML file for the appropriate component application is responsible for included that JSON functionality. After including the necessary standard GWT functionality we then proceed to include the GWT Widget Library ([#2]) and all of the component applications that are included in the Dashboard application ([#3]). We inject the Dashboard’s style sheet at [#4], but note that this style sheet only includes the main Dashboard specific styling – styling for all of the component applications is delegated to the Module XML files for each of those applications. At [#5] we set the entry point for the Dashboard application and then proceed to extend the locale property ([#6]) to include a Swedish locale and a locale for American English (see chapter 15). Not content with extending the locale property, we create our own user defined property (again see chapter 15) at [#7] along with providing some code in a property provider ([#8]) which determines the initial value of the user defined property. Finally, we define a Generator (see chapter 14) that will support our Dashboard component applications ([#9]) and will perform some “introspection” on the classes, and wrap the whole file up with defining some replacement functionality which will make the application use (see chapter 15) an intranet version instead of the default restricted internet version (restricted here is our own definition and just refers to the fact that the internet version of the Dashboard offers less component applications than the intranet version). We saw in [#2] that we inherited the GWT Widget Library, which is a third party set of modules and functionality authored by a number of people (and can be found at http://gwt-widget.sourceforge.net/). Just including this line in the Module XML file though is not enough to fully integrate a third party library, there are a couple of other steps we need to go though, and we will look at those next.
Including Third Party Modules One of the real benefits of GWT’s modularization approach is the ability to package together chunks of your application for re-use in other applications – by including the resource aspects within the XML module file, all the necessary information to use the components remains together, ready for reuse. This bundling opens up the opportunity for you to reuse your code in your other projects, to offer your code for use by others, and for you to use other people’s code they have provided. There are a few examples a third party packages that are available for GWT – we, of course, are partial towards the GWT Widget Library (http://gwtwidget.sourceforge.net/), but there is also a project looking at combining the various efforts so far into a consistent gwtcommons library of third party components (http://groups-beta.google.com/group/gwt-commons). Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Using a third party library in your code takes a few simple steps, which are shown in Table 9.38, but forgetting any of them is likely to cause issues! Table 9.38 Steps involved in using an third party GWT library in an application. Step Description 1 Download third party library 2 Update Java class-paths so that the compiler (application-compile command) and shell (application-shell command) scripts can access the new Java code for the library. These class-paths must point to the source code of the library as that is what the GWT compiler uses, just having class files will not work. If you are using an IDE you may need to update any launch configurations that are also used – this is the case for Eclipse where you will need to right click on the project, select Run As and then add the jar file to the classpath as an external jar. 3 Update the application’s XML module file to include the new XML module definitions from the appropriate parts of the third party library.
The first step is to download your library of choice – in the case of the GWT Widget Library that we will use in the Dashbaord, go to http://gwt-widget.sourceforge.net/ and retrieve the latest file widgets file. Within the download you will find a gwt-widgets-version.jar file which you need to copy to your project – we normally place our library files in the lib directory of the project, and so for the Dashboard, this will be in the DashboardDir/DashboardPrj/lib directory. As with any library in Java, we need to add details of the third party library to our classpath. For GWT it is easiest to do this to the command line tools, i.e. Dashboard-compile and Dashboard-shell. After updating, for example, our Dashboard-shell looked similar to this: 1.2216 @java -cp "%~dp0\src;%~dp0\bin; 1.2217 C:/Program Files/gwt/gwt-user.jar; 1.2218 C:/Program Files/gwt/gwt-dev-windows.jar; 1.2219 C:/GWTApp/DashboardDir/DashboardPrj/lib/gwt-widgets0.1.3.jar" #1 1.2220 com.google.gwt.dev.GWTShell -out "%~dp0\www" %* 1.2221 org.gwtbook.Dashboard/Dashboard.html (Annotation)
where [#1] shows the additional entry for the GWT Widget Library. Within our code we need to update the application’s XML module file to indicate that we will be using resources from another module within our code. For the Dashboard, we change the top of our Dashboard XML module file to show the following: 1.2222 1.2223 1.2224 1.2225
#1
(Annotation)
1.2226
Now we can use components of the GWT Widget Library in our application. To add some spice to the Dashboard, we will use the Scriptaculous (http://script.aculo.us/) wrapper included in the GWT Widget Library to hide the color picker in a flashy way. When the user clicks on the color picker’s close button we will hide it, but using the switchoff effect. This is easily achieved by using the code: 1.2227
Effect.switchOff(colourPicker);
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Here, we are using the static Effect object, calling the switchOff() method and passing our instance of the GWT color picker widget as a parameter. Before this works though, we need to include the actual Scriptaculous JavaScript in our application – this is done in the Dashboard.html file (although as we discussed in chapter 8, it could be done by injecting the JavaScript through the Module XML file). By including the GWT Widget Library module in this way, all of the other functionality in the library is available to you. But of course, there will be times when you build your own functionality that will be useful either to other people or across various applications that you have. In that case, you’ll be interested in packaging your own modules.
Packaging your own modules When developing GWT applications you are bound to come across the situation where you write some widgets or functionality that you end up thinking about using again in another application. The simple way to solve this is to just copy across the necessary class files into your new application, but that is not really best engineering practice as you have to remember when you make changes in one copy to do so in the others (if you want to maintain consistency). Far better is to package your functionality up as its own module and treat it in the same way you would when importing a 3rd party module as we have just discussed. Luckily, this is really easy to do and effectively just requires you to create a JAR file of the classes you require – but, it does take some care since you need to conform to the rules of GWT and ensure that your JAR file contains the source code (in a normal Java sense, your “libraries” would only contain the compiled classes). This situation happens quite a few times once you start developing. As an example, for one application we were building we had to construct some chat functionality over the GTalk network. It initially started off as functionality within another application and then we decided it could be shared across other applications (to be honest, a little prethought/design would have identified this at the outset and so it could have been set up in such a way initially). To create your own package, you need to set up a “mini” GWT project that comprises of at least a client package where your code goes (and perhaps server and rpc packages if needed) as well as a Module XML file that describes the module. Our Chat module (which is not included in the downloadable code for the book just for size considerations) was set up as the structure shown in Figure 9.144
Figure 9.144 Structure for some GWT code that we are about to package as our own module.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
This looks pretty much like a standard GWT project; which it is, however there is no need for any of the compilation or shell commands, HTML files or entry points normally associated with a GWT application. The smackx and smack libraries you can see in Figure 9.144 are libraries that we used to support the XMPP (Jabber) protocol for Instant Messaging. The Module XML file is really simple and is just defined as importing the standard GWT user module (it still follows the same rules that we have defined earlier in this chapter, for example, if you wanted you code in a package other than client then you would need to add a source entry (see chapter 9.1.3)): 1.2228 1.2229 1.2230
<module>
Now we have a standalone GWT module, the next step is to export it into a JAR file. You do this using whatever technique you are most familiar with – for us using Eclipse it is as simple as right clicking on the project and selecting the export option. When creating the JAR file, it is vitally important that you include the Java source files. If you do not do so, then when using the module in another application, GWT will not be able to the source code and will therefore be unable to work (remember that the GWT compiler and shell mode both work on the Java source files). With a JAR file produced that includes a Module XML file, and the Java source files, then using it in another application is as simple as following the steps given in the previous section. But enough of GWT modules; we have said before that the module file structure and java package are independent, but it is often convenient to link them together.
Creating the Java Package Structure GWT binds our hands slightly in our choice of package names and structure. When we used the creation tools in chapter 2, we saw that our application’s entry point code had to be in a package with the sub-package client as the last name. Thus our Dashboard.java file must be found in that package. There are also some other conventions that GWT applications are beginning to adhere to (mainly as a result of the example code shipped with GWT). Any server side code is generally placed in a sub-package called server, and if we are using GWT Generators, then their code is usually to be found in a sub-package called rebind. You can see all of these packages in Figure 9.145.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 9.145 Java package structure that will be used in the GWT In Action Dashboard example.
After the conventionally named packages, we are free to pick our own package names and structure. For the Dashboard, as you can see in Figure 9.145, we will create a sub-package called ui, under client, which will contain a new sub-package for each component application – and it is here that you can find each component application’s module file. Additionally, the ui package will contain all of the new widgets, panels, and composite widgets we need to build for our application.
Summary This chapter concludes demonstrating the basic client-side aspects of a real-life application – the Dashboard. We started in chapter 2 and 3 where we created the first version of the Dashboard application that was pretty limited in functionality but showed how we need to take the default GWT application and change a number of files to produce our own application. In chapters 4 through 6 we examined all the widgets, panels, and event handling that GWT provides by looking at how they are employed in our running Dashboard application or its components. Chapter 9 has discussed how we could lay the foundations for an application whose size is similar to one that may be found in the real-world. We should now have a very clear understanding of the notion of GWT XML module files and their relationship with Java package structuring. From this understanding, we have developed the module and package structure that we will be using for the Dashboard. The code for the Dashboard, and all the component applications discussed in this book, can be downloaded from http://www.manning.com/hanson. We haven’t discussed the detailed construction of the component applications in this book, as we have focused more on them being vehicles to understand GWT aspects. In the following few chapters, we introduce some more
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
complicated component applications: including the Server Status application, which introduces client-server communication, using RPC; the Yahoo Search application, which shows how to use the classic Ajax HTTPRequest approach as well as parsing a JSON response; and also how to change components of your application based on GWT properties.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
10 Communicating with GWT-RPC When building a rich Internet application, it is likely you won’t get too far before having a need to contact the server. The reasons for doing so are numerous and can range from updating the contents of a shopping cart to sending a chat message. In this chapter we explore the primary remote procedure call (RPC) mechanism that ships with the GWT toolkit. Throughout this chapter, and the chapters that follow, we will refer to this mechanism as GWT-RPC, to distinguish it from other general RPC flavors. If you are unfamiliar with RPC, it is a term used to describe some mechanism that allows a program to execute a program on another computer and return the results of the calculation. This is a rather simplistic view of RPC, but this is essentially what we will be doing in this chapter. Throughout this chapter, as well as the next, we will learn by building and extending an example component. This component, once completed, periodically requests performance data from the server and displays the values to the user. We call this example component the “ServerStatus” component. To make the task of writing RPC code as intuitive as possible, this chapter follows a strict organization. In the first section we define the component we are going to build, including a basic UI layout and defining the data that will be displayed in our component. In that context, we discuss asynchronous communication and some security restrictions of browser-based RPC. In the second section we examine all of the nuts and bolts of GWT-RPC. We define the data object that will be passed on demand between the server and client, and. with that. we discuss the serialization of data objects. We then get down to business and write the actual code for the component from beginning to end. At the end of the chapter, we wrap up the discussion with a detailed overview of the project and a review of the core participants of the GWT-RPC mechanism. But it doesn’t end there, as chapter 11 extends the example component by applying software patterns and polling techniques, providing for reusable and maintainable code. So, without further delay, let us begin the journey by defining our ServerStatus example project and examining the fundamental concepts behind the GWT-RPC mechanism.
Underlying RPC Concepts In this section we explain how the GWT-RPC mechanism works by building a sample component. We wanted the component to be of interest to the widest audience possible, so we chose to create what we call the ServerStatus component. The purpose of the component is to provide up to date memory and thread usage for the Java Virtual Machine, a useful tool indeed. As we go through the process of building the component, think about what other information you might want to add, like perhaps the number of logged in users or maybe disk usage, and add them to the component. Once completed, you will be able to use this component in the GWT Dashboard project introduced in Chapter 3, or in any of your own GWT projects. There are three pieces of the puzzle that we need to assemble to have a working RPC application. The first is the service that runs on the server, the second is the client in the browser that calls the service, and third are the data objects that are transported between the client and the server. Both the server and the client have the ability to serialize and deserialize data so that the data objects can be passed between the two as ordinary text. Figure 10.1 provides a visual representation of what our completed ServerStatus component will look like, along with an example service request and response.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 10.1 The completed GWT-RPC based ServerStatus component receiving serialized data from the server. The three pieces of the RPC application include the service that runs on the server, the client in the browser that calls the service, and the data objects that are transported between the client and the server.
The connection between client and server in figure 10.1 is initiated by the client and passed through a proxy object provided by GWT. The proxy object then serializes the request as a text stream and sends it to the server. On the server, the request is received by a special Java servlet provided by GWT. The servlet then deserializes the request and delegates the request to our service. Once our service returns a value to the GWT servlet, the resulting object is serialized and sent back to the client. On the client side, the response is received by the GWT proxy, which deserializes the data back into a Java object and returns the object to the calling code. The important part to remember about the round trip of the request is that our code deals with ordinary Java objects, and GWT handles serialization and deserialization of the data automatically. The example transaction in figure 10.1 shows the actual request and response data that is passed between the client and server, but in practice you will never deal with this data directly, although it may sometimes be helpful to view this data when debugging a problem. In this book we will not explain the actual serialization scheme. The reason for this is that the serialization scheme isn’t a documented part of GWT and will likely change in later GWT versions as performance enhancements are introduced. The whole idea of GWT doing most of the work for us sounds great, but the devil is in the details. There are a few things we need to understand before we start coding, the first of which is the asynchronous nature of browserbased communication.
1.
Understanding Asynchronous Communication
Calling a remote service with GWT-RPC is just like calling a local method with just a couple additional lines of code. There is, of course, one caveat, and that is that the call is made in an asynchronous manner. What this means is that, once you call the method on the remote service, your code will continue to execute without waiting for a return value. Figure 10.2 shows this graphically, where a gap of time exists between the call to the server and the response.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 10.2 A visual representation of the time-delay of asynchronous communication used in browser-based communication.
Asynchronous communication works much like an event handler in that you provide a callback routine that is executed when the event occurs. In this case the event is the return of the call to the service. If you haven’t dealt with this sort of behavior before, it may feel a little foreign at first, and it can require some additional planning when building your applications. The reason this type of communication is used by GWT is due to the way the underlying XMLHttpRequest object works. The reason why the XMLHttpRequest object behaves this way is beyond the scope of the book, but in part it is due to the nature of JavaScript implementations. This asynchronous nature, though, has some advantages. The first advantage is that network latency and long running services can slow down communication. By allowing the call to happen asynchronously, the call won’t hold up execution of the application waiting for a response, plus it feels less like a web application and more like a desktop application. The other benefit is that we can use some tricks with this to emulate a server-push. Server-push is a mechanism where the server pushes data out to the client without it being requested. This is used in applications like chat, where the server needs to push messages out to its clients. We will look at how we can emulate server-push along with some polling techniques in chapter 11.
Besides asynchronous communication, another RPC issue we need to deal with is security, which affects how we use any browser-based RPC mechanism.
2.
Restrictions for Communicating with Remote Servers
Security is always a concern on the Internet, especially when a call can be made to a server that is potentially returning sensitive data to the client, or perhaps provides access to secure systems. We have no intention of conducting a complete examination of security for web servers, which we leave to the experts. We do, though, want to explain one feature of making remote calls from the browser, which may seem like more of an annoyance than a feature. When you make a remote call from the browser, it must make the call to the same server from where the JavaScript code originated. The annoyance is that this means that your GWT application, which was loaded from your web server, can’t call services hosted by other sites. For some this is more than an annoyance; it provides a problem where they may not be able to deploy their code in the manner they intend. Before we explain why this is truly a feature, understand that there are ways around this, and that is to provide a proxy on your server that calls the remote server. In chapter 13 we use such a proxy to communicate with a third-party search service. Note that some browsers may let you override this behavior. For example, with Internet Explorer you can inform the browser to allow the remote connection to proceed, even though it is attempting to contact a foreign server. Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Requiring your users to bypass restrictions like this is never a good idea, and it can make your users vulnerable to attacks. This is a pretty strong statement, but, as we will see shortly with cross-site request forgery, there is a very good reason for this. To help you better understand how an attack like this might play out, let's examine a hypothetical situation. Let’s pretend that you are a high-ranking executive for company “X-Ray Alpha Delta,” and you are logged into the topsecret extranet application doing some product research. Once logged into the top-secret application, it keeps track of who you are by giving you a web cookie. The connection between the client and server is an encrypted connection provided by SSL. Using cookies as a way of handling user sessions and SSL are common tools used by most “secure” web applications. While working on the top secret extranet, you receive an email prompting you to review some competitor content on the Internet. You click the link and start reading the page that seems to be a legitimate news site. Unknown to you, the “news” site is running JavaScript in your browser and is making requests against your top secret extranet. This is made possible because your browser automatically passes your session information contained in a cookie to the top-secret extranet server, even though the JavaScript calling the server originated from the “news” site. Figure 10.3 shows the order of events of such an attack, allowing the malicious JavaScript access to the “protected” site.
Figure 10.3 Cross-site scripting attack, using JavaScript to break in to a “secure” application
This scenario is plausible and has been proven to work when the web browser allows JavaScript to call foreign servers. At the Black Hat convention in 2006, the security firm iSEC Partners provided details on how they used this technique to remove $5,000 from a stock account. The user was logged into a financial site, and then viewed a foreign site, which had contained the malicious JavaScript code. The JavaScript code in question was contained in five separate hidden iframes. Each script ran in turn, each making one call to the financial service. The scripts changed the user’s email notification settings, added a new checking account for transferring funds, transferred $5,000 out of the account, deleted the checking account, and restored the email notification settings. All of this occurred while the user was viewing the malicious site. This is a scary scenario, and is why browsers don’t typically allow JavaScript code to contact foreign hosts. At this point we have gotten far off track and need to get back to where we started, and that is an overview of RPC architecture of GWT. So far we have discussed the asynchronous nature, automatic data serialization, and
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
addressed why the service must be provided by the same host that served the GWT application (for critical security reasons). To get back on track, we will create a new GWT project and then finish up this section by creating a new GWT project that we will use to create the ServerStatus component.
3.
Creating the ServerStatus Project
By now you know how to set up a new GWT project, but this one will be a little different. When we create a GWT project that will perform RPC, we need to account for the fact that it will contain both server-side and client-side code. We need the GWT compiler should only compile the client-side code to JavaScript, without including the server-side portion of our code. We will be including code for both the client and server sides of project, so we need to do a few extra things to inform the GWT compiler. The first step is to use the projectCreator or applicationCreator command-line tool to create a new project. If you need a refresher on how to do this, you should consult chapter 2. For our purposes, this chapter assumes you already now how to do this. The following command and subsequent output is what we used to create the project: 1.2231
Next we need to remove the sample Java code that is provided by the applicationCreator tool. The following code is our ServerStatus.java after removing the sample application. 1.2241 1.2242 1.2243 1.2244 1.2245 1.2246 1.2247 1.2248 1.2249 1.2250 1.2251 1.2252
package org.gwtbook.client; import com.google.gwt.core.client.EntryPoint; public class ServerStatus implements EntryPoint { public void onModuleLoad () { // code } }
We also want to start with a new HTML page. Listing 10.1 shows the ServerStatus.html page in out project. We have removed the various comments, provided a better page title, and added an empty style block. Once we finish the component, we will provide some style code that you can use to make the component look like the picture we provided in figure 10.1. Listing 10.1 – The minimal HTML page that we will use to host our ServerStatus project
1.2253 1.2254 1.2255 1.2256
Server Status <meta name='gwt:module' content='org.gwtbook.ServerStatus'>
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The following CSS styles will be used to style the components to look like the example look and feel we provided in figure 10.1. Feel free to adjust the styles to your liking or use your own. You can place the following CSS code into the <style> element in the HTML page or put the CSS code into an external file and reference it with the HTML element. Listing 10.2 – A CSS file for styling the ServerStatus component
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.2304
}
Now that we have a new project to work with, we can get down to writing some code. In the next section we begin by creating a data object that will be passed from the server to the client, and then get into creating the service and calling it from the client.
Implementing GWT-RPC As we stated in the beginning of this chapter, there are three parts to the GWT-RPC mechanism, as can be seen in figure 10.4. The first is the service that will run on the server as a servlet, the second is the web browser which will act as a client and call our service, and last are the actual data objects that pass between the client and server.
Figure 10.4 The three parts of GWT-RPC: client, server, and data
We will start with the last of these, the data objects, and explain what types of objects that GWT can serialize for us. Following this we will take a look at the server side of the threesome, and look at how we implement the service on the server. Finally we will actually call the service from the browser. During the course of the discussion, we will reference our Server Status project that we started in the previous section. We will also provide code examples not related to the Server Status project if it helps explain details of the GWT-RPC mechanism that aren’t explicitly used by the Server Status component. By the end of this section, we will have completed the Server Status component, and you will be able to reuse it with any of your own GWT projects. Now let’s look at the serializable data objects.
4.
Understanding Serializable Data Objects
We need to begin our discussion of GWT-RPC with data objects because the data is what gives GWT-RPC life. Describing the functionality of GWT-RPC without data would be akin to describing the function of the human heart without first understanding the purpose of life-giving blood. At the top of section 10.1 we mentioned that, with the GWT-RPC mechanism. you can call methods that are executed on the server, and a resulting value is passed back to the client. Just like any Java method, we may pass arguments to the method, which may be a primitive like an int, an object like a String, or an array of values. The list of value types that GWT can serialize though is finite, and consists of those listed in Table 10.1.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Table 10.1 Java object and primitive types that can be serialized and transmitted via the GWT-RPC mechanism.
Java primitive types Java primitive wrapper types Subset Java objects User defined classes Arrays
boolean, byte, char, double, float, int, long, short Boolean, Byte, Character, Double, Float, Integer, Long, Short Only ArrayList, Date, HashMap, HashSet, String, Vector Any class that implements IsSerializable An array of any of the serializable types
The first two groups of value types in table 10.1, namely primitives and their wrapper counterparts, are self explanatory, and all are supported. Only a limited number of Java data types are supported. In chapter 1 we discussed the JRE Emulation Library provided by GWT, and how GWT only allows certain Java classes to be used in client side code. The list here of supported Java classes here includes all of the value object types from the emulation library. In practice this usually isn’t a limiting factor. All of these first three groups consist of types that are part of standard Java, but what about user-defined types? The IsSerializable interface answers this question.
Implementing the IsSerializable Interface The second from the bottom of our list of serializable types in table 10.1 was any class that implements the IsSerializable interface. This is an interface that is part of the GWT libraries, and is used to signify that a class may be serialized via reflection techniques. This serves the same purpose as Java’s own java.io.Serializable interface, but is specific to GWT applications. Most of this section will be about using the IsSerializable interface. The IsSerializable interface has no methods, it is only used to let the GWT know that this object may be serialized, and implies that you created the class with serialization rules in mind. The class implements com.google.gwt.user.client.rpc.IsSerializable All non-transient fields in the class are serializable The class has a zero argument constructor, or no constructor at all. By non-transient field we mean any field not using the transient modifier. GWT will also not serialize fields that have been marked as final, but don’t rely on this and be sure to mark all final fields as transient. That isn’t to say that bad things will happen if you don’t mark the final fields, but it will be clear as to the intention of the field if you or someone else ever needs to revisit the code for maintenance. 1.2305
GWT serialization also traverses relationships between parent and child classes. So you could have a super-class that implements IsSerializable and a sub-class that does not, but, because the super-class is serializable, so are all of its subclasses. 1.2306 1.2307 1.2308 1.2309 1.2310 1.2311 1.2312 1.2313
public class Person implements IsSerializable { String name; Date birthday; } public class Programmer extends Person { String favoriteLanguage; }
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Another thing to consider when working with serializable data objects is that they will be used by both the client and server code, and, therefore, must adhere to the rules for client-side code. This includes being compliant with the Java 1.4 language syntax and may only reference classes that are part of the JRE Emulation Library or are user created classes. In terms of optimizations for the GWT compiler, it is good to be as specific as possible when specifying the types of your fields in the data object. For example it is common practice to specify java.util.List as a type instead of either ArrayList or Vector. The benefit of using a generalized type is that it allows you to change the underlying implementation without changing the type declaration. The problem is that when you generalize the type it is harder for the GWT compiler to optimize the code, and you often end up with larger JavaScript files. So the rule of thumb it to try to be as specific as possible in your typing. Perhaps you may have already noticed the catch-22 situation we have run into. We are limited to the Java 1.4 syntax, which rules out using generics. So, if we are to be as specific as possible, then we need some way to let the GWT compiler know what type of objects are contained within an ArrayList or Vector. This leads us to the typeArgs annotation.
Using the typeArgs Annotation One place where specific typing isn’t possible is when your data object has a member that is a collection of objects. Collections in Java 1.4 hold values of type java.lang.Object, and this is as generic of a type you can get. This would be an issue if, for example, your data class had a member type of ArrayList, Vector, HashSet, or any other type that implements java.util.Collection. 1.2314 1.2315
The GWT compiler has no way of knowing what types of objects are held by either listOfNames or listOfDates, so it will not be able to properly optimize the client-side JavaScript. GWT provides an annotation that allows you to let the compiler know what types of objects are in a collection. This is not a Java 5 annotation, so it isn’t part of the Java language; instead you provide the annotation inside of a Java comment.
There are two variations of this annotation, the second one used when we define our service interface later in the chapter. In this variation the only parameter is the contained object type inside of angled brackets. The contained type is specified using its full package and class name. When we apply this to the two fields listOfNames and listOfDates it looks like this. 1.2316 1.2317 1.2318 1.2319 1.2320 1.2321 1.2322 1.2323
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.2324 1.2325
*/ private Vector listOfDates;
Now that we understand the basics, let’s apply that knowledge to our example component.
Implementing Our ServerStatus Data Object To get back to our ServerStatus component we introduced at the beginning of this chapter, we need a data object that will be used to hold server statistics data that will be requested by and sent to the client browser. Here is the complete data object: 1.2326 1.2327 1.2328 1.2329 1.2330 1.2331 1.2332 1.2333 1.2334 1.2335 1.2336 1.2337
package org.gwtbook.client; import com.google.gwt.user.client.rpc.IsSerializable; public class ServerStatusData implements IsSerializable { public String serverName; public long totalMemory; public long freeMemory; public long maxMemory; public int threadCount; }
Our class complies with both rules of serializable objects, those being that it implements IsSerializable and all of the fields are also serializable. We built this class with only public fields, but it would be just as appropriate to provide private or protected fields with associated getter and setter methods, which is common in Java programming. Also note that this class is in the org.gwtbook.client package, and it will be compiled into JavaScript to allow it to be used in the browser. On the server we will also use this class, but the server will use a compiled Java version of this class. As part of the data serialization handled by GWT, it will handle the mapping for the fields of the client-side JavaScript version to the server-side Java version of this same class. Now that we have our data object, the next step is to define and implement our service. We need to define a service by using a Java interface and then implement a servlet that adheres to that interface.
5.
Defining the GWT-RPC Service
The next step in using the GWT RPC mechanism is to define and implement the service that will live and be executed on the server. This consists of one Java interface, which describes the service and the actual service implementation. When you write your service, you will probably want to integrate it with other backend systems like databases, mail servers, and memory. Besides the basics of using GWT-RPC, we will try to touch on how you might do some of these things. First, we look at the GWT RemoteService interface.
Extending the RemoteService Interface To define our service, we need to create a Java interface and extend the GWT RemoteService interface. This is just as easy as it sounds. If you recall, our ServerStatus project will call a RPC service method which will return a ServerStatusData object, which will contain various server metrics. Our interface looks like the following: 1.2338 1.2339 1.2340 1.2341
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.2342 1.2343 1.2344 1.2345
public interface extends RemoteService { ServerStatusData getStatusData (); }
That is really all there is to it: just provide a name for the interface, in this case we called it ServerStatusService, and have it extend GWT’s RemoteService interface. The RemoteService interface doesn’t define any methods, so we won’t need to implement anything special to get our service running. There are some additional fairly subtle requirements when defining this interface: The interface must extend com.google.gwt.user.client.rpc.RemoteService All method parameters and return values must be serializable The interface must live in the “client” package We have covered the first of these, but we want to explain it a little further. GWT will automatically generate proxy classes for the client-side application and forward method calls using reflection on the server. To put it another way, GWT goes out of its way to make RPC easy by minimizing the amount of code you need to write. To this end, the RemoteService interface is used to signal which interfaces define our remote service. This is important because this might not be the only interface our implementation implements. The second requirement is that all parameters and return values must be serializable. As mentioned in the last section, this includes all primitive Java types, certain objects that are part of the standard Java library, and classes that implement the IsSerializable interface. For our ServiceStatusService interface, our only method, getStatusData(), returns a ServerStatusData object that we created in the last section, and it implements IsSerializable. Here are some more examples, all of which include parameters and return values which can be serialized. 1.2346 1.2347
boolean serviceOne (String s, int i, Vector v); String[] serviceTwo (float f, Integer o);
The last requirement is that the interface must be in the “client” package. By this we mean that the interface code must be in your project where is will be compiled by GWT into JavaScript. Typically this will be in a package name ending in “.client” unless you have configured your project differently. The reason for this is that this interface will be used by both the client and the server. On the server, our service implementation will implement this interface. We will look at how we reference this interface from the client-side when we get to the next section and actually call the service.
Using the typeArgs Annotation We introduced you to the typeArgs annotation in the previous section when we used it to define the contents of collections in a serializable object. Here we introduce an alternate syntax, but with the same purpose -- to provide a hint to the GWT compiler about what type of object a collection is holding. The specific collection types that GWT supports are ArrayList, Vector, HashSet, and their respective interfaces List and Set.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
You should provide the typeArgs annotation for each parameter and return value that is a collection. The diagram shows the syntax, which differs in what we saw in the last section, because it also allows us to specify the parameter name. When you add an annotation for the return value you will not specify the parameter name. 1.2348 /** * @gwt.typeArgs arg1 <java.lang.Integer> * @gwt.typeArgs arg2 <java.util.Date> * @gwt.typeArgs <java.lang.String> 1.2349 */ 1.2350 ArrayList operationThree (List arg1, Vector arg2);
Here we have defined a method that takes a List of Integer values, a Vector of Date values, and returns an ArrayList of String values. In practice you can probably leave off the typeArgs and everything will run fine. If you do decide to leave these off, you may end up with additional JavaScript code being generated for the client because the GWT compiler will not be able to optimize the handling of these. This covers serialization, but if we want remote calls to be able to pass Java objects, we also want it to have the ability to pass Exceptions.
Throwing Exceptions Often it is desirable to allow a method to throw an exception that will be handled by the calling code. For example you may have a login function that returns a user data object on success, but, on failure, it throws an appropriate exception. 1.2351 1.2352 1.2353
When GWT calls your service method, and it throws an exception, it will serialize the exception and return it to the client browser. The only requirement is that the exception be serializable just like any data object. In other words it must implement IsSerializable, all of its fields must be serializable, and the class must be in the “client” package. See the previous section, Understanding Serializable Data Objects, for a complete discussion of creating serializable objects. As an alternative to writing your own serializable exception class from scratch, GWT supplies an exception class SerializableException, in the package com.google.gwt.user.client.rpc. You may use this exception class instead of writing your own, or perhaps you will want to use this as the base class for your exceptions. Next lets look at how we can implement our service interface.
Implementing the Service With our service interface defined for our service, now we need to implement its methods. We do this by creating a servlet that extends GWT’s RemoteServiceServlet, and implements our service interface. Below is our complete ServerService implementation. Listing 10.3 ServerStatus server-side implementation 1.2354 1.2355 1.2356 1.2357 1.2358 1.2359
There are no real surprises here, and, in fact, there is nothing special that we need to do to make this a service other than extend the RemoteServiceServlet and implement an interface that extends RemoteService. When our service is called, the underlying RemoteServiceServlet will parse the request, converting the serialized data back into Java objects, and call our implemented method. When our method returns a value, we are returning it to the RemoteServiceServlet that called our method, and in it will in turn serialize the result and return it to the client browser. Unlike what we have seen with the rest of GWT, there are no restrictions on our server-side code. We can use any Java class, we can use Java 5 syntax, and there are no special annotations we need to use. There is though one restriction, and that is the package name. Because our server-side code will contain Java code that can’t be compiled into JavaScript, and because there is no need for this to be served to the client, we need to make sure that this class lives outside of the “client” package. To refresh your memory, figure 10.5 shows what our current project directory layout looks like.
1.2385
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 10.5 Reviewing the current project directory layout
1.2386
The root of our project is the Java package org.gwtbook, and in that package is the client package and the public folder. The public folder contains any non-Java assets for the project, like our HTML file, and the client package contains Java classes which will be compiled to JavaScript. For our server-side code it is the standard practice to create a server package just beneath our root package, which for this project would be org.gwtbook.server. Don’t get too hung up on this, though; depending on what you are building, it may be more appropriate to use a different package name, and this is fine to do just as long as it isn’t within the client package. Next we look at how we can configure our development environment to use our new servlet.
Setting Up Your Service for Hosted-Mode Testing There is one last step, and that is to register your service with GWT by adding it to the project configuration file. The project configuration file will be located in the root package, and it will be named the same as our entry-point class with a “.gwt.xml" extension, in this case it will be found user org.gwtbook and named ServerStatus.gwt.xml. By default it will contain both an element to provide access to the core GWT libraries, and an <entry-point> element indicating the runnable class for this project. To this we need to add a <servlet> element to register our new service. The completed ServerStatus.gwt.xml file looks like this: 1.2387 1.2388 1.2389 1.2390 1.2391 1.2392 1.2393
We have stripped out the comments that the GWT application creator added in to shorten the example, so it might not look exactly like yours. The <servlet> element has two attributes: path to indicate the URL of our service, and class to indicate the servlet that should be run when this URL is requested. Note that, just like a Java application server, the path to the servlet is relative to the web project root and not the root of the server. With this project, the default project URL in hosted mode is the following. 1.2394
If you have worked with the servlet path setting before, this will be the expected behavior, but, for some, this is the common cause of frustration when configuring a servlet. So, with our service defined, implemented, and configured on the server, the next step is to address the client side issues, and then finally call the service from the client.
6.
Preparing the Client Side of a GWT-RPC Call
When we call the remote service from the client, GWT does most of the work for us; however, we still need to create one last interface. This interface will be used by the GWT compiler when it generates the service proxy object. A proxy object is an object instance that forwards the request to another target. In this case we will be calling a local
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
method, and the proxy object is responsible for serializing our parameters, calling the remote service, and handling the deserializing the return value. We do not write the code for the proxy class; instead, the GWT compiler handles this for us. In our client-side code, we create the proxy object by writing the following: 1.2396
GWT.create (ServerStatusService.class)
Here we called the static method create() of the com.google.gwt.core.client.GWT class, passing it the class object of our remote service interface. This returns a proxy object that we can then use to set the service URL and call the remote methods. The proxy object returned will implement two interfaces, one that we will need to create, and one supplied by GWT (as shown in figure 10.6).
Figure 10.6 The client-side service proxy generated by the GWT compiler, and the interfaces it implements
In diagram 10.6 we see that the proxy object is an instance of ServerStatusService_Proxy, which implements two interfaces. The proxy class is created at compile time so you will not be able to reference this class directly in your code. Instead you need to cast it to each interface separately to be able to call its methods. Of the two interfaces, there is the ServiceDefTarget interface, which is part of the GWT library and includes a method setServiceEntryPoint() for specifying the URL of the remote service. The other interface, ServerStatusServiceAsync, provides asynchronous methods for calling the remote service. We will need to write this second Asynchronous Service interface ourselves, which we look at next. This asynchronous service interface always has the same name as our service with the name “Async” appended to it. The methods in the interface must match all of the method names in our original service interface, but the signatures need to be changed. Specifically, for each method in our original interface, we must do the following: Set the return value to void. Add an extra com.google.gwt.user.client.rpc.AsyncCallback parameter. Table 10.2 shows some side-by-side examples of how a method in the service interface looks in the asynchronous service interface:
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Table 10.2 Comparing a methods in the service interface to how the method would look in the asynchronous interface of GWT-RPC.
Service interface
Asynchronous service interface
String methodOne (int i);
void methodOne (int i, AsynCallback cb);
List methodTwo ();
void methodTwo (AsynCallback cb);
boolean methodThree (int a, int x);
void methodThree (int a, int x, AsynCallback cb)
This interface is only used by the client code and not the server. Because of this, we do not need to include this interface in any code deployed to the server, and we must place the interface in the client package. Here is what we get when we apply this to our ServerStatusService interface: 1.2397
public interface ServerStatusServiceAsync { void getStatusData (AsyncCallback callback); }
This sets us up for the last piece of the puzzle, and that is to actually call the remote service from the client.
7.
Calling the Remote Server Service
Now that we have our remote service interface defined and implemented, have a remote interface, and we have created a serializable object to pass between the server and client, we now need to call the service from the client browser. To do this we need to do the following: Instantiate a proxy object that will forward method calls to the server. Specify the URL of the service. Create a callback method to handle the result of the asynchronous method call. Call the remote method. The first time you do this, it doesn’t quite feel natural, especially since we are calling the method asynchronously, but, after a few tries, it becomes second nature. We will examine each of these steps in turn and clearly explain what needs to be done and, perhaps more importantly, why it needs to be done. We will also be sure to point out any areas of common mistakes. Now on to step 1, creating the proxy object.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Step 1: Create the Proxy Object Step one is to create our proxy object. We do this by calling GWT.create(), passing our remote service class as an argument. In return the create() method returns out proxy object which we need to cast to our asynchronous interface. A common mistake is passing the wrong class as an argument to GWT.create(), so be sure to pass the remote service interface, and not the asynchronous interface which will be implemented by the proxy object. 1.2399 1.2400
With the proxy object in hand, we move on to the next step and use it to target the remote service.
Step 2: Cast the Proxy Object to ServiceDefTarget The second step is to cast this same object to ServiceDefTarget so that we can specify the remote service URL. As we saw in the last section, the proxy object implements both the asynchronous service interface and ServiceDefTarget. So all we need to do is cast this same object to the ServiceDefTarget interface. Once we do this, we can use the setServiceEntryPoint() to set the URL for the service. 1.2401 1.2402
This sets the URL to “/org.gwtbook.ServerStatus/server-status”, which will match our servlet definition in the “.gwt.xml” file, but may not match your production environment when you deploy the application. The GWT.getModuleBaseURL() will return the location of the client-side code and append a slash to the end, which works fine for hosted-mode development, but when you deploy your service this might not be what you want. What you can do instead is detect if the code is being executed in hosted-mode, and use the appropriate service URL. You can do this by calling GWT.isScript(), which returns true when the application isn’t running in hostedmode. It may even be useful to take this a step further and define the web-mode service path in a Constants file or as a Dictionary object. You can get more information on how to use Constants and Dictionary in chapter 15. 1.2405 1.2406 1.2407 1.2408 1.2409
At this point we have set up the proxy object, so now we need to construct our callback object.
Step 3: Create a Callback Object The third step in calling the RPC service is to create a callback object. This object will implement the GWT com.google.gwt.user.client.rpc.AsyncCallback interface, and will be executed when a result is returned from the server. As you may recall, we had added an AsyncCallback parameter to every method in our asynchronous service interface.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The callback object will be passed as this additional parameter. Here we are creating an anonymous object instance that implements AsyncCallback. 1.2410
The AsyncCallback interface has two methods that must be implemented, onSuccess() and onFailure(). If an error occurs where the service can’t be reached, or if the server-side method throws an exception, the onError() method is called with the exception that occurred. If the call is successful, then the onSuccess() method is called, and it receives the return value of the remote method call. In the sample above, we are using the GWT.log() method to log information to our hosted-mode console. This would be replaced with actual code, which does something with the resulting object and handles the error in an appropriate manner for the application.
Step 4: Make the Remote Service Call Now, the fourth and final step in calling a remote service is to actually make the call. This will be a little anti-climatic because it all comes down to one line of code. 1.2413
serviceProxy.getStatusData(callback);
This method kicks off a chain of events. Any parameters other than the callback object are serialized, passed to the remote service, and the appropriate callback method is executed based on the server response.
Summary Overall, the GWT RPC mechanism is pretty simple, but with all of the details it is easy to lose site of the overall architecture. So, as promised at the beginning of this section, we provide an overall summary of GWT RPC, and we use a lot of visuals to hopefully make it easy to understand the system as a whole. We begin with an overview of the files in our project, then look at the server-side code, followed by the code on the client.
8.
Project Overview
The entire project thus far only includes seven files, so it really isn’t that big. Figure 10.7 provides a list of these files for us. It contains the usual configuration file, project HTML page, and entry point. That only leaves four files that represent the concepts covered in this section.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 10.7 An overview of the ServerStatus project files that we have created
The service interface defines the remote service. It extends RemoteService, and may only reference parameters and return values that can be serialized by GWT. When a method accepts arguments that implement java.util.Collection, or returns a Collection, we use the @gwt.typeArgs annotation to provide a hint for the GWT compiler, letting it know what types of objects it can contain. Our methods may declare that they throw exceptions just as long as any exception thrown can be serialized by GWT. The asynchronous service interface contains the same method names as the service interface, but with altered method signatures. Each method’s return type is changed to void, and an AsyncCallback parameter is added as the last argument to each method. The project may contain serializable data objects, each of which will implement the IsSerializable interface. GWT will serialize any field not marked as transient or final in the class, and every field not marked as transient must itself be of a GWT serializable type. The service implementation will be in a package outside of the other client-side code because it cannot be compiled by the GWT compiler. Typically server-side code will live in a package at the same level as the client side code in a package with the last part named “server”. The service implementation extends the RemoteServiceServlet and implements the service interface. That is a description of each part of the project in a nutshell. In practice, the only difference will be the number of serializable data objects. You may also find that, in a large project, you may wish to reorganize the package structure and place all of the interfaces and data objects relating to a single service into a package by themselves. Now let’s take a closer look at the server-side of our project.
9.
Server-Side Service Implementation
In diagram 10.8 we present a class diagram of all of the classes on the server and their relationship to each other. We have used the stereotype to mark those classes that will be used on the client-side as well as the server, and will need to be compiled by the GWT compiler. That means that those specific classes must meet the requirements for client-side code, including referencing only classes that are a part of the JRE Emulation Library, or client-side user defined classes. All other classes may take advantage of Java 5 syntax, and use any classes that are available on the server.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 10.8 Overview of the server-side classes used for our ServerStatus project, but user generated and GWT supplied
Here we see that the server-side service implementation is just a servlet, as it extends RemoteServiceServlet, which in turn extends HttpServlet. When you deploy your service. you will install it on the server just like any other servlet. Finally, let’s look at the client-side of the project.
10.
Calling the Service from the Client
We call the remote service by creating an instance of a service proxy object, specifying the URL of the service, and calling the server-side method on the object. The last parameter to any such method call is a callback handler, which is executed following the server returning a result. Code listing 10.4 is a complete listing of our entry point class that sets up and calls the remote service. Listing 10.4 Client-side implementation
While this covers the basics for how to call a remote service, we haven’t really discussed how to solve common real world problems. For instance, how do you continuously poll a server for updates, and what is the best way to architect client-side RPC? In chapter 11, we answer both of these questions as we finish our ServerStatus project and take a hard look at client-side architecture.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
11 Examining Client-Side RPC Architecture In chapter 10 we began building a component that we called the ServerStatus component. The purpose of this example component was to present information about the Java virtual machine running on the server, including vitals such as memory and threads. During the course of that chapter we took a deep look at how to use the GWT-RPC mechanism to solve the problem of sending data between the client and server. When we finished that chapter, we had some code, but not quite a finished component. It still needed some polish. Some of the missing parts included structuring the presentation logic for our component, implementing a polling mechanism, and encapsulating the component so that it can be used over and over. In chapter 11, we begin by jumping back into that project from chapter 10 and finishing it as we take a long look at architecting a reusable and maintainable client-side component, one that takes advantage of server-side resources.
Structuring the Client Code So far in our ServerStatus project, which we started in chapter 10, we have unceremoniously plopped the RPC code into our entry point class with little care about application architecture. In this chapter, we continue the ServerStatus project and polish it up a bit, allowing it to be easily extended and maintained. We do this by applying some software patterns to our RPC code, none of which are GWT specific, but we will be applying them in a GWT specific manner. We begin by encapsulating our entire ServerStatus component as an extension of the GWT Composite class, which we have seen in prior chapters, and adding some RPC specific code. Following that, we see how the Façade software pattern can be used to simplify RPC interaction, and then finally we look at how we can use the Command software pattern to simplify the calling of remote methods. Let’s begin with encapsulating the component.
11.
Encapsulating the ServerStatus Component
The first step in building a component is to formulate the basic visual layout of the component and then decide which Widget class should be extended. For our ServerStatus component, the desired structure and the resulting styled HTML can be seen in figure 11.1.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 11.1 The structure of the ServerStatus component, and what the finished component looks like
The ServerStatus component is a VerticalPanel with four components. The first component is the “Server Status” title bar for the component. We will use a Label for this, and with a little CSS styling we will be able to center it and make the title appear as bold and white. The second component we will employ is a Grid component to display the server statistics that we are retrieving via RPC. The Grid will be two cells wide and five cells tall to hold the five labels and five data values. These labels, like “Server Name” and “Total Memory”, will be added to the Grid as plain text. The values on the other hand will each be a separate Label component so that we will be able to update their values by make use of the setText() property of the Label component. We could have just set the text in the Grid for these, but by providing a named Label instance for each value in our code the code is somewhat self-documenting, making it easier to understand and maintain. At the bottom of the ServerStatus component is an update Button component, used to manually update the statistics, and a Label to indicate the last time the values were updated. It is our intention that this component will use some sort of polling mechanism to update itself periodically, but the manual update button can be used to force an early update. We will hook the ServerStatus component up to a polling mechanism when we discus polling techniques in the next section. Once we have designed a general structure, the next step is to code this as a component.
Writing the Component Shell In writing the shell for our component, we only deal with the layout of the component, and not worry too much about the inner workings yet. Once we complete the shell, we can move on to the next step and start having the component display data. As we stated earlier, we need to decide on which GWT user-interface component to extend. In diagram 11.1 we showed that the outer shell of the component uses a VerticalPanel, but this isn’t a very good choice for the basis of our component. The reason is that VerticalPanel exposes several methods that we don’t want our component to support. For example, the add() and remove() methods of VerticalPanel could be used to add or remove widgets from our finished component, changing the desired layout. For this reason, we want to extend com.google.gwt.user.client.ui.Composite. The Composite class provides several protected methods for use by an extending class and only publicly exposes a getElement() method. In the following code listing, we create the class ServerStatusComponent, and place it in the org.gwtbook.client package with the rest of out client code. We have defined a private variable for each component that will make up our composite. This includes the each of the components we discussed, including the five labels to hold the statistics we receive from the server. Listing 11.1 Coding the structure of the ServerStatusComponent
Label titleBar = new Label(“Server Status”); Button updateButton = new Button("Manual Update"); Grid serverStats = new Grid(5, 2); Label labelLastUpdated = new Label();
| | | |
#2 #2 #2 #2
private private private private private
Label Label Label Label Label
| | | | |
#3 #3 #3 #3 #3
labelTotalMemory = new Label(); labelFreeMemory = new Label(); labelThreadCount = new Label(); labelMaxMemory = new Label(); labelServerName = new Label();
Thus far, the only code we have included is the constructor for our ServerStatusComponent. The constructor calls the initWidget() method of the super class, passing it a newly created VerticalPanel instance. This sets the user interface component that will be used as the outer shell for our component. To this we add the title bar Label, the statistics Grid, the update Button, and the last-updated Label. Next we need to update our entry point class to reflect that we are now packaging the ServerStatus as a component. Notice that, by encapsulating the ServerStatusComponent as a component, we are able instantiate it and add it to the RootPanel with only two lines of code. Listing 11.2 Instantiating our ServerStatusComponent
import com.google.gwt.core.client.*; import com.google.gwt.user.client.rpc.*; import com.google.gwt.user.client.ui.*; public class ServerStatus implements EntryPoint { public void onModuleLoad () { ServerStatusComponent serverStatus = new ServerStatusComponent(); RootPanel.get().add(serverStatus); } }
At this point, you should be able to view the project in the hosted-mode browser, and it should look like what you see in figure 11.2.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 11.2 The incomplete shell of the ServerStatus component example running in the hosted-mode browser.
The component looks a little sparse at this point, not only because we haven’t supplied any data, but also because we have not yet applied any CSS styling.
Adding Style and Labels to Our Component As we saw in chapter 4, we can attach CSS class names to the individual parts of the component. We need to specify separate style class names for the title-bar, Grid component, update Button, last-updated Label, and the Composite as a whole. We also need to add labels to each row in the Grid, to the left of where each data value will be displayed. Listing 11.3 Adding style classes and labels to the ServerStatusComponent
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
#1 #1 #1 #1 #1
#3 #3 #3 #3 #3
1.2492 1.2493 1.2494
getStatusDataFromServer();
| #4
}
1.2495
In the listing 11.3 we reference two methods that we have not yet defined. The first is addRowToGrid() which is used to help us populate the statistics grid, and the second is getStatusFromServer(), which will is the method which will actually trigger a call to the server. For each row, the addRowToGrid() sets the title of the value, which appears on the left side of the grid, and inserts the Label component to the right of it. For each title, we set the style class name to be “stat-name”, and for each value we set the style name to be “stat-value”. Creating methods like this which group repetitious code is extremely helpful, especially since GWT code can easily become extremely verbose. The getStatusFromServer() will be used to trigger an RPC call to the server. We will be describing this method and its purpose shortly, but for now we are leaving that method as an empty stub. Code listing 11.4 shows the two methods that we will add to out ServerStatusComponent. 1.2496 Listing 11.4 Adding the addRowToGrid method
If you run the application now, you will see that it is all starting to come together. Figure 11.3 shows what the application should look like.
Figure 11.3 A near complete version of the ServerStatus component example.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
This assumes that you have been following the example throughout the chapter and are using the CSS provided in section 10.1. Now that we have the majority of the moving parts of our application we need to fill in the getStatusDataFromServer() method, which will call the remote method. We are going to be doing this a little differently than we had in the prior section, in that this time we are going to encapsulate the interface to the server as a Façade.
12.
Encapsulating Remote Calls in a Façade
The Façade pattern is one of the software patterns described in the famed book Design Patterns: Elements of Reusable Object-Oriented Software by Gamma, Helm, Johnson, and Vlissides. This book defines the façade software pattern as a way of providing a higher-level interface to make a subsystem easier to use. In our case we want to encapsulate all of the RPC logic into a class that provides a simple interface in an effort to achieve the following: Reduce complexity by reducing the number of classes we need to interact with. Promote weak coupling, allowing us to alter the underlying RPC mechanism without affecting the client. We all like reduced complexity, since easier is almost always better, so it is not hard to understand the motivation for wanting the first bullet point. We will realize this by creating a new class named ServerService with one method for each remote method plus a single getInstance() method.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
package org.gwtbook.client; 1.2508 1.2509 public class ServerService 1.2510 { 1.2511 public static ServerService getInstance(); 1.2512 public void getStatusData (AsyncCallback callback); 1.2513 }
This class follows the Singleton software pattern, providing a single static method to retrieve an instance of the ServerService class. From this instance, we have provided a getStatusData() method to call the remote service. It may be tempting to do away with the getInstance() method and just make the remote method call static, but this isn’t a good idea. This would reduce the flexibility of the class and make it more difficult to do things such as allowing multiple instances to be created. It is one of those things where we need to make the code a little more verbose to make future maintenance easier. With the interface for the service class defined, we now need to implement the RPC service it will provide. In our case our needs are rather simplistic, we only have a single endpoint, or service URL to call on the server, and we only have one method. So for us the implementation is rather straightforward. Listing 11.5 The service façade
package org.gwtbook.client; import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.rpc.ServiceDefTarget; public class ServerService { private static ServerService instance; private ServerStatusServiceAsync proxy; private ServerService() { proxy = (ServerStatusServiceAsync) GWT.create(ServerStatusService.class); ((ServiceDefTarget) proxy).setServiceEntryPoint( GWT.getModuleBaseURL() + "server-status"); } public static ServerService getInstance() { if (instance == null) { instance = new ServerService(); } return instance; } public void getStatusData(AsyncCallback callback) { proxy.getStatusData(callback); } }
In our implementation, we have made the constructor private, allowing only the ServerService class to create an instance of itself. So what happens is that when getInstance() is called it will create a new instance of the ServerService
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
class only if one has not been created. This ensures that there will only ever be one instance of this class. When the ServerService class is instantiated it creates the proxy instance that will be needed to call the server. When the getStatusData() method is finally called the proxy object will have already been created and it can simply pass the callback object to the proxy. With this we have also effectively met our initial goals for using a façade. First we have reduced the complexity of the ServerStatusComponent class because it no longer needs to deal with any of the underlying RPC code, including the creation of a proxy object and needed to specify an endpoint. We have also decoupled the calling class from the actual RPC implementation. So we could potentially replace the GWT-RPC call with some other RPC mechanism such as XML-RPC and we can do this without having to change any code in the ServerStatusComponent. We can do this only because we have completely delegated the RPC responsibility to the ServerService class. Now that we have the façade class implemented, we can write the rest of the code for the ServerStatusComponent. We do so by using the Command software pattern to provide some future flexibility.
13.
Callback Routine Using the Command Pattern
Early in our discussion on RPC, we addressed the fact that calls to the server are done in an asynchronous fashion, where the call to the server is as a separate thread of execution. We then went on to examine how you can pass an anonymous instance of the AsyncCallback interface to receive the result from the server. Below is the example we had provided earlier in the chapter. 1.2543 AsyncCallback callback = new AsyncCallback() { public void onFailure (Throwable caught) { GWT.log("RPC error", caught); } public void onSuccess (Object result) { GWT.log("RPC success", null); } };
This is certainly a valid way of providing a callback object, but it has a couple of drawbacks. First, it locks us into using this specific functionality, and if we wanted to change the functionality we would need to rewrite the existing routine. Secondly, we can’t extend the functionality of the callback because it is an anonymous class. The solution to both of these is to use the Command pattern. The benefits that we hope to achieve are as follows: Allows us to create a new callback without having to rewrite or remove an existing callback. Allows the callback to be extended and reused. Allows for the possibility of creating a callback that can execute other callbacks. Applying the command pattern is very easy, especially with the way the existing callback mechanism works in GWT. All you need to do is create a named class for your callback. 1.2544 Listing 11.6 A callback command
1.2545 1.2546 1.2547 1.2548 1.2549 1.2550
private void getStatusDataFromServer () { ServerService.getInstance().getStatusData( new ServerStatsUpdater()); }
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
class ServerStatsUpdater implements AsyncCallback { public void onFailure(Throwable caught) { } public void onSuccess(Object result) { ServerStatusData data = (ServerStatusData) result; labelServerName.setText(data.serverName); labelTotalMemory.setText(toKB(data.totalMemory)); labelFreeMemory.setText(toKB(data.freeMemory)); labelMaxMemory.setText(toKB(data.maxMemory)); labelThreadCount.setText(Integer.toString(data.threadCount)); labelLastUpdated.setText(new Date().toString()); } }
In the getStatusDataFromServer(), we have passed a new instance of ServerStatsUpdater, an inline class, instead of passing an anonymous callback routine. The ServerStatsUpdater class is our command object. In this case, we have made the command an inline class due to the fact that it needs to update various Label objects inside of our ServerStatusComponent. This, however, does not take away from the fact that we have added some flexibility with almost no additional coding time, and actually we find this code easier to read than if we had used an anonymous AsyncCallback instance. To provide an example of how this can add flexibility to your application, let us add an additional requirement to our callback. Let us require that we use the GWT logging mechanism on all callbacks to log the result of a callback. We could add this directly to the onFailure() and onSuccess() methods like in the anonymous AsyncCallback below, but there are some problems with this design. 1.2567 AsyncCallback callback = new AsyncCallback() { public void onFailure(Throwable caught) { 1.2568 GWT.log(“RPC Error”, caught); 1.2569 } 1.2570 1.2571 public void onSuccess(Object result) { 1.2572 GWT.log(“RPC Success”, null); 1.2573 ... 1.2574 } 1.2575 }; 1.2576
In practice we will likely have many different RPC commands, and we would need to add this code to each callback. This in itself is not the problem; the problem is when we change our mind on how to log this information. For example, the above does not let us know which RPC command was executed; instead, it only lets us know if it was successful or not. As developers, we are not fortune-tellers, so a good design can prevent pain later when you ultimately need to update existing code. Using chains of callbacks is one solution that we could apply to this problem.
Chaining Callbacks for Separation of Activities Now, if we go back to our Command pattern example, there are two ways in which we could provide shared logging functionality. The first is by chaining callbacks, and the second is to provide a super class to our ServerStatusUpdater class, which includes logging capabilities. Let’s examine how we solve this problem by chaining callbacks. To do this, we need to create a new class that we will call LoggingChainingCallback.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Our LoggingChaningCallback class implements the AsyncCallback interface and, as such, includes the onFailure() and onSuccess() methods. Notice that we have added a constructor to this class. It takes an AsyncCallback as a constructor argument and stores the object in a private instance variable. We reference this instance in both the onFailure() and onSuccess() methods, using it to pass the result to that object after logging the event. To use this class, all we need to do is alter the calling code slightly. We now pass the new ServerStatusUpdater callback to the constructor of the LoggingChainingCallback. The LoggingChainingCallback instance is then passed as an argument to the getStatusData() method. 1.2600 1.2601 1.2602 1.2603 1.2604 1.2605
If you are a little confused about the chain of events happening here, diagram 11.4 might help. It shows the sequence of what happens when a successful result is returned from the server. Our ServerService calls the onSuccess of the LoggingChainingCallback. This class logs the event and then passes the result on to the ServerStatsUpdater, which updates our fields.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 11.4 Chaining actions, where one action performs its task then passes control to the next
In essence, what we have done is chained two callbacks together where each performs their own function without any overlap. In this design, we have separated out the logging portion of our application so that it can be shared and modified independently of the callbacks that it passes the request to. So, with a little architecture, we have helped to future-proof application making it easier to alter logging functionality at a later date. Besides chaining callbacks, we also mentioned that the Command software pattern allows you to subclass the command to enhance or replace the existing functionality.
Using an AsyncCallback Super-Class As we have seen already, we have applied the Command software pattern by creating a class that implements the AsyncCallback interface. One of the original reasons for doing so was so that we could extend our command class, which is something we couldn’t do if we had used an anonymous class for our callback. Again, our goal goes back to the need for separating out the logging for all RPC commands into a single class that can be reused and maintained in a single place. There are two ways in which we can achieve this with a super-class. The first is by convention, and the second is to create a new type of callback interface. By convention, we mean that it is up to the developer to follow the rules, and if the rules are followed the system will work. In general, it is a bad idea to allow a developer the ability to not follow the rules when that means that functionality will break, so we do not recommend this approach. For this reason, we will concentrate on the second approach. The goals we attempt to achieve include the following: Provide an abstract callback super-class which can be sub-classed to add specific behaviors (e.g. logging) Give the super-class the ability to handle the RPC result before the subclass. To do this, we need to do a little design work. In diagram 11.5 we have provided a high-level overview of what the new structure will look like.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 11.5 Creating our own handler interface so that we can add additional functionality, like logging, in a reusable manner
Figure 11.5 introduces a new interface called AsyncHandler that contains the two methods handleSuccess() and handleFailure(). We then create the class AbstractAsyncHandler that will implement both the new interface as well as GWT’s AsyncCallback interface. The way we achieve the desired behavior is to have this new abstract class intercept the RPC onSuccess() or onFailure() event and then forward it on to either the handleSuccess() or handleFailure() method. So the abstract handler in this sense is just passing on the event to a method of a different name. The reason we want to do this is so that our MyAsyncHandler will only implement the handleSuccess() and handleFailure() methods, and not the methods defined by the AsyncCallback interface. This allows us to extend the AbstractAsyncHandler with a handler with a specific function, like a logging handler, which only overrides the onSuccess() and onFailure() methods. To build this, we will start at the top and work our way down. So, first we create our AsyncHandler interface, which defines our two new methods. 1.2606 1.2607 1.2608 1.2609 1.2610
package org.gwtbook.client; public interface AsyncHandler { void handleSuccess(Object result);
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.2611 1.2612 1.2613
void handleFailure(Throwable caught); }
The AsyncHandler interface mirrors the AsyncCallback interface in that it takes has two methods with the same parameters and return value, with the only difference being the method names. Next we need to create the AbstractAsyncHandler class. 1.2614 1.2615 1.2616 1.2617 1.2618 1.2619 1.2620 1.2621 1.2622 1.2623 1.2624 1.2625 1.2626 1.2627 1.2628 1.2629 1.2630 1.2631 1.2632
package org.gwtbook.client; import com.google.gwt.user.client.rpc.AsyncCallback; public abstract class AbstractAsyncHandler implements AsyncCallback, AsyncHandler { public void onFailure(Throwable caught) { handleFailure(caught); } public void onSuccess(Object result) { handleSuccess(result); } public abstract void handleFailure(Throwable caught); public abstract void handleSuccess(Object result); }
We have declared the class as abstract, as well as the handleFailure() and handleSuccess() methods. This forces subclasses to either declare themselves as abstract or implement the two abstract interfaces. The next class in our diagram is the MyLoggingHandler class, but we are going to skip this one for now since it is really just a placeholder for a handler class containing any type of needed functionality. So we will skip to the MyAsyncHandler, which is the specific handler we would need to create for a specific RPC call. In our ServerStatusComponent RPC call, we had implemented ServerStatsUpdater as our callback handler. Let’s revisit that handler and alter it to use the new AbstractAsyncHandler as its parent class. The following code replaces the code we used earlier for both the call to our service class, and the response handler: 1.2633 private void getStatusDataFromServer () 1.2634 { 1.2635 ServerService.getInstance().getStatusData( 1.2636 new ServerStatsUpdater()); 1.2637 } 1.2638 class ServerStatsUpdater extends AbstractAsyncHandler { public void handleFailure(Throwable caught) { } public void handleSuccess(Object result) { // success code }
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
}
This is essentially the same as we had started with, except the ServerStatsUpdater now extends our new AbstractAsyncHandler instead of implementing GWT’s AsyncCallback interface. With this change comes the fact that our handler method names are different, matching the abstract methods of the AbstractAsyncHandler class. With this new structure comes added flexibility. As a reminder, these were the goals we had set forth for this organizing the classes in this manner. Provide an abstract callback super-class that can be sub-classed to add specific behaviors (e.g., logging). Give the super-class the ability to handle the RPC result before the subclass. To demonstrate that we have achieved both of these goals, let’s go back to the logging example and create a subclass of the AbstractAsyncHandler that will automatically log RPC events. We will name the class LoggingAsyncHandler to indicate that it provides automatic logging. 1.2639 1.2640 1.2641 1.2642 1.2643 1.2644 1.2645 1.2646 1.2647 1.2648 1.2649 1.2650 1.2651 1.2652 1.2653 1.2654
package org.gwtbook.client; import com.google.gwt.core.client.GWT; public abstract class LoggingAsyncHandler extends AbstractAsyncHandler { public final void onSuccess(Object result) { GWT.log("RPC Success [" + GWT.getTypeName(this) + "]", null); super.onSuccess(result); } public final void onFailure(Throwable caught) { GWT.log("RPC Failure [" + GWT.getTypeName(this) + "]", caught); super.onFailure(caught); } }
Notice that we have made the LoggingAsyncHandler class abstract, so it does not need to implement the handleSuccess() and handleFailure() methods. We have also overridden the onSuccess() and onFailure() methods, and made them final. The purpose of making them final here is so that subclasses cannot alter the behavior of our class which would most likely break it. The onSuccess() and onFailure() methods were overridden to add the required logging capabilities and logs the event prior to the handling of the event by it’s subclasses. This meets both of the requirements we have set out to achieve. To sequence diagram summarizes the chain of events when a response is returned from the server. The LoggingAsyncHandler gets the first look at the response, then passes the response up to it’s parent class, which then passes it down to the only class that implements the abstract handleSuccess() and handleFailure() methods.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 11.6 The sequence of events for our new logging handler that has been inserted between the service and the response handler
It may seem as though we went the long way around to add a couple simple logging statements, but, as your GWT application gets larger, it becomes increasingly important to have a good design. The payback from this additional work comes from reuse of the logging handler and ease of maintenance. Now that we have discussed a couple of patterns for maintainable client-side code, we can turn our attention back to the more general concept of polling.
Examining Different Polling Techniques Often you may find that you need to keep in constant contact with the server via RPC call to keep the client updated. This is usually to inform the client browser of some event like the arrival of an email, a new chat message, a server is out of disk space, etc. The solution to this is to poll the server. This can be done by contacting the server via RPC on some regular basis, like every five minutes. Some applications require more frequent updates than this, and perhaps want to be as close to real-time as possible. In this chapter section, we explore GWT’s functionality for scheduling tasks and examine various polling techniques. We begin our examination by providing a high-level overview of polling terminology, including pull versus push, and what COMET is. Then we look at the GWT Timer class and explain how it works. We then use the Timer class to enhance our ServerStatusComponent by making it update its contents on a regular basis. Following this, we look at a technique called blocking to emulate a server-push mechanism.
14.
Explaining Polling Issues
Before we get into actually implementing polling, it is useful to understand the different ways of delivering content and the consequences of each. Delivery techniques can be broken down into two categories: server push and client pull. A push is when the server pushes content out to a client, and pull is when the client requests data from the server -- the primary difference between these two being who is initiating the request. Let’s look a little closer at the two of these types, first push, then pull.
Examining Server-Push
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
In theory, pushing data out to your clients is very useful. We use it daily when we send an email or an instant message. For example, when we send an email, we are sending it to a mail server, which in turn pushes the mail to another mail server. The same goes for instant messaging, where the message gets pushed to the intended recipient. By pushing the data out to the clients, we can have any number of clients, perhaps many thousands, and no server or bandwidth resources are used until we actually have something to send. This scales well, and you can easily support any number of clients with this scheme with relative ease. There is one small hitch to this, and that is that web browsers do not have this capability. During the 1990s there was quite a bit of work going on in this area, but it never really caught on. Part of the problem is that, if you could push data to the browser, it would likely pose security issues, opening the user up to a new breed of attack. So, although we cannot achieve true server push, it is often required that we emulate it -- for example, if we wanted to build a chat client that would immediately broadcasts messages it has received. There are several techniques for doing this, like COMET, Pushlet, Pjax, and others. For the most, part these are somewhat difficult to implement in GWT, so we will not be discussing them here. Blocking server threads, on the other hand, is easy to implement in GWT, and we will provide a full example of implementing this a little later. The opposite of push is pull, and we examine pull technologies next.
Examining Client-Pull Client pull is another name for the standard request-response sequence that is used by the browser. When you go to a URL with your browser, you are using a requesting a page from the server or, in other words, pulling the page from the server. This technique is useful when a delay between the publication of an event and the actual receiving of an event is acceptable. Client pull has it’s own challenges associated with it. Because there can be a delay between sending and receiving the event, you often need to queue up data on the server waiting for a client to check in. For example, if you wrote a chat application using client pull, the server would need to store any unsent messages for a user until that user pulled the messages. Between the two types of delivering data, pulling it is the natural for web browsers, so we will examine a server pull implementation first by implementing a continuously updating component.
15.
Implementing a Continuously Updating Component
In this section we revisit our ServerStatusComponent and allow it to periodically update itself. To do so, we will add the ability to set the refresh rate for the data, as well as add the ability to change or stop the automatic updates. In doing so, we need to look at the GWT Timer class, which allows us to trigger timed events. If you have been following along with us, your ServerStatusComponent should already be working and have a method that can be called to have it update its information from the server. If you haven’t been following with us, here is the basic shell for the ServerStatusComponent, which includes a method, getStatusDataFromServer(), that triggers an RPC call and updates the displayed data. 1.2655 1.2656 1.2657 1.2658 1.2659 1.2660 1.2661 1.2662 1.2663 1.2664
In the full version of the ServerStatusComponent, the getStatusDataFromServer() method is called when the component is first initialized or when an update button was clicked. Next we add a Timer to trigger an update as well.
Using the GWT Timer Class The Timer class can be found in the com.google.gwt.user.client package. It provides a no argument constructor and several methods. The method signatures are below. 1.2672 1.2673 1.2674 1.2675 1.2676 1.2677
public abstract class Timer { public abstract void run(); public void schedule(int milliseconds); public void scheduleRepeating(int milliseconds); public void cancel(); }
The first thing to point out is that the Timer class is abstract, so you must subclass it to be able to use it. You can do this by creating an anonymous class or creating your own specialized timer class. When you implement your class, you need to implement the abstract method, run(), that is called when the timer is triggered. In this example, we have created a Timer that will alert the user with a “hello” message when a timer event is triggered. 1.2678 1.2679 1.2680 1.2681 1.2682
Timer example = new Timer() { public void run () { Window.alert(“hello”); } };
To trigger a timer event, you need to set either a one-time event with the schedule() method or a recurring event with the scheduleRepeating() method. In the case of schedule(), you specify the number of milliseconds to wait before executing the run() method. The scheduleRepeating() method also takes the number of milliseconds as a parameter, and it repeatedly executes the run() method. The first execution of run() begins in the number of milliseconds specified and then continuously recurs at that same interval. The cancel() method, as apparent by its name, allows you to stop the timer. Once stopped, you may start it again by calling either the schedule() or scheduleRepeating() methods. The following example sets the timer for five seconds, then cancels it: 1.2683 1.2684
example.schedule(5000); example.cancel(); // just kidding
The next step is to add some additional methods to our ServerStatusComponent to provide the ability for setting the update interval as well as the ability to stop the automatic updates. You do this via a schedulable class.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Creating a Schedulable Class There are many ways to implement a timer, but we will provide a way we think is highly reusable. The code we present here can be used in not only the ServerStatusComponent, but applies to any Composite component. It is also possible, with some minor changes, to adapt it to other base class. We will accomplish this by creating a subclass of Composite called UpdateableComposite, shown in figure 11.7, that will provide methods for setting and stopping the update timer. Before we start coding, let’s look at a class diagram so that you can see the relationships between the new class and our existing ServerStatusComponent.
Figure 11.7 A structural overview of our reusable updateable-composite
We begin by introducing the new UpdateableComposite as a subclass of Composite. UpdateableComposite is an abstract class, meaning that it must be subclassed to use it, and it contains three methods. The startUpdateTimer() method starts the update timer. It allows us to specify the number of seconds between updates, and we can use this same method to change the update interval. The update() method is an abstract method and must be implemented by the subclass. Once we implement UpdateableComposite class, we will alter our ServerStatusComponent to implement the update method, and then look at how we can set the timer. 1.2685 1.2686 1.2687 1.2688 1.2689 1.2690 1.2691 1.2692 1.2693 1.2694 1.2695 1.2696 1.2697 1.2698
package org.gwtbook.client; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.Composite; public abstract class UpdateableComposite extends Composite { private Timer timer = null; public abstract void update(); public void startUpdateTimer(int seconds) {
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
if (timer != null) { stopUpdateTimer(); } timer = new Timer() { public void run() { update(); } }; timer.scheduleRepeating(seconds * 1000); } public void stopUpdateTimer() { if (timer == null) return; timer.cancel(); timer = null; } }
The implementation is pretty straight-forward but is worth some explanation. The class contains a single private field called timer, which holds our Timer object. We need to hold onto a reference of the Timer object so that we can cancel it when stopUpdateTimer() is called. Looking at the stopUpdateTimer() method, you can see that the method calls cancel() on the Timer object, then sets the timer field to null. The startUpdateTimer() method first checks to see if the timer field is null. If it isn’t null, this is a sign that it has already been sent. The method responds by calling stopUpdateTimer() to cancel the existing timer. This allows us to use startUpdateTimer() to change the interval period without having to worry about stopping the existing timer. The method then goes on to create a Timer instance. We are creating the Timer as an anonymous class which prevents external clients of this class to be able to control the timer directly. In the run() method of the timer, we call the abstract method update(). It will be left up to the subclasses of UpdateableComposite as to what the update() method will actually do. With the UpdateableComposite complete, the only thing left to do is alter our ServerStatusComponent to take advantage of the scheduling capabilities. 1.2721 1.2722 1.2723 1.2724 1.2725 1.2726 1.2727 1.2728
public class ServerStatusComponent extends UpdateableComposite { ... public void update() { getStatusDataFromServer(); } }
The only change is that we now extend UpdateableComposite instead of Composite directly, and we have implemented the update() method. The update() method, in turn, calls our private method getStatusDataFromServer(), which we had created before triggering an RPC call to the server. In projects other than
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
the ServerStatusComponent, you would include whatever code was required to update the component in the update() method. With this mechanism in place, we can now easily create and schedule updates for our component. The code to do this for the ServerStatusComponent is below, but it would essentially be the same for any custom component that also subclassed the UpdateableComposite. 1.2729 1.2730 1.2731
ServerStatusComponent serverStatus = new ServerStatusComponent(); serverStatus.startUpdateTimer(60);
In summary, using the GWT Timer class, it is easy to schedule a piece of code to execute on a schedule. The concepts for doing this are the same no matter if you are using the Timer to schedule polling of the server or updating a display. In either case, though, it is always a good idea to think through the design and use the tools that the Java provides to create reusable code. The UpdateableComposite is just one example of this. Now that we have explored Timers, and in conjunction with this the concept of polling, it is now time to look at how we can emulate server-push, and push events to the client browser.
16.
Emulating Server-Push by Blocking Server Threads
As we briefly mentioned earlier, there is no true server-push available to browser, meaning that the server cannot send data to the browser without the browser requesting it. The reasons for this are security and lack of a standard. To accommodate for this lack of functionality, there are several schemes for emulating server-push. In this section, we discuss and implement one such variation called blocking server threads. From a very high level, RPC involves a client browser requesting data from a server. The server then processes the request and returns a result. This is not only how browser based RPC works, but it is also how web pages are served. With blocking server threads, we add one small alteration, and that is that we hold up the processing on the server until there is data available to process. It is similar to a company with poor customer service -- you end up waiting on hold until there is someone to talk to you. To help understand how this works, figure 11.8 provides a visual comparison between polling and blocking server threads.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 11.8 A visual comparison of server polling and the blocking threads technique
In figure 11.8 you can see that, when we use polling, the browser handles the orchestration by calling the server every 20 seconds. With blocking the server becomes the conductor, placing the request on hold until it needs to process the request. Another important difference is that, with blocking, we have an almost constant connection between the client and the server. A web-based chat client is the classic example of the need for blocking server threads, and we will use a this sort of application as our example. We will not build a complete component because it would require covering a lot of the same topics we have already discussed, but we will cover the few changes required to make it work. We start with the code required on the client, then look at what changes are needed on the server.
Implementing Blocking on the Client When we implement the client-side of blocking server threads, there is very little that we need to do. The only requirement is that, each time we get a response from the server, we need to initiate a new request. Take this AsyncCallback implementation for example. We have removed the implementation details, but notice that it calls some method getDataFromServer() on both success and failure. 1.2732 1.2733 1.2734 1.2735 1.2736 1.2737 1.2738 1.2739 1.2740 1.2741 1.2742 1.2743
class ChatPanelUpdater implements AsyncCallback { public void onFailure (Throwable caught) { ... getDataFromServer(); } public void onSuccess (Object result) { ... getDataFromServer(); } }
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The method getDataFromServer() is a method that you would need to define, and the only requirement is that this method triggers another RPC request. In our ServerStatusComponent we had a method getStatusDataFromServer() which served the same purpose. With just this one minor change, we can now turn our attention to the more interesting aspects of blocking server threads on the server.
Implementing Blocking on the Server Implementing the server portion of a blocking RPC server can be done the same way you block any Java thread, and that is to issue a sleep command. When you issue a sleep command to the Java Virtual Machine, it stops processing of that specific thread of execution for some amount of time. Once the sleep time is finished, it wakes-up the thread and continues processing. Issuing a sleep command involves a single statement. The method Thread.sleep() takes a number of milliseconds to sleep for, so passing 1000 to the method causes the thread to sleep for one second. 1.2744
Thread.sleep(1000);
For our purposes, though, we need to do more than just block the thread for some amount of time. What we need to do is first determine if we should process the request, which in the case of a chat application will be when we have a message that we want to send the user. On the other hand, if we don’t have a message to send, we should put the thread to sleep for some short period of time. Once the thread wakes-up, we check again if we should process the request. To accomplish this we would use a loop similar to this. 1.2745 1.2746 1.2747 1.2748 1.2749 1.2750 1.2751 1.2752 1.2753 1.2754 1.2755 1.2756 1.2757 1.2758 1.2759 1.2760 1.2761 1.2762
public List getMessages () { List result = new ArrayList(); try { while (true) { if (canProcess()) { result = process(); break; } Thread.sleep(1000); } } catch (InterruptedException ignore) { } return result; }
We use the while(true) loop to create an endless loop, and at the end of the loop we use Thread.sleep() to sleep for one second. Prior to sleeping, we check to see if we can process the request by calling canProcess(), and if we can we call process() to get the result of the RPC call and then break out of the while loop. The try statement catches an InterruptedException, which could result from the call to Thread.sleep(); for example, if the application server was being shut down. This loop is fine, but what happens if the user closes their browser or navigates to a different web page? Unfortunately, we don’t have a good way to be notified of these events, so what could potentially happen is that a
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
thread could loop forever even though the browser client is no longer present. To solve this problem, we need to introduce a timeout. public List getMessages () 1.2763 { 1.2764 List result = new ArrayList(); 1.2765 long timeout = System.currentTimeMillis() + (30 * 1000); 1.2766 1.2767 try { 1.2768 while (true) { 1.2769 if (canProcess()) { 1.2770 result = process(); 1.2771 break; 1.2772 } 1.2773 1.2774 if (System.currentTimeMillis() > timeout) { 1.2775 break; 1.2776 } 1.2777 1.2778 Thread.sleep(1000); 1.2779 } 1.2780 } catch (InterruptedException ignore) { 1.2781 } 1.2782 1.2783 return result; 1.2784 }
To our original example, we have added a timeout value, and we set the value to the current time in milliseconds and add 30 seconds to that value. We have also added a check, and, if the current time exceeds the timeout value, it breaks the loop. The timeout value you use depends on your specific project, but in most cases 30 seconds is sufficient to keep the number of requests down while removing stale connections quickly. The exact implementation of the canProcess() and process() depends a lot on the purpose of the method. For example, with our chat application, we would likely have a queue of messages for each user of the system, in which case canProcess() would return true if there were any messages in the queue for that user. The process() method would then return the list of messages and clear out the queue, or mark the messages in the queue as being delivered. Now that we have shown how you can easily implement polling, let’s turn our attention to a completely different topic, although equally important, and that is how to create custom serialization routines for your objects.
Writing Custom Field Serializers With GWT’s basic serialization system, you can create data objects that implement the IsSerializable interface, which can be passed between the client and server. When you implement this interface, GWT handles all of the details of serialization and deserialization for you. This will usually be sufficient for most of your work with GWT, but occasionally this mechanism isn’t sufficient for a given project. Three common reasons for needing to write a custom field serializer include: The default serialization causes performance issues for a complex object. The class that needs to be serialized does not implement IsSerializable. The class that needs to be serialized does not have a zero argument constructor. Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Writing a custom field serializer is fairly easy to do. In this section we take the ServerStatusData class that we used as our data object for the ServerStatusComponent example, and we write a custom serializer for it. To begin, let us look at the original implementation of the ServerStatusData class. 1.2785 1.2786 1.2787 1.2788 1.2789 1.2790 1.2791 1.2792 1.2793 1.2794 1.2795 1.2796
package org.gwtbook.client; import com.google.gwt.user.client.rpc.IsSerializable; public class ServerStatusData implements IsSerializable { public String serverName; public long totalMemory; public long freeMemory; public long maxMemory; public int threadCount; }
Our data object is fairly simple, including only five fields, so our custom field serializer will also be fairly simple, but it will highlight the important concepts. We begin by creating a custom field serializer class, then we look at implementing both serialization and deserialization. We then look at how we can serialize objects that don’t have a zero argument constructor.
17.
Creating a Custom Field Serializer Class
To begin with, we need to create a class for each custom field serializer, which must follow some rules: The serializer class must reside in the same package as the class that it serializes. The serializer class must have the same name as the class that it serializes plus “_CustomFieldSerializer”. It must implement a serialize() and deserialize() method. It may optionally implement a instantiate() method if the class requires custom creation. The rules provide the convention of adding the suffix “_CustomFieldSerializer” to a class to specify that it is a custom serialization class. Also, as we will see shortly, the three methods have signatures that depend on the class type being serialized. To help you understand how all of this works, we start by building the shell of the class, then discuss each of the method signatures it contains. 1.2797 1.2798 1.2799 1.2800 1.2801 1.2802 1.2803 1.2804 1.2805 1.2806 1.2807 1.2808 1.2809
package org.gwtbook.client; import com.google.gwt.user.client.rpc.SerializationException; import com.google.gwt.user.client.rpc.SerializationStreamReader; import com.google.gwt.user.client.rpc.SerializationStreamWriter; public class ServerStatusData_CustomFieldSerializer { public static ServerStatusData instantiate( SerializationStreamReader reader) throws SerializationException { }
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The first method, instantiate(), is a static method that takes a SerializationStreamReader as an argument, which we will discuss shortly, and returns an object of the type which this class was built to serialize, namely the ServerStatusData class. The instantiate() method is optional and is only required when the target class doesn’t have a zero argument constructor. It must return the same type of object that we have built this class to serialize and deserialize, in this case a ServerStatusData instance. The instantiate(0 method, like all three methods, throws SerializationException. The serialize() method is a static method that takes receives a writer object, used for adding data to the serialization stream, and an instance object which is the object that is being serialized, which in this case is a ServerStatusData object. This instance object will be of the same type that you are serializing, so if your custom field serializer was serializing an object of type Foo, then the instance parameter would be of type Foo. The deserialize() method receives a reader object, which can read data from the serialized stream, and an instance object of the type of object that we are deserializing, in our case a ServerStatusData object. The instance object is the same object that was created by the instantiate() method, and the reader object is the same reader that was given as a parameter to instantiate(). The pattern for deserialization is that instantiate() is called first, and is given an opportunity to read data from the serialized stream, and use it in the creation of the object. The instance created by instantiate() is then passed to derserialize() along with the reader, allowing it to continue reading from the stream and setting any properties of the object. Now that we have a basic shell, let’s discuss the usage of the serialization reader and writer classes by implementing the methods in our class.
18.
Implementing Custom Field Serialization
Thus far we have looked at the method signatures for our custom field serializer, but we haven’t yet implemented them. To accomplish this, we will inspect each method individually and explain its purpose and how it should be implemented.
Implementing the instantiate() Method When GWT serializes or deserializes an object using your custom field serializer class, it calls instantiate(). The purpose of this method is to create a new instance of your object, including passing any values to the constructor required for its creation. This method does have a default behavior, though, and you do not need to implement this method unless you need to override the implementation. The default method, as shown below, is to call the zeroargument constructor of the data class and return the instance.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.2825 reader) 1.2826 1.2827 1.2828 1.2829
public static ServerStatusData instantiate(SerializationStreamReader throws SerializationException { return new ServerStatusData(); }
In this implementation, we are implementing a field serializer for the data class ServerStatusData. If we were writing a field serializer for a different class, the method signature would change to return an object of that type. A SerializationStreamReader object is passed to this method and can be used to read values from the serialized stream and use those values in the construction of the object. For example, in the example below, the class FakeData’s constructor takes a string argument, which we pull from the reader. 1.2830 1.2831 1.2832 1.2833 1.2834 1.2835 1.2836
public static FakeData instantiate(SerializationStreamReader reader) throws SerializationException { String val = reader.readString(); return new FakeData(val); }
We discuss using the SerializationStreamReader in more detail when we look at the deserialize() method (after the serialize() method).
Implementing the serialize() Method The serialize method of our custom field serializer is passed a writer object, which is used to write data to the serialized stream, and an existing object instance, which is the object we will serialize. The way serialization works is that you read some data from the object you are serializing, then write that data to the writer. The writer is an instance of SerializationStreamWriter from the Java package com.google.gwt.user.client.rpc, and includes methods for writing different types of data to the serialized stream. Below is a list of available writer methods, all of which may throw a SerializationException. writeBoolean(boolean value) writeByte(byte value) writeChar(char value) writeDouble(double value) writeFloat(float value) writeInt(int value) writeLong(long value) writeShort(short value) writeString(String value) writeObject(Object value) The writer includes a method for each of the Java primitive types as well as the String and Object classes. Writing an object to the stream will in turn call the serializer for that type of object, which could be one of the serializers that comes with GWT or could be another custom field serializer. You may write the fields to the stream in any order. The only restriction is that the deserialize() method read the values from the stream in the same order that you wrote them.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Implementing the deserialize() Method The deserialize() method is passed the object instance created by instantiate() and passed a reader so that it can continue reading from the serialized stream. The reader is an instance of SerializationStreamReader from the package com.google.gwt.user.client.rpc. It has a set of methods that allow you to read one object at a time from the serialized stream. The reader methods mirror the writer method that we looked at previously. It is important that you read the values in the same order as you wrote them, or you will likely cause a SerializationException to be thrown, or, even worse, end up with a deserialized object with the wrong data in the wrong fields. The reader object has the following methods, all of which throw SerializationException: 1.2837 1.2838 1.2839 1.2840 1.2841 1.2842 1.2843 1.2844 1.2845 1.2846 1.2847
boolean readBoolean() byte readByte() char readChar() double readDouble() float readFloat() int readInt() long readLong() short readShort() String readString() Object readObject()
Just like the writer, there is one method for each of Java’s primitive types, plus String and Object. readObject() in turn causes the deserialization of the object you are reading. Here is our deserialize() method for the ServerStatusData object. 1.2848 1.2849 1.2850 1.2851 1.2852 1.2853 1.2854 1.2855 1.2856
Our deserialize() method, like the serialize() method, is very simple; we read each value from the stream in the proper order and set the appropriate field of our object.
Summary In chapter 10 we introduced an example component, the ServerStatusComponent, which would make calls to the server and display statistics on memory usage and the number of server threads. As we progressed through the chapter, we followed an entire build process for the component and discovered something new at each step. In chapter 11 we polished our working example by focusing our attention on the architecture and design of the application. We started by encapsulating our code as a reusable composite component. We then looked at how the façade software pattern could be used to hide the implementation details of the RPC call, providing the flexibility for changing this later without disturbing the entire application. We then focused our attention on the callback routine that would receive the data from the server. We encapsulated this object as a command object and discussed how this pattern allowed us to easily add functionality like logging, or chaining multiple commands together. Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Now that we had a pretty stable and reusable component, we then looked at polling techniques, starting with a comparison of server-push and client-pull. We then created an abstract Composite class, which could be reused and would allow any component which subclassed it to have the ability to pull data from the server on a scheduled basis. We followed this up with a discussion of how we could emulate server-push by blocking server threads. Last, but not least, we discussed that there are cases where the built-in serialization routines are not enough. We found that we could write our own custom field serializer class to handle a data object that wasn’t able to follow the requirements of the IsSerializable interface. We have covered quite a bit, but GWT offers even more with respect to RPC. In this chapter we looked at GWT’s proprietary RPC mechanism, which is tailored to deliver Java objects between the client and server. But what if your server isn’t Java? In the next chapter, we will look at the rest of GWT’s RPC offering, including the ability to communicate with non-Java servers.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
12 Classic Ajax and HTML Forms In chapter 10 we showed you how you can use the GWT-RPC mechanism to communicate between the Java application server and the browser to pass Java objects. But what if you don’t have a Java application server? What if you use PHP, .NET, or even plain vanilla CGI scripts? Or what if you just want to load static configuration files from the server? These are just some of the situations where the GWT-RPC mechanism doesn’t quite do the job. In this chapter, classic Ajax and good old HTML forms come to our rescue. These tools are generic and flexible, and GWT provides a set of objects that allows you to take advantage of them. In the first half of the chapter we examine the RequestBuilder class, which provides support for classic Ajax communication. In the second half of this chapter we explore GWT’s FormPanel component, which allows the building of traditional HTML forms in a smart and powerful way. We will show you some different ways of using this component, including using it for handling file uploads. So, with our path set, let’s begin this journey by looking at the RequestBuilder class.
Classic Ajax with RequestBuilder In chapter 10 we discussed asynchronous communication and the role of the underlying XMLHttpRequest object. To recap, the XMLHttpRequest object is a tool that is a part of modern browsers, which can be scripted with JavaScript. This XMLHttpRequest object allows you to fetch content from a remote URL and handle the response programmatically. The benefit of loading content this way is that we can use code to communicate with the server without needing to refresh the web page, making you web application feel more like a desktop application and hopefully more user friendly. The GWT-RPC mechanism that we saw in chapter 10 an 11 is just a higher-level tool built on top of this lower-lever JavaScript object. In this section we will get a lot closer to the XMLHttpRequest object than we did when we used GWT-RPC. There are many reasons why we might need to do this. One such situation is where we need to communicate with legacy systems, often not written in Java, where it is too costly to rewrite the server components. Another motivation for not using GWT-RPC is when you aren’t running a Java application server and don’t plan to, which is a requirement of GWT-RPC. Whatever the reason, we will explore how you can access this basic functionality. We begin the discussion with a brief overview of HTTP, and the difference between a GET and a POST. This will provide the groundwork for exploring RequestBuilder, which requires you to specify which of these two methods you want to use. If you are already well versed in HTTP, you may still want to quickly browse through the material, we promise to keep it focused and short. After going over the basics of the communication protocol, we examine the RequestBuilder class itself. RequestBuilder provides a very simple interface with sensible defaults for making remote calls to the server without sacrificing the ability to alter the low-level details of the request when you need it. This includes the ability to alter and add HTTP headers to the request and specifying a timeout. So lets get going and get some background on the HTTP protocol and the request-response cycle.
19.
Examining HTTP Methods
When you load a web page into your browser, the Hypertext Transport Protocol is used to communicate the transaction. Hypertext Transport Protocol, or HTTP, involves a request and a response. The browser sends the
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
request to the server, and the response is sent from the server back to the browser. The exact details on how HTTP works can get very complex, but for the purpose of using GWT we only need to discuss how the basic HTTP messages work. Each request in HTTP involves issuing a command to the server that includes a method name, a URL, header name-value pairs, and a message body. There are numerous methods, but with GWT we only need to focus on the GET and POST methods, as these are the only methods supported by GWT. Both the GET and POST can be used to send data to the server, but the way they accomplish this differs. Let’s take a look at the GET method first.
Dissecting the HTTP GET Method The GET command sends all of its data as part of the URL. Before we get into the specifics, let’s take a look at an example URL, which is the URL used to perform a Google search for “GWT”. 1.2857
When your browser sends this URL request to google.com, it sends the following GET message using HTTP. You can see that the URL is still intact, except for the host name that has been added as a header. For clarity, we have only included the Host header, but there could be any number of additional name-value pairs used to identify information about your browser. 1.2858 1.2859
GET /search?hl=en&q=GWT&btnG=Google%2BSearch HTTP/1.1 Host: www.google.com
The server at google.com will parse this HTTP request, and handle it accordingly. In Java, and most other serverside platforms it will be possible to get complete information about the request, including the method used, the URL requested, and any of the header name-value pairs. This can be useful if you need to know what specific types of content the client-browser can handle. The URL, or Uniform Resource Locator, may reference an HTML page, an image, or just about anything. The URL is then itself made up of various parts.
The important part for us is the query portion of the URL. A single question mark separates the query from the path, and it contains name and value pairs. The name/value pairs are separated by ampersands (&), and the individual pairs use an equals sign (=) to separate the name from the value. So the example URL contains the following names; “hl”, “lr”, “q”, and “btnG”. This query is how a GET can be used to pass data to the server. Because the query uses both equal signs and ampersands for delimiting data you may not use either of these characters in a name or value. In fact there are several reserved and unsafe characters including percent (%), star (*), slash (/), space, period (.), plus (+), and hash (#). So it
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
is important that escape these characters before passing them to the server. GWT provides a tool to perform this function for us. 1.2860
To decode an escaped value you would use the decodedComponent() method. The following example decodes the escaped value "=unsafe value=". 1.2861
String val = URL.decodeComponent("%3Dunsafe+value%3D");
The URL class belongs to the com.google.gwt.http.client package, and you must specifically import the HTTP module to your module configuration file. You can do this by adding the following line to your module configuration. 1.2862
URL escaping involves converting unsafe characters to hexadecimal values, and prefixing them with a percent sign. So for example, a slash (/) will become %2F. The only exception to this is that a space will be encoded as a plus (+) symbol. You typically don’t need to worry about these details though as the encode methods will do all of the work for you. With the encodeComponent() method, we can use the following code to build a query whose values were variables that could potentially contain unsafe characters. 1.2863 1.2864 1.2865 1.2866 1.2867 1.2868 1.2869 1.2870
Notice that we need to escape each potentially unsafe value individually. We also need to make sure that each parameter name is separated from the value by an equal sign (=), and ensure that each name-value pair is separated by an ampersand (&). Once we have a completed query string we can then attach the script or servlet URL to the beginning, and separate it from the query string with a question mark (?). One limitation of the GWT method is that the entire URL has a maximum length that is allowed. Unfortunately this isn’t documented, and each browser and server could impose different length restrictions. Generally the max length you often hear is 1024 characters, but again, this can vary depending on the browser and server. So if you expect your URL to be more than a few hundred characters you shouldn’t use the GET method, instead you will want to use a POST, which brings us to our next topic.
Comparing GET to the POST Method The POST method is typically used for HTML forms, where there can be a lot of data that needs to be transmitted. The POST method differs from the GET method in how it sends data to the server. Instead of including the data as part of the URL query it uses the body of the HTTP message. In HTTP the command and headers are separated from the body with a single blank line. When we looked at GET we issued a simple query to a web server. This time we will look at the same query, but sent as a POST. Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.2871 1.2872 1.2873 1.2874 1.2875 1.2876
POST /search HTTP/1.1 Host: www.google.com Content-type: application/x-www-form-urlencoded Content-length: 32 hl=en&q=GWT&btnG=Google%2BSearch
Again we have left out some of the common HTTP header values, but we have added some new ones that apply to a POST. The header now includes the content type of the message body as well as the length of the body. The message body is not limited to a certain size, so any amount of data can be passed to the server in this fashion. The body content in this example is URL-encoded data, as noted by the Content-type header. This is extremely important because the server needs to know how to read the data so that your server-side application can use it. For example, if you don’t specify that the data is URL-encoded, like the example above, a Java application server will not make this data available to the request object. This is important to know because the default content-type for the RequestBuilder class, which we look at next, is to use “text/plain”, which isn’t always appropriate.
20.
Simple RPC With RequestBuilder
The RequestBuilder class allows you to call a URL, and register a handler to receive the result. The handler works in a similar fashion as to how it worked with GWT-RPC in chapter 11, except for the fact that we can only send and receive text-only data. Any data that is not in a text form, like a Date value, will need to be converted to text when the data is sent. The data can be sent by either GET or POST, which implies URL-encoded data when using a GET, but for a POST you can pass any sort of text-based data. For both GET and POST we can also specify a username and password to gain access to the server resource we are requesting. In most cases you will not need to do this, but it is offered if the need arises. RequestBuilder resides in the com.google.gwt.http.client package, which is part of the HTTP module. To be able to use this class and its helper classes, you need to add the following inherits line to your module configuration file. 1.2877
The RequestBuilder must be instantiated in order to use it. Its only constructor takes two parameters, a method, and a URL. The method can be one of two static constants, either RequestBuilder.GET or RequestBuilder.POST. Here is an example of setting up the RequestBuilder to use a POST. 1.2878 1.2879
RequestBuilder builder = new RequestBuilder( RequestBuilder.POST, "/rpc/storedata");
Creating the builder is just the first step. We now have the opportunity to set the properties of the request. This includes adding header pairs, setting the credentials to use, and specifying a timeout. 1.2880 1.2881 1.2882 1.2883
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
By default the content-type is set to “text/plain”, which is not always appropriate. If you will be passing a query string payload to the server, you should specify a content-type of "application/x-www-form-urlencoded". On many server platforms this content-type value is used to signify that the URL-encoded data should be automatically parsed and decoded by the server. An example of such a server-side framework is a Java application server, which will only make the data available via calls to request.getParameter() if this specific content type is used. Once the additional parameters are set, you need to actually make the call. To do this, you call the sendRequest() method of the RequestBuilder object, passing it any data to add to the body of the HTTP message, and a callback handler to handle the server response. The sendRequest() method throws a checked exception RequestException if an error occurs when sending the message. Listing 12.1 Code snippet initiating the RequestBuilder request
When you make the call to the server the RequestBuilder returns a handle to the Request object. This is often useful if you want to ensure that only one request is hitting the server at a time, or if you need the ability to check on the status of a long running request. For example, to ensure that we have only one server request executing at any given time we might set up a property in our class called currentRequest, which holds the last executed request. We could then check the status of the request by calling isPending(), and if it is still active we call cancel() to cancel the request. 1.2894 1.2895 1.2896 1.2897 1.2898 1.2899 1.2900 1.2901 1.2902 1.2903 1.2904
The callback handler implements the RequestCallback interface and must implement two methods, one for handling a completed request, and one for handling an error. 1.2905 1.2906 1.2907 1.2908 1.2909 1.2910
RequestCallback storeCallbaclHandler = new RequestCallback() { public void onError(Request request, Throwable exception) { GWT.log("Error", exception); }
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.2911 1.2912 1.2913 1.2914
public void onResponseReceived(Request request, Response response) { ... } };
When an error occurs the onError() method of the handler is called. This could happen for any number of reasons, including communication errors or if the request timeout has been exceeded. If the result returns successfully from the server, the onResponseReceived() method will be called, and the Request and Response object will be passed to it. It is important to understand that a “successful” response means the server returned a valid result; it does not mean the server-side application behaved properly. To account for this, it is a good practice to check the status code of the response by calling the method getStatusCode(). The status codes are part of the HTTP protocol, where a code of 200 indicates a successful response. 1.2915 1.2916 1.2917 1.2918 1.2919 1.2920 1.2921 1.2922
public void onResponseReceived(Request request, Response response) { if (response.getStatusCode() == 200) { Window.alert(response.getText()); } else { GWT.log("Error status code", null); } }
Other codes are grouped by type. Codes in the 300 range indicate that the object has been moved, the 400 range indicates that the resource is either missing or otherwise inaccessible, and the 500 range indicates some sort of serverside error or exception. Other methods of the Response object include getStatusText(), which provides a description of the status code. For a 200 code this will return the text “OK”, and for a 500 error it will return “Internal Server Error”. The request object also provides several methods to investigate the headers that were returned with the HTTP message from the server. You can use getHeader(name) to retrieve the value of a specific header, getHeaders() to get an array of Header objects, or getHeadersAsString() to get a String value with all of the headers. In most cases the header data is relatively not useful, unless your server-side service specifically returns data in the header pairs. For example, you could use custom header values to augment the data sent in the result. Thus far we have spent a lot of time discussing the API, and the differences between POST and GET. What we haven’t yet done is put all of this together into a good example. In the next section we provide an example of using the RequestBuilder to read configuration information from a static XML file.
21.
Using RequestBuilder to Load XML Data
Describing the API as we have just done is useful, but a real-life example can help put everything into context. In this section we are going to put RequestBuilder to work for us by using it to load and parse an XML configuration file. There are real world reasons why you might have a need for this, and even in our Dashboard there are lots of places where this could be used. Since we need to pick a reason, we have decided to use an external XML file to add bookmarks to our menu bar. The idea is simple; we will create an XML file of bookmarks, load the bookmarks via RequestBuilder, and add them to a menu bar. Now, because using an XML configuration file to set the values of a menu bar isn’t very specific to the Dashboard project found throughout this book, it is our hope that you will find other uses for this example in your own projects. To that end, we provide the example as an encapsulated piece that can be easily added to any project. Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
We begin with a visual of the project and then discuss the overall architecture of the Bookmarks menu.
Designing the Bookmarks Menu Because we aren’t building a new component type, the design is relatively simple. It consists of a main MenuBar, to which we will be adding the menu-item “Bookmarks”. When the Bookmarks menu item is clicked, it opens a submenu that has been populated by our XML data file.
Figure 12.1 - A wire-frame representation of our example project that will load an external data file and populate a menu bar.
Because our intention is to add this to our existing Dashboard project, we will assume the main MenuBar object already exists. So what we need to do is write a method that takes this menu as input, as well as the URL of the XML file. In turn, the method we are going to create loads the XML data file and populates the sub-menu. Specifically, the steps that it will perform are listed in table 12.1. Table 12.1 – The flow of the main method of our example, which adds a list of bookmarks to a MenuBar based on the contents of an external XML file.
#1 #2 #3 #4
Load external XML file based on the URL passed to the method. Parse the XML data file using GWT’s XMLParser class. Create a Bookmarks MenuBar for the drop-down menu listing the bookmarks. Create a MenuItem for each bookmark in the data file where clicking on the MenuItem will trigger the bookmarked page to load into the existing window. Each MenuItem is added to the MenuBar. #5 Attach the Bookmarks MenuBar to the main MenuBar. This main MenuBar will be passed to our method as a parameter. If you have been skipping around in this book, we covered the MenuBar and MenuItem controls in chapter 4. Fortunately, these are pretty simple controls to use, so if this is the first time seeing these controls it will probably not require any explanation as to what is going on. If you do find yourself with a question, you can go back and review the details of these controls. Because this book is about GWT, and not about XML, we will keep the message format very simple, and hopefully self-explanatory.
Creating the Bookmark XML Data File For the purposes of our example, our data file will be static, short, and extremely simple. It will use as the root element, and contain a list of elements. Each of the elements will use attributes to specify the name and URL for each link. As you read through this example, think of other
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
extensions that could be added. A possible extension could be the ability to group bookmarks by sub-menu, or attaching icons to each bookmark. The possibilities are endless, but as this chapter can’t be, we will stick to the basics. 1.2923 1.2924 1.2925 1.2926 1.2927 1.2928 1.2929 1.2930 1.2931 1.2932
Our list of bookmarks was handpicked to provide invaluable resources to the GWT developer. The first two are for the official GWT site and for the developer’s forum; both are great places to go when trying to find a solution to a problem. The third is our favorite third-party GWT library, which we briefly discussed in section 9.2 in our discussion on installing third-party libraries. The last bookmark is one of the best examples of what GWT is capable of, not to mention it is a good way to spend a little down time. With our plan spelled out, and now with the data format, hopefully you are starting to understand how we will implement our method.
Implementing the Sub-Menu Reader Method The implementation of the Bookmarks reader is broken down into several parts to make it easier to understand, and to add some value for code reuse. The main method that triggers the building of the sub-menu will be named loadSubMenu(). Because we will be requesting an external resource externally, we need a handler, which will be provided as a private class named MenuLoaderHandler. And lastly we need a Command object that will load a given bookmark when the menu item is clicked. We will create a private class to encapsulate this command, called LinkCommand. This one method and two private classes will provide us with the desired functionality. Together you will be able to drop in this method and two classes into the entry-point for the Dashboard, or any other project, to add the functionality. The method signatures are summarized by this code snippet: 1.2933 1.2934 1.2935 1.2936 1.2937 1.2938 1.2939 1.2940 1.2941 1.2942 1.2943 1.2944 1.2945
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Besides dropping in this code into the entry-point class, you also need to ensure that you have imported both the XML and HTTP modules. You can do this by adding these two lines into your configuration file if they aren’t already listed. 1.2946 1.2947
We approach the solution to this problem in reverse, starting with the LinkCommand class, then to the MenuLoaderHandler, and finally the loadSubMenu method. Typically we would code this in the reverse order, but, because the two private classes require some variables to be passed to them, it is easier to explain and understand by starting at the bottom and working our way up.
Implementing the LinkCommand Class The purpose of the LinkCommand is to load a web page when it is triggered. The LinkCommand will implement the Command interface, which is part of the com.google.gwt.user.client package. Implementing the Command interface requires us to implement a single method execute(). Take a look at the implementation in listing 12.2, and then we will explain what we have done. Listing 12.2 – The LinkCommand implementation
private class LinkCommand implements Command { private String url; public LinkCommand(String url) { this.url = url; }
| #1 | #1 | #1
public void execute() { Window.open(url, "_self", ""); }
| #2 | #2 | #2
}
The execute() method in our class uses the Window.open() method to open the provided URL. The special window name “_self” is used to indicate that the page should open into the same browser window that the GWT application is running in. If you decide that you always want the URL to open into a new window you should replace this with the special window name “_blank”. Both “_self” and “blank” are not specific to GWT, they are part of the HTML specification. In order to open the URL, we of course need a URL to open. To do this, we need to provide a constructor for the command, which takes a URL as a parameter. The constructor stores this value into an instance variable so that it can be referenced later by the execute() method. By creating our command this way, we have achieved encapsulation while still allowing the command to take parameters. Moving up the call stack, the LinkCommand class is used by the MenuLoaderHandler.
Implementing the MenuLoaderHandler The MenuLoaderHandler class for our example implements the RequestCallback interface so that it can receive events from remote call that will originate from the RequestBuilder class. At this point we haven’t actually made the remote call, and we will get to that next, but for now we need to define the handler. In our example of populating a
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
menu from remote data, the handler is where all the real work is done. As with the LinkCommand class, we will start with the code, then describe how it works. Listing 12.3 The MenuLoaderHandler implementation
public void onError(Request request, Throwable exception) { Window.alert("Some problem has occurred, we are" + " unable to load your bookmarks"); }
| #2 | #2 | #2 | #2
public void onResponseReceived(Request request, Response response) { if (response.getStatusCode() != 200) { | #3 Window.alert("Some problem has occurred, the serve" + " returned a status code " + response.getStatusCode()); return; } MenuBar subMenu = new MenuBar(true);
| #4
Document doc = XMLParser.parse(response.getText()); NodeList elements = doc.getElementsByTagName("bookmark");
| #5 | #5
for (int i = 0; i < elements.getLength(); i++) { Node element = elements.item(i); NamedNodeMap attrs = element.getAttributes();
| #6
String title = attrs.getNamedItem("title").getNodeValue(); | #7 String url = attrs.getNamedItem("url").getNodeValue(); | #7 MenuItem bookmark = new MenuItem(title, new LinkCommand(url)); subMenu.addItem(bookmark);
| #8 | #8 | #8
} parentMenu.addItem(menuTitle, subMenu);
| #9
} }
There is quite a bit going on in listing 12.3, so we will take it one step at a time. Rest assured that we have already seen most of this, so there isn’t much new.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
We begin with a constructor [#1] that serves the same purpose as the one we saw with the LinkCommand class in listing 12.3, which is to allow us to pass parameters to our handler. In this case we need a reference to the main menu bar to which we are attaching the sub-menu. We also receive a title for the sub-menu that provides us with some additional flexibility, for example, if we wanted our sub-menu to be named something other than “Bookmarks”. As part of the interface we need to implement the onError() method [#2], which can get called for various network related reasons or if a timeout occurs. We have a provided a generic error message using the Window.alert() method to inform the user of the problem. As we implement the onResponseRecieved() handler we first check [#3] that the status code returned by the server indicates success. If any code other than the successful 200 code is received we display it to the user as an error message. At this point in the code we have our response from the server, so we first create [#4] a MenuBar to hold our submenu items, passing “true” to the constructor to indicate that it should appear vertically. Next the [#5] response text is parsed, and the elements are extracted from our external configuration file. The next step is to [#6] loop over each of these elements, and [#7] extract the title and URL attributes from the XML element. These values then [#8] get turned into a new MenuItem, and added to the sub-menu MenuBar. The handler ends [#9] by utilizing the parentMenu and menuTitle variables that were initially passed to the constructor. The code attaches our sub-menu to the parentMenu, using the title that is stored in menuTitle. Of course, all of this could have been done by employing anonymous classes, eliminating the need for the constructor. The drawback of that approach is that this can make the code difficult to read, and makes it impossible to reuse without copying and pasting each time it is needed. So unless the handler is very small, it is always a good idea to shy away from anonymous classes. Now to get back to our example, there is only one piece left, and that is to implement the loadSubMenu() method..
Implementing the loadSubMenu Method When we examined the code for the MenuLoaderHandler, we mentioned that the handler did most of the heavy lifting. So it might not come as a big surprise that our implementation of the loadSubMenu method in listing 12.4 is very simple. Listing 12.4 Implementing the loadSubMenu method
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
|
In listing 12.4 we simply create a new RequestBuilder instance, specifying that it should use the GET command for interacting with the server, and the URL of the configuration file. This URL, as well as the parent MenuBar and the sub-menu title are all passed into the method as parameters. In listing 12.4 we broke out the two arguments to the sendRequest() method to make it clear what we are sending. The first argument is a list of data to be passed in the body portion of the HTTP message, which for this example is blank. The second argument, shown as the variable callback, is a new instance of our MenuLoaderHandler method. Now to add have this code add a sub-menu to our Dashboard project, or any other project, we simple add a call to this method in the entry-point class, after instantiating the main MenuBar object. For example, if we have an external configuration file bookmarks.xml, and our main menu is defined by the mainMenu variable, a call like the following would add the Bookmarks sub-menu. 1.3021 1.3022
This example has been a rather lengthy one, but we hope that you find a few gems in it. In fact, the example doesn’t need to end here. If you find that this functionality, or similar functionality is useful you may also wish to extend it. One possible enhancement would be to replace the static XML file on the server with a Java servlet or CGI script capable of generating XML configuration data from a database or other source. Another idea would be to extend it to allow for sub-menus on the sub-menu, so that you can group your bookmarks by category. We hope that this example gives you for many possibilities. Next we are going to look at yet another form of client-sever communication, falling back to the ever-useful HTML form.
Examining FormPanel Basics GWT provides a rather interesting twist on the HTML form. It mixes the standard form support of HTML, and adds to it some old-school RPC techniques. In the old days, sometime after the dinosaurs but before being able to use the XMLHttpRequest object, we had to use other techniques for communicating with the server. One popular technique was to create a hidden iframe for sending and receiving data. You would send data by loading content into the iframe via JjavaScript, passing parameters via the query string to the server. Once the iframe loaded you could then inspect the contents of the hidden page to see what was returned. The FormPanel uses this technique to allow a standard form submission to return a response to your application without refreshing the page. Thus far we have already seen a few RPC tools provided by GWT, which begs the question why the FormPanel is better than the others? Well, the FormPanel isn’t really better, it’s just different. The FormPanel is best used when you want to present a set of input controls to the user, and that data is destined to be sent to the server for processing. You could use GWT-RPC or RequestBuilder for this as well, but it would require more code to get the same effect. There is though one area where the FormPanel functionality can’t be matched by any of the other RPC mechanisms, and that is when you want the browser to upload files to the server. In HTML there is a file input tag that allows a user to select a file on their local system via a browse dialog box, which can then be uploaded to the server by submitting an HTML form. Currently modern browsers don’t have any other mechanism other than an HTML form. In this section we present the functionality of the FormPanel in several parts. We will begin with an overview of how the FormPanel works, and explore its functionality. We will then take a look at the controls that can be used
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
with the FormPanel including Checkbox, RadioButton, TextBox, and others. We will round out our exploration with perhaps the most interesting use of the FormPanel, and that is to process file uploads. So let’s begin with a detailed look at how the FormPanel works.
22.
Introducing the FormPanel
It is likely that you have seen dozens of forms on the Internet. They are used for everything from webmail to shopping, registrations to subscriptions. If you have done web development before it is likely that you are well aware as to how they work, but in case you haven’t run into them before we will provide a quick overview. HTML forms consists of a element, inside of which are form controls. Each control is given a name to identify it. For example a registration form might use the names fullName, streetAddress, cityState, phoneNumber. Most forms will also include a submit button which triggers the form, and sends the data to the server. The example form we just described would look like the following code example in an HTML page. 1.3023 1.3024 1.3025 1.3026 1.3027 1.3028 1.3029
You may have noticed the method and action attributes of the form element. The method is used to identify the HTTP message type that should be used to send the data to the server, and may be either “POST” or “GET”. We discussed the difference between these two methods earlier in this chapter in section 12.1.1. The action is used to specify the URL to which the form data should be sent. The action URL may reference any server side application that can normally handle processing form data. The example points to a JSP page, but it could have been a .NET application, CGI, PHP, or just about anything. As we will see shortly the method and action concepts carry over to the FormPanel component. Inside of the form element are several form controls. In our example, the first four are text-box controls, and the last is a submit button. Each of these controls, including the submit button has a name associated with them. These names are passed to the server, along with the values, when the form is submitted. It is possible to use the same name for multiple fields, but this isn’t usually done because it complicates the processing of the data on the server. There is one exception to this rule, and that is the handling of radio buttons, where only one in a group of buttons can be selected. We will take a closer look at the controls a little later, but for now all you need to know is that each control needs to name a name. The way a form works is that when the user clicks the submit button it triggers the data to be sent to the server. The rule is that any control inside of the form element is sent to the URL specified by action. Because only the controls inside the form element are sent, you may have multiple forms on the same page. So you might, for instance, have a search form and a registration form on the same page with two different actions. The FormPanel works in the same way, when you submit the FormPanel it will send the data for all of the controls contained within the component. Figure 12.2 shows the relationship between the controls and the FormPanel container.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 12.2 A visual representation of the relations between the controls and the FormPanel in which they are contained.
Setting up a new FormPanel is a little different than other panels because you will want to set some of the properties that we already mentioned, like the action and method. So let’s start by setting up our constructing our FormPanel and setting some of these values. 1.3030 1.3031 1.3032
final FormPanel form = new FormPanel(); form.setAction("/process.jsp"); form.setMethod(FormPanel.METHOD_POST);
The setAction() method, as you probably guessed, sets the action we already mentioned, and it’s value is set to a the URL of our application. The setMethod() method sets the HTTP method to use for sending the data. You can use one of two constants for this, FormPanel.METHOD_POST and FormPanel.METHOD_GET. Unless there is a reason to do otherwise you should use the POST method since the GET method is usually limited to the amount of data that can be sent. The FormPanel is a subclass of SimplePanel, which means that it can only contain a single element. So unless you form has only one control you will need to place your controls into a panel, and then add that panel to the FormPanel. For a simple registration we will use a VerticalPanel to hold the form controls, displaying them one above the next. 1.3033 1.3034
VerticalPanel layout = new VerticalPanel(); form.setWidget(layout);
Next we need to add some controls to the VerticalPanel. In figure 12.2, we showed a simple form with four text fields. So, to keep things simple, for now we will do the same. 1.3035 1.3036 1.3037 1.3038 1.3039 1.3040 1.3041 1.3042 1.3043
final final final final
TextBox TextBox TextBox TextBox
fullName = new TextBox(); streetAddress = new TextBox(); stateZip = new TextBox(); phoneNumber = new TextBox();
When we examined HTML forms earlier we mentioned that each control contained within the form needed to have a name associated with it. This is required so that you can access the data by name when you process the form
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
on the server. To allow for this the TextBox control, and all other controls that can be used inside of a FormPanel to send data to the server implement the HasName interface, all implementations of which include getName() and setName() methods 1.3044 1.3045 1.3046 1.3047
. The only thing we are missing now is a way to submit the FormPanel. Submitting a FormPanel is done by executing the submit() method of the FormPanel instance. In a standard HTML form we would use the special submit button control, but in GWT we will need to create a Button instance and attach a listener to trigger the form submission. 1.3048 1.3049 1.3050 1.3051 1.3052 1.3053 1.3054 1.3055 1.3056
Button submit = new Button("Submit"); layout.add(submit); submit.addClickListener(new ClickListener() { public void onClick(Widget sender) { form.submit(); } });
Up to this point the FormPanel doesn’t really differ much than an ordinary HTML form. We created a FormPanel, the equivalent of the element, and added some TextBox controls that are rendered as elements inside of the form. So far so good, but FormPanel has an added trick up its sleeve, and that is event handling.
23.
Listening to FormPanel Events
The FormPanel allows you to register an event listener, allowing you to write handler code for exposed events. The two events are the submission of the form, and the completion of the submission.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The onSubmit() handler is triggered just before the actual form submission, allowing you to validate the form, and even cancel the submission. In order to cancel the submission you call setCancelled(true) on the event object passed to the method. Here we have added some validation code to the onSubmit() method from the previous example, and verify that the user supplied their name. 1.3066 1.3067 1.3068 1.3069 1.3070 1.3071 1.3072 1.3073
public void onSubmit(FormSubmitEvent event) { if (fullName.getText().length() == 0) { Window.alert("You must enter your name"); event.setCancelled(true); return; } }
If all the onSubmit() method returns without setting the cancelled flag the form will be submitted to the server. The FormPanel by default specifies the target of the form to a hidden frame. By doing this it is instruction the browser to load the server response into this hidden frame instead of the main browser window. We will discuss the options for setting the target frame shortly, but for now we will stick with the default. When the response is returned from the server, the onSubmitComplete() method of our handler is called, and it is passed a FormSubmitCompleteEvent object. This object has a getResult() method which returns the content that was returned by the server. For our example, the server returns the text “success” if everything went well. 1.3074 1.3075 1.3076 1.3077 1.3078 1.3079 1.3080 1.3081 1.3082 1.3083 1.3084
public void onSubmitComplete(FormSubmitCompleteEvent event) { String result = event.getResults(); if (result.equals("success")) { Window.alert("Registration accepted"); } else { Window.alert("Sorry, we can not accept your registration"); } }
The beauty of this system is that the server result is not limited to just plain text. So, for instance, the server might return HTML code that can then be inserted into the page by your handler. Or perhaps the server might return a complex data structure like XML data. Because the result is not limited to just text, it makes the FormPanel usable for a wide variety of operations.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Now, to take a short step backwards, we mentioned that you could alter the default target of the FormPanel submission.
24.
Altering the FormPanel Target
Now there is a good reason why you might want to change the target of the form submission, and this goes back to how to traditional HTML forms work. Typically when you submit a form on the web the browser will typically load a completely new page. Although the idea behind Ajax techniques is that the page doesn’t need to reload, there are often reasons where you will still need to do this. With the FormPanel, you can set the target of the form with the constructor only, and you cannot change it once it has been set. There are three constructor options: use the default hidden frame, use a named frame, and use a NamedFrame object. We already discussed the default behavior, so let’s start with the named frame. In HTML, each frame may be given a name, and there are also a couple of special frame names. Table 12.2 provides a list of the special frame names that can be used to identify the target of the form. Table 12.2 A list of special target names that can be used to identify certain frames on an HTML page.
_self _top _blank
_parent
Specifies the target frame as being the frame in which the FormPanel resides. For most GWT applications this will be the same as _top. Specifies that the target frame is the entire browser window. The results sent back from the server will completely replace what is currently displayed in the browser. Specified that the results from the server should be placed into a new pop-up window. This is a useful target, but only use it when there is a good reason to do so as users tend to dislike new windows. Note that you will not be able to set the features of target window. By features we mean that you will not be able to specify the height/width, remove toolbars, of similarly affect the new window. Frames may be placed into other frames, creating a hierarchy. The parent frame is the frame above the current frame in that hierarchy. We have included this target for completeness, but it will almost never need to be used.
Besides these special frames, you may want to specify a specific named frame. If you look at the description of _blank in table 12.2, it provides a hint as to one reason for doing this. When you want the results of the form submission to appear in a pop-up window, you often want to set some of the features of the pop-up, like setting the size and turning off the toolbars. To do this, you need to create the pop-up window first, then submit the form into that window. To do this, we need to first alter the FormPanel creation by passing in a named frame. We will set the named target frame to “resultPopUp”. In case you were wondering, we just made up this name; we have not actually created this frame yet. 1.3085
final FormPanel form = new FormPanel("resultPopUp");
So far so good. We now have the FormPanel targeting a named frame that does not yet exist. By default, if we don’t actually create a frame of this name and submit the results, the browser will create a new window for us with the specified name, similar to what would happen if we had specified “_blank”. Now, to get the desired behavior, we need to alter the onSubmit() method in our FormHandler code. We will use Window.open() to open an empty pop-up with the name “resultPopUp”, with a height of 300 pixels, and a width 400 pixels.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
public void onSubmit(FormSubmitEvent event) { if (fullName.getText().length() == 0) { Window.alert("You must enter your name"); event.setCancelled(true); return; } Window.open("", "resultPopUp", "width=300,height=400"); }
As you can see in the example, the window features are passed as a comma delimited String. Besides setting the height and width of the window, you can also turn on and off various features of the window. For example, the following code turns off the menu bar, and turns off the ability to resize the window. 1.3096 1.3097
String features = "width=300,height=400,menubar=no,resizable=no"; Window.open("", "resultPopUp", features);
These features are not specific to GWT, and there are a large number of them, some of which are only available in certain browsers. In general, there are only a small number that are used frequently. Table 12.3 lists the most often used features. Table 12.3 Common browser window features that can be used with the Window.open() method.
height Left location menubar resizable scrollbars status toolbar Top width
The height of the window in pixels. The number of pixels from the left edge of the screen. Specifies whether the address bar is displayed (yes/no). Specifies whether the menu bar is displayed (yes/no). Specifies if the new window is resizable (yes/no). Specifies whether scroll bars will be available (yes/no). Specifies whether the status bar should be displayed (yes/no). Specifies if the tool bar will be displayed (yes/no). The number of pixels from the top edge of the screen. The width of the new window in pixels.
We have spent a lot of time talking about the named frame constructor of the FormPanel, and most of this information also relates to the last constructor of the FormPanel, which takes a NamedFrame object as an argument. 1.3098 1.3099
NamedFrame frame = new NamedFrame("resultFrame"); final FormPanel form = new FormPanel(frame);
NamedFrame is a subclass of the Frame widget that has a name. The NamedFrame instance will render as an iframe when it is added to the page, and it must be added to the page to use it with the FormPanel. The reason for wanting to use this constructor is usually because you have a frame that is part of the page, and you want the results of the form submission to be displayed in that frame. With FormPanel you have several options for the constructor based on what you are trying to accomplish. Table 12.4 summarizes them for us. Table 12.4 A list of FormPanel related uses and the appropriate FormPanel constructor to use to solve the problem.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Problem You need to send form data to the server, and optionally be notified of the server response. You need to send the form results to a specific frame, and the frame has a name. You need to send the form results into a pop-up window.
Solution Use the default constructor.
Use the String constructor, passing in the name of the frame. Use the String constructor, specifying a name for the pop-up window, then use Window.open() to create the pop-up. You need to send the form result into a GWT Modify your code to use NamedFrame Frame control. control instead of the Frame control. Pass the NamedFrame control to the FormPanel constructor. We have spent most of our time this far looking at the actual FormPanel and how the overall mechanism works. Next we take a tour of the GWT widgets that can be used in our FormPanel.
25.
Using the Various Form Controls
The underlying functionality of the FormPanel lies in the standard functionality of the HTML form. The FormPanel is a wrapper around this existing functionality that is already supplied by the browser. In turn this means that any controls that will be used to pass form data to the server must be rendered in the page as a control type supported by standard HTML forms. So the GWT components that we will list here will mirror an existing HTML control types. For each component, we offer a brief explanation, a short code example, and a visual example of what the control will look like in your application. You will find that we have already used many of these controls in earlier chapters. To begin, we start with what is likely to be the most often used component: the TextBox
Using TextBox to Capture Text You see them everywhere, the standard TextBox control. The TextBox renders a text input box that consists of a single line, but may be any width. The following snippet creates a new TextBox with a name of “name” and a character limit of 100 characters. 1.3100 1.3101 1.3102
TextBox text = new TextBox(); text.setName("name"); text.setMaxLength(100);
Be sure to not confuse the width of the TextBox, which can be set with CSS and the maxLength property. The maxLength property is the maximum number of characters that the field can hold. This is useful when your data is inserted into a database or other fixed width persistence. The TextBox also has a sister component, the PasswordTextBox.
Using PasswordTextBox to Hide Text The PasswordTextBox is the same as TextBox with one small difference. Any text typed into the box will appear as a star or round circle to obscure the contents of the field. As the name implies, this input component is typically Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
only used for password fields. The idea being that someone looking over your shoulder won’t be able to read what you have typed. 1.3103 1.3104 1.3105
PasswordTextBox pass = new PasswordTextBox(); pass.setName("password");
1.3106
Like the TextBox, you must explicitly set the name property of the component if you plan on sending the data to a server as form data. This name is then used on the server to fetch the data by field name. Now, if PasswordTextBox is the sister to TextBox, then both of them have a big brother that is used to capture long text: the TextArea.
Using TextArea to Capture Long Text The TextArea is like the TextBox except that it can span multiple lines and include a scroll bar when the text exceeds the bounds of the display area. The following example creates a TextArea that is 30 characters wide and five lines tall. 1.3107 1.3108 1.3109 1.3110
TextArea textArea = new TextArea(); textArea.setName("description"); textArea.setCharacterWidth(30); textArea.setVisibleLines(5);
The width of a character and height of a line is subjective and will differ between browsers, so it may be preferable to set the height and width using CSS. Besides text controls, there are several other input types, all of which are probably familiar. These input types include check boxes, radio buttons, and list boxes.
Using CheckBox for Boolean Values The CheckBox component is rendered as a square that can be checked off by the user. The CheckBox constructor takes an optional label as an argument. The label, if specified, appears to the right of the check box. The following snippet asks the user to subscribe to a newsletter: 1.3111 1.3112
CheckBox subscribe = new CheckBox("Subscribe to newsletter"); subscribe.setName("subscribe");
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
On the server, the CheckBox data is handled a little differently than other form controls. Instead of the value being a specific String value, it will either be “on” or null. If the user does not check off the box, the browser will not send any information about that control to the server. A control that is similar to the CheckBox is the RadioButton.
Using RadioButton to Offer Options Like the CheckBox, the RadioButton presents a control that can be “checked” off, but will appear to the user as a circle instead of a box. The way radio buttons work is that the user is provided a list of options, and selecting a single option will deselect the others. In a group of RadioButtons, only a single RadioButton can be selected. Below is a code snippet that provides four age options: 1.3113 1.3114 1.3115 1.3116
The RadioButton constructor includes the group name for the RadioButton. In the example, all four buttons use the name “age” as the group name, which is what links them all together. The browser will only allow you to select one option from this group. When your selection is sent to the server, the name of the group can be used to retrieve the value. The RadioButton widgets are useful when presenting a short list to the user, but are not appropriate when the list is lengthy, like a list of countries. In this case, the ListBox is more appropriate.
Using ListBox to Display an Option List The ListBox is rendered as a single line, similar to the TextBox, except that at the right or the ListBox will be a small arrow. Clicking the arrow opens the list and reveals all of the possible values that can be selected. The following example creates a list with three country names that appear on the list in the same order that they are added. 1.3117 1.3118 1.3119 1.3120 1.3121
ListBox list = new ListBox(); list.setName("country"); list.addItem("Canada", "CA"); list.addItem("United Kingdom", "UK"); list.addItem("United States", "US");
.
On the server, the value in this example can be referenced by "country" field of the form data. Each of the items on the list includes a name and a value. The name is what appears on the list to the user of the application, and the
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
value is what is sent to the server. So, if the user had selected “Canada” from the list, the value “CA” would be passed to the server. The ListBox also has an alternate style that will display a scrolling list of options instead of a drop-down list. To use this alternate style, all you need to do is specify the number of items that should be visible. If there are more items in the list than can be visible, a scrollbar will appear on the right-hand side of the list allowing a user to scroll through the options. 1.3122
list.setVisibleItemCount(3);
The ListBox by default only allows the user to select a single item on the list. When an item is selected, any previously selected item is deselected. On occasion it may make sense to allow a user to select multiple options. To allow this, you need to set the multipleSelect property to true, as in the code below. 1.3123
list.setMultipleSelect(true);
This allows the user to select multiple items by holding down the control key as they select additional items. In practice, this is typically not used because it has a tendency to confuse users. So far we have only talked about the user entering on content in some fashion, but HTML, and GWT, also support hidden data.
Using Hidden to Hide Data Hidden data is passed a value to the server with the form data that you don’t want the user to see or interact with. For example, if the user is logged into your application and has obtained a security key, you would need to pass that key to the server, but it doesn’t make sense for the user to be able to edit it. The following example does precisely this; it creates a hidden form field with the security key data. 1.3124
Hidden hidden = new Hidden("securityKey", "KLHiuky$45eEW98h%YS");
When using the Hidden control, you need to make sure that you don’t abuse it. Just because the data is “hidden” from the web page doesn’t mean that it can’t be detected. It is always a good idea to ask yourself if it would be a potential security risk to allow a user to see and modify this data. If the answer is “yes”, then you probably are building flaws into your application that could be exploited by malicious attackers. Along the same lines, there is one other form control that should be used cautiously, and that is the FileUpload component.
Using FileUpload for Uploading Files The FileUpload control presents a text-box along with a “Browse” button. Clicking the Browse button allows the user to select a file on their system, and once selected the path of the file will appear in the text-box. When the form is submitted, the browser sends the contents of the selected file along with the form data. 1.3125
FileUpload upload = new FileUpload();
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.3126 1.3127
upload.setName("file");
1.3128
To be able to use the FileUpload control, you must set the method type of the FormPanel to POST, and set the encoding-type multipart. Multipart is a special content-type that allows different types of data to be mixed in a single message. If you ever sent an email with an attachment, then you have sent a multipart message. The form passes the data to the server using the same technique your mail client uses to send attachments. 1.3129 1.3130 1.3131
FormPanel form = new FormPanel(); form.setMethod(FormPanel.METHOD_POST); form.setEncoding(FormPanel.ENCODING_MULTIPART);
On the server, handling a file upload can be a rather messy task. The tools you use will depend on the language you are using on the server, but most languages have tools available to make the task more manageable. In Java, our tool of choice for handling uploaded files is the commons-fileupload library, which is one of the Apache Jakarta projects. You can download the library from http://jakarta.apache.org/commons/fileupload/. Listing 12.5 provides an example of using commons-fileupload in a servlet. The servlet will iterate over the data passed to the servlet, processing only uploaded files and ignoring all other data. Listing 12.5 A servlet using the Jakarta commons-fileupload library to capture files uploaded with the FileUpload control.
} for (Iterator i = items.iterator(); i.hasNext();) { FileItem item = (FileItem) i.next();
| #4
if (item.isFormField()) continue;
| #5
String fileName = item.getName();
| #6
int slash = fileName.lastIndexOf("/"); if (slash == -1) { slash = fileName.lastIndexOf("\\"); } if (slash != -1) { fileName = fileName.substring(slash + 1); }
| | | | | | |
try { File uploadedFile = new File("/uploads/" + fileName); item.write(uploadedFile); } catch (Exception e) { e.printStackTrace(); } } // end for
#7 #7 #7 #7 #7 #7 #7
| #8 | #8
} // end service() }
The handling of uploaded files in listing 12.5 might look a little intimidating at first, but it isn’t all that complex due to the commons-fileupload library. When the service() method of our servlet is called, the first thing we do [#1] is to make sure that the form was submitted as multipart content. As we mentioned previously, this is required in order to use a form to submit files to the server. To accomplish this, we use the static routine ServletFileUpload.isMultipartContent from commons-fileupload to perform the check for us. If the form data isn’t multipart, we simply return from the method, doing no additional work. The commons-fileupload library requires a little setup before it will parse the form data for us. So we [#2] create a new instance of ServletFileUpload and pass it a reference to a FileItemFactory. The FileItemFactory is used to generate a FileItem instance for each file that was uploaded to the server. In this example we use a DiskFileItemFactory instance. The DiskFileItemFactory factory will store the uploaded file in memory if it is less than 10 kilobytes, or save it in a temp directory otherwise. This is currently the only FileItemFactory that ships with commons-fileupload, so you don’t have a lot of choices, but it does allow you to create your own factory for cases where special handling of the data is required. We then [#3] parse the request data, which builds a list of FileItem objects. If an error occurs during the parse, a FileUploadException is thrown, providing some information on the problem. In our example, if an error occurs, we simply return, although this may not be appropriate for all applications. Next we [#4] loop through each of the FileItem instances. If the FileItem is [#5] a simple form field, or rather not a file, we simply ignore it and continue with the next FileItem instance.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
If the FileItem is a file, we then [#6] get the name of the file. The name of the file isn’t quite what you might expect, as it is isn’t just the file name but instead is the full path to the file on the client’s computer. So, for instance, the name might be “C:\Document and Settings\rhanson\Desktop\shopping_list.txt”, and this has only limited value for us on the server. So we need to [#7] strip the path information from the name so that we only have the filename. In the listing 12.5, we look for the last forward-slash (Unix style) or back-slash (Windows style), stripping anything up to that point. What we do next really depends on the application. In our servlet, we have decided to [#8] store the file in the /uploads/ directory. We accomplish this by creating an instance of java.io.File, and passing it to the helper method write() of the FileItem instance. This writes the file to disk at the location we specified. We don’t expect that you would use this servlet as-is because it likely won’t fit general application usage. For example, it is probably bad practice to place all of the files in the same directory. If your application uses user accounts, perhaps it is better to store the files of each user in their own directory. Or, if your system is an issue tracking system, of the likes of BugZilla or Jira it might be better to store the file in a directory that has the same name as the ticket number. How you store the files depends on what you are building. Now, with the end of our discussion on the FileUpload control, so ends our discussion of the FormPanel component.
Summary In this chapter we looked at two different tools: RequestBuilder and FormPanel. Both of these pass data to the server, so it is more a question as to what is the best tool for the job. If you need to send or request data from the server behind the scenes without presenting a form to the user, the RequestBuilder will be your tool of choice. RequestBuilder is easy to use and offers good error handling, allowing access to the server status code so that you can handle errors appropriately. The RequestBuilder also allows you to set a timeout for the request, cancel the request, and set and modify the headers that are sent to the server. Modifying the headers is useful if you want to set the content type to url-encoded so that server-side systems like JSP’s and servlets know that it should parse the message data. The RequestBuilder provides this flexibility at the cost of additional lines of code. The FormPanel class takes a completely different approach to sending data to the server by wrapping existing HTML form functionality found in web browsers. This makes it useful when you want to send user-supplied data to the server, where the data is submitted via a form. In the same way that the RequestBuilder response handlers work, event handlers can be registered with the FormPanel to listen for form submission and submission complete events. On completion, the results returned by the server are sent to a hidden frame, which can be read by your code, allowing you to provide feedback to the user on the server’s response. Arguably, you could use RequestBuilder along with some additional coding to provide the same functionality as the FormPanel, but there is one specific function that sets the FormPanel apart. Web browsers allow you to use an HTML form to upload data to the server. In modern web applications, this is being used more and more, especially as traditional desktop applications are ported to the web as run-anywhere applications. With the FormPanel, along with the FileUpload control, you can provide this functionality in your GWT application. Handling file uploads on the server isn’t a simple task, but tools are available for most languages to make this easier. In Java there is the commons-fileupload library available from www.apache.org, which makes it easy to write a servlet that can read uploaded files. Table 12.X summarizes these tools and compares them to GWT-RPC from chapter 10 and 11. Table 12.5 – A breakdown of the benefits of the three main tools for sending data to the server to help you pick the right tool for the job.
RPC Tool
Underlying
Benefits
Disadvantages
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
GWT-RPC
Request Mechanism XMLHttpRequest
Automatic serialization and deserialization of Java classes.
RequestBuilder XMLHttpRequest
Easy to use, good error handling, allows control over the HTTP headers and request timeout.
FormPanel
Uses a standard HTML form. Allows for simple integration with server-side frameworks like Struts, and allows for uploading files.
HTML form
Can only communicate with a server that is running a Java servletcontainer. Complex setup, requires several interfaces to be built in addition to your servlet. Can only send text data. If you need to send complex data structures like XML or data objects you need to write custom serialization and deserialization code. Requires form fields to be displayed on the page for use, which makes it inappropriate for some tasks.
In the next chapter we build on the functionality of RequestBuilder by using it to pass structured data to the server. The format we will be using is called the JavaScript Object Notation, or JSON for short. As we will see, JSON makes it easy to send structured data to server-side applications written in not just Java, but any popular programming language.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
13 Achieving Interoperability with JSON JavaScript Object Notation, or JSON, is a message format designed to be light-weight and easy to learn. The format it provides is meant to be JavaScript friendly and, at the same time, easy to read and write in any language. The JSON specification can be found at json.org, and it has met with quite a bit of success, assuming that you equate adoption with success. On the home page of the JSON site, they have nearly 40 implementations in no less than 20 different languages. Because JSON is easy to learn and is widely available, it is an ideal choice for communication between your GWT application and any type of server. In this section we discuss the basic structure of a JSON message and provide an overview of the different objects the JSON format supports. We then map the JSON format to the JSON support found in GWT and present details on how to use JSON in your application. Finally, we round out our discussion by implementing a search component that takes advantage of Yahoo’s JSON API. Because developers using JSON will most likely be doing so because they don’t run Java on the server, we implement the server portion of our component in not only Java, but in Perl and Ruby, as well. So now lets begin with an introduction to JSON.
Introducing JavaScript Object Notation (JSON) In this section we begin with a brief overview of the JSON message format and how you can use the GWT JSONParser class to read JSON messages. This lays the groundwork for the rest of the chapter where we get into the details of JSON value objects and provide an example application. To help us understand what the JSONParser is actually doing under the hood, we need to first understand what a JSON message looks like.
26.
Understanding the JSON Data Format
JSON is a message format that was designed specifically to take advantage of JavaScript’s ability to evaluate strings as code. This way this works is that the JSON format is really just valid JavaScript code, and when evaluated can rebuild the object. This benefit is that the code required to deserialize a JSON message is only a single line of JavaScript code. To understand how this works, consider the following snippet of JavaScript code. var code = '["this","that","other",1,2,3]'; 1.3189 var x = eval(code);
In this example the variable code is a string value that contains valid JavaScript code. The brackets denote an array value, and it contains six values of both strings and numbers. In the second line, we evaluate the string and assign the result to the variable x. The result of this is that x is now an array of values. This is how JSON works, by simply evaluating whatever is in the string. Of course we could use eval() to execute any JavaScript code, but JSON specification puts some limits on this. The reason for doing so is that JSON endeavors to be easy to implement in any language, not just JavaScript. To this end, JSON is limited to only to a few basic values: null, true, false, string, number, array, and object. Let’s look at another example that uses all of these different types. 1.3190
var code = '{
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
We have spaced out the code variable this time for readability, but this would usually be a single long value. In the value we have used curly-braces to create a JavaScript object, in which there are five name/value pairs. This object is roughly equivalent to a Map in Java. Each name/value pair consists of a string name, and a value that can be any of the allowable types we mentioned. JSON arrays, just like objects, can contain any of the value types. This allows you to create complex data structures any number of levels deep. One type we want to focus some attention on is the JSON number value. This type is a generic type used to hold integers and floating point values of any size. Numbers may be negative or positive, and may use scientific notation. 1.3198 1.3199
var code = ' [-90823, 987345, 24345.23445, 1.234e8]'; var x = eval(code);
For further information on the JSON format you should visit the json.org website. It spells out in minute detail the allowed JSON values. They also list quite a few JSON implementations in various languages that would be used on the server to parse and build JSON messages. None of these implementations is especially useful with GWT on the client-side, so GWT provides its own JSON support. The first, and arguably the most important, GWT class for using JSON is the JSONParser.
27.
Using JSONParser to Parse JSON Messages
The JSONParser is responsible for deserializing JSON data into Java objects, and it is extremely easy to use. You parse a JSON message by calling the static parse() method of JSONParser, passing in the raw JSON message. If the JSON message is malformed, the parser will throw a JSONException. 1.3200 1.3201 1.3202 1.3203 1.3204 1.3205 1.3206
The JSONValue object that is returned by the parse is a generalized JSON type that could be any one of a number of JSONValue subclasses. We examine the JSONValue class as well as all of its subclasses next. The JSONParser doesn’t really “parse” the JSON message. Instead it evaluates the JSON message as JavaScript, essentially executing it, much like you would do if you were writing your JSON client code in JavaScript.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Examining GWT’s JSON Data Objects As we mentioned in the last section, JSON consists of only a few basic data types; string, number, Boolean, and null. To this JSON also has the concept of objects and arrays, which hold collections of JSON values. To this end, GWT provides a set of classes that correspond to each of these JSON values types. In the text that follows, we provide class diagrams of each of these data types as well as examples on how to use them. Besides having these JSON data objects constructed as the output of the JSONParser, we will also show you how to construct JSON objects, tie them together as an array or object, and generate a JSON message which can be sent to the server. But before we get to these specific JSON types, we need to start at the beginning, and examine the parent of all JSON types in GWT, the JSONValue object.
28.
Introducing the JSONValue Object
At the top of the JSON stack is the JSONValue object. This class is the abstract super-class of all JSON value objects. It contains a test method for each JSON types and a toString() method, which will return a JSON message string. Each of the test methods will either return an object of the specific type, or null of the object isn’t of that type. For example, the isArray() method returns the JSONArray object if the value is an array, or null if it isn’t.
Figure 13.1 A class diagram of the JSONValue class, which is the super-class of all JSON data objects in GWT.
The test methods allow for some creative coding. In this example we have some JSON value stored in the variable jsonValue, and we want to log an error if it isn’t a JSONString value. 1.3207 1.3208 1.3209 1.3210 1.3211
Inside the if-statement condition, we are calling jsonValue.isString(), which will return a JSONString object if it is a string, or null if it is not. We then test the contents of the variable data to determine if it is null. If it is null we log the error. This format may take a little to get used to writing if you haven’t used it before, but it is concise and compact. Another way to code the same thing would be the following. 1.3212 1.3213 1.3214 1.3215
JSONString data = jsonValue.isString(); if (data == null) { GWT.log("bad data", null); }
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The style you use depends a lot on your coding style and how much error checking you want to include. If you don’t require any error checking, you can simply cast the value object to the appropriate type. 1.3216 1.3217
JSONString data = (JSONString) jsonValue;
Next we look at the specific JSON data.
29.
Examining Basic JSON Types
Besides JSON array and object types, there are four basic value types in JSON: string, number, Boolean, and null. Figure 13.2 provides a visual view of these classes and shows their relationship to the JSONValue parent class.
Figure 13.2 A class diagram of the basic JSON data types that are part of the GWT library.
To construct a JSONString, you pass the String value as a argument to the constructor. The value can then be retrieved using the stringValue() method. 1.3218 1.3219 1.3220
JSONString name = new JSONString("John Doe"); String value = name.stringValue();
The JSONNumber class is similar in that you pass the value to the constructor, which is a Java double. JSON does not include support for granular number types like Java; in JSON all numbers are equal. For example Java provides several primitive number types like short, integer, long, float, char, and byte. To retrieve the encapsulated value from a JSONNumber instance, you call the getValue() method. 1.3221 1.3222 1.3223 1.3224
JSONNumber age = new JSONNumber(25); double value = age.getValue();
To create a JSONBoolean instance, you call the static method getInstance(), passing a Java boolean to indicate the JSON value type to return. To test the value of the object once it is created, you call the booleanValue() method. 1.3225
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
JSONBoolean married = JSONBoolean.getInstance(true); 1.3226 boolean value = married.booleanValue(); 1.3227
A JSONNull object is the equivalent to a Java null value. To get an instance, you call the static method getInstance(). 1.3228 1.3229
JSONValue children = JSONNull.getInstance();
All of these objects are subclasses of the JSONValue class and, therefore, inherit all of its methods, including toString(), which returns a JSON message that represents the object’s value. Several of these values can also be tied together as an array or map.
30.
Storing JSONValue Objects in a JSONArray
The two container type objects supported by JSON are arrays and objects. A JSON array is, as you probably guessed, an array of JSONValue objects. It is just a simple ordered collection of values, with a get() and set() method to read and write values to the array.
Figure 13.3 The JSONArray class, which can be used to store collections of JSONValue objects.
The JSONArray has two constructors, one being a zero-argument constructor, and the second taking a JavaScriptObject. The second constructor is typically only used from within the JSON parser, but could be used read values returned from native JSNI methods that return a JavaScript value type. To use the fill a JSONArray, you first create a new instance and add values using the set() method. The JSONArray doesn’t include an add() method like you find with Java’s Vector or ArrayList, so if you want to append a value to the end of the array you need to call the size() method to get the current size. 1.3230 1.3231 1.3232
JSONArray pets = new JSONArray(); pets.set(0, new JSONString("Maxie")); pets.set(1, new JSONString("Minnie"));
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
When putting values into an array, you must ensure that you do not skip any indices. When you call toString() on the JSONArray, it throws an exception if any of the array indices do not contain an object. 1.3233 1.3234 1.3235 1.3236
JSONArray pets = new JSONArray(); pets.set(0, new JSONString("Maxie")); pets.set(5, new JSONString("Minnie")); String jsonValue = pets.toString(); // throws exception!
If your code could potentially skip indices, you could use the following method to fix the array by inserting JSONNull values where no values exist. Any value in the array that returns a Java null value will not be able to be converted to a JSON message and needs to be set with an instance of JSONNull. 1.3237 1.3238 1.3239 1.3240 1.3241 1.3242 1.3243 1.3244 1.3245
private void fixJsonArray(JSONArray array) { for (int i = 0; i < array.size(); i++) { JSONValue val = array.get(i); if (val == null) { array.set(i, JSONNull.getInstance()); } } }
To read data from a JSONArray, you can use the size() method to determine the number of values, and use the get() method to retrieve each value. 1.3246 1.3247 1.3248 1.3249 1.3250
for (int i = 0; i < pets.size(); i++) { JSONValue val = pets.get(i); // ... processing ... }
JSONArrays are useful for ordered data, but we still have yet to look at a way to store mapped data. The JSONObject fills that need.
31.
Collecting JSONValue Objects in a JSONObject
The JSON object type is roughly equivalent to the Java class java.util.Map, where it is a collection of name/value pairs. In languages other than Java, this type of data structure may be known as a hash, associative array, or as in JavaScript, an “object”. 1.3251 1.3252
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.3253 Figure 13.4 A class diagram of the JSONObject class, used to a collection of JSONValue objects.
The JSONObject, like the JSONArray, may be constructed with the zero-argument constructor or by passing a JavaScript object. The ability to pass a JavaScriptObject is handy if you want to convert a JavaScript object that was returned from a native JSNI function to a JSONObject. Adding values to a JSONObject is the same as a Java Map. It provides a put() method that takes a String as the key and a JSONValue object as the value. 1.3254 1.3255 1.3256 1.3257 1.3258 1.3259
JSONObject person = new JSONObject(); person.put("name", name); person.put("age", age); person.put("married", married); person.put("children", children); person.put("pets", pets);
To iterate over the values in the JSONObject, you can use the keySet() method to return a java.util.Set of keys. You can then pass each key to the get() method to retrieve the stored JSONValue. 1.3260 1.3261 1.3262 1.3263 1.3264 1.3265
Set keys = person.keySet(); for (Iterator i = keys.iterator(); i.hasNext();) { String key = (String) i.next(); JSONValue value = person.get(key); // ... processing ... }
The JSONObject class also includes a size() method to get the number of name/value pairs and a containsKey() method to test for the existence of a key. int pairs = person.size(); Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
boolean hasName = person.containsKey("name"); One method that is not available is a remove method, so, once you place something in the JSONObject, you will not be able to remove the name/value pair without rebuilding the entire object. In most cases this isn’t an issue because JSON objects are typically only used for transport, and not modified after creation. This concludes our examination of not only JSONObject, but also all of the JSON value objects. Now let’s put JSON to work for us as we build a search component that uses the Yahoo search API.
Creating a Search Component Using JSON Yahoo is one of those companies that has provided a lot in the way of open APIs that developers can use. The most obvious choice for an API for a search engine, and Yahoo provides support for several data formats including JSON. Over the course of this section and the next, we implement a search component that displays search results from the Yahoo search engine.
Figure 13.5 A completed view of our Yahoo-service search component running in of the GWT hosted browser.
We will break our component into two parts, the client-side and the server-side. On the client we will send a JSON request to the server and then display the results of the search by parsing the JSON response. On the server side we will utilize a 3rd party Java API to read the JSON request from the client, then call the remote Yahoo server API. The server portion will then return the raw JSON response from Yahoo to the client browser. And then once we finish that, we will re-implement the server-side code in Ruby and Perl. To start off, we explain the Yahoo search API and provide details on the returned JSON data.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
32.
Examining the Yahoo Search API
Yahoo provides a rather rich set of search APIs, allowing you to write applications that can search for audio, images, web sites, videos, and then provides a ton of options. For the purposes of our search component, we will be just the web search service, and we will request that the returned data be in the JSON message format. Due to space constraints, we can only cover a small portion of the search API and its options, but, if you decide to explore more of what Yahoo has to offer, you can find the full details at http://developer.yahoo.com/search/index.html. The Yahoo API can be accessed by passing the search arguments in a URL. This is the base URL for the Yahoo search API. 1.3266
To this you can add various query parameters. Table 13.1 describes some of the available parameters, which will be using in our component. Table 13.1 A subset of Yahoo’s search service parameters which apply to our search service example.
Appid
Output Results Query
The application id is a required parameter. The Yahoo developer network provides information for registering your appid and the terms of use. Visit http://developer.yahoo.com/faq/index.html#token for additional information. There are several output formats including xml, php, string. For our application we will use “json” as the value for this parameter. The number of results to return. The default is 10 results. This parameter is used to pass the search terms to the Yahoo service. This is a required parameter.
When we pass the value “json” as the output parameter, the results are returned as JSON data. We can test this by appending the following search parameters. When you use this you should replace with appid value “test” with your own. ?appid=GWT-Book&output=json&results=1&query=Manning The results returned from this search look like the what you see in listing 13.1. Note that we have reformatted the data to make it more readable, and that when you use this in your browser or application the data will be a single line. Listing 13.1 An example of a JSON formatted message received from Yahoo’s search API when we search for the term “Manning”.
The root object returned is a JSON object, as denoted by the curly-braces. Inside the object is a single property named ResultSet. ResultSet is itself an object, which contains various properties like type, total available results, total results returned, a link to additional results, and a Result property. The Result property is an array of results, with each result being a JSON object. Each result object contains the title of the result, a summary, a display URL, a click URL, and other details about the result. Now that we have an idea of what the Yahoo data looks like, we now need to create our client application and implement the code to read this JSON data.
33.
Implementing the Yahoo Search Component
We have built several components at this point, so we won’t repeat the steps for creating a new project here. Instead we will get right into the project. Your project name my vary, but for the purposes of the code examples found here, our entry point class is org.gwtbook.client.JSON. Setting up your JSON project is the same as any other with one addition. By default the JSON classes are not in the build path for the GWT compiler, so we need to add them to our project configuration file. To do this we inherit the com.google.gwt.json.JSON module, which will allow the GWT compiler to find the JSON classes. With this addition, our project configuration looks like the following. 1.3294 1.3295 1.3296 1.3297 1.3298
<module> <entry-point />
For our component we have created a class called YahooSearchComponent that extends the GWT Composite class, and resides in the org.gwtbook.client.ui package. Again, we have covered how to do this several times, so we will only briefly describe the general structure of the component.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Listing 13.2 provides the basic structure for our component. The widget we are using for the top level of this component is a FlowPanel. Inside it we will include a TextBox, where the user will enter their search text, a Button to trigger the search, and a FlowPanel to hold the search results. In the constructor for the component we have added a ClickListener which will trigger the search() method, which we will define shortly. If you comment out the search() call, you should be able to create an instance of the component from your entry point and test it in the hosted browser. The following is the code for out entry point. (Nothing special here; we are just instantiating the YahooSearchComponent and adding it to the RootPanel.) 1.3332 1.3333 1.3334 1.3335 1.3336 1.3337 1.3338 1.3339
public class JSON implements EntryPoint { public void onModuleLoad() { YahooSearchComponent search = new YahooSearchComponent(); RootPanel.get().add(search); } } Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 13.6 shows you what you should have so far, a simple page with just a search box and button. Also notice that we have not provided any special CSS styling for this component, and instead leave that up to you to add.
Figure 13.6 The beginnings of our Yahoo-service search component
Now that we have the basic structure of the component set, it is time to start using some of the JSON knowledge we have provided, and make the component work. Part of this work involves having the search component send data to the server.
34.
Sending JSON Data to the Server
When you use JSON to communicate with the server, you do so with the RequestBuilder class that we looked at in chapter 12. JSON by itself is not a transport mechanism, it is just a message format, so we need to use RequestBuilder to actually transmit the message. In the discussion that follows, we will no doubt repeat some of what we stated in the section on RequestBuilder, but this is not a substitute for our earlier explanation. So, if you find that you do not quite understand something about the transport of the JSON message, you should refer to the appropriate topic in chapter 12. First, in order to use RequestBuilder we need to import the com.google.gwt.http.HTTP module by adding the following line to the module configuration for this project. 1.3340
This will allow the RequestBuilder class, and associated classes, to be included into our project path for use by the GWT compiler. Getting back to the project, when we ended our coverage of implementing the search panel in the last section, we had created a click listener to trigger a search() method. This method, as we had explained, would trigger the search. In this section we define the search() method that provides an example of sending a JSON message. We provide the code first, then provide some explanation. 1.3341 1.3342
private void search() {
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
String searchString = searchBox.getText()); JSONObject o = new JSONObject(); o.put("searchString", new JSONString(searchString); o.put("maxResults", new JSONNumber(5)); resultsArea.clear(); RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, GWT.getModuleBaseURL() + "search"); try { rb.sendRequest(o.toString(), new SearchResultsHandler()); } catch (RequestException e) { GWT.log("Could not send search request", e); } }
The first line of this method, if you haven’t been following along, gets the search terms for our YahooSearchComponent from the TextBox. The next three lines following this are where we actually build the JSON message. We are constructing a new JSONObject and adding two properties to it, searchString and maxResults. We do this by creating the appropriate JSON object type and adding it to the JSONObject. Again, this should all be review, but, if you missed that part, go back and read section 13.2 where we define each of the JSON class types. Following this we clear the search results panel and finally send our request to the server. This is the important part of this method. Here we are using RequestBuilder with the POST method. We need to use this HTTP method instead of an HTTP GET because we want the JSON message to be passed in the body of the HTTP message. We favor this over passing the data in the HTTP query string because an HTTP POST doesn’t limit us as to the length of the JSON message. The sendRequest()method takes two parameters: the POST message, and a reference to a RequestCallback object. For this last parameter, we are passing a new instance of SearchResultsHandler, which we will define shortly, whose job it will be to parse and display the resulting JSON message and display the search results to the user. For the POST message we very simply call the toString() method on our JSON object. This will build a JSON message from the object, which actually looks like the following: 1.3360
{"searchString":"google web toolkit", "maxResults":5.0}
With the JSON format, the curly braces denote the boundary of a JSON object, and within are two properties. The searchString property in this specific example contains the string “google web toolkit”, and maxResults property contains the value 5.0. As we mentioned earlier in this chapter, JSON doesn’t distinguish between integer and floating point numbers, which is why the value “5”, which we had set the value to in the code, is being passed as “5.0”. In case you missed it, we discussed the JSON format in section 13.1.1. Now the only thing left on the client-side of our component is to implement the SearchResutlsHandler class that will parse and validate the results from the server and display them to the user.
35.
Parsing and Validating a JSON Server Response
When we receive the JSON message results from the server, we want to add comprehensive validation to the message. Because this requires a bit of code, we will break up the functionality into several bite-sized pieces. The following is the shell for our response handler, and it contains four methods, three of which are currently not defined.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
class SearchResultsHandler implements RequestCallback { public void onError(Request request, Throwable exception) { GWT.log("Search request failed", exception); Window.alert("Sorry, the search request could not be sent."); } public void onResponseReceived(Request request, Response response) { if (response.getStatusCode() != 200) { Window.alert("Sorry, there was an error..."); return; } JSONArray results; results = extractYahooResults(response.getText()); updateResultsArea(results); } private JSONArray extractYahooResults(String responseText) {} private void updateResultsArea(JSONArray results) {} private String getString(JSONValue value) {} }
The flow of the handler is fairly simple. It will first examine the JSON response and extract just the JSONArray that contains the search results. This is done in the method extractYahooResults(), and this is where we will provide a lot of error handling. The results are then passed to updateResultsArea() which will update the results panel of our component. The last method is a simple utility method that takes a JSONValue and returns the String representation. Remember that calling toString() on a JSON object returns a JSON message string. This isn’t always what we want and the reason for this method, since it would automatically add quotes to a String value. Let’s begin by defining the extractYahooResults() method. Listing 13.3 Handling the Yahoo API response
if ((resObject = resVal.isObject()) == null) { GWT.log("resObject is unexpected type", null); return null; }
| #3
if ((resultSet = resObject.get("ResultSet").isObject()) == null) {| #3 GWT.log("ResultSet object not found", null); return null; } if ((results = resultSet.get("Result").isArray()) == null) { GWT.log("Result array not found", null); return null; }
| #3
return results; }
Because we need to write the validation of a JSON message ourselves, it can become a little verbose. Because of this, there will be a strong desire to just skip it and assume that everything will just work. This may work out most of the time, but when something isn’t working right it can be difficult to debug. In our validation routine we want to make it easy to find problems, and in the long run it is usually worth the time involved in writing a detailed validation routine. #1 We begin our validation by verifying that the message we are going to parse is not null, and that it is not empty. If either of these is true, the parse automatically fails and throws either a NullPointerException or IllegalArgumentException. By making this check ourselves, we can log the root cause of the problem without causing an exception. #2 Next we attempt to parse the JSON message. If something goes wrong, the JSONParser throws a JSONException. This is not a checked exception, so we don’t need to surround this in a try block, but again we are attempting to make it easy to debug any issues with the message we received from the server. Following this we have three separate checks #3 as we dig down deeper in the JSON message to find the search results we need. We first verify that the object we get back from the JSONParser.parse() call is a JSONObject. We then verify that this object has a property ResultSet, which is also a JSONObject, and that object has a property Result, which is a JSONArray. This followed the result format for the Yahoo JSON message, as we discussed in section 13.3.1. In each check we call the appropriate isXYZ() method, which returns null if the underlying object is not of that type. If everything checks out, we return a JSONArray of search results. The next method we need to define is updateResultsArea(), which displays the results to the user. Listing 13.4 Updating our component’s display
1.3424 1.3425 1.3426 1.3427
private void updateResultsArea(JSONArray results) { for (int i = 0; i < results.size(); i++) { JSONObject result;
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
In this method, we loop over the results in the JSONArray, and add each to the resultsArea FlowPanel. #1 Again we are cautious with the JSON data, and verify that each result is a JSONObject. We then #2 get the string value of the title, summary, and link. For each of these we use the getString() method, which we will define shortly. We then #3 create a Hyperlink for each result, and add a ClickListener, which sends the user the URL for the result when clicked. The only method left to define is the getString() method, which returns the String representation of a JSONValue object. Again, we are careful in not making any assumptions about the JSON data. 1.3449 1.3450 1.3451 1.3452 1.3453 1.3454 1.3455 1.3456
private String getString(JSONValue value) { if (value == null) return ""; if (value.isString() != null) return value.isString().stringValue(); return value.toString(); }
Here we plan for three cases. If the value is null, we return an empty String. This makes the method null safe and prevents NullPointerExceptions. We then test to see if the value is a JSONString, in which case we call the stringValue() method to return the value. The third case is when we don’t know what it is, so we call isString(), and return a JSON formatted message for the object. At this point we have come a long way, but we don’t quite have a working search. So far we have a client, but we still need to write the code for the server. For our server, we will write a create a server-side application that will proxy requests between the browser client and the Yahoo search service.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Implementing a Yahoo Search Proxy Service When we began our project, we stated that we would build the server-side portion of the application using Java, Perl, and Ruby. In the sections that follow, we have done just that. We expect that only one or two of these might be of interest to you, so feel free to look at only those languages you are interested in. In each of the implementations, we explain everything you need to know with regard to that language, and we do not reference any text across those sections. So you won’t miss anything if you skip a section. Now we are calling this a proxy service, meaning that our server code is merely the man in the middle, passing data between the client browser and the Yahoo service. Again, the reason for this is that, due to security restrictions in the browser, our client application will not be able to hit the Yahoo service directly. If you missed the section on RPC security concerns, you can take a look at section 10.1.2 where we explain the reasoning behind this. In each of the following sections, the purpose of our code is to first read the request from the client browser and pass it along to the Yahoo service. The request from the client browser comes to the server as a JSON message, so, in each of the implementations, we include notes on the JSON implementation that we used, and where you can get it. When we call the Yahoo service, we do so by hitting a URL that has been provided by Yahoo. Again, in each language, we need to use a language library to do this, and we will provide some information on which library we used. The contents returned by the Yahoo service are not processed by our server-side code; instead it just passes the content to the browser to be processed on the client. So our server code will complete its work by “printing” the contents to our client. So, with that brief explanation of what we are trying to accomplish, let's take a look at how this all works, first with Java.
36.
Using JSON with Java on the Server
As we stated, we need to implement a proxy service for our Yahoo-based search component. For this project we will implement this as a Java servlet, and use third-party libraries to allow us to parse JSON data, and to hit an external web address. There is no requirement that this be implemented as a servlet, but we have done so because we feel that, as a stand-alone application, this it the best fit. To begin, we need to first address the source of external libraries that will be required to deploy this project. To allow our servlet to hit a remote web site, we have chosen to use the Jakarta Commons HttpClient. If you are not familiar with the Jakarta project, it is a highly respected project in the Java world and is the Java arm of the Apache Foundation. You can download the latest version of HttpClient library from the following URL: http://jakarta.apache.org/commons/httpclient/ The other external library you will need is a library for parsing JSON objects. When we visited the JSON.org web site, there were several Java implementations available as well as a reference implementation provided by JSON.org. Each of the libraries had their own set of features and tools, some easier than others. In the end, we decided to use the reference implementation because it wasn’t dependant on third-party libraries. The reference implementation can be found at this URL: http://www.json.org/java/ Unfortunately, there is one downside to this, and that is that the JSON.org folks don’t currently have their code packaged as a JAR file, so you will need to download the source and add it to your project. Our decision to use this implementation was based on our need to make this example easy to implement, and we didn’t want to have our readers downloading dozens of external libraries to get the application working. On the other Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
hand, if you don’t mind tracking down dependencies, you may find one of the other implementations more appropriate for your project needs. In which case, take a look at the list provided on the JSON.org home page for a list of alternate Java distributions. Once you have the prerequisites downloaded and added to your classpath, we can proceed with the actual implementation. Compared to the other languages we will look at, the Java one is by far the longest. It isn’t more difficult, though; it is just that Java, by its nature, tends to be more verbose than dynamic languages. We want you to know this because the code to follow looks a little daunting, but it isn’t really complex. So, without further ado, we present the implementation, followed by a careful explanation as to what is exactly going on. Listing 13.5 A JSON server proxy in Java 1.3457 package org.gwtbook.server; 1.3458 1.3459 import java.io.*; 1.3460 import java.net.URLEncoder; 1.3461 import javax.servlet.ServletException; 1.3462 import javax.servlet.http.*; 1.3463 import org.apache.commons.httpclient.HttpClient; 1.3464 import org.apache.commons.httpclient.methods.GetMethod; 1.3465 import org.json.*; 1.3466 1.3467 public class YahooSearchService extends HttpServlet 1.3468 { 1.3469 1.3470 protected void service( 1.3471 HttpServletRequest request, 1.3472 HttpServletResponse response) 1.3473 throws ServletException, IOException 1.3474 { 1.3475 1.3476 BufferedReader input = new BufferedReader( 1.3477 new InputStreamReader(request.getInputStream())); 1.3478 1.3479 StringBuffer data = new StringBuffer(); 1.3480 String buf = input.readLine(); 1.3481 while (buf != null) { 1.3482 data.append(buf); 1.3483 buf = input.readLine(); 1.3484 } 1.3485 1.3486 String searchString = null; 1.3487 int maxResults = 0; 1.3488 1.3489 JSONObject json; 1.3490 try { 1.3491 JSONTokener tokenizer = new JSONTokener(data.toString()); 1.3492 json = new JSONObject(tokenizer); 1.3493 searchString = json.getString("searchString"); 1.3494 maxResults = json.getInt("maxResults"); 1.3495 } catch (JSONException e) { 1.3496 e.printStackTrace(); 1.3497 } 1.3498
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
GetMethod get = new GetMethod( "http://search.yahooapis.com/WebSearchService/V1" + "/webSearch?appid=GWT-Book&output=json" + "&results=" + maxResults + "&query=" + URLEncoder.encode(searchString, "UTF-8"));
| | | | | | |
#4 #4 #4 #4 #4 #4 #4
HttpClient client = new HttpClient(); client.executeMethod(get); String result = get.getResponseBodyAsString(); get.releaseConnection();
| | | |
#5 #5 #5 #5
response.getWriter().print(result);
| #6
} }
#1 As we mentioned, we have implemented this service as a Java servlet. The entry point method for a servlet is the service() method, which a request and response object as parameters. These objects can be used as handles to read input from and write output to the browser. #2 Because the content being sent by the browser is not a standard query, we need to do a little work to extract the JSON data. Here we use the request object to get a handle on the input stream, and then read it into the StringBuffer variable data. This code should be fairly standard if you plan on using servlets to handle JSON requests, so it may be worthwhile to package this code as a method so that you can easily reuse it. #3 Once we read in the JSON message request from the browser, we need to parse the message and extract the parameters. The exact code you use here depends on the JSON library that you chose to use. In our case we are using the reference Java implementation from JSON.org. To parse the message, we create an instance of the JSONTokenizer, and pass it to the constructor of the JSONObject. The JSONObject constructor uses the tokenizer to parse the message, and, if something is wrong with the message, it throws an exception. Once the message has been parsed, we just call the appropriate methods on the JSONObject to get the data, in this case getInt() and getString(). The names “searchString” and “maxResults” are the parameter names we defined when we built the search client in the last section. #4 Next we utilize the Commons HttpClient library and build a GET request. The URL we are building is the one that is defined for the Yahoo search service, and note that we are careful to URL encode any String values. Encoding values that are sent in the URL required since the search text might include characters that are considered reserved in the URL query format. #5 Once we have our request, we need to actually send it to the Yahoo service. This involves creating a HTTP client, executing the request, grabbing the result, and closing the HTTP client. This is analogous to opening your web browser, going to a URL, reading the page, then closing your browser. #6 With the result in hand, all that is left to do is print the JSON message that came from the Yahoo service. We print this value using a special writer which will ultimately deliver the data to the client browser. This last part ends the request-response cycle between the browser and the servlet. Before we move on to the next language implementation, there is issue that may arise, depending on the remote service you are trying to proxy to. Some services attempt to detect the type of client hitting their service and may deny
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
access to the service in some cases. The service is able to do this because your web browser, and the HttpClient class as well, sends information about what type of client, also known as the user agent, it is. This is what allows web site log analyzers, for example, to determine what types of browsers are being used by the users of a web site. One example that we know of is Google Groups, which denied a servlet that we wrote from being able to access their service. In order to access the service we needed to provide a user agent name other than the default used by the Commons HttpClient library. 1.3515 1.3516 1.3517 1.3518
HttpClient client = new HttpClient(); HttpClientParams clientParams = new HttpClientParams(); clientParams.setParameter(HttpMethodParams.USER_AGENT, "SecretAgent"); client.setParams(clientParams);
When we made a request with this HttpClient, it now reports our agent type as being “SecretAgent”. There are lots of other reasons to set this value to something other than the default, including masquerading as a specific browser type, or to include your contact information. Hopefully we have covered everything you need to know to start writing your own JSON services in Java. So with that we now move on to our second implementation in Perl.
37.
Using JSON with Perl on the Server
One of the old workhorses of the web is Perl, and it is still very popular today. Here we provide the code required to get the server portion of our Yahoo-based search service project up and running in Perl. In the course of doing so, we will have a chance to see how Perl is able to work JSON messages, as well as its ability as a proxy between the client and a remote service. As with the Java version, we need to get a few external libraries, the first of which is to provide support for JSON. As with all things Perl, there is more than one way to do it, and, with a quick search of CPAN.org, we found many. The one we settled on is the module JSON, by Makamaka Hannyaharamitu, which can be downloaded from the following URL. http://search.cpan.org/~makamaka/JSON/ The second library we need is one that we can use to call the Yahoo web service from our Perl script. The obvious choice for doing this is libwww-perl by Gisle Aaa. This is probably one of Perl’s most popular libraries, so you may already have it installed, but, if you don’t, you can download it from the following address. http://search.cpan.org/~gaas/libwww-perl/ If you aren’t sure if LWP is installed on your system, you can run the following command, which will print the version number of LWP if it is installed. If you are running this command on Windows, you should replace the single-quotes in the command with double-quotes. 1.3519
perl -MLWP -e ‘print $LWP::VERSION’
For our Perl example, there is a third library we need as well, and that is the URI::Escape module, again by Gisle Aas. This library is used to URL encode strings and can be found here: http://search.cpan.org/~gaas/URI/URI/Escape.pm
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Once you have the prerequisites installed, it is time to write some code. Due to Perl’s concise nature, this example is actually rather short. Following the code example, we will explain what the various parts of the script are doing. Listing 13.6 A JSON server proxy in Perl
#1 The first thing we need to do is create a new CGI instance, and print the content headers. CGI is the defacto standard module when it comes to CGI support in Perl. Here we are using it to print the standard “Content-type” header. This header informs our web server what type of content we are printing out, which in this case is plain text, so that it can be handled accordingly. #2 Next we need to read in the JSON message that was passed to the client, and parse the message so that it can be read. The CGI module makes the first task easy, by allowing us to retrieve the “POSTDATA” parameter, which returns the entire message that was posted by the client browser. The parsing of the JSON message is done by calling jsonToObj(), which is one of the methods automatically imported when we used the JSON module. The jsonToObj() method converts the JSON message to a regular Perl object. #3 With the JSON request in had we now need to build the query that will ultimately be sent to the Yahoo service. We are using sprintf() to help us format the string, and we call uri_escape() to URL encode the search string. The uri_escape() method is automatically imported when we use’ed the URI::Escape module. #4 The last two steps needed to finish up our proxy, that is calling the Yahoo service and printing the result., are performed by the last statement in the script. The getprint() method is automatically imported when we use the LWP::Simple, and handles both of these tasks for us. With less than 20 lines of code, you should be up and running in Perl. We expect that the JSON module, along with LWP::Simple, will cover most of your JSON needs, but if not your first stop should be CPAN. CPAN has thousands and thousands of modules available, so, if you require some bit of functionality, it is likely someone else needed that same functionality and shared their code. One interesting module we found in our search is the DBIx::JSON module, which generated JSON messages from database queries. So, before you build it, see if someone else already did the work for you.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
http://search.cpan.org/ With that, we conclude the Perl version of our implementation. Next we look at how we can implement this in a language that has been gaining a lot of popularity lately, which is Ruby.
38.
Using JSON with Ruby on the Server
Our third and final implementation of the Yahoo search service proxy is in Ruby, a popular language that has been gaining a lot of momentum the last few years. If you have read either the Java or Perl sections we will follow a similar pattern, and start by discussing the third-party libraries required for the example. Following this we will look at the implementation, then explain in some detail what it is doing. With Ruby, we only require one external library, and that is to parse the JSON data. We chose to use the json library, by Frank Florian, for our project. You can download the JSON library from RubyForge at the following address. http://rubyforge.org/projects/json Alternatively, you can use RubyGems to install the package by executing the following command at the command line: 1.3540
gem install json
With the JSON library installed, it is time to take a look at our Ruby implementation. implementation, we explain it in some detail.
base_url = "http://search.yahooapis.com:80/WebSearchService/V1" base_url Normally we write both tags in the HTML page, and the browser ignores the irrelevant one, as shown in Listing 15.127. Listing 15.127 Standard way of dealing with browser differences when loading a Flash movie. <EMBED src="/support/flash/ts/documents/myFlashMovie.swf" quality=high bgcolor=#FFFFFF WIDTH="550" HEIGHT="400" NAME="myMovieName" ALIGN="" TYPE="application/x-shockwave-flash" PLUGINSPAGE="http://www.macromedia.com/go/getflashplayer">
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
But, we can do better than that with the use of GWT Browser specific code, where we can create a widget whose implementation is set up for either Internet Explorer or for all the other browsers. First we created a simple Flash Movie using one of the free online Flash generators (we’re not exactly the world’s best artists!). The benefit of the movie we have is that it allows us to change the text as a parameter to the movies URL, thus we can simply change the movie text as shown in the two images in Figure 15.2 so we can easily see what browser we are running in. We follow the pattern used in the majority of GWT browser specific code, in that we create a main class that instantiates one of the specific implementation classes to provide the functionality – the appropriate implementation is chosen by the compiler based on some replace tags we will place in the component application’s Module XML file.
Creating the Flash Widget The Flash widget is a simple extension of the Composite class which uses deferred binding to get a reference to a FlashMovieImpl class, the implementation class, as shown in Listing 15.128. We have set the code up so that it can be made flexible in the future with the ability to pass in a set of parameters in the FlashMovieParameters class, but for now this is not used. Listing 15.128 Creating a simple Flash widget. 1.3797 public class FlashMovie extends Composite{ 1.3798 1.3799 FlashMovieImpl impl = 1.3800 (FlashMovieImpl) GWT.create(FlashMovieImpl.class); #1 1.3801 1.3802 public static class FlashMovieParameters{ 1.3803 public String movieName; 1.3804 } 1.3805 1.3806 public FlashMovie(FlashMovieParameters params){ #2 1.3807 SimplePanel panel = new SimplePanel(); 1.3808 String tag = impl.createMovie(params); 1.3809 DOM.setInnerHTML(panel.getElement(), tag); 1.3810 initWidget(panel); 1.3811 } 1.3812 1.3813 public FlashMovie(){ #3 1.3814 new RuntimeException("Need Flash Movie Parameters!"); 1.3815 } 1.3816 } (Annotation) (Annotation) (Annotation)
In the widget, we create an instance of the implementation class at point [#1], which is then used in the constructor to create the appropriate tag for the browser ([#2]). In the constructor we create a simple panel into which the Flash movie will reside, and then we obtain the appropriate tag from the implementation. Once we have that, we use some DOM manipulation to place the tag inside the HTML or the simple panel and then initialize the panel as the widget. Next we look at the two implementation classes – one for the majority of browsers and the other specifically for Internet Explorer.
Implementing the Standard Flash Widget
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The standard implementation for the Flash widget creates the EMBED tag, as shown in Listing 15.129 by simply stringing together a number of Strings. In a real implementation, we would use the parameters passed in to set dimensions, etc., but, in our example, we just hard code all the parameters. Note that, in the URL, we set the title value to be the word “Other”, which is what we expect to be displayed on screen. Listing 15.129 Defining the Flash widget’s implementation class for the majority of browsers supported by GWT. 1.3817 public class FlashMovieImpl { 1.3818 1.3819 public String createMovie(FlashMovieParameters params) { 1.3820 String theMovie = ""; 1.3821 theMovie += "<embed src=\"flash_movie.swf?slogan=&slogan_white=&title=Other&title_white=Other&url=\" ”; 1.3822 theMovie += ”width=\"318\" height=\"252\" play=\"true\" loop=\"false\"”; 1.3823 theMovie += ” quality=\"high\" ”; 1.3824 theMovie += ”pluginspage=\"http://www.macromedia.com/go/getflashplayer\">”; 1.3825 theMovie += ”"; 1.3826 return theMovie; 1.3827 } 1.3828 }
Now that we have defined the standard implementation, we need to provide the Internet Explorer specific implementation.
Implementing the Internet Explorer Specific Flash Widget The Internet Explorer implementation returns the OBJECT tag equivalent of the standard implementation. The two key things to note with the code shown in Listing 15.130 is that it must extend the standard implementation, and that its URL sets it up to display the text “IE6”. Listing 15.130 Defining the Flash widget’s implementation class for Internet Explorer. 1.3829 public class FlashMovieImplIE6 extends FlashMovieImpl{ 1.3830 1.3831 public String createMovie(FlashMovieParameters params) { 1.3832 String movie = ""; 1.3833 movie += "<param name=\"movie\" value=flash_movie.swf?slogan=&slogan_white=&title=IE6&title_white=IE6&url= />"; 1.3834 movie += "<param name=\"play\" value=\"true\" />"; 1.3835 movie += "<param name=\"loop\" value=\"false\" />"; 1.3836 movie += "<param name=\"quality\" value=\"high\" />"; 1.3837 movie += ""; 1.3838 return movie; 1.3839 } 1.3840 }
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
With the implementation files in place, it is time to set up the Module XML file so that we can replace the standard version by this one if we need to.
Setting Up the Property Replacement The FlashMovie’s Module file contains only one entry, and that is the directive to replace the standard implementation with the Internet Explorer specific one if the user-agent property is ie6. It is written as shown in Listing 15.131. Listing 15.131 Creating our own replacement entry in the Module XML file which will replace the FlashMovieImpl class with FlashMovieImplIE6 class if the user agent is set to ie6. 1.3841 <module> 1.3842 1.3843 <when-type-is /> 1.3844 <when-property-is name="user.agent" value="ie6"/> 1.3845 1.3846
As well as changing the application components based on the user-agent property, we can also manipulate the way that GWT uses the standard internationalization functionality to change components based on the locale. But, before you change components based on the locale, you need to fully understand how GWT manages internationalization.
Supporting Internationalization in the Full We have seen GWT’s static i18n approach twice already in this book, and in this section we will explain it in full, as well as looking at the dynamic approach that could be used which is particularly useful if you already have an existing i18n approach. In these approaches, we define a default properties file, say Filename.properties, and then we define a number of locale specific properties files whose filenames follow a predetermined structure, as shown in Figure 15.155.
Figure 15.155 How local specific filenames are constructed
It is not necessary to have an ISO Country Code, but if one is present then so also must be the ISO Language Code. Locales also exist in a hierarchy, as shown in Table 15.40. Table 15.40 Which permutation is selected when using types that implement the Localizable interface If the locale is set to Then use the type…. Unspecified value The default type, Type X The type called Type_x if it exists, or The default type, Type, if Type_x does not exist. x_Y The type called Type_x_Y if it exists, or
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The type called Type_x if that exists, or The default type, Type if both Type_x_Y and Type_x do not exist.
First we look at the static approaches we have already used in the Dashboard application.
Using Static String Internationalization The concept of string internationalization stems from the same principles that are used in standard code development. In normal code, it is common practice to avoid writing constants directly in the code and, instead, to define the String literal as a constant in the code and then refer to that. We can also take it a step further by moving the constants out of the code and into a resource file. Table 15.41 shows each of these three ways, writing the constant directly, and defining and referring to the constant as a string literal, and defining the constant in a separate resource file. Table 15.41 Different approaches to using strings in code Type Code Writing a Constant Directly in Code 1.3847 Label Referring to a String Literal in Code Referencing Constants in a Resource File
newLabel = new Label(“Some Label Text”); 1.3848 final String labelText = “Some Label Text”; 1.3849 Label newLabel = new Label(labelText); 1.3850 MyConstants constants = 1.3851 (MyConstants)GWT.create(MyConstants.class); 1.3852 Label newLabel = new Label(constants.labelText());
The approach shown in the second column (defining a string literal constant first and then referring to it in the code) makes it easy to change the value of labelText, especially if it is used in numerous places in the code. We can take this a step further by moving the constants out of the code and into a resource file, referencing them from that file; this way they are available across different classes and it is at this point that GWT static string i18n can step in. In the static approach, the compile uses Generators that we saw earlier in this chapter to tie together the interface file and the variety of properties files. Since the GWT compiler creates permutations for each locale you identify by extending the locale property in the applications module XML file, then it can employ the power of static analysis to only include those constants and messages that are actually used in the code. It also only includes code related to the specific locale the permutation is being created for, further reducing the size of code per permutation, which translates to faster loading times for applications. To implement static string internationalization, you first provide a series of one or more properties files and a single Java interface that extends one of three GWT provided Java interfaces shown in Table 15.42. Table 15.42 Brief summary of the different Java Interfaces that can be used in the approaches to internationalization. Interface Description Constants Simple user interface constants where the compiler will remove unused ones to improve efficiency. Constants with Simple user interface constants where the compiler will keep all provided key/value pairs. There is the Lookup capability to look up constants by their name. Message Simple user interface messages that can take parameters, placing them in predetermined locations of the message.
These three interfaces themselves extend the Localizable interface, which is the link into the Generators, as we discussed earlier in this chapter. In this section we look at each of these three interfaces and see how they are used. The first one is the Constants interface.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
GWT i18n constants are simple strings that as the name suggests are constant – as opposed to messages that allow for parameters to be displayed within the string. There are two types of files needed to implement i18n Constants functionality within GWT, and we will look at each of these in turn A set of one or more property files. A Java interface that extends the Constants interface. The property files are where the key-value pairs are defined. They are simple files that follow the naming convention shown for localized classes, i.e. there is a default file, say myAppConstants.properties, and locale specific files can be defined such as myAppConstants_en.properties or myAppConstants_en_US.properties. These files live in a hierarchy and constants are searched for in that hierarchy in the same manner as indicated in Table 15.40. That is to say if the locale is set to en_US then a constant will be searched for in the following order of properties files: 1.3853 1.3854 1.3855
In the Dashboard application we use this hierarchy (although the standard English text is kept in the default file) in order to cope with the different spelling of colour/color between English and the version of English used in USA – try it, when the application loads, the Help menu contains an entry for the Colour Picker; if you change the local to American, then that entry is spelt Color Picker. This hierarchy also means that we do not have to define all constants in all files. Look at the property files shown in Table 15.43. Here we can see the contents of a non-locale specific property file – myAppConstants - which provides no values for any constants apart from the ok key. There are also two location specific property file contents – an English (en) locale myAppConstants_en and a Swedish (sv) locale myAppConstants_sv. In these locale specific files language specific values are defined for all keys except the ok one.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Table 15.43 Showing the contents of three i18n property files that exist within a hierarchy myAppConstants myAppConstants_en myAppConstants_en_US hello: Hello there Colour:color Colour: colour hi:Hi hello: yes:Yes hi: no:No yes: no: ok:OK
We could now use the i18n tool provided in the GWT download to create the appropriate interface file (see Chapter 3), or we could create it by hand. Either way, we would end up with an interface that looks as follows: 1.3856 1.3857 1.3858 1.3859 1.3860 1.3861 1.3862
Note that this interface contains a method name for each of the constant keys in our property file that we may wish to access – including the OK key. Regarding our part in the coding of i18n set up, this is all that we need to do, the GWT Generator will perform the task of creating necessary Java class files at run/compile time. To use the Swedish and English locales in our application, we must extend the locale property using the module XML file by adding the appropriate extend-property tags, e.g. 1.3863 1.3864 1.3865
and we let the application know which locale to start with by setting a new meta tag in the head section with the name “gwt:property” and its content sets the locale of choice. Changing the locale can also be performed by setting it as a parameter in the URL, e.g., to use the Swedish version the URL would be: 1.3866
http://www.example.org/myapp.html?locale=sv
We need to look into the i18n Module file (held within the com.google.gwt.i18n package) to see how the system copes with changing the locale through URL paramters. In that file a propert-provider tag is defined to extract the URL and find the locale. This provides a good basse code to use if we were to ever implement some properties that were also changeable through URL parameters. Finally, to use the different constants in the Java application code we first need to obtain an independent version of the constants class. Since this is an interface to our code, we use the GWT.create() method to infer that we will decide the exact version at a later date. We do that by using the following code: 1.3867 MyAppConstants constants = (MyAppConstants)GWT.create(MyAppConstants.class);
We use constants by calling the appropriate method defined in the interface, e.g. 1.3868
Label newLabel = new Label(constants.hello());
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Our aim is to produce a menu appearing similar to that in Figure 15.156
Figure 15.156 Menu system when the locale is set to be the default one.
Figure 15.157 Menu system when the locale is set to Swedish by changing the DashboardConstants_sv.properties file contents.
To create the text for the menu items in the default locale, we take the DashboardConstants.properties file previously created in chapter 3 and replace its contents with that shown in Listing 15.132.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Listing 15.132 Defining the constants used in the default locale 1.3869 1.3870 1.3871 1.3872 HelpMenuName: Help |#1 1.3873 CreateMenuName: Create #1 1.3874 AboutMenuItemName: About #2 1.3875 LoginMenuItemName: Login 1.3876 LocaleMenuItemName: Locale 1.3877 ConfirmDelMenuItemName: Confirm Delete 1.3878 CalculatorMenuItemName: Calculator 1.3879 ClockMenuItemName: Clock 1.3880 AddressBookMenuItemName: Address Book 1.3881 SlideshowMenuItemName: Slideshow 1.3882 GSMenuItemName: Search With Google 1.3883 GVSMenuItemName: Video Search 1.3884 FinanceNewsMenuItem: Finance News 1.3885 ServerStatusMenuItem: Server Status 1.3886 FlashMovieMenuItem: Flash 1.3887 EnglishLocale: English #3 1.3888 SwedishLocale: Svenska 1.3889 NameChangeOK: Change #4 1.3890 NameChangeCANCEL: Don't Change
Listing 15.133 Defining the constants used in the Swedish locale (notice that some items are missing when compared to the default locale – in this case GWT will use the constnat values in the default locale) 1.3891 HelpMenuName: Hjälp #1 1.3892 CreateMenuName: Nya 1.3893 CalculatorMenuItemName: Kalkylator #2 1.3894 ConfirmDelMenuItemName: Godkännar Delete 1.3895 ClockMenuItemName: Klocka 1.3896 AddressBookMenuItemName: Adressbok 1.3897 SlideshowMenuItemName: Slideshow 1.3898 GSMenuItemName: Söka med Google 1.3899 GVSMenuItemName: Söka Video 1.3900 FinanceNewsMenuItem: Finans Nyheter 1.3901 NameChangeOK: Byta #3 1.3902 NameChangeCANCEL: Ej Byta (Annotation) (Annotation) (Annotation)
. With the default locale established, we should add constants for the other locales that we are going to expect our Dashboard to manage. We also need to replace the Swedish locale that we created in chapter 3 to match the entries in the default locale, but with Swedish text instead (don’t forget that the encoding of the text file you are using should be set to UTF-8 if you are using a language’s special characters). You should replace the DashboardConstants_sv.properties file contents with that shown in Listing 15.133, which produces a menu similar to that shown in Figure 15.157. We have made a subtle “error” in our properties file for the Swedish locale to discuss a specific attribute that GWT i18n has. If you compare the default and Swedish locales you will see that the Swedish property file misses a
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
number of constants. When GWT comes across this, then it will use the constant value in the default locale – which explains the number of English words visible in Figure 15.157. If you want to add additional locales, then now is a good time to do so – remember that the filename must follow the format specified in chapter 15 and that you also need to add an <extend-property> tag to the Dashboard.gwt.xml file defining each locale you are adding. Once you are happy that your default property file contains all the constants you need in an application, then we need to execute the DashboardConstants-i18n tool again (remember that this tool was created in chapter 2). This tool will re-generate the necessary DashboardConstants interface file, taking into account of any changes you have made since the last time it was executed – the result of executing the tool for our default file is shown in Listing 15.134. Listing 15.134 Looking at the result of the DashboardConstants-i18n tool showing the interface that it has created that will be used to access the constant values within the properties files. 1.3903 public interface DashboardConstants extends com.google.gwt.i18n.client.Constants { 1.3904 1.3905 String AboutMenuItemName(); 1.3906 String CreateMenuName(); 1.3907 String HelpMenuName(); 1.3908 String CalculatorMenuItemName(); 1.3909 String SlideshowMenuItemName(); 1.3910 String AddressBookMenuItemName(); 1.3911 : 1.3912 : 1.3913 String NameChangeOK(); 1.3914 String NameChangeCancel(); 1.3915 String SwedishLocale(); 1.3916 String EnglishLocale(); 1.3917 }
As well as a Constants interface, GWT provides a ConstantsWithLookup interface, which we look at next. The main difference is that ConstantsWithLookup does not perform any static code reduction.
Defining Static String Internationlization Constants With Lookup The ConstantsWithLookup interface works exactly the same way as the Constants interface and values in the properties file can be accessed in exactly the same way. What sets these two apart is that the ConstantsWithLookup sends all constants into the Java file (so no reduction in file size), and it provides a number of specific retrieval methods that return the actual constant as a variety of Java objects. These retrieval methods are shown in Table 15.44. Table 15.44 Various methods that can be used to access static string constants with lookup Method Definition getBoolean(String) Looks up a particular method name and returns a boolean value. getDouble(String) Looks up a particular method name and returns a double value. getFloat(String) Looks up a particular method name and returns a float value. getInt(String) Looks up a particular method name and returns a int value. getMap(String) Looks up a particular method name and returns a map value. getString(String) Looks up a particular method name and returns a String value. getStringArray(String) Looks up a particular method name and returns a String[] value.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
An exception is raised if the method name does not exist in your interface or if it is not possible to cast the constant type to the object expected, e.g., a String to a Boolean. If we define the interface as shown in Listing 15.135 Listing 15.135 Looking at the result of the DashboardConstants-i18n tool showing the interface that it has created that will be used to access the constant values within the properties files. 1.3918 public interface MyAppConstants extends ConstantsWithLookup{ 1.3919 String hello(); 1.3920 String hi(); 1.3921 String yes(); 1.3922 String no(); 1.3923 String ok(); 1.3924 }
then we could look up values from our property files using one of the following two approaches: 1.3925 1.3926
constants.hello();
1.3927
constants.getString(“hello”);
If we tried to look up constants.getString(“farewell”) it would raise an exception as the method name farewell does not exist. Similarly, looking up constants.getInt(“ok”) would raise an exception since the ok method returns a String and not an int. But let’s get even racier with our examples and start defining some i18n Messages, which is something we did not do earlier, that we will use in the Dashboard application. As well as constants, it is possible to use messages in the same style by extending the Messages interface. Messages allow parameters to be passed in the interface and the value of those parameters appears in pre-determined sections of the message. In the next section we will look at how these messages are created.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Defining Static String Internationlization Messages Messages differ from constants in that they are parameterized. As with Constants, there are two types of files needed: A set of one or more property files. A Java interface that extends the Messages interface. The property files are again where the key-value pairs are defined, and again they follow the same naming convention and hierarchical properties as those discussed for Constants. As an example, we can define myAppMessages.properties file as the following: 1.3928 1.3929
hello:Hello {0},
Defining a message is like creating a template where values will be slotted in. In the example hello message, there is one slot, denoted by the text {0}. Other messages could contain more slots, for example goodbye:Goodbye {0} at {1}. Here there are two slots and it is in the interface that we define what goes into them. You can, of course, define messages with zero slots, in which case they will act as constants in the way we described earlier. The interface we need to define for messages is almost the same as for constants except we should extend the Messages interface and we define the parameters that the methods should take – there should be one parameter for each slot in the message. For our property file example, we would define the interface method as follows: 1.3930 1.3931 1.3932
public interface MyAppMessages extends Messages{ String hello(String name); }
We are saying that the hello() method will take one parameter and that is a String. Using the code within the application and setting the locale is performed the same way as for the other two techniques we have just discussed, except of course we need to pass the appropriate number of parameters to the chosen method. For example, to display the message “Hello Tiina Harkonen” the code would be written as: 1.3933 1.3934
MyAppMessage messages = (MyAppMessage)GWT.create(MyAppMessage.class); Label newLabel = new Label(messages.hello(“Tiina Harkonen”));
Other specific locale property files are created in the same way as for the other interfaces we have looked at. For example, a property file for the Finnish locale, fi , would be called myAppMessages_fi.properties and may have the contents: 1.3935
hello: Tervetuloa {0},
If the application was set to the Finnish locale, then the hello message would become “Tervetuloa Tiina Harkonen”. We have now exhausted the static approaches; they are the ones that should be used unless you have existing i18n approach. Whether you use constants, constants with lookup, or messages is going to depend on your application. In chapter 2 we discussed that the i18nCreator tool can be used to messages as well as constants (messages allow us to add a parameterized value into the text at run-time, where as constants do not). For our Dashboard, we
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
now bring in a message that we will use to set the default name on the Dashboard (which is stored in the EditableLabel). The first thing to do is to create the necessary structure and files using our i18nCreator tool.
Establishing the Messages File and Directory Structure To create the messages structure, you should execute the appropriate command line shown in Table 2.6, depending upon whether you are using the Eclipse IDE or not (this will work assuming you have followed the same structure that we created in chapter 2). Table 15.45 The different versions of the i18nCreator tool in Action used to create the Message framework for the Dashboard internationalization. Version Command Line Non Eclipse I18nCreator –createMessages –out DashboardDir org.gwtbook.client.DashboardMessages Eclipse I18nCreator –eclipse DashboardPrj –createMessages –out DashboardDir org.gwtbook.client.DashboardMessages
Successful execution of the command results in a new DashboardMessages.properties file in the code directory and a new application called DashboardMessages-i18n (if you used the Eclipse version you will also get a new Eclipse launch configuration – in Eclipse you should now refresh your project, by right clicking on it and selecting the refresh option, this will now show these new files in your package explorer view). Now that we have created our message structure, let’s create the default locale implementation.
Creating Messages for the Default Locale Just like creating constants for the default locale, creating messages for the default locale means we must update the DashboardMessages.properties file created by the i18nCreator tool. You should find it now and replace it with the contents shown in Listing 15.136.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Listing 15.136 Looking at some of the contents in the default DashboardMessages.properties file 1.3936 DashboardDefaultNameMessage = New Dashboard Created at {0} #1 1.3937 ConfirmDeleteMessage = Are you sure you want to delete the {0} application? #2 1.3938 WindowResizedMessage = New window size: ({0},{1}) #3 1.3939 WindowClosingText: You are attempting to close the Window - are you sure? #4 (Annotation) (Annotation) (Annotation) (Annotation)
This creates a set of GWT i18n messages each with a differing style. [#1] places the variable text at the end of the message (we will use this in the Dashboard as the initial text for the EditableLabel which represents the Dashboard’s name). It is simple to place our variable text in the middle of the message, as shown in [#2] or it can even go at the start. Placing two or more variables in the message? Not a problem, we do just that in [#3]. Finally, we hit a slightly philosophical point where there are no variables to add to a message – should it be a message or a constant? Technically, it should be a constant, but look at [#4], it is really a message. So where should it be placed? Well, in professional development you can always refer back to your coding standards for guidance (or if it is not there, then create it). For the Dashboard, we decided that where the text is presented to the user as a message then it should sit in the messages are regardless as to whether it had variables or not. If we now execute the new DashboardMessages-i18n command (created by the i18nCreator tool), then it creates a new interface file called DashboardMessages.java. The contents of which will look resemble Listing 15.137. Listing 15.137 Looking at the results of the DashboardMessages-i18n tool execution showing some of the methods defined in the interface. 1.3940 public interface DashboardMessages extends com.google.gwt.i18n.client.Messages { 1.3941 public String DashboardDefaultNameMessage(String time); |#1 1.3942 public String ConfirmDeleteMessage(String appName); |#1 1.3943 public String WindowResizedMessage(int x, int y); #2 1.3944 public String WindowClosingText(); #3 1.3945 } (Annotation) (Annotation) (Annotation)
1.3946
The resultant Java interface is very similar to our constants interface, except the method takes a parameter, the value of which is inserted in the place-marker {0} defined in the message. We must also add similar messages for the different locales that the application will manage.
Adding Messages for Other Locales GWT’s i18n approach is very standard. To create different locale messages, we create different property files named according to exactly the same naming convention as for constants. In our case, we create a DashboardMessages_sv.properties file for our Swedish locale and place the following contents in it. 1.3947
DashboardDefaultNameMessage = Nya Dashboard började vid {0}
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
But, we needn’t stop with just constants and messages being localized, we can also do the same thing with components in the application – for example the trash icon changes based on the locale. We explain how we can alter components based on the locale in more detail in chapter 15 when we look at GWT properties. One of the restrictions of using a static approach you should be aware of is that changing locale requires you to reload your GWT application. For small applications, this might not be a major problem, but if your application becomes substantial in size then a simple locale change may start frustrating your user. Dynamic string i18n is the other approach that GWT provides and this allows a more flexible approach to i18n with regards to changing locale although this comes at the expense of loosing the benefit of static analysis. The dynamic approach also allows you to use existing approaches you may already have to i18n used in the rest of your site/organisation. We now look at this dynamic approach.
Using Dynamic String Internationalization Using the dynamic approach was originally designed to allow existing i18n approaches to be quickly incorporated into GWT applications. If your existing approach used JavaScript associative array objects containing sets of key-value pairs, like the two shown in Table 15.46 then dynamic string i18n is potentially for you. Table 15.46 Two JavaScript associative array objects containing English and Swedish user interface constants
In this approach, the two JavaScript objects list key-value pairs for a simple user interface, the first in English and the second in Swedish. In JavaScript, it is easy to select an element in an associative array by referencing it via the key – to select the correct text to display for the index hello in a Swedish locale, we could write: 1.3962
userInterfaceSV[hello];
which would result in the text “Hejsan” being selected. If this is the approach that you currently have used for i18n, then implementing this within GWT will be swift and easy. Essentially the JavaScript objects need to be inserted into the HTML file as you normally would and then they will be accessed using the GWT Dictionary class. Assuming the HTML file into which the GWT application is to be loaded has the userInterfaceSV associative array included, then we create an object that accesses it using the getDictionary() method as 1.3963
The Dictionary.get(key) method is used to retrieve values based on the supplied key. In this case, to retrieve the value for to display for the index hello in a Swedish locale, we could write: 1.3964
uiText.get(“hello”)
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
There are a couple of benefits of dynamic string i18n, namely: you can quickly use existing i18n approaches you currently have. no recompilation of code is required to make any changes or additions to the constants. changing locale does not necessarily require a complete reload of the application. However, the disadvantage of the dynamic approach is that GWT provides no help to determine that constants you are referring to actually exist. With the dynamic approach, it is entirely possible to refer to keys that do not exist, creating unexpected results in your UI. In addition, the compiler cannot perform any optimization removing any unused keys. This means that all key-value pairs, used or not, will be sent by the web server, increasing the application’s size and, therefore, slowing down the delivery of applications. GWT’s i18n approach is great for dealing with the display of different messages and constants based on locales, but we can actually go one better and start changing complete components of the application based on the locale.
Altering the Application for the Locale Let’s imagine that one of our Dashboard applications will be an application that can access some restricted information. That information could be anything, but for the sake of the example we will say that it is financial information on a company that is restricted by financial market regulations. While GWT’s i18n approach is set up for dealing with messages and constants, we can subvert it to change application components based on locale by borrowing the code structure. There are two basic types that we need to implement to get our differing functionality by locale: A default class A set of zero or more localized classes. Let’s look at both these cases in a little more detail through examining the Dashboard Finance News component application.
Implementing the Default Component It is common that, when certain announcements are made by companies, this information is restricted to certain financial markets due to regulations – for example, the details of one company intending to purchase a controlling stake in another company listed on the UK market is not usually for release in the USA or Australia. We could use localization to restrict this access. For our financial system application’s functionality, we wish to be able to display a button allowing access to the announcement if a user is in a locale that can see the announcement and a Label expressing regret if not. As a starting point we will define the default type, in this case a class, and provide a single method that returns a GWT widget. Since we wish to be safe, and not attract the wrath of regulatory bodies, we would like users coming in from any locale to be treated to a display of the default screen shown in Figure 15.158.
Figure 15.158 Finance application running in a locale that is not allowed to view financial information
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
To do this, we set the default class to return a Label with denial message within it. If we call our class Proceed, then we would write the following: 1.3965 1.3966 1.3967 1.3968 1.3969 1.3970 1.3971
public class Proceed implements Localizable{ public Widget getProceed(){ return new Label("Unfortunately regulations restrict access in your country to this content"); } }
The default class is quite simple and has the simple rule that it must implement the Localizable interface. Let’s move on to the locale specific classes.
Locale Specific Classes Let us say that the financial announcement is being made on the Swedish stock market. Therefore, users in the Swedish locale are ok to read the text and, therefore, instead of the message we would like to present a button allowing the user to click through to the announcement. In theory, a user in the Swedish locale would see the application as shown in Figure 15.159, where we have already clicked the presented button.
Figure 15.159 Finance application in en_UK locale
We distinguish users from the Swedish locale by defining that the locale they are in has the ISO language code sv. We want to write a class that is used for this locale instead of the default class, and for this new class to return a button rather than a label. To do so, we write a class which extends the original Proceed class and is named following a simple naming convention. Here it is: 1.3972 1.3973 1.3974 1.3975 1.3976 1.3977
public class Proceed_sv extends Proceed{ public Widget getProceed(){ return new Button("Proceed"); } }
That is all we need to do from a code perspective to implement Localization classes in a GWT application. Unfortunately it does not yet mean that your localized code is available to your application - that takes a couple of
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
more steps. The first of these steps is to tell the application which locales it needs to be concerned about, and the second is to tell it which locale to use (although we have already done this for the Swedish locale in our Dashboard application some chapters back). Unlike with browser specific code, for replacing components due to locale, there is no need for us to enter anything beyond the locales in the Module XML file – the existing set up provided by GWT in the i18n’s Module XML file is sufficient. So now we have seen how to use the existing approaches for properties with our own code, it is time to make the next step and start defining our own new properties and managing them.
Implementing User Defined Properties We have seen that GWT provides a nice way to manage different browsers through properties, and it also gives us a way of managing different locales for messages and constants, which we can subvert to managed the changing of application components based on locales. It is possible to take this property-based approach further and completely define our own properties and handling code. In this section we will do just that for or Dashboard application to present an intranet and a more restricted Internet view. In your own applications you may need to do something similar so that external users get a restricted set of functionality compared to users on your intranet. There are, of course, many ways to provide this type of division outside of GWT, this is just an approach that you might want to investigate (we set the property in the HTML file, which is not that secure since the user can just override it, but you could expand the property-provider approach and generate some JavaScript which selects which version the user sees based on IP addresses or something similar).
Defining User Specified Properties Our property definition is quite simple; we will define a user-defined property that will have two values: intranet and internet and we will call this property the externalvisibility property. We set this up using the standard way that we described at a few sections back by making the following entry in out Dashboard.gwt.xml file. 1.3978
Before it can be used in any meaningful way, we first need to be able to get the value of this property.
Defining User Specified Property Provider In our Dashboard example, we will look at the meta-data defined in the HTML file to determine the start value for the external visibility flag. More realistically, you might implement some IP checking code to set the value, but we will stick with getting it from the HTML file. We do that in the property provider shown in Listing 15.138, which is placed in the Dashboard’s Module XML file. Listing 15.138 Defining the Flash widget’s implementation class for Internet Explorer. 1.3979 <property-provider name="externalvisibility"> 1.3980 Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
In the property provider, we first try and get the externalvisibilty value from the meta tags in the Dashboard.html file using the getMetaProperty() method ([#1]). If that is not available, then an exception is thrown, which we catch at [#4] and then set the value to be the most restrictive value of internet. If we successfully get a value in [#1], then we check to make sure it is not null, if it is, we set it to be the value “internet;” otherwise we return the value that we found. With the basics in place, we now build our code.
Building Our Code Finally, we can build the complete final version of the Dashboard which comes in two flavors; one for the Internet and one for the intranet. In essence, the Internet version provides access a small number of component applications, whereas the intranet version provides the same component applications as the Internet version but also a large number more. The way we implement this is to follow the same pattern as we have for all the property-based functions. First we build the Internet class as a default class for the property, which we will call Dashboard. Within this Dashboard class we will create the simple menus containing the limited number of menu items corresponding to component applications. Next, we build a Dashboard_intranet class which extends that Dashboard class and overrides the methods in Dashboard which are responsible for building the menus (and therefore giving access to the additional component applications). To complete the functionality, we need to add a replace-with tag in the Dashboard’s Module XML file. 1.3994 1.3995 1.3996 1.3997
This entry says that the Dashboard class must be replaced by the Dashboard_intranet class if the externalvisibility property is set to intranet.
Summary This concludes our walk through using properties to change the application – which can be a very powerful tool, particularly if you need to alter aspects of your application to suit differing locales. Just remember that the general pattern is to create a default class and then the variations, all which extend the default class. Then, either use replacewith tags in the Module XML file to replace the default file when properties match values, or, if you are using the i18n approach, then the default class must implement Localizable, and all class names should follow the i18n naming structure. Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
In the next part of this book, we look at the final practical aspects of GWT, including testing and deploying our applications.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
16 Testing and Deploying GWT Applications At some point you will make the leap from merely evaluating GWT to actually deploying it on production systems. This chapter shows you how to write tests for your application, making long term maintenance easier, and also shows you how to migrate your application from hosted-mode development to a server. In this chapter we won’t assume that you are a JUnit wizard, or know your Java application server backwards and forwards. Although we provide useful information for experts in these tools, we also provide step-by-step information if this is your first time working with these tools. We get to setting up your production environment shortly, but first let’s examine testing, starting with an overview of JUnit.
Testing GWT Code Using JUnit Many developers and whole organizations have taken up the notion of test-driven development, or at least recognize the importance of writing tests. The return on investment when creating tests comes in the form of time saved as the application gets larger and also during the maintenance phase of the software lifecycle. Almost in contradiction to the notion of writing automated tests as being a good thing, we find that writing those tests for JavaScript applications can be difficult and time consuming. In Java-land we have had powerful testing tools available for many years. At the top of the testing framework heap is JUnit, so popular that it has been ported to many other languages. It has remained popular because it is easy to use and integrates with IDEs and build tools like Eclipse and Ant. In the land of JavaScript, we haven’t seen the success of any single testing framework, and none of which can match the usability and ease of use of JUnit. The lack of a good testing framework for JavaScript may be because writing complex JavaScript applications is a relatively new idea, or perhaps it is because it isn’t easy to test code that is meant to be run by the browser. Whatever the reason, testing JavaScript is hard, but GWT provides a solution. When it came time to add testing capabilities to GWT, they turned their attention to the defacto standard, namely JUnit. But instead of creating something similar to Junit, they harnessed the power of JUnit itself. What this means for you as a developer is that you get to use a time tested framework for testing, and all of the tools that go along with it. In this section we take a look at how to use JUnit to test your GWT code and we point out those areas where the support isn’t quite perfect. But before we get started with that, we need to have a quick review of JUnit.
39.
Overview of JUnit for GWT Developers
If you haven’t seen or heard of JUnit before, the best place to start is the JUnit project site at www.junit.org. The site contains links to dozens of articles about the framework dating back to 1999. This overview in no way means to replaces the hundreds if not thousands of pages written on the subject, but it will provide enough detail to allow you to write some simple tests even if this is your first experience with JUnit. Unless you already have JUnit ready to go, you will need to visit the JUnit site to download the latest release of the 3.8.x branch. When visiting the JUnit site, you may notice that there is a 4.x version available, but this version is
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
not compatible with GWT. JUnit 4.x takes advantage of some new functionality that has been added in Java 5, but as we explained in chapter 1, GWT does not yet support the Java 5 syntax. Let’s begin by opening our favorite IDE or text editor and writing a simple test case.
Writing a Simple TestCase In JUnit a test-case is a class that may contain one or more tests. The purpose of the test case is to group a set of tests together typically based on their function. For example, you may want to place all tests for a calendar widget in the same class, potentially allowing you to write private methods that can be shared among the tests. In our example we will be writing a test-case that will test some basic math functionality, allowing us to get a feel for how to use JUnit. We begin by creating a new class names MathTestCase, and have it extend JUnit’s TestCase class, as we have done in listing 16.1. The next step is to add some tests to the class. For each test that we will be adding, we need to create a method. The method name must begin with the name “test”. The way JUnit works is to interrogate the methods in the class, and any method that begins with the word “test” is taken to be a unit test. In listing 16.1, you can see that we have created four methods, each of which will test a different mathematical function. Listing 16.1 A JUnit test-case that provides several examples of testing common mathematical functions.
package org.gwtbook.test; import junit.framework.TestCase; public class MathTestCase extends TestCase { private int x = 100; private int y = 10; public void testAdd() { assertEquals(110, x + y); }
| #1
public void testSubtract() { assertTrue(x - y == 90); }
| #2
public void testMultiply() { assertEquals("test #1 failed", 1000, x * y); assertEquals("test #2 failed", 1000, y * x); }
| #3 | #3
public void testDivide() { if (x / y != 10) fail(); }
| #4
}
The JUnit TestCase class includes dozens of methods that can be used to test for a valid value, and listing 16.1 provides several examples of this. In [#1] the test testAdd() we use the method assertEquals() from the parent TestCase class to test the equality of two int values. We pass two values the assertion method, the first being the expected result, or 110, and the second being our tested value. In this case, we are testing that the variables x plus y are equal to 110. The TestCase class also includes similar variations of the assertEquals() method for testing each of the primitive types, as well as String and Object values.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
In [#2] testSubtract() we use another of the TestCase methods to test the truth of the statement x – y == 90. If this statement returns a false value, the test will fail. Other similar methods in the testCase class include assertNull(), assertNotNull(), and assertFalse(), each of which behave as the name implies. In [#3] testMultiply() we see a variation on the assertEquals() method that includes an error message as the first argument. The purpose for this message is that you may have as many assertions in a single test as you would like, which can cause confusion if you can’t tell which assertion in the test failed. By adding a message to the assertion, if the test should fail the message will be displayed in the test results, making it easy to determine what part of the test failed. The last variation in our example [#4] is in use in testDivide(). Here we test the value ourselves, and if the result isn’t correct we call the fail() method. The fail() method does as you might expect, it fails the test. The fail() method takes an optional message parameter that can be used to provide additional information on the failure. These are the TestCase assertions that you will use most often, but there are still a few more. The assertSame() method can be used to test that two object references point to the same object. The failSame() does the exact opposite, failing if the two references point to the same object. There is also a variation of the assertEquals() method for float and double values that allows the addition of a delta argument. The delta is used to specify a maximum difference between the values being tested. For example, the following assertion is true because 99.5 is within 0.6 of 100.0. 1.4024
assertEquals(100.0, 99.5, 0.6);
When you add up all of the different variations of assertions with and without messages and deltas, there are threedozen different assertion methods to choose from. Now that we have our test-case written, we need to have JUnit run our tests.
Running Your Tests JUnit provides two tools for executing your unit tests called test runners. There is one for running your tests at the command line, and a second that presents you with a GUI tool for point-and-click testing. Unfortunately the GUI version of the testing tool does not behave as expected when running your tests against a GWT application, so we will only explain how to use the command line version of the tool. To run your tests at the command line, you need to execute the junit.textui.TestRunner class that is bundled in with the JUnit jar file. It takes a single parameter, which is the class name of your test case class. We also need to include the path to the JUint jar file and the path to our compiled test case class file. If you don’t typically run Java applications at the command line, you would execute something like the command below. 1.4025 1.4026 1.4027 1.4028 1.4029 1.4030 1.4031
Note that in this example we have used a back-slash at the end of the line to denote that the command continues onto the next line. This is, however, only supported in Unix and Unix-like shells, so in Windows do not include the backslash in the command.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
As the tests execute, it prints dots to indicate the progress of the testing. Once completed it displays the total time that it took to run the tests along with the results of the testing. In this case JUnit displays that all four tests ran successfully. It is nice to see that all of the tests passed, but if this was always the case we wouldn’t need to write the tests to begin with. So lets introduce some failures into our code. We can do this by altering the value of the y variable in our test case class, changing it from 10 to 11. 1.4032
private int y = 11;
After making this change we need to recompile the test case code. Once this is done we can run the tests again, and we will see the output below. 1.4033 1.4034 1.4035 1.4036 1.4037 1.4038
.F.F.F.F Time: 0.016 There were 4 failures: ... FAILURES!!! Tests run: 4, Failures: 4,
Errors: 0
In the output we have used ellipses (…) to denote the additional messages that will appear in the output. When you run this yourself, what you will see here is a message and partial stack track indicating the reason for the failure. For example, the following is the message displayed at the command line for the failure report on the testAdd() method. 1.4039 1.4040 1.4041
1) testAdd(org.gwtbook.test.SampleTestCase) junit.framework.AssertionFailedError: expected: but was:
Notice that JUnit tries to provide a helpful message by displaying not only the expected value, but the result as well. This will potentially help us in debugging the problem without the need to add logging to our test case. At the very bottom of the output JUnit now tells us that there were failures and provides a count of the test run, the number of failures, and the number of errors. This might seem confusing at first, but a failure is not an error.
Writing a Test to Properly Handle Errors When JUnit reports an error, it means that a runtime exception was thrown, and that the test didn’t properly account for it. This is different from a failure, which is meant to indicate that an assertion failed. To illustrate this point, lets use the following test example. 1.4042 1.4043 1.4044 1.4045 1.4046 1.4047
public void testDataProvider() { List data = DataProvider.getData(); String value = (String)data.get(0); assertEquals("Testing", value); }
This test may seem correct, but there are potentially a lot of runtime errors that could occur, which means that JUnit will not be able to properly report on them. The first is that we don’t test that getData() actually returned a value, which would result in a null pointer exception. We also don’t check that there is actually a value in the List, so grabbing the first element could result in an index out of bounds exception. Lastly we cast the value to a String, but if
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
it isn’t a String value a class cast exception will be thrown. If any of these runtime exceptions occurred, they would be reported by JUnit as an error and not as a failure. A better way to write our test would be like the following example. 1.4048 1.4049 1.4050 1.4051 1.4052 1.4053 1.4054 1.4055 1.4056 1.4057
public void testDataProvider() { List data = DataProvider.getData(); assertNotNull("data is null", data); assertTrue("data is empty", data.size() > 0); Object value = data.get(0); assertTrue("value is not a String", value instanceof String); assertEquals("value is incorrect", "Testing", (String)value); }
We now test for each of the possible error conditions using assertion methods, and provide a message for each assertion that will add value to our test report if the test should fail. You may also notice that we didn’t need to use conditional blocks in our test to prevent any of the runtime errors from occurring. Specifically, after we call assertNotNull() to test for a valid return value from the DataProvider class, we don’t need to provide any code to prevent the next line from executing, which would result in a null pointer exception. This is not needed because JUnit will end the execution of a test as soon as any assertion fails. What this means is that any cleanup in the test that follows a failed assertion will not be executed, so it is important to not use your test for any cleanup. Of course this could be problematic because there will be occasions where you might need to free up some resources used by the test.
Setting Up and Cleaning Up the Test Because there is often a need to perform some setup prior to a test, and cleanup after, JUnit provides two methods for this purpose. The setUp() method is called before executing each test in the test case class, and tearDown() after each test. The following is an example of using the setUp() method to create a connection pool using the Jakarta commonsdbcp library before each test, and shutting it down after the test has completed. Listing 16.2 – An example of overriding the TestCase setUp() method
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Although managing a connection pool is more relevant to testing server-side code than it is to GWT code, it is a concept that most developers can understand. You initialize or reset the services needed by the test in the setUp() method, and shutdown or release the services in the tearDown(). Although you may not use these methods often, it is good to know that JUnit makes it easy to do. At this point we now know everything that we need to for getting started with writing some tests. In this next section we discuss the specifics of using JUnit to test your GWT projects.
40.
Creating a New TestCase
Back in chapter 2 we briefly discussed the junitCreator tool that ships with GWT. In this section we provide some additional details, and point out some important points that can avoid pitfalls. The first question is, what does the junitCreator tool do? The short answer is that it creates a single TestCase class, two shell scripts that can be used to execute that single test, and optionally two launch files for Eclipse. The first shell script or launch file is for executing your test case in hosted-mode, and the second executes the test case in webmode. Lets take a closer look at the options for the junitCreator and the files it creates.
Running junitCreator The command line options for the junitCreator tool are very similar to the other tools. Following is the command line syntax and a description of the available options. 1.4073 1.4074
Table 16.1 Available command line switches for the junitCreator tool -junit Path to the JUnit libraries -eclipse Name of the Eclipse project (optional) -module The GWT module of the application you wish to test. -out The directory to write output files into (defaults to current) -overwrite Overwrite any existing files (optional) -ignore Ignore any existing files; do not overwrite (optional)
The –junit option is required, and it must point to the location of the JUnit jar file. Again, note that it must point to JUnit version 3.8.x, as 4.x is not yet supported. This path is used in generating the scripts, where the path to JUnit is added to the classpath. The -module option is also required, and is the name of the module that contains the code that you want to test. The module name is added to the generated Java source code. We will look at what the generated code looks like shortly. The –eclipse option is optional, but if included it will generate the Eclipse launch files that can be used to execute the test case. The last three switches -- –out, –overwrite, and –ignore -- are the same as the other command line tools that come with GWT. The –out switch can be used to change the output directory, –overwrite can be used to overwrite existing files, and –ignore will ignore existing files. When running junitCreator you will want to have the output directed to the same directory as your project. The scripts and launch files will be placed in the root of the directory, and the test case source will be placed under a new source directory test. So, for example, if I had an existing project called ExampleProject, the structure would look something like the left hand side of figure 16.1.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 16.1 A sample project directory structure before and after adding a test case with junitCreator.
When I add a test case using JUnit creator, I might use the following command from inside the ExampleProject directory. 1.4075 1.4076
After running this command, the project would now look like the right side of figure 16.1. You will see that it created the two scripts for executing the tests, two Eclipse launch files, and created the test cast class ExampleTest under a new test source tree. When running the junitCreator, you need to have the test case that you are creating reside in the same client package used by the project. In the case of figure 16.1 you can see that the project has a client package org.gwtbook.client. The test case must reside in this package, or a sub-package underneath it. Now let’s take a closer look at the generated code for the test case and explain the slight differences between a GWT test case and a standard JUnit test case.
Examining the Generated Test Case The full source for the generated test case, minus comments, can be seen in listing 16.3. Listing 16.3 - The source code for the basic test case generated by the junitCreator tool.
1.4077 1.4078 1.4079 1.4080 1.4081
package org.gwtbook.client; import com.google.gwt.junit.client.GWTTestCase; public class ExampleTest extends GWTTestCase {
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
public String getModuleName() { return "org.gwtbook.ExampleProject"; }
| #2 | #2 | #2
public void testSimple() { assertTrue(true); } }
The code shows a very basic JUnit test case with a single sample test named testSimple(), which will also succeed. What is different is that the test case extends the class GWTTestCase instead of the normal TestCase class from the JUnit library. The second difference is the addition of the setModuleName() method. This method informs the testing system the name of the module that contains this test case. Because this test case itself is essentially a GWT entry point, the module needs to point to the source of this test case. When we created the test case we specified the module name as the same as the main project, which means that we won’t need to create a separate one just for the test case. From here you can create tests just like we did at the beginning of this chapter when we examined the basics of JUnit. The rules for writing individual tests are the same as when you are writing your GWT application. You may only reference core Java classes when they exist in the JRE Emulation library, and you may include JSNI methods in the test case. This covers the basics, but GWT also provides an additional feature that allows testing RPC driven GWT applications a little easier.
41.
Testing Asynchronous Code
When we looked at the various flavors of RPC in chapters 10 through 13, one thing held common between then, and that is that they are all asynchronous. That makes it difficult to test since your tests could potentially end before the server returned a response. To allow for this, the GWTTestCase provides a mechanism where you can tell the test to delay ending for some period of time, allowing your server code to return a response to the client. In this section we look at how you can use this mechanism to test your server-side functions. For the purpose of discussion we will use the RequestBuilder class for performing our RPC. We chose this method because it is the simplest of the available mechanisms, but it could have been any of the available RPC mechanisms. To delay the ending of a test, GWTTestCase provides the method delayTestFinish(), which takes a number of milliseconds to delay before ending the test. Calling this method puts the test into asynchronous mode, and with that comes some important semantics. When in asynchronous mode you must call finishTest() before the delay or the test will fail -- the idea being that if finishTest() was never called it is likely that your RPC call never returned. Let’s look at a sample test so that we can see how this might work. Listing 16.4 – An example of using the delayTestFinish() for testing asynchronous application logic
1.4092 1.4093 1.4094 1.4095 1.4096 1.4097
public void testLoginService() { delayTestFinish(5000); RequestBuilder rb = new RequestBuilder(RequestBuilder.GET,
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
GWT.getModuleBaseURL() + "login?user=david&pass=m0mmy"); try { rb.sendRequest("", new RequestCallback() { public void onError(Request req, Throwable ex) { fail(); } public void onResponseReceived(Request req, Response res) { assertEquals(200, res.getStatusCode()); assertEquals("OK", res.getText()); finishTest();
| #2
| #3 | #3 | #4
} }); } catch (RequestException e) { fail(); }
| #5
}
In this example shown in listing 16.4 we are testing our login service. We first set a five second delay so that the server code will have time to complete execution and return a result. This delay only starts after the test has ended normally, so it can be set anywhere in the test, and doesn’t need to be called before the RPC call. Next we make an asynchronous call to our login service, which could be a servlet or any other server-side service that is accessed via login.rpc. In order to handle the response, we pass in a RequestCallback that has an onResponseReceived() method to receive the server response. In the onResponseReceived()method we have an assertEquals() method to test the server response, both the HTTP status code and the actual text result passed back from the server. We then call finishTest() to signal a successful test completion. If the code remote call should throw an exception or fail, we call the JUnit fail() method to indicate a failure. The end result is a clean and simple test case for testing remote calls. Due to the asynchronous nature of the RPC call, the actual order that the code is executed differs from its order in the method. Figure 16.2 shows the real order, with the onCompletion() handler being called after the normal execution of the test has ended.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 16.2 A diagram showing the flow of execution of our sample unit test where the test performs an asynchronous call.
During the delay period, there are three events that will end the delay and complete the test. If finishTest() is called, the delay ends, and the test completes successfully. If an exception is thrown, including one caused by one of the assert methods, the delay will end and the test will show an error. If the delay ends normally, without finishTest() being called, the test will show a failure due to a timeout. So, as you can see, the GWTTestCase extension to JUnit allows you to test client-side code the same way that you would test server-side code. This allows Java developers already familiar with JUnit to leverage their existing knowledge without needing to learn yet another testing framework. Once you have tested your application and feel confident everything is working properly, you need to actually deploy the application.
Deploying GWT Applications It is fairly typical to spend most if not all of your development time using just your IDE and the hosted-mode browser. The hosted-mode browser, and web-mode as well, make it very easy to write and test your application without ever actually deploying it to the server. When it comes time to deploy your application to a server, it would be nice if you could just toss it up on the server and it will just start working. Unfortunately this isn’t the common case, and often a source of some amount of pain. This section means to help alleviate that pain, and provide some tips to making the move to your server an easy one. Throughout this section we discuss general deployment issues, as well as those relating to RPC. When we discuss RPC, we do so with an eye towards the Apache Tomcat application server, which is freely available and widely used. Of course you may be using a different server such as Resin or JBoss, but the discussion will still generally apply. We also assume that you know a little about your application server, at the very least how to stop and start it. We begin by looking at a simple GWT application that uses no RPC and examining how we can better organize the files in the project.
42.
Organizing Your Project
When it comes to the files generated by GWT, it isn’t very organized. If this is the first app that you are deploying, it might not seem like a big deal, but as you add additional modules, images, CSS, and other supporting files to the
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
server it can quickly become a mess. In this section we examine the compiled output of your project, and look at what options are available for organization. As an example project, we will write an application that prints “Hello World” to the browser window. The entire application contains two lines of code along with the usual imports and class declaration. The entire application can be see in listing 16.5. It is far from earth-shattering, but it suits our need for a very simple application just fine. Listing 16.5 A sample application that displays “Hello World” in the browser.
package org.sample.client; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.RootPanel; public class HelloWorld implements EntryPoint { public void onModuleLoad() { Label output = new Label("Hello World"); RootPanel.get().add(ouput); } }
This application is simple enough, but it generates quite a few files when it is compiled as can be seen in figure 16.3. It may come as no surprise that we don’t need most of them, especially given the simple nature of our project.
Figure 16.3 – A plethora of files are created when you compile your GWT project to JavaScript
Before we go any further, let’s remove the files we don’t need, starting with the “rebind decision” files.
Removing Unneeded Rebind Decision Files
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The “rebind decision” files are all of those files that end with “cache.xml”. These files provide information to you about the choices the compiler made when generating the JavaScript for the project. As we discussed in chapter 9, the GWT compiler generates a different JavaScript file for each target browser based on the tags found in the module configuration files used by the project. These files specify the choices that the compiler made for each version of the JavaScript file. For example, if the compiler was compiling the IE version of the JavaScript code, you would see this line in this file, indicating that it used the IE6 implementation of the DOM class. 1.4133 1.4134 1.4135 1.4136
Although this information might be useful for debugging, it is not used by the deployed application. Next we want to look at the three Tree images in the directory and explain what can be done to clean them up.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Cleaning Up the TreeItem Images The next set of files to prune are the three image files: tree_closed,gif, tree_open.gif, and tree_white.gif. These images are used by the TreeItem widget that comes with GWT. If you aren’t using the Tree widget, as in our sample project, you should remove these three images. On the other hand, if you are using a tree in your application, this becomes unfortunate because it isn’t the best idea to have images intermingled with your HTML files. Fortunately the GWT tree widget allows you to move them to someplace more appropriate. Let’s begin by looking at some sample Tree code, shown in listing 16.6. Nothing fancy, just a simple tree of fictitious beasts that have been reportedly seen in the wild, yet no real proof exists. Listing 16.6 Example code that produces a small tree listing several fictitious beasts
Tree tree = new Tree(); TreeItem myths = new TreeItem("Mythical Beasts"); myths.addItem("Loch Ness Monster"); myths.addItem("Big Foot"); myths.addItem("An under-budget software project"); tree.addItem(myths); RootPanel.get().add(tree);
This produces a small tree that can be seen in figure 16.4. Figure 16.4 shows both the open and closed state of the tree, including the small plus (+) and minus (-) images used to indicate the open or closed state of the tree branch. This accounts for the tree_open.gif and tree_close.gif images. The third image, tree_white.gif, is used as a spacer for leaf items that don’t have children.
Figure 16.4 A Tree widget with sample data, showing the images used by the widget for the different tree states.
To change the location of these images, the Tree widget provides a method setImageBase() that can be used to set a path that will be added to the beginning of image name. So, if we wanted the images to be pulled from the images directory, we could use the following code when constructing our Tree.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Tree tree = new Tree(); 1.4146 tree.setImageBase("images/");
The trailing slash is important because the code for Tree will not automatically add it. So with this it will now use the paths images/tree_open.gif, images/tree_closed.gif, and images/tree_white.gif. After adding this to your code, you need to manually create the images directory and move those three images into the directory. Until you actually move the images, they will appear as broken images. Going back to our original code sample, we still need to do some more cleaning. We continue by taking a look at the history.html file.
Removing an Unneeded History File In your generated project files you will find a history.html file. This is used by the History sub-system that we discussed in chapter 4. If you are not using the History class, as in our example, then you should remove this file along with the reference to it in the HTML page. In the HTML page that is generated by the applicationCreator, you will see these lines in the body of the HTML page. 1.4147 1.4148 <iframe id="__gwt_historyFrame" style="width:0;height:0;border:0">
Just as the comment states, this is optional, and is only used for the history system. If you aren’t going to use the history system, remove these two lines from the HTML file along with the history.html file. With this file removed, our project space is looking quite a bit cleaner, although not really organized since all of the files are still lumped into a single directory. In this next section we organize our JavaScript files by placing them into subdirectories.
Relocating the JavaScript Code If you have been following along and removing the unused files from the sample project, you will see that there aren’t many files left. Figure 16.5 shows the current file list for our project. What is left is the project HTML file (HelloWorld.html), the gwt.js file, the one loader script ending in .nocache.html, and the four browser specific script files ending in .cache.html. We could deploy this lump of files as-is without any further organization, but if you are deploying your GWT project with other pages on your web site, you may want to keep your GWT project files separated from other site pages. In this section we make the assumption that our Hello World application files need to be segregated from the rest of the pages on the site. This will involve creating a script directory, and moving all of the supporting files into that directory. Furthermore we want to separate the files that are specific to the org.sample.HelloWorld module from other GWT projects, while still sharing the common gwt.js file for all GWT applications on our site.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 16.5 The current list of files in our project after pruning unused files like the rebind decision files, tree images, and history file.
The first step is to create a new directory to hold the JavaScript code, which we will name “scripts”. Next we want to create a directory under scripts for the files specific to the module, and we will name this directory the same as the module, which is “org.sample.HelloWorld”. Now move the gwt.js file into the scripts directory, and all of the other script files into scripts/org.sample.HelloWorld. When you get done, your project should look like figure 16.6.
Figure 16.6 – Ax example of a reorganized GWT application to use a directory for holding scripts and modules
This structure makes it easy to deploy additional modules to the same server. You would just add a new directory under scripts for the new module, and place the files for the new module there. As for the gwt.js file, all of the modules may share this file. If you are using the GWT resource injection mechanism to include external JavaScript files or CSS files from your module configuration, you will need to move the injected files into the same directory as the “nocache.html” file. This also applies to any images referenced from injected CSS files that are using a relative path.
Now that we have the files reorganized, we need to make some minor changes to the HelloWorld.html file to point to these new locations. In the HelloWorld.html file there are two lines we need to change. The first is the script tag that points to the gwt.js file. Here we need to update the src attribute to point to the gwt.js file in the scripts directory. When you get done, it should look like this.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
The second change is to the meta tag that specifies the name of the module to load. The following snippet is what the tag looks like before we make our change. 1.4150
Without any additional path information, this tag will cause the GWT loader to look for the module JavaScript files in the same directory as this HTML file. Because we have moved these files into the scripts/org.sample.HelloWorld directory, we need to add this path information to the meta tag. We do this by adding the path to the content attribute, followed by an equal sign (=), then the usual module name. With this change in place, the updated meta tag will look like this. 1.4151 1.4152
Simply put, the equal sign separates the path from the module name, and the GWT loader will look for the module at this alternate path instead of in the current directory. With this last organizational change in place, we now have a tidy little application ready to be deployed to the server. Deploying the application from here is as simple as copying the files to any place on the server and optionally renaming HelloWorld.html to some other name, like index.html. Now this is all well and good, but this doesn’t take into account setting up any servlets that are used to receive and respond to GWT-RPC calls from the client. This next section addresses that, and provides a very brief tutorial of setting up a servlet if you have never done so before.
43.
Installing RPC Servlets
If you have a need to install an RPC servlet, we need to assume that you already know how to write one, and that you probably already have it running in hosted-mode. In this section we show you how to deploy your servlet to a server that supports Java servlets, also known as a servlet container. For the purposes of our exploration, we will keep it fairly light and generic when we talk about the server, which is all we really need to deploy a servlet. What this section won’t discuss is connection pooling, caching, or other functions that are typically specific to the server software that you are running. Specifically we will cover are the basics of the web.xml configuration file, and how the organization choices from the previous section affect our application settings. To begin we need to provide a very high-level look at how a Java servlet container works and how it processes incoming servlet requests.
How Servlet Containers Work A servlet container is the part of a web server or application server that handles a network request and manages servlets throughout their lifecycle. Some servlet containers are also application servers, meaning they meet the requirements of the Java Enterprise Edition in addition to the servlet specification. JBoss is a freely available application server. Other servlet containers are only servlet containers, like the freely available and popular Apache Tomcat server. If this is your first attempt at setting up a servlet container, we recommend Apache Tomcat, which can be found at http://tomcat.apache.org. It is available for most popular operating systems, and is relatively easy to install and use. Throughout this section, any server specific information that we do provide will be for Apache Tomcat. Rest assured that, if you do run a different software package, nearly all of the information in this section is relevant and will be kept as generic as possible.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
For developers new to servlets, it can be a little confusing how they work, especially if you are familiar with how CGI scripts, ASP pages, or HTML pages are served. With these technologies the URL in the browser matches the location of the file on the file system. So, if a user requests /works/gwt_in_action.html, you would expect that there was a file on the system named “gwt_in_action.html” in the directory “works”. Servlets don’t work like this. Instead you use a configuration file to map a URL to a servlet class. This allows you to map any arbitrary URL, or set of URLs by using wildcards, to any servlet. This configuration file that is used to map URLs to servlets is named web.xml, and it will always reside under the WEB-INF directory on your site. When you are developing your GWT application using the hosted-mode browser, you are actually using Tomcat. Since this provides a good example of servlet configuration, it is a good place to start the discussion about the web.xml file.
Understanding Servlet Configuration in the Development Environment When you were testing your RPC servlet in hosted-mode, you may have noticed that a directory named “tomcat” was created. In that directory, under tomcat/webapps/ROOT/WEB-INF/, there is a file named web.xml, which is known as a deployment descriptor. Unless you manually modified this file to integrate other services into your development environment, it will look like the listing 16.7. Listing 16.7 The default deployment descriptor that is used by the GWT development environment in hosted-mode
If you take a good look at listing 16.7 you will see that it isn’t very complicated. It first defines a servlet by giving it an arbitrary name and specifying the full package and class name of the servlet. In this case the servlet is the GWTShellServlet. It is important to note that this servlet should not be used on your server, it is only meant for use in the GWT development environment. The second part of the configuration maps a URL to the servlet. It does so by specifying a URL pattern and the name of the servlet that will handle requests to that URL, and in this case the URL is “/*”. The URL may include the wildcard “*” to match anything, so the pattern “/*” will match any request to the server. This is not typically what you want, but in this case the GWTShellServlet servlet was written to read your project’s module file and dispatch the request to the right place. If you recall, your module configuration uses the <servlet> tag to define your GWT-RPC servlets. This information is used by the GWTShellServlet, but isn’t used when you move your code to a production server. With these basics, let’s apply this to how we would configure our production environment.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Configuring the Production Environment The first step is to find the directory your server uses to deploy web applications. If you are using Apache Tomcat, figure 16.7 shows this directory structure for the Tomcat installation. Figure 16.7 shows the directory structure on Windows, but it will be the same regardless of the platform.
Figure 16.7 - The directory structure of an Apache Tomcat installation on Windows
In figure 16.7 you see a bin directory to hold executables, conf for the overall server configuration, logs to store log files, and a few others. The directory that we need to be concerned with is the webapps directory. This directory is used to hold individual web applications running in Tomcat. In figure 16.7 we show a few applications that we have placed there, like echo2-demo and jsf-demo. To access these applications in a browser, you would use the URL http://your-host-name/echo2-demo and http://your-host-name/jsf-demo. The point to be made is that each application directory in the webapps folder is accessible in your browser by using the same name as the application directory. We are pointing this out because the ROOT directory under webapps works a little differently. The ROOT directory can be accessed in the browser via http://your-host-name, without specifying the application directory. This is important because you need to decide if you want your application to be accessed using just the host name in the URL, or in an application directory. For example, if we want our Dashboard application to be accessible as http://your-host-name then we would place it under ROOT, otherwise we would create a new directory under webapps, perhaps called “Dashboard”, and then it would be accessible as http://your-host-name/Dashboard. For the purposes of the discussion that follows, it does not matter what you choose since all applications, including the root application, have the same directory structure. Inside each of the application directories, they follow a specific structure. Figure 16.8 shows this structure, including the location of the web.xml file that is used to configure the application.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 16.8 – The directory structure inside of a web application, including the location of the deployment descriptor.
In the root of the application, you would place the HTML and JavaScript files that were generated by your project. Inside of the WEB-INF/lib directory, you would place any jar files that your project relies on. If you are using GWTRPC, this will at least include the gwt-servlet.jar file that was part of the GWT distribution. This jar includes all of the GWT classes that are required for the GWT-RPC mechanism. You should never deploy the gwt-user.jar or gwtdev.jar files to your server, as they will likely interfere with your server. This is because these jar files contain their own Tomcat server code, which is used when you are testing in hosted-mode. The WEB-INF/classes directory can be used for loose classes. You will need to place any needed compiled class files for your project under the WEB-INF/classes directory, or package them as a jar and place it in the WEB-INF/lib directory. The servlet container will automatically include all the classes and jar files in these directories in the classpath for the application. Note that this only applies to code that needs to be run on the server. For example, if you are using GWT-RPC, you will need to include the class files for your servlet, and any Java interfaces it references. You do not however need to include any classes that are only used on the client-side, like widgets and other client-side only code, since these are not executed on the server. Also of note is that it will not hurt anything if you do deploy class files that are only used on the client, since they will never be called. Once that is complete, the only step left is to create the web.xml file, known as the deployment descriptor, and reference your servlets.
Writing the Application Deployment Descriptor When you are creating the deployment descriptor, you will need to map each individual GWT-RPC servlet to a URL. This can be done in the same manner as we saw previously when we looked at the deployment descriptor used by the GWT development environment. The easiest way to explain this is to provide an example, then explain it. In listing 16.8 we provide the deployment descriptor file that could be used to deploy the Server Status project from chapter 10. In most cases you can simply copy the example and alter it to match your servlet classes.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Listing 16.8 – A deployment descriptor for the Server Status project from chapter 10 1.4169 1.4170 <web-app> 1.4171 1.4172 <servlet> | #1 1.4173 <servlet-name>server-status | #1 1.4174 <servlet-class> | #1 1.4175 org.gwtbook.server.ServerStatusImpl | #1 1.4176 | #1 1.4177 | #1 1.4178 1.4179 <servlet-mapping> | #2 1.4180 <servlet-name>server-status | #2 1.4181 /server-status | #2 1.4182 | #2 1.4183 1.4184
Just as we saw with the deployment descriptor that is used in hosted-mode, we first define the servlet using the <servlet> tag, providing a name and class. We then provide a <servlet-mapping> that maps the servlet to a specific URL. The servlet we list in the deployment descriptor is your GWT-RPC class that extends the RemoteServiceServlet class. If you have multiple servlets, you will have multiple <servlet> blocks, one for each servlet that you need to define. The servlet-mapping is usually where it is easy to run into problems. The first thing to understand is that the “/” at the beginning of the pattern refers to the root of this specific web application, and not necessarily the root of the web site. This might seem a little confusing at first, but if we look back at figure 16.7 we showed you that each subdirectory in the Tomcat webapps directory was its own “application”. So, if we place the Server Status application in the webapps/status directory so that it is accessible via the URL http://your-host-name/status, then the servletmapping “/server-status” will refer to the URL http://your-host-name/status/server-status. Besides this you also need to be concerned about the use of the method GWT.getModuleBaseURL() in your code, which is typically used to reference GWT-RPC and other serv-side resources. The part to understand is that this method will return the path to your project’s JavaScript file that ends with “nocache.html”. At the beginning of this chapter we showed you how you can relocate this file, as well as the other JavaScript files, to better organize your site. If you used getModuleBaseURL() to reference an RPC servlet, you will need to account for this in the servletmapping. As an example, if you take a look back at figure 16.6, you will see that we moved the generated JavaScript files for the module into the directory /scripts/org.sample.HelloWorld/. Let’s imagine that some code in this module referenced the following URL in an RPC call. 1.4185
GWT.getModuleBaseURL() + "/hello-world-service"
In this case we would use a servlet-mapping that looks like the code below. Notice how it takes into account the path to the generated JavaScript. 1.4186 1.4187 1.4188 1.4189
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.4190 1.4191
When you get done setting up your deployment descriptor, you should restart the servlet container to make sure that the changes take effect. If you find that, after carefully write the deployment descriptor, the RPC doesn’t seem to be working, then check the server logs. The logs for the server will show you what URL the client browser is calling, and then you can compare this to the web.xml, making any necessary changes.
Summary In this chapter we showed you how to use JUnit, and how it can be used to test your GWT code. We showed you how you can use the included junitCreator tool to painlessly create new JUnit test cases for testing your clientside and RPC interactions, which is not typically available when building a non-GWT Ajax application.
We then turned our attention to deploying our GWT application to the server. We showed you how you can better organize your project and what unused files can be safely deleted. In the end we showed you how you can keep your deployments organized, allowing you to deploy multiple GWT applications to the same server without having a mess of files. After a good cleanup, we examined how a servlet container works, and how it is used to host your GWT-RPC servlets. We looked at the deployment descriptor file in some detail, and we compared the hosted-mode deployment descriptor against what you would use in your production environment. Now that we have our application tested and deployed, the next chapter looks under the hood. There is a lot to GWT, and the more you know about how the magic works underneath, the better you will be able to maintain and debug your GWT application.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
17 Peeking into How GWT Works We have now arrived at the final chapter in our discussion on GWT, where we round off by taking a look at how GWT performs the magic that it does. When we started using GWT, we were first interested in building applications, and then, as our curiosity got the better of us, we started wondering just how GWT was working behind the scenes. We were asking ourselves questions like how does the compilation work, why do we get so many HTML and XML files produced, and just what do they do? How does an application get loaded into the web browser? What does the produced JavaScript look like? How does GWT manipulate the DOM? There were, and still are, many more questions, and we suspect that you may have the same questions now that we have gotten this far in the book. So, after seeing the practical uses of GWT and learning how to actually develop a web application using this “toolkit,” we finish off by setting out to answer a number of those questions that hounded us once we got past the initial learning curve with GWT. To answer our questions, we felt the best place to start was with the looking at the compilation process, and we hope it begins to answer your questions, as well.
Examining the Compilation Process and Output The whole purpose of the GWT compilation stage, stage 4 in the lifecycle we defined in chapter 2, involves taking the source files we have written in Java and produce an output comprising JavaScript and HTML that runs in a number of browsers. We have spent the majority of this book discussing the Java source files and so, in this section, our end goal is to look at the actual output from the compiler, but, to get there, we need to look at how our files get from the source to the compiled side.
Investigating the Compilation If you look at the compilation process, though, it is not a one-for-one translation of Java to JavaScript files. In Figure 17.160 we can see many more files are produced as the result of the compilation process than are supplied to it.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 17.160 Source to object file mapping in the compilation process of GWT Java to web browser usable files.
The files that are produced fall into three different types: those that are passed straight through, such as the style sheets and most of the resources in the public folder; those that are “magically” created by the compiler almost from thin-air, such as gwt-js; and finally, those files that are created by the true compilation of source Java files to produce object JavaScript files. These files are listed and described in Table 17.47. Table 17.47 Brief description of the files produced in the compilation of the Dashboard application Filename Description Dasboard.html The HTML file that we created in which our GWT modules will be placed. gwt.js The basic GWT JavaScript code that is used to bootstrap our GWT modules into the Browser and get them up and running. history.html A simple HTML file that is used in the history management capability of a GWT application. org.mycompany.Dashboard.nocache.html The first file loaded by the bootstrapping mechanism; it contains all of the code
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
MD5_coded_name.cache.xml
MD5_coded_name.cache.html
necessary to derive the set of properties this application has, i.e what browser, which locale and what values any other properties may have – in the case of the Dashboard this is the externalvisibility property (it determines all of these values by directly using the code described in the property-provider tag of the XML Module file). Once all the property values are determined the necessary permutation can be deduced by some more code in this file, and is then subsequently loaded into the browser. A set of files, all prefixed by an MD5 coded name which list, in XML format, the various choices taken by the compiler in producing this particular permutation of JavaScript code. A set of files, all prefixed by an MD5 coded name that correspond to a matching cache.xml file. Unlike the XML file, the HTML file contains the actual JavaScript code for this permutation of the application.
We now look at each of these files in turn in a little more detail.
Examining the Output In this section we will look in some detail at the contents of the various outputs from the compiler, starting with the History.html file.
Looking at History.html This file is an HTML file that contains some JavaScript for managing History within the application. Listing 17.139 The GWT code that is used in the History management subsystem; you can see the definition of the hst() history function that is used to decode any URL that might have a history token and subsequently move our application to the necessary state) 1.4192 1.4193 1.4194 <script> 1.4195 function hst() { 1.4196 var search = location.search; 1.4197 var historyToken = ''; 1.4198 if (location.search.length > 0) 1.4199 historyToken = 1.4200 decodeURIComponent(location.search.substring(1)); #1 1.4201 document.getElementById('__historyToken').value = historyToken; #2 1.4202 if (parent.__onHistoryChanged) |#3 1.4203 parent.__onHistoryChanged(historyToken); |#3 1.4204 } 1.4205 1.4206 1.4207 1.4208 1.4209 1.4210 (annotation) (annotation) (annotation)
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
History is stored within the input element with id __historyToken and is accessed and managed via your application when it uses the History management operations. Next we look at the file that is the important in the loading mechanism: the applications nocache.html file.
Looking at Org.mycompany.HelloWorld.nocache.html The org.mycompany.Dashboard.nocache.html file is another file that is automatically generated from nothing. It establishes the standard variables to be used as references between your application code and the browser that are used in any JSNI code you may have. For example, Listing 17.140 shows how it sets the $wnd and $doc variables we have previously mentioned: Listing 17.140 The GWT code that is used to set basic variables for our applications (including the link between our application and the main browser window variables) var $wnd = parent; var $doc = $wnd.document; var $moduleName = null; var $moduleBaseURL = null;
It is also used to aid the identification of which specific permutation of the application’s JavaScript code is required. Permutations are generated under the directive of the compiler when considering the various options and paths that could be taken. The most obvious permutations are driven by the different browsers that are supported by GWT – one permutation for each. Other permutations can be driven by using more advanced techniques of GWT. Internationalization, for example, will drive a number of permutations based on the number of locales that the application is told to manage. If you are interested in seeing what type of code GWT uses to determine browser type, then it is reproduced in Listing 17.141. Listing 17.141 Looking at the code GWT provides to perform browser detection – at present, GWT will recognise the following types of browser: Opera, Safari, IE6, Gecko and Gecko 1.8 (for example Firefox and Mozilla) 1.4211 window["provider$user.agent"] = function() { 1.4212 var ua = navigator.userAgent.toLowerCase(); 1.4213 if (ua.indexOf('opera') != -1) { 1.4214 return 'opera'; 1.4215 } 1.4216 else if (ua.indexOf('safari') != -1) { 1.4217 return 'safari'; 1.4218 } 1.4219 else if (ua.indexOf('msie 6.0') != -1 || ua.indexOf('msie 7.0') != -1) { 1.4220 return 'ie6'; 1.4221 } 1.4222 else if (ua.indexOf('gecko') != -1) { 1.4223 var result = /rv:([0-9]+)\.([0-9]+)/.exec(ua); 1.4224 if (result && result.length == 3) { 1.4225 var version = parseInt(result[1]) * 10 + parseInt(result[2]); 1.4226 if (version >= 18) 1.4227 return 'gecko1_8'; 1.4228 }
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
1.4229 1.4230 1.4231 1.4232
return 'gecko'; } return 'unknown'; };
Similar code is used for determination of other properties that may be used to select permutations – such as the code we introduced in chapter 15 to look for our user defined externalvisibility property. Next we take a little more look at the permutations of JavaScript application that are produced. Two types of files are produced: an XML file that details the options taken by compiler, and an associated HTML file that contains the JavaScript permutation corresponding to the options taken.
Looking at the Cache XML Files The compilation process relies heavily on the Module XML file and knowledge of the types of browsers that are supported in order to produce numerous permutations of JavaScript files. In the simplest case, the only property dealt with is the user.agent property and subsequently there are four permutations - one JavaScript file for each browser that GWT supports. Using GWT more extensively, for example making use of the internationalization aspects drives up the number of permutations required. Using two locales would mean two sets of browser specific permutations are required – a set of four for the first locale and another set of four for the second locale. This scales linearly, so if you had ten different locales for your application, then you would have some forty different permutations. What is the benefit of this madness? Well, it considerably reduces the amount of code that needs to be sent to the browser, decreasing download size and times. The downside is the amount of time needed to perform the compilation. If you have already tried to compile our Dashboard application yourself then you will have noticed just how long it takes to compile (hopefully there will be compiler optimizations appearing in the near future, especially as GWT is now open source). One way of reducing this compilation time during the testing phase is to restrict the number of permutations that are created by adding an entry such as 1.4233
<set-property name="user.agent" value="ie6"/>
into the Module XML file, which will restrict the number of user.agent permutations to 1 (you could easily replace ie6 with whatever browser you prefer). In the case of our little example above, where we had 10 different locales to manage and it originally produced forty different permutations, placing this line in the Module XML file reduces the number of permutations to ten. Tip To reduce the number of permutations created during web mode testing, and therefore speed up compilation, add a set-property tag to your application’s Module XML file setting the name to “useragent” and value to the browser you will test with (e.g. ie6, safari, opera, gecko1_8 etc).
The purpose of the cache XML files is to allow the compiler to keep track of the various permutations it has already created, and to know what more it needs to do. Once compilation has finished, then the files are of limited use, and server only as a record of what the compiler did. This is why we suggested in chapter 16 that you could get rid of them when packaging the application to deploy.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Sometimes, it is useful to look at these cache xml files, particularly if your compiled application is not performing nicely and you wish to look at what the compiler did. When we compiled the Dashboard application on our system, the compiler produced a number of .cache.xml files including one called E36B89235ACF98A4C55874ACEBBDF6E8.cache.xml, which contained the information shown in Listing 17.142.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Listing 17.142 Example Cache.XML file from the Dashboard application showing the decisions that the compiler took when creating this particular permutation (where variability exists within GWT, for example which browser the code is being viewed within, GWT will create a new permutation of code. This performance hit at compile time greatly reduces the size of code necessary to send to the browser) 1.4234 1.4235 |#1 1.4239 : 1.4240 |#2 1.4243 : 1.4244 |#3 1.4248 : 1.4249 |#4 1.4253 : 1.4254 |#5 1.4258 : 1.4259 (annotation) (annotation) (annotation) (annotation) (annotation)
OK, so the cache.xml file does not readily say that as a compiler I chose the default locale and compiled for the Opera browser using the intranet property as the value for externalvisibility property (and Listing 17.142 is an edited highlight of the file). We can, though, infer this by looking at the rebinding decisions listed in the xml file. At [#3] we can see that the compiler took the decision to use the XMLParserImplOpera in place of the XMLParserImpl class (if we were to look into the bowels of GWT we would see this rebinding direction defined in a Module XML file where the property of user.agent was set to Opera). The choice of the default locale can be seen at [#4] where our DashboardMessages interface we used in the code is to be replaced by the DashboardMessages_ class. We know this is the default locale since GWT uses a Generator to produce a set of classes that bind the user provide interface to the user Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
provided properties files, which must conform to a particular naming convention. The generated classes also conform to the same naming convention, where DashboardMessages_ represents the default locale class (DashboardMessages_sv would be the Swedish locale class). But, we didn’t write these classes, the compiler has done that for us by using a GWT provided Generator, and at point [#2] in the listing you can see a reference to that generated class. [#1] shows a class that was created by our user defined Generator, in this case the Slideshow class has been generated into the SlideshowProxyintranet class as the Generator provided in chapter 14 requires. Finally, [#5], indicates where our generated class for the slideshow is chosen to be used in this permutation. Closely associated with cache XML file is the actual JavaScript file implementation relating to the permutation described. This JavaScript is contained within the specific HTML files, which we will look at next.
Looking at the Specific HTML Files For each permutation identified in specific xml files, there will be an associated cache.html file that can be loaded by the browser under the loading mechanism. For the xml file mentioned above, there will be a E36B89235ACF98A4C55874ACEBBDF6E8.cache.html file. It is this file that contains the JavaScript specific to the Opera browser where the externalvisibility property is set as intranet and locale is the default locale. Now that we have seen the output of the compilation process, the next logical step is to look at how the application is loaded into the web browser in web mode (hosted mode is really out of scope for this book, through the recent open sourcing of GWT means that the mechanics behind hosted mode is more readily accessible).
The GWT Application Loading Mechanism As a final step in examining our Dashboard example, we look at what exactly is happening when a GWT application is loaded. You have seen all the files that we need to create a GWT application and the result of the compilation process. When you load the Dashboard.html file, either from the file system or a web server, what is really going? Well, it is the process shown in Figure 17.161.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 17.161 Steps performed behind the scenes in the loading process of a GWT application
Table 17.48 summarizes these steps, which we detail more in this section. Table 17.48 Behind the scenes steps for the GWT Loading Mechanism Step Name Description 1 Load HTML The HelloWorld.html file is loaded by the browser. 2 Load gwt.js The browser processes the html file and identifies it needs to retrieve and execute the gwt.js JavaScript. 3 Process Meta Tags The gwt.js JavaScript file processes the HelloWorld.html file looking for meta tags with the name gwt:module1 to determine which permutation of javascript file it needs to load 4 Load className.nocache.html For each required permutation meta tag it finds, where the content of that tag is className, the gwt.js script creates a new IFRAME element and attempts to load the className.nocache.html file into it. 5 Replace className.nocache.html Once loaded the className.nochache.hml file is responsible for identifying what type of browser it is sitting within and then based on that information, it replaces itself with the browser specific HTML file (which contains the applications Javascript) for that GWT application. 6 Start application(s) When the browser specific file is loaded the gwtOnLoad() method is invoked to initialize the application.
To use a GWT application, you must load the HTML page that includes the application; for our Dashboard this means the Dashboard.html file. It has been a long time since we saw this file, so let’s just quickly review it again in Listing 17.143.
It also searches for meta tags with the names gwt:property (for example for internationalization), gwt:onPropertyErrorFn and gwt:onLoadErrorFn.
1
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Just to remind ourselves, we identify the entry point of the application at [#1], set the value of our user defined GWT property variable at [#2] and load the GWT JavaScript file at [#4]. With this HTML in mind, we will go through each of the steps we identified in Figure 17.161. We will assume that you have loaded the HTML into a browser, so the first step is bootstrapping the application by loading in the gwt.js file.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Bootstrapping the Application When the HTML loads, it automatically loads in the standard GWT JavaScript file called gwt.js at step [#3] in the HTML. It is this file that is responsible for bootstrapping our complete application through the well-oiled process shown in Listing 17.144. Listing 17.144 Examining the GWT bootstrap mechanism within the gwt.js code 1.4290 function __gwt_bootstrap() { 1.4291 // Hook onunload for hosted mode. 1.4292 if (__gwt_isHosted()) { #1 1.4293 __gwt_onUnloadHostedMode.oldUnloadHandler = window.onunload; 1.4294 window.onunload = __gwt_onUnloadHostedMode; 1.4295 } 1.4296 1.4297 // Hook the current window onload handler. 1.4298 var oldHandler = window.onload; #2 1.4299 window.onload = function() { 1.4300 __gwt_isHostPageLoaded = true; 1.4301 if (oldHandler) { 1.4302 oldHandler(); 1.4303 } 1.4304 }; 1.4305 1.4306 // Parse meta tags from host html. 1.4307 __gwt_processMetas(); #3 1.4308 1.4309 // Load any modules. 1.4310 __gwt_loadModules(); #4 1.4311 } (Annotation) (Annotation) (Annotation) (Annotation)
First it unhooks any existing onLoad and onUnload listeners that are attached to the HTML page ([#1] and [#2]) – it is quite possible to have these if you are loading an application into a web page that already exists and has other functionality already coded into it. (in the Dashboard this is not an issue). Once it has dealt with these onLoad and onUnload events, it tries to process any meta tags and then loads any available modules.
Processing the Meta Tags The next step that GWT performs ([#3] in Listing 17.144) is to process any meta-data tags that it can find in the HTML file. There is always at least one meta-tag defining a GWT module where the name of the meta-tag is gwt:module. It is possible for there to be more than one module identified that are to be loaded into the page, in which case there is more than one gwt:module meta-tag. GWT also allows us to define meta-tags covering the situations shown in Table 3.11. Table 17.49 Describing the various meta tags that can be entered into an application’s HTML file Meta Tag Description Example gwt:module This defines any modules and Entry Points that we <meta name=”gwt:module”
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
gwt:property
gwt:onPropertyErrorFn
gwt:onLoadErrorFn
will be using in the web page. This defines a deferred binding client property. It could cover many aspects, for example the locale of the application (which would drive the loading of other locale specific constant files if we had defined them). Specifies the name of a function to call if a client property is set to an invalid value (meaning that no matching compilation will be found). This specifies the name of a function to call if an exception happens during bootstrapping or if a module throws an exception out of onModuleLoad(); the function should take a message parameter.
Each of these meta tags are processed according to the following definitions.
Identifying Modules For each gwt:module meta tag found, the gwt.js code sticks the module name into the ModuleControlBlocks JavaScript prototype object, which is then used later for the loading of modules. Within the Dashboard we only identify one module, but it is possible to have numerous module definitions that should be loaded by the loading mechanism.
Managing Property Meta Tags In the Dashboard application, we define one property tag, which indicates if the application is in intranet or Internet. This property is used in our user-defined property approach shown in chapter 15. Each property is stored in an associative JavaScript array called __gwt_metaProps, for example in the Dashboard we store the value intranet as: 1.4312
__gwt_metaProps[externalvisibility] = intranet;
Whilst we define one gwt:property tag, it is feasible to define the initial locale as a property tag (if we do not, the the default locale is taken).
Registering a Property Error Function GWT also allows us to register JavaScript functions that will execute under two error conditions during the loading process. The first is the gwt:onPropertyErrorFn meta tag which allows us to register a function to be executed if a defined property value does not appear in the list of valid properties. In the Dashboard example, we have the externalvisibility property, which can have values of internet and intranet. We set up an error handler that redirects the user to the book’s web page if an invalid property value is used (you can try it by changing the property’s value in the Dashboard.html file and running the example). If the property is wrong, then you would receive the alert shown in Figure 17.162.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 17.162 The result of running the Dashboard example when there is an error in the user defined externalvisibility property.
Listing 17.145 shows the code that we placed in the Dashboard.html file in order to achieve this redirecting upon an error.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Listing 17.145 Defining a JavaScript function that is to be called if we have an wrong value set for our user defined externalvisibility property 1.4313 <meta name='gwt:onPropertyErrorFn' content='handleWrongVisibility'> #1 1.4314 1.4315 <script> 1.4316 function handleWrongVisibility(moduleName, propName, 1.4317 allowedValues, badValue){ #2 1.4318 if (propName == "externalvisibility"){ #3 1.4319 window.location.href = ("http://www.manning.com/hanson"); #4 1.4320 } 1.4321 } 1.4322 (Annotation) (Annotation) (Annotation) (Annotation)
We first need to set the name of the JavaScript function to execute if there is a property error and we do that by setting the content property of the meta-tag ([#1]). With the name set up, we need to provide the actual JavaScript function, which we can do within some normal script tags – usually following out definition, but this is not mandated as the error handling will call the JavaScript function given to it. The error function, in our case handleWrongVisibility(), can take up to the four parameters we have shown ([#2]), allowing us to present a complex error message to the user should we wish. For the Dashboard we just decide to redirect the user to the book’s web page using some simple JavaScript code ([#4]). Before we can handle the error though, we first must check that the property that erred is our externalvisibilty one, and we do that with a simple equality check on the propName variable ([#3]).
Registering a Loading Error Function It is also possible to register a function that is executed if the loading process errors, in a similar manner to the property error just shown, using the gwt:onLoadErrorFn meta tag -- though we do not do that for the Dashboard application.
Loading the Modules Loading modules is a two step process: first the modules nocache.html file is loaded, and then code within that replaces it by the appropriate permutation of JavaScript code for the actual application. Let’s look at both of these steps.
Loading the nocache.html file The nocache.html file for each module is loaded by the following little snippet of code (again, copied directly from the code): Listing 17.146 Looking at the GWT module loading function in the gwt.js file 1.4323 function __gwt_loadModules() { 1.4324 // Make sure the body element exists before starting. 1.4325 if (!document.body) { 1.4326 // Try again soon.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
window.setTimeout(__gwt_loadModules, __gwt_retryWaitMillis); return; } // Inject a frame for each module. gwt_moduleControlBlocks.injectFrames(); #1 // Try to launch module entry points once everything is ready. gwt_latchAndLaunch(); }
The injectFrames() method ([#1]) creates a new IFRAME in the Browser for each module that has been identified and then attempts to load that module’s nocache.html file into it. When loaded, the functionality to determine which permutation is required begins, and the appropriate cache.html file is loaded.
Loading the cache.html file The nocache.html file contains the ability to identify the particular permutation of JavaScript required and then attempts to replace itself with the correct permutation of JavaScript. It performs this identification through the selectScript() method, an example of which can be seen here: Listing 17.4 Example of performing the identification through the selectScript() method 1.4335 function selectScript() { 1.4336 try { 1.4337 var F; 1.4338 var I = [(F=window["prop$externalvisibility"],F(1)), "true", |#1 1.4339 (F=window["prop$locale"],F(1)), |#1 1.4340 (F=window["prop$preventDragEvent"],F(1)), |#1 1.4341 (F=window["prop$user.agent"],F(1))]; |#1 1.4342 O(["internet","true","default","true","ie6"], |#2 1.4343 "0E43C6571621CCC72FDF125352CA6F79"); |#2 1.4344 O(["internet","true","default","false","ie6"], |#2 1.4345 "0E43C6571621CCC72FDF125352CA6F79"); |#2 1.4346 O(["intranet","true","default","false","safari"], |#2 1.4347 "0F7995D28B83B90750241C1AFDA22B22"); |#2 1.4348 O(["intranet","true","sv","false","ie6"], |#2 1.4349 "14E87368273509E1975EC56A0122A802"); |#2 1.4350 O(["intranet","true","default","false","gecko1_8"], |#2 1.4351 "2EA95892A55656F074B95B62DED34AF5"); |#2
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
In [#1] we that the value of I is set to be a set of values that represent all the properties; the values are calculated using the JavaScript that we (or GWT itself) set up in the property-provider tags within Module XML files – the code provided there has been copied into this JavaScript file. So, for example, the prop$externalvisibility method is the one provided by us in chapter 15. In [#2] all the possible permutations are defined, together with a MD5 string corresponding to the associate cache.html file that represents that particular permutation. We determine our exact permutation at [#3], and then at [#4] we create the URL name of the cache.html file and then replace the current location with that new file, and get ready to start the application.
Starting the Application When the required permutation is loaded, then its initialization code is executed. This initialization code includes the compiled JavaScript version of our onModuleLoad() function, and, when this executes, as far as the user is concerned, the application is now up, visible and executing – in the case of the Dashboard, the user is presented with a view similar to that in Figure 17.163.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 17.163: The result of loading in the Dashboard GWT application
Now that we have looked into the loading process, it seems only natural to turn our attention to looking at how the content to be loaded is created.
Compiling Java to JavaScript Although you will write your code in Java, the output of the GWT compiler is pure JavaScript to enable it to execute on a user’s web browser. The code written for the client side uses a subset of the Java 1.4 definition; the main reasons for this restriction are a) the applicability of packages to web applications and b) the ability to represent the OO Java concepts within JavaScript. Those parts of Java that are available for the client side were already discussed in more detail in chapter 1.1.2, though you should again note that there are no restrictions on the java used for any server side components, as we saw earlier. In this section we look at the output of the compiler to get a little bit of confidence that the Java code we write is actually nice, if not that simple, JavaScript.
Exploring the Produced JavaScript When you have some GWT Java code that you wish to execute, you need to decide if you are going to execute it within Hosted or Web mode. Within Hosted mode, the Java code remains as Java code and is executed in a special hosted environment. For Web mode, which this section deals with, the GWT compiler is invoked to compile the Java code to JavaScript files. The result of compilation is then viewed through a normal web browser as a user would. The compilation process is complicated and produces a number of output files, which varies depending upon the number of browsers supported and various options that you may have used (for example internationalization). Code that is produced from the compilation can be visually broken down into the three segments shown in Figure 17.164.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
Figure 17.164 Structure of compiled code
This visual breakdown is not immediately obvious if you were to go straight now to a GWT application and open one of the JavaScript files, since by default the compiler obfuscates its output. The obfuscation can be turned off by sending the –style PRETTY (or –style DETAILED) flag to the compiler. In the first part of the JavaScript file is the JavaScript definition of the standard Java objects supported by the compilation process. To reduce the file size, the compiler only outputs those Java objects that have been used either directly or indirectly in your program code. Further file size reductions are made by only including the object’s methods actually used. We will now look at: an example of a standard object that has been compiled (we show the vector object in our example) some of our own compiled code an application’s initialization code We start with an example of how a standard Java object is compiled behind the scenes of the GWT code.
Reviewing Standard Java Objects: The Vector Object Let’s look briefly at the implementation of the Java Vector class – this will quite commonly appear even if you have not explicitly used Vector yourself. As soon as you use events in your code, Vector will be included since it is used in the internal process of GWT event handling. The prototype JavaScript definition for the java.util.Vector class is (we used –style DETAILED for this view): Listing 17.5 Prototype JavaScript definition for the java.util.Vector class 1.4362 function java_util_Vector(){ |#1 1.4363 } |#1 1.4364 1.4365 _ = java_util_Vector.prototype = new java_util_AbstractList(); |#2 1.4366 _.add__ILjava_lang_Object_2 = |#3 1.4367 java_util_Vector_add__ILjava_lang_Object_2; |#3
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
You will notice that everything in the JavaScript keeps its fully qualified class name with the period ‘.’ replaced by underscores ‘_’. The Java class java.util.Vector, for example, becomes the JavaScript class java_util_Vector. Classname and method are separated by one underscore, and the types of any inputs are separated from the method name by two underscores. This notation is shown in Figure 17.165 which represents the Vector.add(Object) method.
Figure 17.165 Explanation of JavaScript call format
The definition in the JavaScript code for adding an object to a Vector is: _.add__Ljava_lang_Object_2 = java_util_Vector_add__Ljava_lang_Object_2;
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
or, executing the .add method on a Vector is the same as executing the java_util_Vector_add method; the parameter is of type java.lang.Object (which is the same as the Java definition). Further, in the file we find the definition of this function to be the addition of the new object to the end of the array representing the current Vector. function java_util_Vector_add__Ljava_lang_Object_2(o){ var a = this.array; a[a.length] = o; return true; }
Similarly, the method to retrieve an indexed element from the vector, the Vector.get(int) method, is referenced as: _.get__I = java_util_Vector_get__I;
(int is a primitive type in Java and is not treated as an object, thus the input to the get method has type I rather than Ljava.lang.int – if we were using the Java Integer object then the argument would indeed be Ljava.lang.Integer). The get method is implemented in the JavaScript function: function java_util_Vector_get__I(index){ return java_util_Vector_$get__Ljava_util_Vector_2I(this, index); }
which calls the second form of the get operation with arguments (this,index). function java_util_Vector_$get__Ljava_util_Vector_2I(this$static, index){ if (index < 0 || index >= this$static.size__()) throw java_util_NoSuchElementException_$NoSuchElementException__Ljava_util_NoSuchEl ementException_2(new java_util_NoSuchElementException()); return this$static._1get__I(index); }
Here it is easy to see some bound checking taking place. An exception is raised if the index is less than 0 or greater than the size of the vector (or in reality the underlying JavaScript array implementation). If all is OK, then a value is retrieved from the call to the 1get_I(index) method, which returns the value at the correct index in the underlying array implementation. function java_util_Vector__1get__I(index){ return this.array[index]; }
So we have seen that standard Java objects are compiled into JavaScript as a systematic way that is relatively easy to read if we tell the compiler to produce human readable code. What happens to the JavaScript we write? How does it relate to your Java code?
Exploring Program Code as JavaScript The next segment of the code is where you will find the JavaScript relating to your Java code. Consider our Dashboard again, although this time we used the flag –style PRETTY to cut down a little on the verbose output.
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
If you recall our original Java code for the onModuleLoad() function, then this will look extremely familiar! As you can see, the JavaScript for your Java code is written in the same style as the rest of the code. The rest of all of our functions are also included in JavaScript permutation – feel free to have a browse – although, if you have forgotten to put –style PRETTY or –style DETAILED as a flag to the compiler, it will be extremely difficult to follow the output as it will resemble the following: 1.4409 function a(){return window;} 1.4410 function b(){return this.c + '@' + this.d();} 1.4411 function e(f){return this === f;} 1.4412 function g(){return h(this);} 1.4413 function i(){} 1.4414 _ = i.prototype = {};_.j = b;_.k = e;_.d = g;_.toString = function(){return this.j();};_.c = 'java.lang.Object';_.l = 0;function m(n){return n == null?null:n.c;} 1.4415 o = null;function p(){return ++q;} 1.4416 function r(s){return s == null?0:s.$H?s.$H:(s.$H = p());} 1.4417 function t(u){return u == null?0:u.$H?u.$H:(u.$H = p());} 1.4418 q = 0;function v(){v = a;w = y('[N',[0],[24],[0],null);return window;} 1.4419 function z(){var A,B;A = m(this);B = this.C;if(B !== null){return A + ': ' + B;}else{return A;}} 1.4420 function D(E,F){if(E.ab !== null)throw bb(new cb(),"Can't
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
overwrite cause");if(F === E)throw db(new eb(),'Self-causation not permitted');E.ab = F;return E;} 1.4421 function fb(gb){v();return gb;} 1.4422 function hb(ib,jb){v();ib.C = jb;return ib;}
In the final segment of the JavaScript, from Figure 17.164, we can find the code that is called when a module is initialized.
Understanding the Initialization Code Segment Finally, the JavaScript is completed with initialization code segment. This segment includes a method called gwtOnLoad() which is called by the GWT application loading process. In the case of this example, the following JavaScript code was produced 1.4423 1.4424 1.4425 1.4426 1.4427 1.4428 1.4429 1.4430 1.4431 1.4432 1.4433 1.4434 1.4435
function gwtOnLoad(errFn, modName){ if (errFn) try { init(); } catch (e) { errFn(modName); } else { init(); } }
where the init function is defined as: 1.4436 1.4437 1.4438 1.4439 1.4440 1.4441
function init(){ HelloWorld_$onModuleLoad__LHelloWorld_2( new HelloWorld() ); }
At first glance, this particular example may appear to have a lot of overheads associated with it, and you could easily start thinking that a hand coded version would be smaller and more efficient. This is probably true for such a trivial example, but, when you turn your mind to industrial strength applications, the true benefits of coding in Java and letting a compiler produce all this code become clearer. Remember as well that Google quote two important things that you should keep in mind: 1. “A typical, full-featured GWT application will require the user to download about 100K of cacheable JavaScript, which is in line with most hand-written AJAX applications.” 2. “GWT applications are almost always as fast as hand-written JavaScript. The GWT compiler avoids adding any wrappers around any functionality that is implemented natively in the browser.” Of course, we will have to wait and see for the facts and figures to emerge once industrial applications start becoming a reality to confirm the absolute validity of these statements, but out of interest each
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286
permutation of the whole Dashboard application comes in at around 116K when in obfuscated mode. So, at least on the size side, they are pretty close considering all of the functionality that is in the Dashboard (although this does not include the two libraries required for the Google Search widgets). Speed-wise, we did not find any problems on our development machines or any machines we tested on, but have not built an equivalent directly in JavaScript to compare. To be very frank, and this is probably the key point of GWT, we do not even want to think about the amount of effort that would be required to program, let alone debug, any issues or perform maintenance across six different browsers, for an application such as the Dashboard directly in JavaScript.
Summary As we have seen, GWT is a powerful tool firmly based in delivering standards compliant web technologies compiled from Java code and using CSS for styling. It does not bring any magic proprietary technologies that need to be downloaded by the user of your application, which is really good news in these days of Internet security and trojans. It is also the use of these technologies that makes GWT easily expandable and flexible in the ways that you need, coupled with the ability to bring the power of the existing Java tool sets to bear on development it is a great proposition. It is simple to access and manipulate the DOM through GWT both at a low level via the JavaScript Native Interface, through low level methods calls in the GWT DOM Implementation classes or through the more natural Java approach of creating objects and using widget and panel API methods such as add(). These widgets and panels are really simple HTML elements, and we have seen them all in use. On the client side, functionality is provided by writing code in GWT Java and subsequently compiling it to JavaScript code using the provided compiler code. We saw that the resulting JavaScript code contains a) the code you have produced, or which has been generated and b) implementations of the standard Java objects used by your code. Server-side integration is possible with your choice of language, though, if you want to use Java, then GWT provides the RPC approach to make the boundary between client and server almost disappear. Is GWT the answer to every Ajax development problem? Probably not, but it goes a great way forwards to making Ajax applications easier to develop. GWT enables us to use the mature tooling of Java in the development of Ajax applications, as well as accessing JavaScript directly if you really feel that you need to do that. To sum up, we’ll go back to the last sentence of this chapter, in 17.3.4, which sums up our thoughts on GWT at present: “To be very frank, and this is probably the key point of GWT, we do not even want to think about the amount of effort that would be required to program, let alone debug, any issues or perform maintenance across six different browsers, for an application such as the Dashboard directly in JavaScript.”
Please post comments or corrections to the Author Online forum at http://www.manning-sandbox.com/forum.jspa?forumID=286