Deploying a GWT application on GAE

GAE (Google App Engine) is the cloud computing platform from Google that provides the infrastructure for deploying your web applications. In this recipe, we'll look at how Roo simplifies developing an application for GAE. We'll also see how a Roo-scaffolded GWT application is created and deployed on GAE. In the Deploying Spring Web MVC applications on GAE recipe, we'll see a Spring Web MVC application that can be deployed on GAE.

Getting ready

If you only want to run the GWT application locally using App Engine SDK for Java, then you don't need to sign up with Google App Engine and create an application identifier. If you want to deploy the application on GAE, follow the steps mentioned here to create an application identifier for your application.

  1. Sign-up for a free Google App Engine account by going to the following URL: http://appengine.google.com. Once you are signed in, you'll see the following welcome page:
    Getting ready
  2. Now, you need to create an application identifier that uniquely identifies your application and is required for deploying your applications on GAE. Click the Create Application button, which will ask you to select your country information and mobile number to generate a verification code, as shown here:
    Getting ready
  3. Once you have provided the verification code that you received via SMS, you can create the application identifier as shown in the following screenshot:
    Getting ready
  4. Enter a unique value for the Application identifier field and enter a value for Application title. As the given screenshot shows, the application identifier name is prepended to .appspot.com to form the URL to access your application. So, if your unique identifier is myappid, then after deploying the application on GAE you can access it by going to http://myappid.appspot.com.

Now, we are all set to create our GWT application, which we want to deploy to GAE.

Create a sub-directory ch06-gae-gwt inside the C: oo-cookbook directory and start the Roo shell from C: oo-cookbookch06-gae-gwt.

How to do it...

To create a Roo-scaffolded GWT application and deploy it on GAE, follow the steps given here:

  1. Create flightapp-gae-gwt project using project command:
    ... roo> project --topLevelPackage sample.roo.flightapp --java 6 --projectName flightapp-gae-gwt
    
  2. Use persistence setup command to setup DataNucleus as persistence provider and set GOOLE_APP_ENGINE as the database. The applicationId argument is optional and if you only want to test the application locally, then you don't need to specify it.
    ... roo> persistence setup --provider DATANUCLEUS --database GOOGLE_APP_ENGINE --applicationId <your application identifier>
    
    Created SRC_MAIN_WEBAPPWEB-INFappengine-web.xml
    Created SRC_MAIN_WEBAPPWEB-INFlogging.properties
    Updated SRC_MAIN_RESOURCESlog4j.properties
    
    Updated ROOTpom.xml [Added property 'gae.home' with value '${user.home}/.m2/repository/com/google/appengine/appengine-java-sdk/1.4.0/appengine-java-sdk-1.4.0']
    
    Updated ROOTpom.xml [Added dependencies com.google.appengine.orm:datanucleus-appengine:1.0.7.final..]
    
    Updated ROOTpom.xml [Added plugin maven-gae-plugin]
    Updated ROOTpom.xml [Added plugin maven-datanucleus-plugin]
    

    For brevity, the given output only shows GAE-specific actions that are performed by Roo.

  3. Create the FlightDescription JPA entity and add fields to it, as shown here:
    ... roo> entity --class ~.domain.FlightDescription --identifierType java.lang.Long --testAutomatically
    
    ... roo> field string --fieldName origin --notNull
    ... roo> field string --fieldName destination --notNull
    ... roo> field number --type java.lang.Float --fieldName price --notNull
    
  4. Scaffold GWT application using the gwt setup command:
    ... roo> gwt setup
    
  5. If you want to import flightapp-gae-gwt into Eclipse IDE , execute the perform eclipse command:
    ... roo> perform eclipse
    
  6. Exit the Roo shell and execute the gae:run goal of Maven GAE Plugin to run the flightapp-gae-gwt application locally on the Google App Engine development web server that comes bundled with App Engine SDK for Java, as shown here:
    C:
    oo-cookbookch06-gae-gwt> mvn gae:run
    
  7. The Maven GAE Plugin was configured in the pom.xml file of the flightapp-gae-gwt project when we executed the persistence setup command. A successful start of development server will show the following message: The server is running at http://localhost:8080/
  8. Now, open your favorite web browser and go to http://localhost:8080 to access the GWT flightapp-gae-gwt web application, which allows you to perform CRUD operations on the FlightDescription JPA entity, as shown here:
    How to do it...
  9. After you have tested the application locally, you can deploy the flightapp-gae-gwt application to GAE by executing the gae:deploy goal of Maven GAE Plugin, as shown in the following command. If you had not created application identifier and specified it as the value of the applicationId argument of the persistence setup command, then this step will fail.
    C:
    oo-cookbookch06-gae-gwt> mvn gae:deploy
    
    Beginning server interaction for <your-application-identifier>...
    ...
    Email: <email-id>@gmail.com
    Password for <email-id>@gmail.com:
    ...
    
  10. As the given output suggests, while deploying your application you need to provide your e-mail address and corresponding password with which you signed up with Google App Engine.
  11. Once the flightapp-gae-gwt application is successfully deployed on GAE, you can access it via the following URL:http://<your-application-identifier>.appspot.com

    As the flightapp-gae-gwt application is a secured application, you'll be required to log in using your Google Accounts or OpenID credentials.

