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,029,259 hits

Archive for March, 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 »

A (partially) updated SliderBar for GWT 2.2

Posted by David Chandler on March 23, 2011

The GWT team announced a while back that most of the stuff in the GWT incubator would be moving to GWT proper (including the powerful new cell widgets) and the incubator project would no longer be supported. One of my favorite widgets, the SliderBar, has not yet been moved over. It looks like someone has posted an incubator jar for 2.2, but before I realized that, I extracted the SliderBar from the incubator code, simplified the resource packaging a bit, and updated all the deprecated classes to work with 2.2. It’s not implemented as a Cell so probably won’t play nice with CellTables, but it’s otherwise functional. You can find the code in my listwidget sample project.

To use it in your project, just pull in the source or export it as a jar and remember to put the following in your gwt.xml file:

<inherits name='com.google.gwt.gen2.gen2'/>

Happy sliding!

Posted in Google Web Toolkit | 12 Comments »

Working on GWT

Posted by David Chandler on March 18, 2011

Lately, I’ve been working on a GWT patch to enable server-side inheritance in RequestFactory services. Along the way, I’ve picked up a few tips about working on the GWT source itself and thought I’d share them with others who may be interested. (By the way, the Web-based code review tool at the patch link above is used by many teams at Google and is itself an open source project written by Guido vonRossum, creator of python. It runs on App Engine, and is a powerful tool for any open source project).

Check out the GWT source from SVN

> mkdir gwt
> cd gwt
> svn checkout https://google-web-toolkit.googlecode.com/svn/trunk/ trunk
> svn checkout https://google-web-toolkit.googlecode.com/svn/tools/ tools

Import the gwt-dev and gwt-user projects into Eclipse

In Eclipse, go to File | Import | Existing project and browse to gwt/trunk/eclipse. This will find many pre-built projects you can import. Check the boxes to import gwt-dev and gwt-user.

Configure Eclipse

  1. Go to Preferences | General > Workspace > Linked Resources and create a new resource named GWT_ROOT that points the trunk folder above.
  2. Go to Preferences | Java > Build path > Classpath variables and create a new variable named GWT_TOOLS that points to the tools folder above.

At this point, the gwt-dev and gwt-user projects in your workspace should build without errors. If you’re planning to contribute code to GWT, you’ll also want to import the GWT style formatter for Eclipse.

Run Tests

There are a couple gotchas to running the GWT unit tests in Eclipse. First, some tests seem to require more stack memory than the Eclipse defaults, so you’ll want to add the following parameter under the -vmargs in your eclipse.ini:

-Xss1024k

Second, any tests that extend GWTTestCase, whether directly or up the inheritance chain, must be run as GWT JUnit tests, not regular JUnit tests. Google Plugin for Eclipse automatically makes the “Run as GWT JUnit test” option available in the context menu for GWT projects, but gwt-user itself is not a GWT project. To run a test located in gwt-user as a GWT JUnit test, do the following:

  1. Right-click on the gwt-user project and go to Properties | Google | Web Toolkit. Check the box that says “Use Google Web Toolkit”. The version numbers will be grayed out, but that’s OK. Click OK.
  2. Right-click on the test you’re interested in, say, RequestFactoryJreTest, and select Run As | Run Configurations… In the dialog, click GWT JUnit Test and then the New icon in the upper left corner of the dialog. This will create a new launch config for the test. Click the Run button to run it.

For further reading, here are the detailed instructions on setting up Eclipse to work on GWT source. If you want to contribute a patch to GWT, please see the Making GWT Better page for further instructions.

Happy coding!

Posted in Google Web Toolkit | 3 Comments »

Ditch your land line with Google Voice and the OBI110

Posted by David Chandler on March 12, 2011

In tonight’s “Saturday evening post” you’ll learn why I’m one step closer to ditching our home phone number. For the last several months I’ve been making long distance calls with Gmail calling (ever tried that “Call phone” link in the Gchat sidebar?) It was especially valuable during my recent speaking trip to Sweden as I could use the hotel wireless to call home for free. For these calls, I tethered myself to my laptop using my cell phone earbuds plus this handy adapter to split the 3.5mm earbud plug into separate mic / speaker plugs for my notebook. I’ve also used a USB headset, but the earbuds and adapter roll into a tiny ball I can more easily stuff into a backpack and the sound quality is just as good.

Personally, I love Gmail calling as a phone interface. Being a keyboard shortcuts freak, I simply press “g,p” in Gmail and start typing someone’s name. Their phone numbers appear, I arrow down, press enter, and Gmail dials. Very cool. And of course, thanks to Android, I see the same contacts whether in Gmail or on my cell phone. However, the other people in my house don’t necessarily want to turn on and plug into a computer just to make a phone call.

Enter the OBI110, a VOIP adapter that lets you use your plain old telephone with Gmail calling and optionally, Google Voice for inbound calls. Mine arrived last week and I finally got around to setting it up this afternoon. The steps are simple (though NOT particularly clear in the instruction booklet). After you plug in all the cords and turn it on,

  1. Sign up for an account at obitalk.com. It’s free.
  2. Add your OBI110 device to your control panel (click Add Device in the left nav).
  3. Click Tips, Tricks in the left nav and then click Google Voice Configuration Wizard. Put in your Gmail account info using your full username@gmail.com and away you go…

The OBI110 lets you plug in your existing land line as well as the Ethernet connection to your router, and you can configure it to use Gmail calling for all calls, or only when you dial “**1” first. This way, you can continue to make local calls on your land line and use Gmail calling only for free long distance.

Note: instead of step 3 above, you can also click on the OBI110 device in the dashboard at obitalk.com and click the Google Voice setup button; however, when I tried it this way, it always made my Google Voice # the primary number, even if I unchecked that box in the setup, so I couldn’t make local calls on my land line.

