TurboManage

David Chandler's Journal of Java Web and Mobile Development

  • David M. Chandler


    Web app developer since 1994 and Google Cloud Platform Instructor now residing in Colorado. Besides tech, I enjoy landscape photography and share my work at ColoradoPhoto.gallery.

  • Subscribe

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 223 other followers

  • Sleepless Nights…

    March 2011
    S M T W T F S
     12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
  • Blog Stats

    • 1,031,149 hits

Archive for March 25th, 2011

Using GWT RequestFactory with Objectify

Posted by David Chandler on March 25, 2011

I’ve put together a little sample project using GWT’s RequestFactory with Objectify. It’s not very sophisticated, but should get you started.

RequestFactory is a relatively new GWT feature that makes it especially easy to create data-oriented services. In this example, we’ll create a RequestFactory service backed by Objectify to manage arbitrary lists of items such as a ToDo list, grocery list, etc.

Domain Model

First, let’s look at our domain classes (ItemList, ListItem, and AppUser):

ItemList.java

package com.turbomanage.listwidget.domain;

import java.util.List;

import javax.persistence.Embedded;

import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.turbomanage.listwidget.shared.proxy.ItemListProxy.ListType;

/**
 * POJO that represents a list of items such as a ToDo list.
 * The items are stored as an embedded object.
 */
@Entity
public class ItemList extends DatastoreObject
{
	private String name;
	private Key<AppUser> owner;
	private ListType listType;
	@Embedded
	private List<ListItem> items;
        // Getters and setters omitted
}

DatastoreObject.java:

package com.turbomanage.listwidget.domain;

import javax.persistence.Id;
import javax.persistence.PrePersist;

public class DatastoreObject
{
	@Id
	private Long id;
	private Integer version = 0;

	/**
	 * Auto-increment version # whenever persisted
	 */
	@PrePersist
	void onPersist()
	{
		this.version++;
	}
	// Getters and setters omitted
}

ItemList is an Objectify @Entity. In order for it to work as an entity with RequestFactory, it must have ID and version properties, which are inherited from the DatastoreObject convenience class. Items in the list are stored as an embedded object using the @Embedded JPA annotation supported by Objectify. I could store the items in a separate entity in order to minimize the cost of the query to retrieve just the list names; however, most of the lists will contain only a few items so this serves our example better. Because it is embedded, ListItem is not an entity for purposes of Objectify or RequestFactory, just a simple value type.

ListItem.java:

package com.turbomanage.listwidget.domain;

import java.util.Date;

/**
 * POJO that represents an item in a list.
 */
public class ListItem // POJO
{
	private String itemText;
	private Date dateCreated;
        // Getters and setters omitted
}

AppUser represents a user of our application. It’s an Objectify @Entity and is referenced by the ItemList entity.

package com.turbomanage.listwidget.domain;

import javax.persistence.Entity;

/**
 * An application user, named with a prefix to avoid confusion with GAE User type
 */
@Entity
public class AppUser extends DatastoreObject
{
	private String email;

	public AppUser()
	{
		// No-arg constructor required by Objectify
	}
	// Getters and setters omitted
}

Persistence Layer

The persistence layer on the server side is just a standard ObjectifyDAO that handles all the CRUD operations. It has a static initializer that registers the entity types (ItemList and AppUser–for a later post). ItemListDao extends the generic ObjectifyDao and provides a bit of logic to restrict listAll() to those lists owned by the current user.

ItemListDao.java:

package com.turbomanage.listwidget.server.service;

import java.util.List;

import com.googlecode.objectify.Key;
import com.turbomanage.listwidget.domain.AppUser;
import com.turbomanage.listwidget.domain.ItemList;

public class ItemListDao extends ObjectifyDao<ItemList>
{
	@Override
	public List<ItemList> listAll()
	{
		return this.listAllForUser();
	}

	/**
	 * Wraps put() so as not to return a Key, which RF can't handle
	 *
	 * @param obj
	 */
	public void save(ItemList list)
	{
		AppUser loggedInUser = LoginService.getLoggedInUser();
		list.setOwner(loggedInUser);
		this.put(list);
	}

	public ItemList saveAndReturn(ItemList list)
	{
		AppUser loggedInUser = LoginService.getLoggedInUser();
		list.setOwner(loggedInUser);
		Key<ItemList> key = this.put(list);
		try
		{
			return this.get(key);
		}
		catch (Exception e)
		{
			throw new RuntimeException(e);
		}
	}

	/**
	 * Remove a list. Since items are embedded, they are removed automatically.
	 *
	 * @param list
	 */
	public void removeList(ItemList list)
	{
		this.delete(list);
	}
}

