Google App Engine, JDO and GXT(Ext GWT) Grid - Make All These Working Together

Previously I have written some blogs about GXT Grid  where the data resides in the client side. But as per the request of my readers I feel the necessity of writing some tutorials with remote data store. And for this purpose I have chosen Google App Engine data store as the back-end with JDO implementation.

It requires a configuration file in the final WAR named  jdoconfig.xml which resides in the directory war/WEB-INF/classes/META-INF/. Eclipse creates this file as src/META-INF/jdoconfig.xml. This file is automatically copied into war/WEB-INF/classes/META-INF/ when you build your project. This file should contain the following contains.

<?xml version="1.0" encoding="utf-8"?>
<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig">

   <persistence-manager-factory name="transactions-optional">
       <property name="javax.jdo.PersistenceManagerFactoryClass"
           value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
       <property name="javax.jdo.option.ConnectionURL" value="appengine"/>
       <property name="javax.jdo.option.NontransactionalRead" value="true"/>
       <property name="javax.jdo.option.NontransactionalWrite" value="true"/>
       <property name="javax.jdo.option.RetainValues" value="true"/>
       <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/>
   </persistence-manager-factory>
</jdoconfig>

Here I am going to describe the way of loading a comment list from the App Engine data store and making the list viewable in the GXT Grid. So At first I need a POJO class to store and retrieve comments from the App Engine data store using the JDO API. Here is my Comments class.

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Comments {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;

    @Persistent
    @Column(name="comments")
    private String comments;

    @Persistent
    @Column(name="posted_by")
    private String postedBy;
    
    @Persistent
    @Column(name="posted_date")
    private Date postedDate;

    public Comments(String comments, String postedBy, Date date) {
        this.comments = comments;
        this.postedDate = date;
        this.postedBy = postedBy;
   }

    public Long getId() {
        return id;
    }

 public String getComments() {
  return comments;
 }

 public void setComments(String comments) {
  this.comments = comments;
 }

 public Date getPostedDate() {
  return postedDate;
 }

 public void setPostedDate(Date postedDate) {
  this.postedDate = postedDate;
 }

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

 public String getPostedBy() {
  return postedBy;
 }

 public void setPostedBy(String postedBy) {
  this.postedBy = postedBy;
 }
}

Now when you want to transfer data from server to client you need a DTO (Data Transfer Object). As I am going to show the comments in GXT Grid so my DTO has to inherit the properties of GXT's BaseModel. Here is the CommentModel class which will serve my purposes.

public class CommentModel extends BaseModel {

private static final long serialVersionUID = 1L;

public CommentModel(){}

public CommentModel(String comments, String postedBy, Date date) {
set("comments", comments);
set("postedDate", date);
set("postedBy",postedBy);
}

public String getComments() {
return (String) get("comments");
}

public String getPostedBy() {
return (String) get("postedBy");
}

public Date getStartingDate() {
return ((Date) get("postedDate"));
}
}

For any operation on data store you have to request the data store for the desired operation. Each request that uses the data store creates a new instance of the PersistenceManager class through the instance of  PersistenceManagerFactory class. As creating an instance of PersistenceManagerFactory class is a very costly process, I have used a Singleton class PMF for this purpose.

public final class PMF {
private static final PersistenceManagerFactory pmfInstance =
JDOHelper.getPersistenceManagerFactory("transactions-optional");

private PMF() {}

public static PersistenceManagerFactory get() {
return pmfInstance;
}
}

If you are familiar with GWT RPC Service then create a service named CommentService and add a method
List<CommentModel> getAllComment();. Here is the implementation of the method where JDOQL is used to write a query which returns all the comments from the data store.

public List<Commentmodel> getAllComment() 
{
   List<Commentmodel> commentList = new ArrayList<Commentmodel>();
   PersistenceManager pm = PMF.get().getPersistenceManager(); 
   try {
      String query = "select from " + Comments.class.getName()+" order by  postedDate desc"; 
      List<Comments> list = (List<Comments>) pm.newQuery(query).execute();
      if (!list.isEmpty()) {
         for (Comments c : list)
         {
            //convert from entity object to DTO
            commentList.add(CommentConverter.entityToModel(c));
         }
      }
   }catch (Exception ex) {}
   finally {
      pm.close();
   }
   return commentList;
}

CommentConverter is a utility class which converts an object of Comments class to an object of CommentModel class.

public class CommentConverter 
{
   public static CommentModel entityToModel(Comments comment)
   {
      CommentModel model = new CommentModel(comment.getComments(), comment.getPostedBy(), comment.getPostedDate());
      return model;
   }
} 

All the server side coding is done. Let's come to client side.

First create an instance of CommentServiceAsync.
public static final CommentServiceAsync commentService = GWT.create(CommentService.class);
 

Then create a ListStore of CommentModel, commentStore and call the getAllComment method of the commentService. In the onSuccess method add the returned list to the commentStore.
 final Label lbError = new Label();
  final ListStore<Commentmodel> commentStore = new ListStore<Commentmodel>();
  commentService.getAllComment(new AsyncCallback<List<Commentmodel>>() {

   @Override
   public void onFailure(Throwable caught) {
    lbError.setText("data loading failure");

   }

   @Override
   public void onSuccess(List<Commentmodel> result) {
    commentStore.add(result);

   }
  }); 

The rest of the tasks are very familiar with the GXT users. Creating a List of ColumnConfig, a Grid with the commentStore and  ColumnConfig lists, etc.

        List<Columnconfig> configs = new ArrayList<Columnconfig>();
        ColumnConfig column = new ColumnConfig();
        column.setId("comments");
        column.setHeader("Comments");
        column.setWidth(200);
        configs.add(column);

        column = new ColumnConfig("postedBy", "Posted By", 150);
        column.setAlignment(HorizontalAlignment.LEFT);
        configs.add(column);

        column = new ColumnConfig("postedDate", "Posting Date", 100);
        column.setAlignment(HorizontalAlignment.RIGHT);
        column.setDateTimeFormat(DateTimeFormat.getShortDateFormat());
        configs.add(column);

        ColumnModel cm = new ColumnModel(configs);

        final Grid<Commentmodel> grid = new Grid<Commentmodel>(commentStore, cm);
        grid.setBorders(true);
        grid.setAutoExpandColumn("comments");
        grid.setStyleAttribute("borderTop", "none");
        grid.setStripeRows(true);

        ContentPanel cp = new ContentPanel();
        cp.setBodyBorder(false);
        cp.setHeading("Grid with Pagination");
        cp.setButtonAlign(HorizontalAlignment.CENTER);
        cp.setLayout(new FitLayout());
        cp.setSize(700, 300);
        cp.add(grid);

That's all for today. You will get a live example of this tutorial here.
For more about Google App Engine and JDO visit this.

6 comments:

Anonymous October 21, 2010 at 5:57 AM  

Nice work! - Thankyou :)

Shams Zawoad Ratul October 21, 2010 at 7:16 AM  

Welcome:)

Anonymous November 12, 2010 at 9:58 PM  

Hi,
Thanks for this article. Very usefull.

Can you please show us how to put data back into the datastore? Suppose the user edited some properties in the form and the app must submit the changes to the datastore? How would you manage that?
I see the SelectionListener() but it does not save data back to the appengine datastore.

thank you

rrocampojr November 21, 2010 at 2:31 AM  

Sir, I'm new in GXT. I'm studying it for my project in school. Do you have a zip file of this program? There are times that I can't follow. Thank you very much.

Anonymous January 31, 2012 at 6:13 AM  

Hi.
nice tutorial, thx. question: and what if i need to transfer a listo to display it o a simplecombo box?, to what should i extend the DTO class?

Anonymous January 31, 2012 at 6:14 AM  

Hi.
nice tutorial, thx. question: and what if i need to transfer a listo to display it o a simplecombo box?, to what should i extend the DTO class?

Tags

Twitter Updates
    follow me on Twitter

    Followers

    There was an error in this gadget