How it works...

The persistence setup command determines that the target deployment environment is Google App Engine if the value of database argument is GOOGLE_APP_ENGINE . If the value of the database argument is GOOGLE_APP_ENGINE, then it becomes mandatory to specify DATANUCLEUS as the value of the provider argument.

You might be wondering why it's mandatory to specify the persistence provider as DataNucleus and GOOGLE_APP_ENGINE as the database. Well, Google App Engine uses a proprietary schema-less object datastore, BigTable, for persisting application data. Java applications can access the BigTable datastore using JPA or JDO via DataNucleus App Engine plugin (this is not a Maven plugin but a DataNucleus plugin). DataNucleus is a separate product that allows access to datastores (which includes RDBMS, Excel, XML, LDAP, and so on) using JDO and JPA APIs. Also, the Datanucleus App Engine plugin is developed and maintained by Google and is specifically meant for use with GAE. So, you can say that by using DataNucleus, developers can use JDO or JPA APIs in their applications for accessing or persisting data, irrespective of the datastore(s) used by the application. This could be particularly useful in case your application makes use of distinct types of data sources.

In response to persistence setup, Roo performs the following actions:

  • Creates the appengine-web.xml file in the WEB-INF directory
  • Adds dependency on DataNucleus App Engine plugin in pom.xml
  • Configures Maven GAE plugin in pom.xml
  • Configures Maven DataNucleus plugin in pom.xml
  • Creates logging.properties configuration for Java logging API
  • Creates the persistence.xml file in the META-INF directory, which provides persistence provider (DataNucleus in our case) information
  • Creates the applicationContext.xml file in META-INF/spring directory, which contains transaction manager and JPA EntityManagerFactory definitions

Let's now look at appengine-web.xml file and the plugins configured by Roo.

appengine-web.xml

The appengine-web.xml is a configuration file specific to GAE, which specifies application identifier, version of the application, static and resource files in the application, system properties, and so on. The following listing shows the content of appengine-web.xml generated by Roo for the flightapp-gae-gwt project:

<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
  <application>myappid</application>
  <version>1</version>
  <sessions-enabled>true</sessions-enabled>
  <system-properties>
    <property name="java.util.logging.config.file" 
      value="WEB-INF/logging.properties"/>
    <property 
      name="appengine.orm.disable.duplicate.emf.exception"   
      value="false"/>
  </system-properties>
</appengine-web-app>

<appengine-web-app> is the root element of appengine-web.xml. <application> element specifies the application identifier, the value of which comes from the applicationId argument of persistence setup command.

<version> element specifies the version identifier of the application code that you are deploying on GAE. The application version identifier is particularly useful when you want to test your deployed application on GAE before making it the default version, which is accessible to the users. Let's say, you have version 1 of Flight Booking application already deployed on GAE. Now, you make some changes to your application code in order to fix bugs or add/modify application features. To test your modified application on GAE, change the <version> element to a different value, let's say 2, and deploy the application on GAE using gae:deploy goal (more on this later) of maven GAE plugin. GAE uses the value of <version> element to determine if the existing application code needs to be replaced by the newly deployed application code or to create a new version of the application code. As the version of newly deployed Flight Booking application code is 2 and the existing application code had version 1, GAE creates a new version of the application, which you can access by going to the following URL: http://<app-version>.latest.<application-id>.appspot.com. Assuming that the application identifier of the Flight Booking application is myappid and the version deployed is 2, the URL becomes http://2.latest.myappid.appspot.com.

The <session-enabled> element enables GAE's session persistence feature, that is, session data is persisted into App Engine's datastore. So, if you set session data in your web application using setAttribute method of HttpSession, then it is stored in App Engine's datastore. As the session objects are persisted, the objects that you set in the session must implement the java.io.Serializable interface.

The <system-properties> element defines the system properties available to the application. App Engine supports application logging via Logging API of Java (refer to the java.util.logging package). The logging configuration is read from the file, which is specified as the value of java.util.logging.config.file system property. In the appengine-web.xml file of the flightapp-gae-gwt project, the <property> sub-element of the <system-properties> element specifies that the value of the java.util.logging.config.file system property is WEB-INF/logging.properties. Similarly, appengine.orm.disable.duplicate.emf.exception system property with value true instructs App Engine not to raise exceptions when the application attempts to create multiple javax.persistence.EntityManagerFactory instances for a persistence unit. By default, App Engine expects that only a single instance of EntityManagerFactory exists per persistence unit, and an attempt to create a duplicate EntityManagerFactory instance results in exception.

Maven GAE plugin

The Maven GAE plugin simplifies developing Java applications for App Engine by providing goals, which help with downloading and unzipping App Engine SDK, starting and stopping App Engine development server, deploying application to App Engine, retrieving application logs from App Engine, and so on.

The following listing shows Maven GAE plugin specific configuration as defined in pom.xml of the flightapp-gae-gwt project:

<project ...>
    ...
 <properties>
    ...
   <gae.home>
     ${user.home}/.m2/repository/com/google/appengine/
     appengine-java-sdk/1.4.0/appengine-java-sdk-1.4.0
   </gae.home>
 </properties>
 ...
 <plugin>
   <groupId>net.kindleit</groupId>
   <artifactId>maven-gae-plugin</artifactId>
   <version>0.5.7</version>
   <configuration>
     <unpackVersion>1.4.0</unpackVersion>
   </configuration>
   <executions>
     <execution>
       <phase>validate</phase>
       <goals>
         <goal>unpack</goal>
       </goals>
     </execution>
   </executions>
  </plugin>
</project>   

The gae.home property specifies the location of the unpacked version of App Engine SDK.

The sub-element <unpackVersion > of plugin <configuration> specifies the version of the plugin to unpack. The <execution> element specifies that the gae:unpack goal of the Maven GAE plugin is executed in the validate build lifecycle phase. The validate build lifecycle phase is the one in which Maven validates that the project is correct and all the required information to make the build is available. The gae:unpack goal unpacks the GAE SDK to the location specified by the gae.home property.

Note

Spring Roo 1.1.3 generates pom.xml, which makes project dependent on GAE SDK 1.4.0 and Maven GAE plugin 0.5.7, as shown in the listing we just saw. At the time of writing this book, the current version of GAE SDK is 1.5.1 and that of Maven GAE plugin is 0.8.4. To change the version of GAE SDK, modify the gae.home property. And, to change the version of Maven GAE plugin, simply modify the value of the <version> sub-element of the <plugin> element, which configures Maven GAE plugin. If you are using Spring Roo 1.1.5, then the project already uses GAE SDK 1.5.1 and Maven GAE plugin 0.8.4.

The following table specifies some of the goals defined by the Maven GAE plugin:

Goal

Description

gae:run

Runs the project locally on the GAE development web server

gae:deploy

Uploads the application to the GAE server

gae:logs

Retrieves application logs from the GAE server

gae:version

Shows the plugin and GAE SDK versions

Let's now look at the Maven DataNucleus plugin and the role it plays in the GAE application.

Maven DataNucleus plugin

To make a class persistent, DataNucleus expects that the class must implement the PersistenceCapable interface of JDO. Why are we talking about JDO now? Well, it's because DataNucleus support for JPA is built on top of JDO. This means that even if you have annotated your domain classes with the @Entity JPA annotation, DataNucleus can't persist them. To free developers from implementing the PersistenceCapable interface in their domain classes, DataNucleus provides an enhancer, which works on the compiled domain classes and implements PersistenceCapable interface via bytecode enhancement. The Maven DataNucleus plugin provides a datanucleus:enhance goal, which enhances JPA classes annotated with the @Entity annotation. The following code shows this:

   <plugin>
    <groupId>org.datanucleus</groupId>
    <artifactId>maven-datanucleus-plugin</artifactId>
    <version>1.1.4</version>
    <configuration>
     <mappingIncludes>**/*.class</mappingIncludes>
     <enhancerName>ASM</enhancerName>
     <api>JPA</api>
     <mappingExcludes>**/GaeAuthFilter.class</mappingExcludes>
    </configuration>
    <executions>
     <execution>
      <phase>compile</phase>
      <goals>
       <goal>enhance</goal>
      </goals>
     </execution>
    </executions>
   </plugin>

In the plugin configuration, the <execution> element specifies that datanucleus:enhance goal is executed in the compile build lifecycle phase. So, when Java source files are compiled, the Maven DataNucleus plugin enhances the compiled JPA domain classes. The <mappingIncludes> element specifies the classes that should be included for enhancement. The <mappingExcludes> specifies the classes that should not be considered for enhancement.

