Scaffolding GWT applications from JPA entities

In this recipe, we'll look at the gwt setup command, which scaffolds GWT artifacts from JPA entities.

Getting ready

Create a new directory (C: oo-cookbookch05-gwt) in your system. Copy the ch05_gwt_app.roo script that accompanies this book to the ch05-gwt directory. Start the Roo shell from the ch05-gwt directory and execute the ch05_gwt_app.roo script using the script command. Executing the ch05_gwt_app.roo script does the following:

  • Creates a flightapp-gwt Eclipse project
  • Sets up Hibernate as a persistence provider
  • Configures MySQL as the database for the application
  • Creates Flight and FlightDescription JPA entities and defines a many-to-one relationship between Flight and FlightDescription entities

If you are using a different database than MySQL or your connection settings are different from what is specified in the script, then modify the script accordingly.

Install the Google Plugin for Eclipse IDE (http://code.google.com/eclipse/); it simplifies developing GWT applications using Eclipse IDE.

How to do it...

Follow these steps to scaffold GWT applications:

  1. Execute gwt setup command, as shown here:
    ..roo>gwt setup
    ....
    Created SRC_MAIN_WEBAPPWEB-INFspring
    Created SRC_MAIN_WEBAPPWEB-INFspringwebmvc-config.xml
    Created SRC_MAIN_WEBAPPWEB-INFweb.xml
    Updated ROOTpom.xml[...]
    Created SRC_MAIN_JAVAsample
    ooflightappclient
    Created SRC_MAIN_JAVAsample
    ooflightappApplicationScaffold.gwt.xml
    Created SRC_MAIN_JAVAsample
    ooflightappclientmanaged
    equest
    Created SRC_MAIN_JAVAsample
    ooflightappclientscaffold
    equest
    Created SRC_MAIN_WEBAPPindex.html
    Created SRC_MAIN_WEBAPPApplicationScaffold.html
    ...
    

    Note that only partial output has been shown above for brevity.

  2. As of Spring Roo 1.1.3, gwt setup command creates GAE-specific (Google App Engine) Java files that you must remove from the generated source. To do so, remove the following folders from the flightapp-gwt project before going to the next step:
    src/main/java/sample/roo/flightapp/server/gae
    src/main/java/sample/roo/flightapp/shared/gae
    src/main/java/sample/roo/flightapp/client/scaffold/gae

    If you are using Spring Roo 1.1.5, GAE-specific Java files are not generated.

  3. Execute the perform eclipse command to update the .classpath file of the flightapp-gwt Eclipse project and to convert the nature of the project to gwt:
    ..roo>perform eclipse
    
  4. Import the flightapp-gwt project into your Eclipse IDE. Add the Google Web Toolkit library to the build path (Project properties | Java Build Path | Add Library) of the flightapp-gwt project, so that the project doesn't show any compilation errors in Eclipse IDE.
  5. If you want to run the GWT application using the GWT Maven plugin (http://mojo.codehaus.org/gwt-maven-plugin/), then exit the Roo shell and execute gwt:run goal of the GWT maven plugin, as shown here (alternatively, you may go to the next step):
    ..recipe>mvn clean compile gwt:run
    

    Executing the gwt:run goal opens the GWT Development Mode window, as shown here:

    How to do it...

    Click the Launch Default Browser button in the GWT Development Mode window to launch the flightapp-gwt application. If not already installed, you'll be prompted to install the Google Web Toolkit Developer Plugin for your browser, which is required when you are running a GWT application in development mode.

  6. If you want to run the GWT application from your Eclipse IDE, then right-click the flightapp-gwt project in Eclipse IDE and select Google | Web Toolkit Settings... option. Select the Web Application option and check the option This project has a WAR directory, as shown here:
    How to do it...

    As shown in the screenshot, set the WAR directory value to src/main/webapp—Maven's standard WAR directory that contains the ApplicationScaffold.html host page. Make sure that the Launch and deploy from this directory option is unchecked.

  7. Right-click on the flightapp-gwt project in Eclipse IDE and select the Run As | Web Application option. Select ApplicationScaffold.html (or index.html) page in the HTML Page Selection dialog, as shown in the following figure:
    How to do it...

    The HTML page that you select on this screen represents a host HTML page that is responsible for loading the GWT application. Both ApplicationScaffold.html and index.html files are located in the src/main/webapp directory of the flightapp-gwt GWT application. index.html is a simple HTML page that simply loads the ApplicationScaffold.html page—the host HTML page of flightapp-gwt GWT application.

    When you are running the GWT application for the first time, you will be asked to select the location of the WAR directory of the flightapp-gwt project, which is target/flightapp-gwt-0.1.0.BUILD-SNAPSHOT.

  8. The Run As | Web Application option starts the embedded Jetty server (bundled with Google Plugin for Eclipse) and runs the GWT application in the development mode. In development mode, the GWT application executes like a regular Java application, and is not compiled to JavaScript. This makes it possible to debug the GWT application during the development phase and when the application is ready for production, simply compile the GWT application to create corresponding JavaScript files. In the development mode, Eclipse IDE shows a new Development Mode view with the link to access the GWT application, as shown here:
    How to do it...
  9. Click the URL displayed in the Development Mode view to open it in the default web browser or right-click the URL to select the browser in which you want to open it. If the flightapp-gwt application is successfully deployed, you'll see the home page of the application, as shown here:
    How to do it...

You can now use the Flights and FlightDescriptions menu options to perform CRUD operations on Flight and FlightDescription JPA entities.

How it works...

The gwt setup command is processed by the GWT add-on of Spring Roo.

You might be wondering, why the Roo-generated GWT user interface doesn't show a link corresponding to the findFlightDescriptionsByDestinationAndOrigin finder method in the FlightDescription JPA entity? As of Spring Roo 1.1.5, the GWT add-on doesn't add finder functionality to the scaffolded GWT application.

The gwt setup command does the heavy lifting of scaffolding GWT Activities, Places, Proxies, and Views for performing CRUD operations on JPA entities. Let's first take a look at the Roo-generated GWT module descriptor file, ApplicationScaffold.gwt.xml, which describes a GWT module.

GWT module descriptor

Roo creates ApplicationScaffold.gwt.xml in the root package, sample.roo.flightapp, of the flightapp-gwt project. It defines module dependencies, source paths, properties, deferred binding configurations, and module entry points. Let's look at some of the important elements defined in ApplicationScaffold.gwt.xml.

By default, the name of the GWT module is derived from the location of the module descriptor. As Roo creates the ApplicationScaffold.gwt.xml file in the sample.roo.flightapp package, the name of the module is sample.roo.flightapp.ApplicationScaffold. The ApplicationScaffold.gwt.xml file renames the module to applicationScaffold using the rename-to attribute of the <module> element, as shown here:

<module rename-to="applicationScaffold">

The GWT compiler generates JavaScript code in the directory identified by the module name; therefore, the code for our applicationScaffold module is generated in the applicationScaffold sub-directory of the generated WAR file.

The <inherits> element of the module descriptor specifies modules on which the module is dependent upon. For instance, applicationScaffold is dependent on User, Logging, Activity, Places, and so on, built-in modules of GWT, as shown here:

    <inherits name='com.google.gwt.activity.Activity'/>
    <inherits name='com.google.gwt.place.Place'/>
    <inherits name="com.google.gwt.user.User"/>
    <inherits name='com.google.gwt.logging.Logging'/>

The <source> element of the module descriptor specifies package, including its sub-packages (relative to the classpath location of ApplicationScaffold.gwt.xml file), which contain Java classes that GWT compiler needs to translate into JavaScript, as shown here for applicationScaffold module:

   <source path='client'/>
   <source path='shared'/>

The given <source> element instructs the GWT compiler to translate Java classes contained in sample.roo.flightapp.client and sample.roo.flightapp.shared packages, and their sub-packages.

The <public> element of module descriptor specifies packages (and their sub-packages) that contain publicly accessible resources, like images and CSS files, as shown here:

<public path="public"/>

As with the <source> elements, the <public> element specifies the location of packages relative to the classpath location of the module descriptor file.

The ApplicationScaffold.gwt.xml file configures logging for the module, as shown here:

<set-property name="gwt.logging.enabled" value="TRUE"/>
<set-property name="gwt.logging.logLevel" value="INFO"/>
<set-property name="gwt.logging.consoleHandler" 
   value="ENABLED"/>
<set-property name="gwt.logging.developmentModeHandler" 
   value="ENABLED"/>
<set-property name="gwt.logging.simpleRemoteHandler" 
   value="DISABLED"/>
...

In this code, gwt.logging.enabled property enables logging for the applicationScaffold module, gwt.logging.logLevel property sets the logging level to INFO, gwt.logging.consoleHandler property enables logger output to appear in the IDE console, gwt.logging.developmentModeHandler property enables logger output to appear in the 'Development Mode' console of the IDE and gwt.logging.simpleRemoteHandler property disables remote logging of log messages. Later in this recipe, we'll see that gwt.logging.simpleRemoteHandler property is set to ENABLED to enable logging messages on the server-side.

Roo-generated GWT applications by default provide support for the mobile Safari browser. So, if you are developing a GWT application, it'll work seamlessly on mobile phones that use the mobile Safari browser. If the application is accessed using the mobile Safari browser, then the GWT application will create a web UI suitable for display in mobile devices. To support both desktop and mobile Safari browsers, the applicationScaffold module makes use of the deferred binding feature of the GWT compiler.

To use the deferred binding feature, the <define-property> element of module descriptor is used to define a new property named mobile.user.agent, as shown here:

<define-property name="mobile.user.agent" 
        values="mobilesafari, none"/>

The values attribute specifies a comma-separated list of values that the mobile.user.agent property can accept.

To set the mobile.user.agent property value, the module descriptor makes use of the <property-provider> element, as shown here:

 <property-provider name="mobile.user.agent">

      <![CDATA[
      var ua = navigator.userAgent.toLowerCase();
      ...
 ]]>

The CDATA section contains the JavaScript that is used to obtain the value of the mobile.user.agent from the user-agent information sent by the web browser to the server hosting the GWT application.

Now, the most important part: the deferred binding rule is defined using a replacement technique in the applicationScaffold module descriptor file, as shown here:

<replace-with  
     class="...client.scaffold.ioc.MobileInjectorWrapper">
  <when-type-is 
      class="...client.scaffold.ioc.DesktopInjectorWrapper"/>
  <all>
     <when-property-is name="mobile.user.agent" 
          value="mobilesafari"/>
  </all>
</replace-with>

This configuration instructs the GWT compiler to replace the code of DesktopInjectorrapper with MobileInjectorWrapper (while generating JavaScript for the applicationScaffold module) if the value of mobile.user.agent property is mobilesafari. This is possible because both DesktopInjectorWrapper and MobileInjectorWrapper implement the same interface, InjectorWrapper . When the GWT compiler executes, it uses deferred binding rules (defined in the module descriptor file) to generate separate flightapp-gwt application's JavaScript code for mobile Safari and desktop browsers. This ensures that the desktop and mobile browsers download JavaScript code meant specifically for that browser type. For instance, the mobile Safari browser will not download JavaScript code that is specific to the desktop web browser and vice versa. The classes that need to create an instance of DesktopInjectorWrapper or MobileInjectorWrapper make use of the create static method of the com.google.gwt.core.client.GWT class to instruct the GWT compiler to instantiate the DesktopInjectorWrapper or MobileInjectorWrapper instance using deferred binding.

Note

The code generated for mobile Safari browser follows a similar design approach as the code for the desktop browser; therefore, in this book we'll limit the discussion of Roo-generated code specific to desktop browser.

A module descriptor also describes entry points into the GWT application using <entry-point> element, as shown here for applicationScaffold module:

<entry-point  
   class="sample.roo.flightapp.client.scaffold.Scaffold"/>

The above code suggests that Scaffold class represents the entry point for the applicationScaffold module. Scaffold class implements com.google.gwt.core.client.EntryPoint interface of GWT—a mandatory requirement for entry point classes.

The GWT module's entry point

As mentioned earlier, the Scaffold class of the flightapp-gwt application represents an entry point into the applicationScaffold module. The Scaffold class implements GWT's EntryPoint interface and implements its onModuleLoad method to bootstrap the flightapp-gwt application, as shown here:

Scaffold.java
package sample.roo.flightapp.client.scaffold;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;

public class Scaffold implements EntryPoint {
   final private InjectorWrapper injectorWrapper = 
         GWT.create(DesktopInjectorWrapper.class);

   public void onModuleLoad() {
      injectorWrapper.getInjector().getScaffoldApp().run();
   }
}

The first thing to notice in this code is the use of the create method of the com.google.gwt.core.client.GWT class to create the DesktopInjectorWrapper instance. As the DesktopInjectorWrapper implementation needs to be replaced by MobileInjectorWrapper for the mobile Safari browser, DesktopInjectorWrapper is created using the create method of the GWT class.

The onModuleLoad method is like Java's main method, and is responsible for initializing the flightapp-gwt application. In the Scaffold class, the onModuleLoad method is responsible for creating the application's web UI, registering event handlers with EventBus, and so on. The DesktopInjectorWrapper and MobileInjectorWrapper classes represent a wrapper around GIN's Ginjector implementation.

Dependency injection using GIN

GIN is a dependency injection framework that uses Google Guice framework to support dependency injection in GWT applications. In GWT applications, references to objects that are needed throughout the application can be created using GIN or a factory. The Roo-generated flightapp-gwt GWT application makes use of GIN to create EventBus , ApplicationRequestFactory , and PlaceController —objects that are used across the flightapp-gwt application. The following table describes the importance of these objects in the flightapp-gwt application:

Object

Description

EventBus

The EventBus is used in a GWT application for publishing events and registering event handlers.

ApplicationRequestFactory

A Roo-generated interface that extends GWT's RequestFactory interface. The implementation of this interface is generated by the GWT compiler. The flightapp-gwt application makes use of RequestFactory to interact with the JPA layer.

PlaceController

A GWT Place represents a location in a GWT application. If you select the Flights menu option from the UI of the flightapp-gwt application, then it represents a place. Now, if you select the FlightDescriptions option from the UI, then it represents a different place in the application. GWT's PlaceController is used to navigate from one place to another in the GWT application.

Now that we know what objects are used across the flightapp-gwt application, let's look at how GIN creates these objects and how these objects are injected into objects that depend on them.

To understand how Ginjector is used in flightapp-gwt, let's look at the following class diagram:

Dependency injection using GIN

In this class diagram, the Ginjector interface is part of GIN API. The ScaffoldInjector and DesktopInjector are generated by Roo. The following are the important points to notice in the above class diagram:

  • ScaffoldInjector extends the Ginjector interface and defines a single method getScaffoldApp, which returns an instance of type ScaffoldApp.
  • ScaffoldApp is a Roo-generated class that defines the contract for initializing the GWT application for both the desktop and mobile browser.
  • The DesktopInjector extends ScaffoldInjector and overrides the getScaffoldApp method to return ScaffoldDestopApp. This change in return type is perfectly legal because ScaffoldDesktopApp is a subclass of the ScaffoldApp class.
  • ScaffoldDesktopApp is responsible for creating the web UI, tailored for the desktop web browser, and performing all the initialization work before the application is put into service. Similarly, ScaffoldMobileApp is responsible for creating the web UI for the mobile browser.

GIN's Ginjector interface is at the heart of the GIN framework and is responsible for performing dependency injection. To use Ginjector, a GWT application must do the following:

  • Define an interface that extends Ginjector—this is the DesktopInjector interface in the case of flightapp-gwt. The GWT compiler is responsible for providing the implementation of this interface.
  • Define one or more methods in the interface to return a top-level object that the rest of the application would use. The Ginjector creates the top-level object by injecting dependencies of the lower-level objects based on the bindings configured by the Ginjector. In the case of the flightapp-gwt application, ScaffoldDesktopApp and ScaffoldMobileApp represent top-level objects.
  • Define a GinModule or AbstractGinModule (both are part of GIN API) class that defines bindings for the dependencies. The ScaffoldModule class in flightapp-gwt defines bindings for EventBus, PlaceController, and ApplicationRequestFactory.
  • Annotate the method, contructor, or field of your classes where you want Ginjector to perform dependency injection with the @Inject annotation (part of Guice API). In the case of flightapp-gwt, ApplicationDetailsActivites, ApplicationMasterActivities, and so on, make use of the @Inject annotation to let Ginjector inject dependencies.

Let's now look at the code created by Roo corresponding to each of the activities described previously.

The following code shows the DesktopInjector interface:

DesktopInjector.java
package sample.roo.flightapp.client.scaffold.ioc;
..
import sample.roo.flightapp.client.scaffold.ScaffoldDesktopApp;
import com.google.gwt.inject.client.GinModules;

@GinModules(value = {ScaffoldModule.class})
public interface DesktopInjector extends ScaffoldInjector {

   ScaffoldDesktopApp getScaffoldApp();
}

DesktopInjector extends the ScaffoldInjector interface (which in turn extends GIN's Ginjector interface) and defines a single method—getScaffoldApp, which returns the ScaffoldDesktopApp object. So, the responsibility of Ginjector implementation generated by the GWT compiler is to return an instance of ScaffoldDesktopApp with all its dependencies injected with appropriate implementations.

The @GinModules annotation specifies the class (which implements the GinModule interface or extends the AbstractGinModule abstract class) responsible for defining dependencies and their providers. The following code shows the ScaffoldModule class, which binds EventBus, PlaceController, and ApplicationRequestFactory dependencies to their respective providers:

ScaffoldModule.java
package sample.roo.flightapp.client.scaffold.ioc;

import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.SimpleEventBus;
import com.google.gwt.inject.client.AbstractGinModule;
import com.google.gwt.place.shared.PlaceController;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;

public class ScaffoldModule extends AbstractGinModule {

 @Override
 protected void configure() {
  bind(EventBus.class).
     to(SimpleEventBus.class).in(Singleton.class);
  bind(ApplicationRequestFactory.class).
     toProvider(RequestFactoryProvider.class).
     in(Singleton.class);
  bind(PlaceController.class).
     toProvider(PlaceControllerProvider.class).
     in(Singleton.class);
 }

 static class PlaceControllerProvider implements 
     Provider<PlaceController> {

  private final PlaceController placeController;

  @Inject
  public PlaceControllerProvider(EventBus eventBus) {
   this.placeController = new PlaceController(eventBus);
  }

  public PlaceController get() {
   return placeController;
  }
 }

 static class RequestFactoryProvider implements 
     Provider<ApplicationRequestFactory> {

    ..
 }
}

The Roo-generated ScaffoldModule class extends AbstractGinModule and overrides the configure method to associate dependencies with their providers. The bind method of AbstractGinModule binds a dependency to its provider. The in(Singleton.class) instructs that only a single instance of EventBus, PlaceController, and ApplicationRequestFactory are created for the application. The PlaceControllerProvider and RequestFactoryProvider static inner classes represent provider for the PlaceController and RequestFactory instances, respectively. SimpleEventBus (part of GWT API) is an implementation of EventBus.

An important point to notice in this code is the use of @ Inject annotation by PlaceControllerProvider for instructing Ginjector to inject an implementation of EventBus. So, if you are creating a hierarchy of objects, then define the method in Ginjector, which returns the top-level object because the lower-level objects in the hierarchy can make use of Ginjector's dependency injection feature using the @Inject annotation.

The following figure summarizes how the Scaffold class makes use of the GIN framework to bootstrap the application:

Dependency injection using GIN

The Scaffold class' onModuleLoad method invokes the getInjector method of DesktopInjectWrapper to access Ginjector implementation, followed by a call to getScaffoldApp to obtain an instance of ScaffoldDesptopApp. The following sequence diagram further clarifies the sequence of method invocations between classes:

Dependency injection using GIN

In the above sequence diagram, the call to the run method of DesktopScaffoldApp results in initialization of the web UI.

Now that we know how the flightapp-gwt application makes use of GIN and initializes itself, we'll look at GWT's EntityProxy and RequestFactory interfaces and how scaffolded code makes use of them.

EntityProxy, RequestContext, and RequestFactory interfaces

An entity proxy represents a client-side (the JavaScript-side) object that mimics an entity on the server-side, which is a JPA entity in the case of the flightapp-gwt GWT application. Entity proxies act as means to transfer data between client and the server. In GWT, a client-side object that acts as an entity proxy extends GWT's EntityProxy interface and defines abstract getter and setter methods for the fields defined in the corresponding server-side entity. In flightapp-gwt, we have Flight and FlightDescription JPA entities; therefore, Roo generates corresponding entity proxies FlightProxy and FlightDescriptionProxy, respectively. By default, Roo creates abstract getter and setter methods in entity proxy for all the attributes defined in the corresponding JPA entity class. The following code shows the FlightProxy entity proxy:

FlightProxy.java
package sample.roo.flightapp.client.managed.request;

import com.google.gwt.requestfactory.shared.EntityProxy;
import com.google.gwt.requestfactory.shared.ProxyForName;
import org.springframework.roo.addon.gwt.RooGwtMirroredFrom;

@RooGwtMirroredFrom("sample.roo.flightapp.domain.Flight")
@ProxyForName("sample.roo.flightapp.domain.Flight")
public interface FlightProxy extends EntityProxy {
    abstract Long getId();
    abstract Integer getVersion();
    abstract Date getDepartureDate();
    abstract Date getArrivalDate();
    abstract FlightDescriptionProxy getFlightDescription();
    abstract void setId(Long id);
    ...
}

Roo's @RooGwtMirroredFrom annotation specifies the fully-qualified class name of the JPA entity for which the FlightProxy entity proxy was created.

Note

It is the @RooGwtMirroredFrom annotation that keeps the entity proxy in-sync with the corresponding JPA entity.

The GWT's @ProxyForName annotation specifies the server-side entity represented by the entity proxy. Notice that in the above code the getFlightDescription method is defined to return FlightDescriptionProxy because it represents the entity proxy corresponding to the FlightDescription JPA entity.

To invoke server-side services, the GWT application's client-side code needs to have client-side stubs for remote services. The client-side code makes use of service stubs to invoke remote services. A service stub is defined by an interface that extends GWT's RequestContext interface and defines methods with a signature similar to that of the corresponding remote service methods. We'll shortly see the difference between methods defined in the remote service class and the client-side service stub.

By default, Roo generates rich entities and the resulting application doesn't have a service layer; therefore, in the case of the Roo-generated GWT application a service stub defines methods corresponding to the JPA entity class. The following code shows the Roo-generated FlightRequest service stub corresponding to the Flight JPA entity in flightapp-gwt application:

FlightRequest.java
package sample.roo.flightapp.client.managed.request;

import com.google.gwt.requestfactory.shared.InstanceRequest;
import com.google.gwt.requestfactory.shared.Request;
import com.google.gwt.requestfactory.shared.RequestContext;
import com.google.gwt.requestfactory.shared.ServiceName;
import org.springframework.roo.addon.gwt.RooGwtMirroredFrom;

@RooGwtMirroredFrom("sample.roo.flightapp.domain.Flight")
@ServiceName("sample.roo.flightapp.domain.Flight")
public interface FlightRequest extends RequestContext {
  abstract Request<java.lang.Long> countFlights();
  abstract Request<java.util.List<...FlightProxy>> 
     findAllFlights();
  ....
  abstract InstanceRequest<...FlightProxy, java.lang.Void> 
     remove();
  abstract InstanceRequest<...FlightProxy, java.lang.Void> 
     persist();
}

As this code shows, a FlightRequest service stub extends GWT's RequestContext interface. GWT's @ServiceName annotation specifies the full-qualified name of the server-side service class corresponding to the client-side service stub, which is the Flight JPA entity in the case of the FlightRequest stub. The above code shows that the FlightRequest service stub defines methods that return the following:

  • Request<T> – where T represents the actual return type of the corresponding method on the server-side class. For instance, countFlights method returns java.long.Long in Flight JPA entity, and so does the countFlights method in FlightRequest stub. If a method on the server-side service class returns an entity, then the client-side service stub returns the corresponding entity proxy. For instance, findAllFlights method in Flight JPA entity returns a java.util.List<Flight>, so the findAllFlights method in FlightRequest stub returns java.util.List<FlightProxy>. It is important to note that the methods corresponding to static methods of the server-side service return Request<T> type in the client-side service stub.
  • InstanceRequest<P,T> – where P represents the entity type on which the corresponding server-side service method acts and T represents the actual return type of the method. For instance, persist method of Flight JPA entity acts on Flight entity instance and returns void; therefore, the return type of the corresponding method in FlightRequest stub is InstanceRequest<FlightProxy, java.lang.Void>. Note that the only stub methods corresponding to instance methods on the server-side service return InstanceRequest.

Note

It is Roo's @RooGwtMirroredFrom annotation that keeps the client-side stub in-sync with the corresponding JPA entity.

Now, let's look at how RequestFactory helps with communication between client-side and server-side code.

RequestFactory acts as a communication bridge between the entity proxy and the corresponding entity on the server-side. RequestFactory manages entity proxies and is responsible for copying server-side entity attribute values to corresponding entity proxy and vice versa. In your GWT application, you are required to define an interface that extends RequestFactory interface and provide methods that return client-side stubs for server-side services. Roo creates an ApplicationRequestFactory class (we discussed earlier that this was created using GIN), which is shown here:

package sample.roo.flightapp.client.managed.request;

import sample.roo.flightapp.shared.scaffold.ScaffoldRequestFactory;

public interface ApplicationRequestFactory extends 
   ScaffoldRequestFactory {

    FlightRequest flightRequest();
    FlightDescriptionRequest flightDescriptionRequest();
}

The following class diagram shows the inheritance hierarchy of the ApplicationRequestFactory class:

EntityProxy, RequestContext, and RequestFactory interfaces

The above figure shows that ApplicationRequestFactory extends the Roo-generated ScaffoldRequestFactory interface, which in turn extends GWT's RequestFactory interface. The hierarchy is created such that if you want to define your custom client-side service stub methods, then you can add them to the ScaffoldRequestFactory.

Note

ApplicationRequestFactory is managed by Roo; therefore, you should not modify it manually to add custom client-side service stub methods. Instead, add them to ScaffoldRequestFactory.

The interaction between entity proxy and server-side entity is achieved by configuring GWT's RequestFactoryServlet in the web.xml file of the GWT web application.

Note

You don't need to configure RequestFactoryServlet in the web.xml file of the flightapp-gwt project because when you execute the gwt setup command, Roo configures GWT's RequestFactoryServlet in the web.xml file.

The following code shows the configuration of RequestFactoryServlet in the web.xml file of the flightapp-gwt application:

web.xml
<servlet>
  <servlet-name>requestFactory</servlet-name>
  <servlet-class>
    com.google.gwt.requestfactory.server.RequestFactoryServlet
  </servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>requestFactory</servlet-name>
  <url-pattern>/gwtRequest</url-pattern>
</servlet-mapping>

This configuration shows that by default Roo maps the RequestFactoryServlet to /gwtRequest URL, which you can change. If you change the URL mapping of RequestFactoryServlet, then you also need to change how ApplicationRequestFactory is created by ScaffoldModule—the Roo-generated GIN module we discussed earlier.

RequestFactory requires that the server-side entity must define a no-argument constructor, getId, getVersion, and find<Entity> methods. The getVersion should return the version, getId should return the unique ID associated with entity instance, and the find<Entity> method which accepts entity ID and returns the corresponding entity instance. So, if you create entities using the Roo entity command, then make sure that you don't set the name of the identifier field or version field to anything other than id and version, respectively. When you execute the Roo gwt setup command, and Roo finds that the names of ID and version fields is different from id and version, then it doesn't scaffold the GWT application.

Let's now look at some of the Roo-generated GWT activities and places.

Activities and places

Roo-generated GWT code consists of many base classes and interfaces that attempt to provide a consistent approach to performing CRUD operations on JPA entities. In this section, we'll look at some of the important classes and interfaces and concepts which will give you a starting point to understand the Roo-generated GWT code.

The Roo-generated GWT web UI consists of a Master display region and a Detail display region. The Master display region shows the list of entities that can be managed from the web UI. The widget that shows Flight and FlightDescriptions in a list represents the Master display region. The Detail display region shows activities that can be performed on each of the entities displayed in the Master display region. The region that shows the list of Flight or FlightDescription entity instances in the system, the form to create a new entity instance, the form to edit an entity instance, and so on, represents the Detail display region.

The following figure shows the Master display region and Detail display region in the flightapp-gwt application:

Activities and places

Let's now look at some of the examples of activities, places and views, in the Roo-generated flightapp-gwt application.

Activities are responsible for driving views and handling events generated by user interaction in a display region. It is created by implementing GWT's Activity interface or by extending GWT's AbstractActivity abstract class. The following diagram shows some of the activities that were generated by Roo for flightapp-gwt application:

Activities and places

This diagram shows that FlightDescriptionDetailsActivity and FlightDetailsActivity inherit from the AbstractActivity abstract class, and FlightDescriptionListActivity and FlightListActivity classes implement the Activity interface. <entity-name>DetailsActivity activities drive views and manage user interactions when an existing entity instance's details are displayed in the Detail display region. <entity-name>ListActivity activities drive views and manage user interactions when the list of entity instances are displayed in the Detail display region.

Places are locations within the display region that can be translated into a URL. An Activity is mapped to a place (changeable into a URL), which makes Activities accessible via URL. A place is created by extending GWT's Place abstract class. The place implementation class also defines how the place instance can be translated into a URL. ProxyPlace and ProxyListPlace classes generated by Roo in flightapp-gwt are examples of places in GWT. The following figure shows the attributes defined by the ProxyPlace and ProxyListPlace classes:

Activities and places

The ProxyPlace corresponds to a place in the 'detail' display region and ProxyListPlace corresponds to a place in the Master display region. When a ProxyPlace instance is created, it knows the EntityProxy for which the place instance is being created (identified by proxyClass attribute), the operation to be performed (identified by operation attribute) on the EntityProxy, and the unique identifier (identified by proxyId attribute of type EntityProxyId) of the EntityProxy. Similarly, when ProxyListPlace is created, it knows about the EntityProxy for which the place instance is being created (identified by the proxyType attribute).

Each display region is associated with an ActivityMapper , which maps each Place in the display region to an Activity. It is created by implementing GWT's ActivityMapper interface. ActivityMapper defines a single method getActivity(Place place), which returns an activity corresponding to a place. In the flightapp-gwt application, ApplicationMasterActivities is an ActivityMapper for the Master display region and ApplicationDetailsActivities is an ActivityMapper for the Detail display region. The following class diagram shows the ActivityMappers created by Roo in the flightapp-gwt project:

Activities and places

This figure shows that Roo creates FlightActivitiesMapper and FlightDescriptionActivitiesMapper classes corresponding to the Flight and FlightDescription JPA entities. Even though these activity mappers don't implement GWT's ActivityMapper interface, they act as activity mappers in the flightapp-gwt application. These activity mappers return Activity instances specific to the EntityProxy. The getActivity method of an <entity-name>ActivitiesMapper class accepts a ProxyPlace (which represents a place in the Detail display region) argument and returns an Activity for that place. The ApplicationDetailsActivities activity mapper (which applies to the Detail display region of the web UI) is responsible for creating the FlightActivitiesMapper and FlightDescriptionActivities instances depending upon the JPA entity on which the user actions are to be performed. The ApplicationMasterActivities returns either the FlightListActivity or FlightDescriptionListActivity instance, depending upon the JPA entity selected from the Master display region.

An ActivityManager is associated with a display region and starts and stops an activity when a user navigates from one place to another. It is created by creating an instance of GWT's ActivityManager class by passing an ActivityMapper instance and an EventBus instance. In flightapp-gwt, ActivityManager is created for both 'master' and Detail display region when the run method of ScaffoldDesktopApp is executed, as shown in the following sequence diagram:

Activities and places

Dealing with entity proxy-specific processing

Roo creates an abstract generic class, ApplicationEntityTypesProcessor<T>, for dealing with different entity proxies in the scaffolded GWT application. ApplicationEntityTypesProcessor<T> is implemented by classes that perform a functionality based on entity proxy type. The following code listing shows the ApplicationEntityTypesProcessor<T> class of the flightapp-gwt project:

ApplicationEntityTypesProcessor.java
package sample.roo.flightapp.client.managed.request;

public abstract class ApplicationEntityTypesProcessor<T> {

  private final T defaultValue;

  private T result;

  private static void process(
    ApplicationEntityTypesProcessor<?> processor, 
    Class<?> clazz) {
    if (FlightProxy.class.equals(clazz)) {
      processor.handleFlight((FlightProxy) null);
      return;
    }
    if (FlightDescriptionProxy.class.equals(clazz)) {
     processor.handleFlightDescription(
                 (FlightDescriptionProxy) null);
     return;
    }
    processor.handleNonProxy(null);  
  }
  ....

  public abstract void handleFlight(FlightProxy proxy);

  public abstract void 
     handleFlightDescription(FlightDescriptionProxy proxy);

  public T process(Class<?> clazz) {
    setResult(defaultValue);
    ApplicationEntityTypesProcessor.process(this, clazz);
    return result;
  }
  ....
}

ApplicationEntityTypesProcessor<T> class represents a generic class. The handleFlight and handleFlightDescription methods are defined as abstract methods; therefore, subclasses need to provide implementation of these methods.

Note

You'll find an abstract handleXXX method defined for each entity proxy in the ApplicationEntityTypesProcessor<T> class.

The result attribute identifies the result to return when the public process method of the ApplicationEntityTypesProcessor<T> class is invoked. Note that the return type of the result attribute is a generic type—determined by the generic type associated with the class. The public process method accepts the class or object of the entity proxy as the argument and internally invokes the private process method, which in turn calls the handleFlight or handleFlightDescription method, depending upon the entity proxy object or class.

So, what is the handleXXX method expected to do in the implementation class? It simply sets a return value, which is returned when the public process method is called. Let's see this in the context of an example.

We mentioned earlier that the ApplicationDetailsActivities class represents an ActivityMapper for the Detail display region of the Roo-generated GWT application. The ApplicationDetailsActivity returns a FlightActivityMapper (specific to the FlightProxy entity proxy) or FlightDescriptionActivityMapper instance (specific to the FlightDescriptionProxy entity proxy) by implementing the ApplicationEntityTypesProcessor<T> class.

Note

ApplicationDetailsActivities extends the ApplicationDetailsActivities_Roo_Gwt class, which actually implements the ActivityMapper GWT interface.

The following code shows how ApplicationDetailsActivities makes use of the ApplicationEntityTypesProcessor<T> class:

ApplicationDetailsActivities_Roo_Gwt.java
public Activity getActivity(Place place) {
 ....
 final ProxyPlace proxyPlace = (ProxyPlace) place;
   return new ApplicationEntityTypesProcessor<Activity>() {
   @Override
   public void handleFlight(FlightProxy proxy) {
     setResult(new FlightActivitiesMapper(requests, 
          placeController).getActivity(proxyPlace));
   }

   @Override
   public void handleFlightDescription(FlightDescriptionProxy 
       proxy) {
    setResult(new FlightDescriptionActivitiesMapper(requests, 
       placeController).getActivity(proxyPlace));
   }
 }.process(proxyPlace.getProxyClass());
}

In this code, the following are the important points to note:

  • ApplicationEntityTypesProcessor<T> is associated with Activity type.
  • The handleFlight method sets the return value to the Activity returned from FlightActivityMapper.
  • The handleFlightDescription method sets the return value to the Activity returned from FlightDescriptionActivityMapper.
  • The process method of ApplicationEntityTypesProcessor<T> is invoked at the end to obtain the return value set by the handleFlight or handleFlightDescription method.

This code showed that ApplicationEntityTypesProcessor<T> generic class is associated with an Activity type and is used to retrieve Activity specific to FlightProxy or FlightDescriptionProxy. Similarly, Roo-generated GWT code makes use of ApplicationEntityTypesProcessor<T> class to perform other entity proxy-specific processing, like rendering the list of entities in the Master display region.

There's more...

Let's now look at:

  • How to compile and run the Roo-generated GWT application in an embedded Jetty container
  • How to access the mobile version of the Roo-generated flightapp-gwt application
  • Round-tripping support in the Roo-generated GWT application
  • How to enable remote logging

Compiling and running the GWT application in an embedded Jetty container

In the Development Mode, the GWT application is not compiled into JavaScript. You can compile the GWT application into JavaScript and run it using an embedded Jetty container by executing mvn jetty:run-exploded command, as shown here:

C:
oo-cookbookch05-gwt> mvn jetty:run-exploded

You can now access the flightapp-gwt application by entering the following URL: http://localhost:8080/flightapp-gwt/index.html

Accessing the mobile version of the GWT application

If you have compiled and deployed the flightapp-gwt application using the mvn jetty:run-exploded command, then access the mobile version of the flightapp-gwt application using the following URL:

http://localhost:8080/flightapp-gwt/index.html&m=true

If you are running the flightapp-gwt application in Development Mode, then use the following URL:

http://127.0.0.1:8888/ApplicationScaffold.html?gwt.codesvr=127.0.0.1:9997&m=true

In either case, you'll see the mobile version of the flightapp-gwt application, as shown here:

Accessing the mobile version of the GWT application

In this screenshot, the Flights and FlightDescriptions options are clickable, and by selecting them you can get started with performing CRUD operations on JPA entity instances.

Round-tripping support for GWT applications

If you add, modify, or delete any field from a JPA entity in the Roo-scaffolded GWT application, then Roo makes the necessary changes to GWT artifacts accordingly. To see Roo's round-tripping support for the scaffolded GWT application, start the Roo shell from the root directory of the flightapp-gwt project and add an aircraftModel field to the FlightDescription entity using the field command or by editing the FlightDescription.java file directly from your IDE. In response to the addition of the aircraftModel attribute, the Roo shell shows the following actions taken by Roo:

Updated ...FlightDescriptionProxy.java
Updated ...FlightDescriptionListView_Roo_Gwt.java
Updated ...FlightDescriptionDetailsView_Roo_Gwt.java
Updated ...FlightDescriptionDetailsView.ui.xml
Updated ...FlightDescriptionEditView_Roo_Gwt.java
Updated ...FlightDescriptionEditView.ui.xml
...

The output shows that Roo updates the GWT entity proxy, FlightDescriptionProxy, and other GWT artifacts to reflect the modification to the FlightDescription JPA entity. The other important thing to notice is that most of the modifications are limited to *_Roo_Gwt.java files—files that are managed by Roo. So, if you make changes to files that don't follow the naming convention *_Roo_Gwt.java, then such changes will be preserved by Roo (except in the case that you are modifying Java files in the *.client.managed.* package). In the Roo-scaffolded GWT application, *_Roo_Gwt.java files are equivalent to *_Roo_*.aj AspectJ ITD files, that is, Roo attempts to minimize the impact on the scaffolded GWT code by only modifying *_Roo_Gwt.java files.

Enabling remote logging

The GWT logging framework emulates Java Logging API, making it possible to log messages from the Java code that resides in the sample.roo.client package and its sub-packages. As we mentioned earlier, Java classes contained inside the sample.roo.client package and its sub-packages are translated into JavaScript by the GWT compiler. The remote logging capability in GWT enables client code to send log messages to the server-side logging infrastructure. To configure remote logging, set the value of the gwt.logging.simpleRemoteHandler property to ENABLED in the ApplicationScaffold.gwt.xml file:

<set-property name="gwt.logging.simpleRemoteHandler" 
       value="ENABLED"/>

The above configuration enables remote logging of messages. GWT provides a RemoteLoggingServiceImpl servlet, which acts as a handler for logging messages received from the client-side. You'll need to configure RemoteLoggingServiceImpl servlet in your web.xml file, as shown here:

<servlet>
  <servlet-name>remoteLogger</servlet-name>
  <servlet-class>
com.google.gwt.logging.server.RemoteLoggingServiceImpl
  </servlet-class>
</servlet>
	
<servlet-mapping>
  <servlet-name>remoteLogger</servlet-name>
  <url-pattern>
     /applicationScaffold/remote_logging
  </url-pattern>
</servlet-mapping>

The important point to note is that the RemoteLoggingServiceImpl servlet should be mapped to the /<module_name>/remote_logging URL.

As the method names of the Java class in the client-side are obfuscated when the GWT compiler converts them into JavaScript, you need to resymbolize or deobfuscate them by setting the following properties in the ApplicationScaffold.gwt.xml file:

<set-property name="compiler.emulatedStack" value="true" />
<set-configuration-property   
  name="compiler.emulatedStack.recordLineNumbers" 
  value="true" />
<set-configuration-property 
  name="compiler.emulatedStack.recordFileNames" 
  value="true" />

Also, you'll need to create a symbol maps directory using the –extra option of GWT compiler and place it inside a directory accessible to the server-side code, like the WEB-INF/classes directory of the generated WAR file.

See also

  • Refer to the Auto-generating Spring MVC controllers and JSPX views from JPA entities recipe in Chapter 4, Web Application Development with Spring Web MVC for details on how Roo scaffolds a Spring MVC application from JPA entities
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.187.62