For advanced configuration, you can login to the OBI110 router using your browser by going to its IP address. When you add the device to your account at obitalk.com, it creates an admin password which you can view on the dashboard. This password replaces the default admin/admin and you must enter username admin and this new password in order to connect to the OBI110 with your browser. If you get in trouble, you can always reset to factory defaults using a paper clip in the tiny hole in the back of the device or by logging in to the OBI110 and selecting System Management | Device Update | Reset.

So far, I’ve only made a few test calls to verify that inbound and outbound calls are routed as expected.  If I dial a number directly, it goes through my land line according to caller ID, and if I dial **1 first, it shows up as my Google Voice #. There seems to be a delay of one or two rings before inbound calls on the land line make it through the device to reach my cordless base system.

With the OBI110, you no longer need a computer to use Gmail calling nor a land line to receive calls at your Google Voice # on a corded or cordless telephone. Google has said Gmail calling will remain free at least through 2011, so if you call numbers in the US or Canada, you can make everyone in your house happy with free long distance on your familiar home telephone system.

In case you’re wondering, the event that got this ball rolling was that I got a corporate cell phone (Android, of course), which allowed me to cancel our AT&T family talk plan that we used mainly for free long distance on nights and weekends. We now have much cheaper pre-paid cell phones, free texting through Google Voice numbers, and free long distance at home with Gmail calling. We’ve pretty well drunk the Google Kool-Aid 🙂

Posted in PC Tech | 6 Comments »

Thank you, Greenville

Posted by David Chandler on March 11, 2011

Thank you, Greenville Java community for the good turnout for my GWT+GAE talk at GreenJUG yesterday. Great questions and great feedback from the meeting.

Last night’s “eager beaver” award goes to JUG leader John Yeary, who downloaded a fresh copy of Eclipse, installed Google Plugin for Eclipse, and deployed the sample GWT+GAE app to App Engine during my talk. Yes, it really is that easy.

Find an upcoming Google event near you: code.google.com/events

Posted in Uncategorized | 2 Comments »

GWT + Maven + GAE gotcha

Posted by David Chandler on March 8, 2011

It turns out that gwt-user-2.2.0, weighing in at 10.6 MB, is just over the App Engine size limit for a single file (10 MB) so you must enable jar splitting to deploy a GWT + Maven project to GAE. Actually, though, there is no need to deploy gwt-user because it’s only used for GWT compilation and GWT dev mode. The GWT Expenses sample POM correctly scopes the gwt-user dependency as “provided” in order to prevent this. However, the sample POM also says in the gwt-maven-plugin config:

<copyWebapp>true</copyWebapp>

This feature of gwt-maven-plugin copies src/main/webapp to the target folder in order for GWT dev mode (gwt:run) to work and inadvertently copies the gwt-user jar, too. Fortunately, if you’re using a POM with copyWebapp=true, you can configure the clean plugin to remove the gwt-user jar from the target folder before GAE deployment:

			<plugin>
				<!-- Don't deploy gwt-user jar to GAE -->
				<artifactId>maven-clean-plugin</artifactId>
				<version>2.3</version>
				<executions>
					<execution>
						<id>default-clean</id>
						<phase>clean</phase>
						<goals>
							<goal>clean</goal>
						</goals>
					</execution>
					<execution>
						<id>remove-gwt-user-jar</id>
						<phase>package</phase>
						<goals>
							<goal>clean</goal>
						</goals>
						<configuration>
							<excludeDefaultDirectories>true</excludeDefaultDirectories>
							<filesets>
								<fileset>
									<directory>${project.build.directory}/${project.build.finalName}/WEB-INF/lib</directory>
									<includes>
										<include>gwt-user*jar</include>
									</includes>
								</fileset>
							</filesets>
						</configuration>
					</execution>
				</executions>
			</plugin>

It should be noted that this is only an issue for Maven projects. Google Plugin for Eclipse has long excluded gwt-user.jar from WEB-INF/lib, so standard GPE projects don’t deploy it to App Engine. However, GPE Maven projects allow Maven to manage the classpath, so you’ll need the clean snippet above in your POM to exclude gwt-user from deployment.

A proposed fix is to enhance the gwt-maven-plugin’s copyWebapp feature (which, ironically enough, was contributed by Google) to exclude gwt-user*jar from the copy. Until then, use the clean snippet above or enable jar splitting.

Posted in AppEngine, Google Web Toolkit | 2 Comments »

GWT 2.2 AsyncDataProvider / CellTable gotcha

Posted by David Chandler on March 2, 2011

I’ve been trying out CellTable in GWT 2.2 and found a potential gotcha. I created an AsyncDataProvider and attached the CellTable to it, but found that the table does not refresh properly unless you update BOTH row count and row data. If you call only updateRowData(), the CellTable will show the loading bar when there are zero rows, and will not remove rows when you delete them in the provider. The solution is simple: whenever you want to update a CellTable bound to an AsyncDataProvider, be sure to call both updateRowData() and updateRowCount(). Example code here:

	public static class ListDataProvider extends
			AsyncDataProvider<ItemListProxy>
	{
		private Logger logger = Logger
				.getLogger(ListsActivity.ListDataProvider.class.getName());
		private ListwidgetRequestFactory rf;

		public ListDataProvider(ListwidgetRequestFactory requestFactory)
		{
			this.rf = requestFactory;
		}

		@Override
		protected void onRangeChanged(HasData<ItemListProxy> display)
		{
			getData();
		}
		
		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);
				}
			});
		}
	}

Posted in Google Web Toolkit | 10 Comments »

 
%d bloggers like this: