GWT Project Using Google Charts API

I’ve recently implemented two websites that use Google Web Toolkit, Google App Engine, and Google Charts API.

GWT and GAE are ideally suited to use Google Charts API and the charts API is a free, fast performing, HTTP based tool for serving colorful charts to your end users.

My sites http://www.fishfeedingchart.com (Best Fishing Times) and http://www.deerfeedingchart.com (Best Times to Hunt Deer) are geared towards outdoors men and women and they provide fishing and hunting predictions based on moon and sun data, which is then served up in a chart format by Google Charts API.

Implementing a GWT/GAE project with Google Charts API is very simple. Just create an Image widget, set the URL to a valid Google Charts API URL and you are off and running.

Here’s some sample Java code to get you started:

package com.example.myproject.client;

import java.util.ArrayList;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;

public class ChartsAPIExample implements EntryPoint {

    public void onModuleLoad() {
        VerticalPanel vp = new VerticalPanel();
        Image chartImage = new Image();

        vp.add(chartImage);

        // Let's create an array with and put some data in it
        ArrayList<Integer> data = new ArrayList<Integer>();

        data.add(10);
        data.add(14);
        data.add(25);
        data.add(30);
        data.add(28);
        data.add(20);
        data.add(11);
        data.add(15);

        // set up some strings for the URL parameters
        // sample chart URL
        // http://chart.apis.google.com/chart?chxt=y&chs=300x225&cht=lc&chd=t:1,4,5,6
        String axis = "chxt=y";
        String size = "&chs=300x225";
        String lineChart = "&cht=lc";

        // loop our data array and construct a comma separated string of data
        String strData = "&chd=t:";

        for (int i = 0; i < data.size(); i++) {

            strData += data.get(i);

            // if not at last record add a comma to the string
            if (i < data.size() - 1) {
                strData += ",";
            }

        }

        String strURL = "http://chart.apis.google.com/chart?" + axis + size
                + lineChart + strData;

        System.out.println("strURL is:  " + strURL);
        // Set the Google Charts API URL as the URL of the Image widget
        chartImage.setUrl(strURL);

        // Add the VerticalPanel to the GWT HTML
        RootPanel.get("container").add(vp);

    }
}


The GWT project will display the Google Chart that was generated from the URL:



There are many different parameters to can be used in the Chart URL to control chart type, styles, labels, and much more.  For more information, check out the Google Charts API home page:  http://code.google.com/apis/chart/image/

Happy coding! Click here to continue reading...

Blobstore Tutorial: A GWT application for storing and serving images using the GAE Blobstore

The Google App Engine Blobstore Service is a valuable service that allows you to upload and store files such as pictures, spreadsheets, and other documents in the Cloud. For example, a picture gallery web application could allow users to upload and share their favorite pictures.

The Blobstore is certainly valuable, but it is equally complicated and difficult to use. In this tutorial, I will attempt to unlock the potential of the Blobstore by creating a GWT application that allows users to upload an image and meta-data describing the image and serve the image and meta-data back to the user.

This example uses Objectify to store the image meta-data as an entity in the datastore.  Configuring your project to use Objectify is easy, just make sure to do the following:
  • Inherit Objectify in your <project>.gwt.xml file
  • Add the Objectify JAR to your war/WEB-INF/lib directory
  • Add the Objectify JAR as a reference library to your Eclipse project
For more information on using Objectify see the "Use Objectify to store data in the Google App Engine Datastore" tutorial.

I drew up the following diagram to give an overview of a typical project that uses the Blobstore to store and serve blobs.



There are a lot of moving parts in this example, but a typical flow would be something like this.

A user in the Web Client (1) wishes to upload a picture, so an RPC call is made through the Interfaces (3) to the Blob Service (4) to start a Blobstore session and get a Blobstore upload URL.  This upload URL is returned to the client and applied to the FormPanel (2) and will be called when the FormPanel is submitted.

Submitting the FormPanel (which includes a FileUpload widget) makes an HTTP POST call to the Upload Service (6), which uploads the Blob to the Blobstore (7).  In the doPost method of the Upload Service, an Entity (8) is instantiated and put in the Datastore (5) so that some meta-data about the picture can be stored.

The Upload Service returns a unique identifier for the picture's meta-data to the Web Client and an RPC call is made through the Interfaces and the Blob Service to retrieve the Entity object from the Datastore.  Wrapped in the Entity is an ImageURL which will be placed in an Image widget and displayed to the user.  The ImageURL also points to the Blob Service and calls the doGet method to serve the Image.

Web Client Class

In the Web Client, we have a FormPanel, which contains a couple of text boxes, a FileUpload widget, a submit button, and a FlexTable.

The FormPanel and FileUpload widgets are required for using the Blobstore and they provide you the ability to make an HTTP POST call and browse for a file.

The submit button will Submit the form and the FlexTable will be used to serve the image and it's meta-data back to the user.


package com.example.myproject.client;

import com.example.myproject.client.entities.Picture;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FileUpload;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteEvent;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;

public class BlobstoreExample implements EntryPoint {

  // You must use a FormPanel to create a blobstore upload form
  final FormPanel uploadForm = new FormPanel();

  // Use an RPC call to the Blob Service to get the blobstore upload url
  BlobServiceAsync blobService = GWT.create(BlobService.class);

  VerticalPanel mainVerticalPanel = new VerticalPanel();
  HorizontalPanel hp1 = new HorizontalPanel();
  HorizontalPanel hp2 = new HorizontalPanel();
  HTML titleLabel = new HTML("Title");
  HTML descriptionLabel = new HTML("Description");
  TextBox titleTextBox = new TextBox();
  TextBox descriptionTextBox = new TextBox();
  FileUpload upload = new FileUpload();
  Button submitButton = new Button("Submit");

  FlexTable resultsTable = new FlexTable();

  @Override
  public void onModuleLoad() {

    hp1.add(titleLabel);
    hp1.add(titleTextBox);
    hp2.add(descriptionLabel);
    hp2.add(descriptionTextBox);

    mainVerticalPanel.add(hp1);
    mainVerticalPanel.add(hp2);
    mainVerticalPanel.add(upload);

    mainVerticalPanel.add(submitButton);
    mainVerticalPanel.add(resultsTable);

    hp1.setSpacing(5);
    hp2.setSpacing(5);
    mainVerticalPanel.setSpacing(5);

    uploadForm.setWidget(mainVerticalPanel);

    // The upload form, when submitted, will trigger an HTTP call to the
    // servlet.  The following parameters must be set
    uploadForm.setEncoding(FormPanel.ENCODING_MULTIPART);
    uploadForm.setMethod(FormPanel.METHOD_POST);

    // Set Names for the text boxes so that they can be retrieved from the
    // HTTP call as parameters
    titleTextBox.setName("titleTextBox");
    descriptionTextBox.setName("descriptionTextBox");
    upload.setName("upload");
    
    RootPanel.get("container").add(uploadForm);

    submitButton.addClickHandler(new ClickHandler() {
      @Override
      public void onClick(ClickEvent event) {

        blobService
            .getBlobStoreUploadUrl(new AsyncCallback<String>() {

              @Override
              public void onSuccess(String result) {
                // Set the form action to the newly created
                // blobstore upload URL
                uploadForm.setAction(result.toString());

                // Submit the form to complete the upload
                uploadForm.submit();
                uploadForm.reset();
              }

              @Override
              public void onFailure(Throwable caught) {
                caught.printStackTrace();
              }
            });

      }
    });

    uploadForm
        .addSubmitCompleteHandler(new FormPanel.SubmitCompleteHandler() {
          @Override
          public void onSubmitComplete(SubmitCompleteEvent event) {
            
            //The submit complete Event Results will contain the unique
            //identifier for the picture's meta-data.  Trim it to remove
            //trailing spaces and line breaks
            getPicture(event.getResults().trim());

          }

        });

  }

  public void getPicture(String id) {

    //Make another call to the Blob Service to retrieve the meta-data
    blobService.getPicture(id, new AsyncCallback<Picture>() {

      @Override
      public void onSuccess(Picture result) {
        
        Image image = new Image();
        image.setUrl(result.getImageUrl());
        
        //Use Getters from the Picture object to load the FlexTable
        resultsTable.setWidget(0, 0, image);
        resultsTable.setText(1, 0, result.getTitle());
        resultsTable.setText(2, 0, result.getDescription());
        
      }

      @Override
      public void onFailure(Throwable caught) {
        caught.printStackTrace();
      }
    });

  }
}


Synchronous and Async Interfaces

If you've been working with GWT for any period of time, then you probably know that you have to use a Synchronous interface and an Asynchronous interface to make an RPC call to the server side.

This is the Sync interface:


package com.example.myproject.client;

import com.example.myproject.client.entities.Picture;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath("blobservice")
public interface BlobService extends RemoteService {

  String getBlobStoreUploadUrl();

  Picture getPicture(String id);

}


This is the Async interface:

package com.example.myproject.client;

import com.example.myproject.client.entities.Picture;
import com.google.gwt.user.client.rpc.AsyncCallback;

public interface BlobServiceAsync {

  void getBlobStoreUploadUrl(AsyncCallback<String> callback);

  void getPicture(String id, AsyncCallback<Picture> callback);

}


Blob Service

The Blob Service is a GWT server side class that extends RemoteServiceServlet and it contains three methods.  The getBlobStoreUploadUrl method starts a Blobstore session and returns an Upload URL string that is added to the client side FormPanel.  The getPicture method is used later and returns an entity object from the Datastore that wraps the meta-data for the blob.  The meta-data is used in the client to serve the image itself and the title and description of the image.  The doGet method is called by the Image widget and serves the image from the blob store.

You may wonder why the Upload Service is not included in the Blob Service.  The reason is that a FormPanel cannot be used to submit to a class that extends RemoteServiceServlet because the doPost method cannot be overridden.  FormPanels submit to a class that extends HttpServlet so that the doPost method can be overridden with your code.

package com.example.myproject.server;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.example.myproject.client.BlobService;
import com.example.myproject.client.entities.Picture;
import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyService;

@SuppressWarnings("serial")
public class BlobServiceImpl extends RemoteServiceServlet implements
    BlobService {

  //Start a GAE BlobstoreService session and Objectify session
  BlobstoreService blobstoreService = BlobstoreServiceFactory
      .getBlobstoreService();
  Objectify ofy = ObjectifyService.begin();
  
  //Register the Objectify Service for the Picture entity
  static {
    ObjectifyService.register(Picture.class);
  }

  //Generate a Blobstore Upload URL from the GAE BlobstoreService
  @Override
  public String getBlobStoreUploadUrl() {

    //Map the UploadURL to the uploadservice which will be called by
    //submitting the FormPanel
    return blobstoreService
        .createUploadUrl("/blobstoreexample/uploadservice");
  }

  //Retrieve the Blob's meta-data from the Datastore using Objectify
  @Override
  public Picture getPicture(String id) {
    
    long l = Long.parseLong(id);
    Picture picture = ofy.get(Picture.class, l);
    return picture;
  }
  
  //Override doGet to serve blobs.  This will be called automatically by the Image Widget
  //in the client
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {

        BlobKey blobKey = new BlobKey(req.getParameter("blob-key"));
        blobstoreService.serve(blobKey, resp);

  }
}


Picture Entity

The Picture class is an entity class that wraps meta-data about the picture so that it can be stored in the Datastore.  The Picture object contains the Image's blob-key, the title of the image, and the description of the image.  The blob-key is used to serve the picture from the Blobstore.  Getters and Setters are used to conveniently set data in the object and get it back.

package com.example.myproject.client.entities;

import java.io.Serializable;

import javax.persistence.Id;

@SuppressWarnings("serial")
public class Picture implements Serializable {

  @Id
  public Long id;
  public String title;
  public String description;
  public String imageUrl;

  public void setTitle(String title) {
    this.title = title;
  }

  public String getTitle() {
    return title;
  }

  public void setDescription(String description) {
    this.description = description;
  }

  public String getDescription() {
    return description;
  }

  public String getImageUrl() {
    return imageUrl;
  }

  public void setImageUrl(String imageUrl) {
    this.imageUrl = imageUrl;

  }

  public void setId(Long id) {
    this.id = id;
  }

  public Long getId() {
    return id;
  }
}


Upload Service

The Upload Service is called when the FormPanel is submitted and will submit the Blob to the Blobstore.  At the same time, we are submitting some meta-data about the Blob to the Datastore, and responding with the meta-data unique ID.

package com.example.myproject.server;

import java.io.IOException;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.example.myproject.client.entities.Picture;
import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyService;

//The FormPanel must submit to a servlet that extends HttpServlet  
//RemoteServiceServlet cannot be used
@SuppressWarnings("serial")
public class UploadServiceImpl extends HttpServlet {

  //Start Blobstore and Objectify Sessions
  BlobstoreService blobstoreService = BlobstoreServiceFactory
      .getBlobstoreService();
  Objectify ofy = ObjectifyService.begin();

  static {
    ObjectifyService.register(Picture.class);
  }

  //Override the doPost method to store the Blob's meta-data
  public void doPost(HttpServletRequest req, HttpServletResponse res)
      throws ServletException, IOException {

    Map<String, BlobKey> blobs = blobstoreService.getUploadedBlobs(req);
    BlobKey blobKey = blobs.get("upload");

    //Get the paramters from the request to populate the Picture object
    Picture picture = new Picture();
    picture.setDescription(req.getParameter("descriptionTextBox"));
    picture.setTitle(req.getParameter("titleTextBox"));
    //Map the ImageURL to the blobservice servlet, which will serve the image
    picture.setImageUrl("/blobstoreexample/blobservice?blob-key=" + blobKey.getKeyString());

    ofy.put(picture);

    //Redirect recursively to this servlet (calls doGet)
    res.sendRedirect("/blobstoreexample/uploadservice?id=" + picture.id);
  }

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {

    //Send the meta-data id back to the client in the HttpServletResponse response
    String id = req.getParameter("id");
    resp.setHeader("Content-Type", "text/html");
    resp.getWriter().println(id);

  }

}


Web.xml

Web.xml is a critical piece of the puzzle for all GAE servlets and I have found that many of the errors I make are in web.xml.  Make sure that fully qualified class name and URL pattern are correct and remember the following:
  • For GWT servlets, the URL must map to the @RemoteServiceRelativePath annotation in your Synchronous interface
  • When you get a Blobstore Upload URL, map it to the URL in your uploadServlet.
  • When you serve a Blob, map the URL to your blobServlet.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

  <!-- Servlets -->

  <servlet>
    <servlet-name>blobServlet</servlet-name>
    <servlet-class>com.example.myproject.server.BlobServiceImpl</servlet-class>
  </servlet>

  <servlet>
    <servlet-name>uploadServlet</servlet-name>
    <servlet-class>com.example.myproject.server.UploadServiceImpl</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>blobServlet</servlet-name>
    <url-pattern>/blobstoreexample/blobservice</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>uploadServlet</servlet-name>
    <url-pattern>/blobstoreexample/uploadservice</url-pattern>
  </servlet-mapping>

  <!-- Default page to serve -->
  <welcome-file-list>
    <welcome-file>BlobstoreExample.html</welcome-file>
  </welcome-file-list>

</web-app>


Conclusion

This is what the application looks like in action:
 

Hopefully this tutorial will shed some light on using the GAE Blobstore.  When you are testing on your local development server, you can check the local datastore at http://localhost:8888/_ah/admin/datastore.
Please leave a comment if you have any questions or suggestions on improving this tutorial.
Click here to continue reading...

Automated email using a Google App Engine (GAE) Cron Job

It seems like Google thought of everything when it comes to developing robust web applications for the GAE.  One of the features that is extremely useful is the ability to configure Cron Jobs.  Cron Jobs are background jobs that can be run on a set schedule.  Since Cron Jobs invoke a servlet, the possibilities for this valuable feature are unlimited.

One common functional requirement for many web applications is the ability to send periodic emails to users.  This post will provide a complete working example for setting up a Cron Job to send automated emails.

There are 3 main steps required for setting up the Cron Job.


  1. Create a cron.xml file and add a Cron Job
  2. Update web.xml to point to a servlet
  3. Develop a servlet class that uses the JavaMail API to send an email
Create a cron.xml file and add a Cron Job

The first step in setting up automated email is to create a cron.xml file (if you don't already have one in your project).  The cron.xml file should be created in your project's war\WEB-INF directory.

In this file you can configure one or more cron jobs. When you deploy your application to the GAE, the cron jobs will be shown in your GAE dashboard.  At the time of this writing, Cron Jobs are not supported on the development server.

The cron.xml file is relatively straight forward and has the following elements:
  • url:  This is the url that will be invoked when the cron "wakes up" at it's scheduled time.  Use this url to point to a servlet which you configure in web.xml.
  • description:  This is a short description which will show up in your GAE console
  • schedule:  The schedule on which to run the cron job
  • timezone:  An optional element that can be set to tie the schedule to a particular timezone
Here's an example of a cron.xml file:
<?xml version="1.0" encoding="UTF-8"?>

<cronentries>

  <cron>
    <url>/cronexample</url>
    <description>Send an email to a user</description>
    <schedule>every 1 minutes</schedule>

<!-- Here's an example using timezone 

    <schedule>every day 01:00</schedule>
    <timezone>America/New_York</timezone> -->
  </cron>
  
</cronentries>

Update web.xml to point to a servlet

Server side workload in Google App Engine is all about servlets, so it's a good idea to familiarize your self with servlet technology and the associated Java classes.

Since we set up a cron that uses "/cronexample" as it's url, we need to set up a servlet in web.xml that uses the same url.  This servlet will call a servlet class which will send an email.
<servlet>
  <servlet-name>emailService</servlet-name>
  <servlet-class>com.example.server.EmailServiceImpl</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>emailService</servlet-name>
  <url-pattern>/cronexample</url-pattern>
</servlet-mapping>

Develop a servlet class that uses the JavaMail API to send an email

The final step is to develop the servlet class to send an email.  The cron job will execute the servlet's doGet method, so we will override that method in our class.

Also, one important point to remember is that the "From" email address must be an administrator of your GAE application.  If you do not want to use your personal gmail account as the "From" address, then you can either create another gmail account or use Google apps and create an email address for your domain.

The following is our servlet class.
package com.example.server;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

@SuppressWarnings("serial")
public class EmailServiceImpl extends RemoteServiceServlet {

  String adminEmail = "admin@example.com"; // insert your admin email address
                        // here
  Properties props = new Properties();
  Session session = Session.getDefaultInstance(props, null);

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    sendEmail();
  }

  public void sendEmail() {

    String msgBody = "This is a test email body";

    try {
      Message msg = new MimeMessage(session);
      try {
        msg.setFrom(new InternetAddress(adminEmail, "Administrator"));
      } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
      }
      msg.addRecipient(Message.RecipientType.TO, new InternetAddress(
          "test@example.com"));
      msg.setSubject("This is a test email subject");
      msg.setText(msgBody);  //TIP: use msg.setContent to send an HTML formatted email
      Transport.send(msg);

    } catch (AddressException e) {
      e.printStackTrace();
    } catch (MessagingException e) {
      e.printStackTrace();
    }

  }

}

Click here to continue reading...

A GWT Objectify example for retrieving data from the Google App Engine Datastore

In this post, I will expand on the "Use Objectify to store data from the Google App Engine Datastore" tutorial and demonstrate how to retrieve data from the Datastore and present it to the client.

I have found Objectify to be extremely useful and I use queries in several cases for my GAE application CedarLeaf.

If you remember from the previous post, all calls from the client to the Datastore must use a Service Interface, an Async Interface, and a server side Service "Impl" Java class.

For this example, we will perform the following steps:
  1. Get user input from a text box
  2. Using interfaces, call our servlet to perform an Objectify query using the user input
  3. Add objects retrieved from the Datastore to an ArrayList and return the ArrayList to the client
  4. Loop through the ArrayList and present data in the web application

    Let's add a few widgets to the Client Java class.  I will add a TextBox, a Button, and a FlexTable.  The FlexTable will be used to show the results of the Objectify query to the end user.

    package com.example.myproject.client;
    
    import java.util.ArrayList;
    
    import com.example.myproject.client.entities.Animal;
    import com.google.gwt.core.client.EntryPoint;
    import com.google.gwt.core.client.GWT;
    import com.google.gwt.event.dom.client.ClickEvent;
    import com.google.gwt.event.dom.client.ClickHandler;
    import com.google.gwt.user.client.rpc.AsyncCallback;
    import com.google.gwt.user.client.ui.Button;
    import com.google.gwt.user.client.ui.FlexTable;
    import com.google.gwt.user.client.ui.HTML;
    import com.google.gwt.user.client.ui.HorizontalPanel;
    import com.google.gwt.user.client.ui.RootPanel;
    import com.google.gwt.user.client.ui.TextBox;
    import com.google.gwt.user.client.ui.VerticalPanel;
    
    public class ObjectifyExample implements EntryPoint {
    
      // Create some fields for the UI
      VerticalPanel mainVerticalPanel = new VerticalPanel();
      HorizontalPanel hp1 = new HorizontalPanel();
      HorizontalPanel hp2 = new HorizontalPanel();
      HorizontalPanel hp3 = new HorizontalPanel();
      HTML colorLabel = new HTML("Color");
      HTML speciesLabel = new HTML("Species");
      TextBox colorTextBox = new TextBox();
      TextBox speciesTextBox = new TextBox();
      Button submit = new Button("Submit");
    
      TextBox searchTextBox = new TextBox();
      HTML searchLabel = new HTML("Enter a species to search for");
      Button search = new Button("Search");
      FlexTable searchResultsTable = new FlexTable();
    
      // Instantiate the interfaces to access methods in the interface
      private final PersistentServiceAsync persistentService = GWT
          .create(PersistentService.class);
    
      public void onModuleLoad() {
    
        hp1.add(colorLabel);
        hp1.add(colorTextBox);
        hp2.add(speciesLabel);
        hp2.add(speciesTextBox);
        hp3.add(searchLabel);
        hp3.add(searchTextBox);
        hp3.add(search);
    
        mainVerticalPanel.add(hp1);
        mainVerticalPanel.add(hp2);
        mainVerticalPanel.add(submit);
        mainVerticalPanel.add(hp3);
        mainVerticalPanel.add(searchResultsTable);
    
        hp1.setSpacing(5);
        hp2.setSpacing(5);
        hp3.setSpacing(5);
        mainVerticalPanel.setSpacing(5);
    
        search.addClickHandler(new ClickHandler() {
          public void onClick(ClickEvent event) {
    
            persistentService.searchAnimal(searchTextBox.getText(),
                new AsyncCallback<ArrayList<Animal>>() {
    
                  @Override
                  public void onFailure(Throwable caught) {
    
                    System.out
                        .println("PersistentService RPC call failed "
                            + caught);
    
                  }
    
                  public void onSuccess(ArrayList<Animal> result) {
    
                    int row = 0;
                    searchResultsTable.removeAllRows();
                    searchResultsTable.setText(0, 0, "Color");
                    searchResultsTable.setText(0, 1, "Species");
                    //loop the array list and user getters to add 
                    //records to the table
                    for (Animal animal : result) {
                      row = searchResultsTable.getRowCount();
                      searchResultsTable.setText(row, 0,
                          animal.getColor());
                      searchResultsTable.setText(row, 1,
                          animal.getSpecies());
    
                    }
    
                  }
    
                });
    
          }
        });
    
        RootPanel.get("container").add(mainVerticalPanel);
      }
    }
    

    In order to use a search string from the end user as part of our Objectify query, we will add a new method to our Interfaces and to our server side class.

    The method searchAnimal returns a type of ArrayList<Animal> and requires a search string parameter.

    Here's the new method in the Service Interface:
    package com.example.myproject.client;
    
    import java.util.ArrayList;
    
    import com.example.myproject.client.entities.Animal;
    import com.google.gwt.user.client.rpc.RemoteService;
    import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
    
    @RemoteServiceRelativePath("persistentservice")
    public interface PersistentService extends RemoteService {
      
      ArrayList<Animal> searchAnimal(String searchString);
      
    }
    

    ...and the Async Interface:
    package com.example.myproject.client;
    
    import java.util.ArrayList;
    
    import com.example.myproject.client.entities.Animal;
    import com.google.gwt.user.client.rpc.AsyncCallback;
    
    public interface PersistentServiceAsync {
    
      void searchAnimal(String searchString,
          AsyncCallback<ArrayList<Animal>> callback);
    
    }
    
    

    In the server side class, we will perform the Objectify query in the searchAnimal method and add the results to an ArrayList. The ArrayList is returned and passed back as the result of the client side RPC call.

    In the following class, the entity is registered directly in the servlet.  You may want to consider using a DAO (Data Access Object) to register your entities.  More information can be found on the Objectify wiki here.
    package com.example.myproject.server;
    
    import java.util.ArrayList;
    
    import com.example.myproject.client.PersistentService;
    import com.example.myproject.client.entities.Animal;
    import com.google.gwt.user.server.rpc.RemoteServiceServlet;
    import com.googlecode.objectify.Objectify;
    import com.googlecode.objectify.ObjectifyService;
    import com.googlecode.objectify.Query;
    
    
    @SuppressWarnings("serial")
    public class PersistentServiceImpl extends RemoteServiceServlet implements
    PersistentService {
      
      public ArrayList<Animal> searchAnimal(String searchString) {
        
        ObjectifyService.register(Animal.class);
        Objectify ofy = ObjectifyService.begin();
        
        Query<Animal> q = ofy.query(Animal.class).filter("species",
            searchString);
        
        ArrayList<Animal> animals = new ArrayList<Animal>();
        
        //Loop the query results and add to the array
        for (Animal fetched : q) {
          
          animals.add(fetched);
        }
        
        return animals;
        
      }
    
    }
    

    The results are returned to the client and displayed in the FlexTable.


    This is the complete source code for this tutorial:

    https://github.com/fishbonecloud/ObjectifyExample
    Click here to continue reading...

    An Objectify example to store data in the Google App Engine Datastore

    If you're like me, then you've searched for a complete working example using Objectify with Google App Engine to persist data and utilize the GAE datastore.  Objectify is relatively simple once you learn the basics and it will make your life much easier than using the standalone GAE SDK.

    I use Objectify to persist and query several entities for my GAE web application http://www.cedarleafmusic.com.  CedarLeaf is a free, easy to use service that allows users to track unreleased music and artists that they want to keep on the radar.

    The diagram below is a quick illustration of the way Objectify works with GAE.


    As you can see from the diagram, there are three Java Classes plus two Java Interfaces.  Also, your project's web.xml file must be configured to describe the servlet you are using.

    To summarize, the following steps are required to implement Objectify functionality in your GAE project:
    1. Add the Objectify Jar to your Eclipse project and add the Objectify Jar to your war/WEB-INF/lib directory
    2. Inherit Objectify in your <project>.gwt.xml file 
    3. Create an entity class.
    4. Create a Client Java class (this example uses GWT).
    5. Create a Service Interface.
    6. Create an Async Interface.
    7. Create a server side ServiceImpl class.
    8. Add a servlet to your web.xml file that points to your server side ServiceImpl class.

    Add the Objectify Jar to your Eclipse project and your war/WEB-INF/lib directory

    If you haven't done so already, download the latest Objectify Jar from the Objectify wiki.  Once you have the zip file downloaded, unzip it into a convenient place and open your project in Eclipse.

    Right-click on your project in Eclipse and choose Build Path > Configure Build Path.

    Select the Libraries tab and select the Add External JARs button to add the Objectify Jar to your project.



    Next, make sure to add the same JAR file to your project's war/WEB-INF/lib directory.  You can do this outside of Eclipse through your file system.

    Inherit Objectify in your <project>.gwt.xml file

    Back in Eclipse, open up your project's gwt.xml file.  This file is automatically created in all GWT projects.

    Add the following to the gwt.xml file:
      <inherits name="com.googlecode.objectify.Objectify" />
    

    Create an Entity class

    Entities are one of the key components of the GAE datastore.  You can think of an entity as a table and any fields you declare in your entity class as columns.

    There is one main rule to follow:  You must annotate one field with @id.

    When I create an entities, I like to add getters and setters.  These will come in handy very often in your application.  You can use Eclipse to automatically create your getters and setters.  Once you have declared your fields, right click in the source pane in Eclipse and choose Source > Generate Getters and Setters.

    Entities must implement Serializable or they cannot be passed as an object from the server to the client via RPC.

    The following is a basic example of an Entity class:
    //This package must be a sub package of client
    package com.example.myproject.client.entities;
    
    import javax.persistence.Entity;
    import javax.persistence.Id;
    
    @Entity
    public class Animal implements Serializable {
        
        @Id
        Long id;
        private String species;
        private String color;
    
        public void setSpecies(String species) {
            this.species = species;
        }
    
        public String getSpecies() {
            return species;
        }
    
        public void setColor(String color) {
            this.color = color;
        }
        
        public String getColor() {
            return color;
        }
        
    }
    
    
    Create a Client Java class

    In my example, my client class implements EntryPoint.  You will always have one class that implements entry point to bind to your module's main HTML page.

    My client class has some panels, text boxes, and a button to trigger the servlet call.  The client class will perform a servlet call through your service and async interfaces.  The interfaces must be instantiated by calling GWT.create.

    The following is my client class:
    package com.example.myproject.client;
    
    import com.google.gwt.core.client.EntryPoint;
    import com.google.gwt.core.client.GWT;
    import com.google.gwt.event.dom.client.ClickEvent;
    import com.google.gwt.event.dom.client.ClickHandler;
    import com.google.gwt.user.client.rpc.AsyncCallback;
    import com.google.gwt.user.client.ui.Button;
    import com.google.gwt.user.client.ui.HTML;
    import com.google.gwt.user.client.ui.HorizontalPanel;
    import com.google.gwt.user.client.ui.RootPanel;
    import com.google.gwt.user.client.ui.TextBox;
    import com.google.gwt.user.client.ui.VerticalPanel;
    
    public class ObjectifyExample implements EntryPoint {
    
        // Create some fields for the UI
        VerticalPanel mainVerticalPanel = new VerticalPanel();
        HorizontalPanel hp1 = new HorizontalPanel();
        HorizontalPanel hp2 = new HorizontalPanel();
        HTML colorLabel = new HTML("Color");
        HTML speciesLabel = new HTML("Species");
        TextBox colorTextBox = new TextBox();
        TextBox speciesTextBox = new TextBox();
        Button submit = new Button("Submit");
    
        // Instantiate the interfaces to access methods in the interface
        private final PersistentServiceAsync persistentService = GWT
                .create(PersistentService.class);
    
        public void onModuleLoad() {
    
            hp1.add(colorLabel);
            hp1.add(colorTextBox);
            hp2.add(speciesLabel);
            hp2.add(speciesTextBox);
    
            mainVerticalPanel.add(hp1);
            mainVerticalPanel.add(hp2);
            mainVerticalPanel.add(submit);
    
            hp1.setSpacing(5);
            hp2.setSpacing(5);
            mainVerticalPanel.setSpacing(5);
            
            
            //Add a click handler to the button.  The click handler will
            //make an RPC call using the PersistentService interfaces
            submit.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
    
                    persistentService.persistAnimal(colorTextBox.getText(),
                            speciesTextBox.getText(), new AsyncCallback<Void>() {
    
                                @Override
                                public void onFailure(Throwable caught) {
    
                                    System.out
                                            .println("PersistentService RPC call failed "
                                                    + caught);
    
                                }
    
                                @Override
                                public void onSuccess(Void result) {
    
                                    System.out
                                            .println("PersistentService RPC call succedded");
    
                                }
    
                            });
    
                }
            });
    
            RootPanel.get("container").add(mainVerticalPanel);
        }
    }
    
    

    Create a Service Interface, Async Interface, and ServiceImpl class

    Next we will create a Service Interface that will reside in our client package.  The Service Interface must contain an annotation that points to a servlet which will be configured in the web.xml file.

    Methods in the Service Interface must also exist in the Async Interface and the server side Service "Impl" class.

    Service Interface:
    package com.example.myproject.client;
    
    import com.google.gwt.user.client.rpc.RemoteService;
    import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
    
    //The RemoteServiceRelativePath annotation must match the servlet URL configured in
    //web.xml
    @RemoteServiceRelativePath("persistentservice")
    public interface PersistentService extends RemoteService {
        
        void persistAnimal(String color, String species) throws IllegalArgumentException;
        
    }
    

    Async Interface:
    package com.example.myproject.client;
    
    import com.google.gwt.user.client.rpc.AsyncCallback;
    
    public interface PersistentServiceAsync {
    
        void persistAnimal(String color, String species, AsyncCallback<Void> callback);
    
    }
    

    The Server side Service "Impl" class is where all server side processing occurs.  In this class, you can "put" records in the Datastore, query the Datastore, delete from the Datastore and do any number of additional server side operations such as consuming a web service or sending an email using JavaMail.

    In the following class, the entity is registered directly in the servlet.  You may want to consider using a DAO (Data Access Object) to register your entities.  More information can be found on the Objectify wiki here.

    Server side Service "Impl" class:
    package com.example.myproject.server;
    
    import com.example.myproject.client.PersistentService;
    import com.example.myproject.client.entities.Animal;
    import com.google.gwt.user.server.rpc.RemoteServiceServlet;
    import com.googlecode.objectify.Objectify;
    import com.googlecode.objectify.ObjectifyService;
    
    
    @SuppressWarnings("serial")
    public class PersistentServiceImpl extends RemoteServiceServlet implements
    PersistentService {
        
        //Method must be present in Service Interface and Async Interface
        public void persistAnimal(String color, String species) {
            
            //The Objectify service for the entity must be registered before any 
            //operations can be executed
            ObjectifyService.register(Animal.class);
            Objectify ofy = ObjectifyService.begin();
            
            Animal animal = new Animal();
            //Use setters to populate the object
            //the Id will be auto generated and does not need to be set
            animal.setColor(color);
            animal.setSpecies(species);
            
            ofy.put(animal);
            
        }
    
    }
    
    

    Add a servlet to your WEB.xml file

    The last step in setting up your application to use Objectify is to configure a servlet in your WEB.xml file.  For every servlet you want to use in your application, add a Servlet and a Servlet Mapping to this file.
      <servlet>
        <servlet-name>persistenceServlet</servlet-name>
        <servlet-class>com.example.myproject.server.PersistentServiceImpl</servlet-class>
      </servlet>
      
      <servlet-mapping>
        <servlet-name>persistenceServlet</servlet-name>
        <url-pattern>/objectifyexample/persistentservice</url-pattern>
      </servlet-mapping>
    

    Conclusion

    Now you are ready to use the extremely valuable Objectify extension for the Google App Engine.  When you are testing on your local development server, you can check the local datastore in the local dashboard at http://localhost:8888/_ah/admin/datastore.

    See the "A GWT Objectify example for retrieving data from the Google App Engine Datastore" tutorial for a Java example of retrieving data from the datastore and displaying it to the end user.

    This is the complete source code for this tutorial:

    https://github.com/fishbonecloud/ObjectifyExample

    Please leave a comment if you have any questions or suggestions on improving this tutorial.
    Click here to continue reading...