ItemListDao wraps the put() method from ObjectifyDao with a save() method that returns void. This is because put() returns a Key to the newly persisted object, but RequestFactory on the client doesn’t know about about types such as com.googlecode.objectify.Key that exist only on the server (remember that the GWT compiler requires Java source for all types used on the client).

Through the magic of RequestFactory, we can expose ItemListDao directly as a service to be called from the GWT client. This is simpler than GWT-RPC, which requires each service impl to extend RemoteServiceServlet, and much less code than the Command pattern, which typically utilizes Action, Result, and handler classes for each service method (albeit the Command pattern still gives you the most application control over caching, batching, and the like). All that’s needed is a trivial ServiceLocator class which is used by RequestFactory to request an instance of any RequestFactory service.

DaoServiceLocator.java:

package com.turbomanage.listwidget.server.locator;

import com.google.gwt.requestfactory.shared.ServiceLocator;

/**
 * Generic locator service that can be referenced in the @Service annotation
 * for any RequestFactory service stub
 *
 * @author turbomanage
 */
public class DaoServiceLocator implements ServiceLocator {
	public Object getInstance(Class<?> clazz) {
		try {
			return clazz.newInstance();
		} catch (InstantiationException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}
}

Unlike GWT-RPC, RequestFactory services do not directly implement the service interface. As we will see shortly, this is because RequestFactory service interfaces are intended only for the client side and do not use server-side entity types directly. Services do have to provide all the methods in the client-side interface; however, this is enforced via run-time validation, not at compile time as with GWT-RPC. The loss of some compile-time safety is one disadvantage of the RequestFactory approach.

Proxy Interfaces

When using an object-relational mapping framework like Objectify, entities typically contain annotations and other code from the ORM framework for which the Java source is not available to the GWT compiler. Therefore, the same entity representation cannot be used on both client and server. This problem can be partially solved by using the GWT super-src capability to supply a GWT-safe implementation of server-side classes. Objectify does this for its Key type and annotations so the same annotated domain class can be used on both client and server with GWT-RPC, and Gilead offers something similar for Hibernate.

Another approach to the problem is to use the DTO pattern. However, this has two main disadvantages. First, you now have two different representations of each entity class and must manually keep the code in sync. Second, every service call must copy data from the real entity to its client-safe DTO and vice versa. Again, there are frameworks to help with this, but it’s a pain.

The RequestFactory approach solves the entity / DTO problem in a unique way that overcomes the weaknesses of the classic DTO pattern. With RequestFactory, you declare a proxy interface for each entity. Unlike a classic DTO, the proxy interface exposes only method signatures so there are no implementation details to keep in sync. Furthermore, RequestFactory automatically populates client objects from the corresponding entities on the server and vice versa. Here are our proxy interfaces:

ItemListProxy.java:

package com.turbomanage.listwidget.shared.proxy;

import java.util.List;

import com.google.gwt.requestfactory.shared.ProxyFor;
import com.turbomanage.listwidget.domain.ItemList;
import com.turbomanage.listwidget.server.locator.ObjectifyLocator;

@ProxyFor(value = ItemList.class, locator = ObjectifyLocator.class)
public interface ItemListProxy extends DatastoreObjectProxy
{
	// Note: enums work!
	public enum ListType {NOTES, TODO}

	String getName();
	void setName(String name);
	List<ListItemProxy> getItems();
	ListType getListType();
	AppUserProxy getOwner();
	void setListType(ListType type);
	void setItems(List<ListItemProxy> asList);
}

Because ItemList is a Datastore entity with ID and version properties, ListItemProxy extends the EntityProxy interface. The @ProxyFor annotation specifies the domain type on the server, and the locator attribute specifies an entity locator class (see next section). Note that entities can define their own enum types, and they must be declared in the proxy interface, not the entity itself, in order for the enum type to be available on the client.

ListItemProxy.java:

package com.turbomanage.listwidget.shared.proxy;

import java.util.Date;

import com.google.gwt.requestfactory.shared.ProxyFor;
import com.google.gwt.requestfactory.shared.ValueProxy;
import com.turbomanage.listwidget.domain.ListItem;

@ProxyFor(value = ListItem.class)
public interface ListItemProxy extends ValueProxy
{
	String getItemText();
	void setItemText(String itemText);
	Date getDateCreated();
}

ListItem is not an entity. It doesn’t have ID and version properties because it is embedded within the ItemList entity. Therefore, ListItem extends the ValueProxy interface, which can be used for any “value type” (non-entity).

AppUserProxy.java:

package com.turbomanage.listwidget.shared.proxy;

import com.google.gwt.requestfactory.shared.ProxyFor;
import com.turbomanage.listwidget.domain.AppUser;
import com.turbomanage.listwidget.server.locator.ObjectifyLocator;

@ProxyFor(value=AppUser.class, locator=ObjectifyLocator.class)
public interface AppUserProxy extends DatastoreObjectProxy
{
	String getEmail();
}

Finally, AppUserProxy is an entity so it extends EntityProxy.

Generic Entity Locator

RequestFactory requires each entity to provide four Locator methods. These are used by RequestFactoryServlet to obtain an instance of an entity in order to populate it with property values from the client request before invoking the service method. Fortunately, the required methods can be implemented in a separate class which can be shared by all entities. Here is the generic ObjectifyLocator used with all the entities and specified in the @ProxyFor annotation for each entity proxy above.

package com.turbomanage.listwidget.server.locator;

import com.google.gwt.requestfactory.shared.Locator;
import com.googlecode.objectify.helper.DAOBase;
import com.turbomanage.listwidget.domain.DatastoreObject;

/**
 * Generic @Locator for objects that extend DatastoreObject
 */
public class ObjectifyLocator extends Locator<DatastoreObject, Long>
{
	@Override
	public DatastoreObject create(Class<? extends DatastoreObject> clazz)
	{
		try
		{
			return clazz.newInstance();
		} catch (InstantiationException e)
		{
		  throw new RuntimeException(e);
		} catch (IllegalAccessException e)
		{
			throw new RuntimeException(e);
		}
	}

	@Override
	public DatastoreObject find(Class<? extends DatastoreObject> clazz, Long id)
	{
		DAOBase daoBase = new DAOBase();
		return daoBase.ofy().find(clazz, id);
	}

	@Override
	public Class<DatastoreObject> getDomainType()
	{
		// Never called
		return null;
	}

	@Override
	public Long getId(DatastoreObject domainObject)
	{
		return domainObject.getId();
	}

	@Override
	public Class<Long> getIdType()
	{
		return Long.class;
	}

	@Override
	public Object getVersion(DatastoreObject domainObject)
	{
		return domainObject.getVersion();
	}
}

ObjectifyLocator makes use of the fact that all the entities extend DatastoreObject, which defines an ID property of type Long. If your entities don’t extend a common base class or interface, you can still use a generic entity locator by using reflection to call the getId() and getVersion() methods on the underlying entity.

RequestFactory Interface

The heart of RequestFactory is the interface in which you define your services. Each service stub is an interface that extends RequestContext having one or more service methods, and for each service you define an accessor method in the main RequestFactory interface. If you have a small number of services, you can define everything in one class as below.

ListWidgetRequestFactory.java:

package com.turbomanage.listwidget.shared.service;

import java.util.List;

import com.google.gwt.requestfactory.shared.Request;
import com.google.gwt.requestfactory.shared.RequestContext;
import com.google.gwt.requestfactory.shared.RequestFactory;
import com.google.gwt.requestfactory.shared.Service;
import com.turbomanage.listwidget.server.locator.DaoServiceLocator;
import com.turbomanage.listwidget.server.service.ItemListDao;
import com.turbomanage.listwidget.shared.proxy.ItemListProxy;

public interface ListwidgetRequestFactory extends RequestFactory
{
	/**
	 * Service stub for methods in ItemListDao
	 * 
	 * TODO Enhance RequestFactory to enable service stubs to extend a base interface
	 * so we don't have to repeat methods from the base ObjectifyDao in each stub
	 */
	@Service(value = ItemListDao.class, locator = DaoServiceLocator.class)
	interface ItemListRequestContext extends RequestContext
	{
		Request<List<ItemListProxy>> listAll();
		Request<Void> save(ItemListProxy list);
		Request<ItemListProxy> saveAndReturn(ItemListProxy newList);
		Request<Void> removeList(ItemListProxy list);
	}
	
	ItemListRequestContext itemListRequest();
}

ItemListRequestContext is a service stub and itemListRequest() is its accessor method. The @Service annotation specifies the implementation class on the server, and the locator attribute specifies a ServiceLocator class from which an instance of the service may be obtained–in this case, our generic DaoServiceLocator shown above.

Service method argument and return types use the proxy interfaces, not the real entity types. Server implementations use the real types and RequestFactory automatically maps between the two. In addition, all service methods return a Request object parameterized with the real return type (or rather, its proxy type). Server implementations return the real entity type directly. Services that have no return type should return Request in the client interface. The disparity between entity types on client and server prevents services from directly implementing the service interface as with GWT-RPC.

Again, note that we’re able to use a DAO directly as our service implementation. Furthermore, as of GWT 2.3, a service can be inherited from a base class (in this case, ObjectifyDao) as per this issue. This is a really powerful capability, as it allows you to implement the common CRUD operations in a single class on the server with no boilerplate code for each service impl. However, on the client side, service stubs cannot yet extend a base interface other than RequestContext, so each service stub must declare methods inherited from a base class as well as those implemented in the service class.

Wiring and Startup

Starting up your RequestFactory service layer in the client is as simple as

private final ListwidgetRequestFactory rf = GWT.create(ListwidgetRequestFactory.class);

In the sample app, I’ve done this in a factory class so it will be available to all presenter classes.

Using RequestFactory

First, let’s look at creating a new ItemList and persisting it to the server. Here is an excerpt from ListsActivity which creates a new instance of ItemListProxy and sends it to the server:

	public void persistList(String listName)
	{
		final ListwidgetRequestFactory rf = this.clientFactory
				.getRequestFactory();
		ItemListRequestContext reqCtx = rf.itemListRequest();
		final ItemListProxy newList = reqCtx.create(ItemListProxy.class);
		newList.setName(listName);
		newList.setListType(ListType.TODO);
		reqCtx.saveAndReturn(newList).fire(new Receiver<ItemListProxy>()
		{
			@Override
			public void onSuccess(final ItemListProxy savedList)
			{
				// Refresh table
				listDataProvider.getData();
				...
			}
		});
	}

Since ItemListProxy is an interface, how can we get an instance of it? By calling the create() method inherited from RequestContext. RequestFactory uses the new AutoBeans framework in GWT, which can be used to create an instance of any interface. Using AutoBeans, RequestFactory registers a visitor object for each setter in order to track all changes to the instance. This is what allows RequestFactory to send only changes to the server with each request.

Once we have an instance of our proxy, we can populate it using its setter methods and call the service method, which returns a Request but doesn’t actually do anything yet. In order to invoke the method on the server, we must fire() the Request and pass it a new Receiver object to handle the callback. Receiver is analogous to GWT-RPC’s AsyncCallback, but is hooked in via the return type (Request) instead of an additional service method argument as with GWT-RPC.

Now let’s look at a query to retrieve all ItemLists. Here is the getData() method from the AsyncDataProvider in ListsActivity that updates the CellTable showing all lists.

		private void getData()
		{
			logger.warning("getting data");
			// To retrieve relations and value types, use .with()
			Request<List<ItemListProxy>> findAllReq = rf.itemListRequest()
					.listAll().with("owner");
			// Receiver specifies return type
			findAllReq.fire(new Receiver<List<ItemListProxy>>()
			{
				@Override
				public void onSuccess(List<ItemListProxy> response)
				{
					updateRowCount(response.size(), true);
					updateRowData(0, response);
				}
			});
		}

The listAll() service call is similar to saveAndReturn() above with one important addition: the with(“owner”) method. This retrieves the related AppUser entity (using its proxy type, of course). The with() method specifies the bean-style name of a property to be populated with the request and is required to retrieve entity properties that are themselves proxy types (EntityType or ValueType), as RequestFactory does not retrieve related entities automatically. You can pass a comma-separated list of property names (“owner,creator,…”) and use dot notation to specify nested objects (“owner.address.city”).

Note: this post isn’t about CellTables, but observe that both updateRowData() and updateRowCount() are required to properly update the table when using an AsyncDataProvider.

Finally, let’s look at an example of updating an object previously returned from the server. Here is an excerpt from EditListActivity:

		public void update(int index, ListItemProxy item, final String newText)
		{
			ItemListRequestContext reqCtx = clientFactory.getRequestFactory()
					.itemListRequest();
			ListItemProxy editItem = reqCtx.edit(item);
			editItem.setItemText(newText);
			editList = reqCtx.edit(editList);
			editList.getItems().set(index, editItem);
			// Save the list since items are embedded
			reqCtx.save(editList).fire(new Receiver<Void>()
			{
				@Override
				public void onSuccess(Void response)
				{
					eventBus.fireEvent(new MessageEvent(newText + " updated",
							MessageType.INFO));
				}
			});
		}

This update() method changes the text of an item in an ItemList and takes an ItemListProxy argument that represents an ItemList previously returned from the server. Before any changes can be made to it, we must call RequestContext.edit() in order to make it mutable and allow RequestFactory to track changes. Then we can getItems(), set the new item text, and save the modified ItemList.

Summary

The combination of RequestFactory and Objectify makes it possible to write a service layer for GWT and App Engine with very little boilerplate code. Hopefully this will help you get started. Comments welcome, as well as contributions to the sample project referenced throughout. Enjoy!

Posted in AppEngine, Google Web Toolkit | 73 Comments »

 
%d bloggers like this: