Stig Runar Vangen homepage
Blog

Blog

BeanModel generation with AutoBean support

I've lately been working on converting our in the works web application from using GWT-RPC to using RequestFactory. While working on this, I've found that the ExtGWT 2 BeanModelGenerator is not capable of reading getters and setters from a proxy interface. This generator solves this problem.


package com.wis.wisweb2.intra.core.rebind;

import java.util.List;

import com.extjs.gxt.ui.rebind.core.BeanModelGenerator;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.requestfactory.shared.BaseProxy;

/**
* BeanModel generator with support for GWT AutoBeans.
*
* @author Stig Runar Vangen
*/
public class AutoBeanBeanModelGenerator extends BeanModelGenerator {

@Override
protected final void addGetters(final JClassType cls,
final List methods) {
// Ignore methods of Object
if (cls.getSuperclass() != null) {
addGetters(cls.getSuperclass(), methods);
addClassGetters(cls, methods);
}

if (isAutoBean(cls)) {
addClassGetters(cls, methods);
}
}

/**
* @param cls
* class to test
* @return true if given class qualify for AutoBean generation
*/
private boolean isAutoBean(final JClassType cls) {
if (cls.getQualifiedSourceName().equals(BaseProxy.class.getName())) {
return true;
}

for (JClassType classType : cls.getImplementedInterfaces()) {
return isAutoBean(classType);
}

return false;
}

private void addClassGetters(final JClassType cls,
final List methods) {
for (JMethod m : cls.getMethods()) {
if (m.isPublic() || m.isProtected()) {
String name = m.getName();
if ((name.matches("get.*") || name.matches("is.*"))
&& m.getParameters().length == 0) {
methods.add(m);
}
}
}
}
}

This generator has to be references from the GWT module definition file.


<!-- BeanModel for AutoBean -->
<generate-with class="com.wis.wisweb2.intra.core.rebind.AutoBeanBeanModelGenerator">
<when-type-assignable class="com.extjs.gxt.ui.client.data.BeanModelLookup" />
</generate-with>

This definition should override the generator from the ExtGWT package.
Monday February 7. 2011

ExtGWT and RequestFactory integration

Sencha have been saying that the next version of ExtGWT will be closer to the standards that Google themselves are using for GWT. Earlier one has had to do a lot of custom code to make the data flow move optimally. We've tried to prepare our codebase for the release of ExtGWT 3, and a central part of this is the introduction of the RequestFactory. This helper reduces the amount of data transferred between client and server, which makes is to that we don't need to write as much code as we've earlier needed in order to change data on the client side. These changes span both the ser and client parts of the code.

Spring and RequestFactory
Uses temporarely a servlet defined in web.xml. This servlet is independent of what RequestContext you are using, but every implementation av these are not Spring compatible. This is on the list of things that needs to be improved.

ExtGWT and RequestFactory
Sencha says that in ExtGWT 3 they will do a better job at integration against GWT. This includes using the frameworks that already exists in GWT. One of these frameworks are the RequestFactory (introduced in GWT 2.1), which is a framework for reducing the traffic over the network. This is done by sending changes done to the model instead of the whole shebang. A fundamental introduction is provided here:

http://code.google.com/webtoolkit/doc/latest/DevGuideRequestFactory.html

The first thing that hits me when reading this documentation, is that Google has violated all rules of programming when making this framework. All methods for fetching data from the database are static methods on each entity. These static methods creates some problems regarding an integration towards Spring/Guice or similar. The fact that they are located on the entity itself makes it so that the code cannot be separated in code layers with makes further development easier. This has been improved in GWT 2.1.1, which contain a lot of improvements on the RequestFactory:

http://code.google.com/p/google-web-toolkit/wiki/RequestFactory_2_1_1

In this updated version of the framework it is possible to extract the implementation of the fetching of data from entities using locators. These implementation does not need to be static andy more, which eases the integration against Spring quite a lot. There are however very little documentation on this topic available yet. Neither Google nor the bloggers have started to write about this framework yet. There has therefore been a long process of trial and error in order to integrate RequestFactory into our systems.

RequestFactory GIN provider

public class ContactRequestFactoryProvider implements
Provider {

@Inject
private CoreGinjector coreGinjector;

@Override
public final ContactRequestFactory get() {
ContactRequestFactory contactRequestFactory =
GWT.create(ContactRequestFactory.class);
contactRequestFactory.initialize(coreGinjector.getEventBus(),
coreGinjector.getRequestTransport());
return contactRequestFactory;
}
}

In order to create RequestFactory object we use a GIN provider. The result of this object is defined as a singleton through the GIN configuration. This is integrated using our global eventbus an a custom transport method. The reason why we need a custom transport method is that we fetch data from a different web context than the client itself.

RequestFactory

public interface ContactRequestFactory extends RequestFactory {
ContactRequest contactRequest();
}

The factory in itself simply references a request implementation.

Contact request

@Service(value = ContactAdapter.class, locator = InstanceServiceLocator.class)
public interface ContactRequest extends RequestContext {

Request get(String id);

Request persist(ContactProxy contact);

Request remove(ContactProxy contact);
}

This interface defines the methods used to communicate from the client to the server. These are the methods specific for the contact module. The classes in itself are annotated as a GWT service, where the implementation of these methods a located in the ContactAdapter class (does not need to be the entity class any more in GWT 2.1.1), while ContactLocator (locators are new from GWT 2.1.1) takes care of entity updates.

Contact RequestFactory adapter

@Component
public class ContactAdapter {

private static ContactDao contactDao;

public ContactAdapter() {
}

@Autowired
public ContactAdapter(final ContactDao contactDao) {
ContactAdapter.contactDao = contactDao;
}

public final Contact get(final String id) {
Scanner scanner = new Scanner(id);
scanner.useLocale(Locale.ROOT);
if (scanner.hasNextLong()) {
return contactDao.get(scanner.nextLong());
}
return null;
}

public final void persist(final Contact contact) {
contactDao.saveOrUpdate(contact);
}

public final void remove(final Contact contact) {
contactDao.delete(contact);
}
}

This class implements all the methods defined in the request interface. The class is annotated with @Component so that Spring finds this class through package-scanning. We therefore will not need specific configuration for each module. A contact DAO is provided from the Spring context. This DAO is saved as a static variable. The reason for this is that RequestFactory initiates instances of its object independent os Spring. At application startup the DAO is provided from Spring, and all instances of this adapter will then get access to the DAO in question.

Instance ServiceLocator

public class InstanceServiceLocator implements ServiceLocator {

private static final Log LOGGER = LogFactory
.getLog(InstanceServiceLocator.class);

@Override
public final Object getInstance(final Class clazz) {
try {
Object newInstance = clazz.newInstance();
return newInstance;
} catch (InstantiationException ex) {
LOGGER.fatal("Failed to create instance", ex);
} catch (IllegalAccessException ex) {
LOGGER.fatal("Failed to create instance", ex);
}
return null;
}
}

This is a very simple ServiceLocator that simply creates a new instance of the class you request. It is possible that we on a later point in time will need to fetch object from the Spring context instead of creating standalone objects.

Entity proxy

@ProxyFor(value = Contact.class, locator = ContactLocator.class)
public interface ContactProxy extends BaseEntityProxy {

String getAddress();

String getFirstName();

String getLastName();

void setAddress(String address);

void setFirstName(String firstName);

void setLastName(String lastName);
}

Entity proxies are implemented as autobeans on the client side. These are a reflection of the entities on the server side. In the proxy on have to give names based on the Java Bean standard. This will most probably be a direct copy of the method names one finds in the entity. This because RequestFactory should be able to get data from the entity before they are transferred to the client. If these implementation does not match, one will get an error message while fetching data. Please note that we also reference a locator. This locator is of the type ServiceLocator, which are placed within a request.

 
@Entity
public class Contact extends AbstractDocumentEntity implements Serializable {

private String firstName;
private String lastName;
private String address;

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}
}

This is a simplified extract rom the entity that the autobean proxy points to. Please note that the method calls in the proxy calls the fields in the class, not the methods. Both class implementations follow the same rules for naeming of getters and setters.

Contact RequestFactory locator

public abstract class DocumentEntityLocator
extends Locator {

private static final Log LOGGER = LogFactory.getLog(DocumentEntityLocator.class);

public DocumentEntityLocator() {
}

public abstract BaseDao getDao();

@Override
public final T create(final Class clazz) {
try {
Class entityClass = getEntityClass(clazz);
if (entityClass != null) {
T newInstance = entityClass.newInstance();
getDao().saveOrUpdate(newInstance);
return newInstance;
}
} catch (InstantiationException ex) {
LOGGER.fatal("Failed to create instance", ex);
} catch (IllegalAccessException ex) {
LOGGER.fatal("Failed to create instance", ex);
}
return null;
}

@Override
public final T find(final Class clazz, final Long id) {
return getDao().get(id);
}

@Override
public final Class getDomainType() {
return getDao().getDomainType();
}

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

@Override
public final Class getIdType() {
return Long.TYPE;
}

@Override
public final Object getVersion(final T domainObject) {
return domainObject.getRevisionNumber();
}

@SuppressWarnings("unchecked")
private Class getEntityClass(
final Class clazz) {
Entity entity = clazz.getAnnotation(Entity.class);
if (entity != null) {
return (Class) clazz;
}

Class superclass = clazz.getSuperclass();
if (superclass != null) {
return getEntityClass(superclass);
}

return null;
}
}

A locator can be called the glue between your model structure and Requestfactory. This class defines how one fetches an entity based on an ID. In addition this class also defines how one finds the ID, version and type from an entity. The DAO is provided using an abstract method.


@Component
public class ContactLocator extends DocumentEntityLocator {

private static final Log LOGGER = LogFactory.getLog(ContactLocator.class);
private static ContactDao contactDao;

public ContactLocator() {
}

@Autowired
public ContactLocator(final ContactDao contactDao) {
ContactLocator.contactDao = contactDao;
}

@Override
public final BaseDao getDao() {
return ContactLocator.contactDao;
}
}

This is an implementation of a locator for a given module. This class is annotated with @Component in order for Spring to process this class at startup.
The static contact DAO will the be set. This will have to be static, as it is RequestFactory that at a later time will create instances of this, and is as earlier discussed not completely Spring compliant. The DAO is the given back to the abstract class using the implementation of the DAO getter. This makes it quickly and easily to set up new modules.


We plan to try to extend this RequestFactory implementation on the server side so that we won't be as dependent on static variables for Spring beans. This will make the code better suited for changes later on.
Friday February 4. 2011

Spring and GWT-RPC integration

As a part of the implementation of the serverside part of Wisweb 2, I've done some job on the integration between GWT and Spring. In addition there has been some work on the client side to integrate RequestFactory introduces in GWT 2.1 against ExtGWT. This has a times been a frustrating experience, and I want to present some of the solutions we found here so that others don't have to share this frustration.

For GWT based RPC-calls we want to configure the location of the endpoints using Spring annoations. This creates the need for setting up Spring controllers instead of GWT servlets. This integration is done in the following code snippet.


public abstract class AbstractRpcController extends RemoteServiceServlet {

private static final Log logger = LogFactory.getLog(AbstractRpcController.class);
private ServletContext servletContext;

@Override
public final ServletContext getServletContext() {
return servletContext;
}

@Autowired
public final void setServletContext(final ServletContext servletContext) {
this.servletContext = servletContext;
}

public abstract RemoteService getRemoteService();

@RequestMapping("/")
public final void request(final HttpServletRequest request,
final HttpServletResponse response) {
this.doPost(request, response);
}

@Override
public final String processCall(final String payload)
throws SerializationException {
try {
RPCRequest rpcRequest =
RPC.decodeRequest(payload, this.getRemoteService().getClass());
String result =
RPC.invokeAndEncodeResponse(this.getRemoteService(),
rpcRequest.getMethod(), rpcRequest.getParameters());
return result;
} catch (IncompatibleRemoteServiceException ex) {
logger.error("Caught an exception", ex);
return RPC.encodeResponseForFailure(null, ex);
} catch (Exception ex) {
logger.error("Caught a generic exception", ex);
return RPC.encodeResponseForFailure(null, ex);
}
}
}

The core of this code is the last method. This method routes a GWT service in the GWT impementation of the RPC processing. This makes sure that we get a seamless integration between GWT and Spring. The reference to the GWT service is an avstract method so that every module implementation provides its own service implementation.


@Controller
@SystemController
@RequestMapping("/rpc/contact")
public class ContactRpcController extends AbstractRpcController {

private ContactService contactService;

@Autowired
public final void setContactService(final ContactService contactService) {
this.contactService = contactService;
}

@Override
public final RemoteService getRemoteService() {
return this.contactService;
}
}

This is an example on a module specific RPC controller. The contact service is fetched from the Spring context, and is given back to the abstract implemenation as needed. This implementation makes it fast to add services to other modules.
Thursday February 3. 2011