The <api> element specifies whether the enhancement is for JPA or JDO. As we are using JPA in the flightapp-gae-gwt project, the value of the <api> element is jpa. The <enhancerName> element specifies ASM as the value, which basically refers to the ASM framework (http://asm.ow2.org/) used by DataNucleus for enhancing the bytecode.

Let's now look at the FlightDescription entity that was generated by Roo:

Persistent entities

As GAE datastore is not a relational database, you'll find that some of the concepts that apply while using JPA with relational databases will not apply when using JPA with GAE datastore.

The following code shows the FlightDescription JPA entity generated by Roo.

@RooJavaBean
@RooToString
@RooEntity
public class FlightDescription {
    
    @NotNull
    private String origin;

    @NotNull
    private String destination;

    @NotNull
    private Float price;
}

The given code shows that we are not using @Column and @Table JPA annotations to identify the table into which the entity instances are saved and the table column to which a persistent entity field maps to. As the GAE datastore is schema-less, you don't need to specify the table or column information. You can still use the JSR 303 annotations, such as @NotNull in this code, for validating your domain objects.

The following code shows the FlightDescription_Roo_Entity.aj AspectJ ITD file:

privileged aspect FlightDescription_Roo_Entity {
    
    declare @type: FlightDescription: @Entity;
    
    @PersistenceContext
    transient EntityManager FlightDescription.entityManager;
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long FlightDescription.id;
    
    @Version
    @Column(name = "version")
    private Integer FlightDescription.version; 
    ...
}

It is interesting to note that the primary key generation strategy is specified as GenerationType.IDENTITY. In GAE, this means that the identifier value is not assigned to the FlightDescription entity until the associated transaction completes or you explicitly call the flush method of EntityManager.

Let's now look at how the flightapp-gae-gwt application ensures that only authenticated users can access it.

Authentication and authorization in GAE applications

As with any other web application, web request security constraints for the flightapp-gae-gwt application are specified in the web.xml file of the application. The following listing shows the <security-constraint> element of the web.xml file of flightapp-gae-gwt application:

<security-constraint>
  <display-name>...</display-name>
  <web-resource-collection>
    <web-resource-name>...</web-resource-name>
    <url-pattern>*.html</url-pattern>
  </web-resource-collection>
  <auth-constraint>
    <role-name>*</role-name>
  </auth-constraint>
</security-constraint>

The <url-pattern> specifies that any URL that matches the *.html pattern is secured and would require authentication. In the case of the flightapp-gae-gwt application, the home page of the application is index.html, which is secured according to the URL pattern specified by the <url-pattern> element. As the entry into the flightapp-gae-gwt application is restricted, users need to authenticate using their Google Accounts credentials before accessing the application. The <role-name> element specifies * as the value, which means that any authenticated user can access the application. If you want your application on GAE to be accessible to anonymous users also, then remove the <security-constraint> element from the web.xml file.

If you remove the <security-constraint> element from web.xml of the flightapp-gae-gwt project and upload the application to GAE servers, you'll find that an attempt to access the flightapp-gae-gwt application still asks for authentication. The reason behind this behavior is that the sample.roo.flightapp.server.gae.GaeAuthFilter servlet filter configured in the Roo-generated web.xml file. GaeAuthFilter is a Roo-generated servlet filter, which checks if the user is logged in or not. If the user is not logged in, then it redirects the user to the Google Accounts sign in page. The following code listing from GaeAuthFilter.java shows the GaeAuthFilter class:

import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory; 

public class GaeAuthFilter implements Filter {
 ...

 public void doFilter(...) ... {
  UserService userService = 
     UserServiceFactory.getUserService();
  ...

  if (!userService.isUserLoggedIn()) {
   String requestUrl = request.getHeader("requestUrl");
   if (requestUrl == null) {
    requestUrl = request.getRequestURI();
   }
   response.setHeader("login", 
      userService.createLoginURL(requestUrl));
   response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
   return;
  }
 ...
}

In the given code, UserServiceFactory is a GAE-specific class whose getUserService method returns an instance of UserService. The UserService interface defines methods to create login and logout URLs, get details of the currently signed in user, check if the user is logged in, and so on. In this code, GaeAuthFilter checks if the user is logged in by calling the isUserLoggedIn() method. If the user is not logged in, GaeAuthFilter makes use of the createLoginURL(..) method of UserService to create a login URL and redirects the user to it.

Another interesting point to notice about GaeAuthFilter is its mapping. The following listing shows the mapping of GWT's RequestFactoryServlet and GaeAuthFilter in the web.xml file:

<filter-mapping>
  <filter-name>GaeAuthFilter</filter-name>
  <url-pattern>/gwtRequest/*</url-pattern>
</filter-mapping>

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

The <url-pattern> elements of GaeAuthFilter and GWT's RequestFactoryServlet show that a web request sent to RequestFactoryServlet is intercepted by GaeAuthFilter. This ensures that if the session expires, the application user is redirected to the Google Accounts sign-in page.

UserService provides a getCurrentUser method that returns a User object if the user is logged in. The User object contains user id, nickname, and e-mail information of the authenticated user. If your application requires capturing more information about the user, such as their preferences, address, and so on, then you need to save such information as part of your application data.

By default, the only role defined by App Engine is admin, which you can specify as the value of the <role-name> element. The admin role is assigned to users that are application administrators, that is, users that you add using the Admin Console of GAE. Admin Console gives you complete control over your deployed application on GAE. It allows you to administer your datastore, test different versions of your application, create application, and so on. You can access Admin Console by going to the following URL: http://appengine.google.com. You can use the isUserAdmin method of UserService to determine if the logged in user belongs to admin role or not.

Even though GAE supports only admin role, you can still incorporate role-based security in your App Engine applications by introducing application-specific roles. You can save application-specific role information as part of application data. For instance, you can use Spring Security framework with your GWT or Spring Web MVC application to implement web request security and method-level security based on the roles assigned to users.

..................Content has been hidden....................

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