Jack has posted 1 posts at DZone. View Full User Profile

Generating Enterprise Class GWT Applications for Spring

08.06.2010
| 15193 views |
  • submit to reddit

Client Side Classes

GWTCustomer

The GWTCustomerClass is the automatically generated DTO Object that is used to transfer information about a Customer from the client to the server and is also the DTO Object that is bound to all of the UI Components that have been generated in support of Customer. The DTO Object does NOT contain relationships of any kind. Instead, the GWTService for Customer contains methods to load relationships lazily and there are Actions, Events, and UI Components that support the management of relationships for each Data Type in the Domain model.

package gwt.customer.client;


import java.io.Serializable;

import java.lang.StringBuilder;


/**
 * A lightweight POJO without relationships that can be used for RPC style remote calls with GWT.
 * The GWT Services have methods for adding, removing and loading Objects and for managing relationships on Objects.
 * This approach encourages loose coupling between objects and asynchronous loading of related data
 * 
 * @see  Customer 
 */
public class GWTCustomer implements Serializable{
	private static final long serialVersionUID = 1L;

		/**
		* Declare customernumber
		*/
		 Integer customernumber;
		/**
		* Declare customername
		*/
		 String customername;
		/**
		* Declare contactlastname
		*/
		 String contactlastname;
		/**
		* Declare contactfirstname
		*/
		 String contactfirstname;
		/**
		* Declare phone
		*/
		 String phone;
		/**
		* Declare addressline1
		*/
		 String addressline1;
		/**
		* Declare addressline2
		*/
		 String addressline2;
		/**
		* Declare city
		*/
		 String city;
		/**
		* Declare state
		*/
		 String state;
		/**
		* Declare postalcode
		*/
		 String postalcode;
		/**
		* Declare country
		*/
		 String country;
		/**
		* Declare salesrepemployeenumber
		*/
		 Integer salesrepemployeenumber;
		/**
		* Declare creditlimit
		*/
		 Double creditlimit;
	

		/**
		* Setter for customernumber
		 */
		public void setCustomernumber(Integer customernumber) {
			this.customernumber = customernumber;
		}

//** This is only a partial listing of the generated file **//
Full listing of GWTCustomer.java

GWTCustomerServiceImpl

The GWTCustomerServiceImpl class is the implementation of the GWT RemoteServiceServlet class which enables the GWT RPC layer. The purpose of this class is to handle the requests from the GWT Client which arrive at the server in the form of Actions (or Commands) . This class implements provides an implementation of the execute(Action<T> action) method and then delegates those actions to appropriate handlers which use the CustomerUtil class to convert back and forth from DTO Object to JPA Object and perform the basic CRUD and Relationship management operations.

package gwt.customer.server;

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

import com.showme.dao.CustomerDAO;
import com.showme.dao.PaymentDAO;

import com.showme.service.CustomerService;

import gwt.common.client.Action;
import gwt.common.client.CrudException;
import gwt.common.client.Response;

import gwt.customer.client.GWTCustomer;
import gwt.customer.client.GWTCustomerID;
import gwt.customer.client.GWTCustomerService;

import gwt.customer.client.actions.DeleteCustomer;
import gwt.customer.client.actions.DeleteCustomerResponse;
import gwt.customer.client.actions.LoadCustomer;
import gwt.customer.client.actions.LoadCustomerResponse;
import gwt.customer.client.actions.StoreCustomer;
import gwt.customer.client.actions.StoreCustomerResponse;

import gwt.payment.client.GWTPayment;

import gwt.payment.client.actions.AddPaymentToRelated;
import gwt.payment.client.actions.AddPaymentToRelatedResponse;
import gwt.payment.client.actions.LoadRelatedPayment;
import gwt.payment.client.actions.LoadRelatedPaymentResponse;
import gwt.payment.client.actions.RemovePaymentFromRelated;
import gwt.payment.client.actions.RemovePaymentFromRelatedResponse;

import gwt.payment.server.GWTPaymentUtil;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.beanutils.ConvertUtils;

import org.apache.commons.beanutils.converters.BigDecimalConverter;
import org.apache.commons.beanutils.converters.CalendarConverter;
import org.apache.commons.beanutils.converters.DateConverter;

import org.springframework.web.context.WebApplicationContext;

import org.springframework.web.servlet.support.RequestContextUtils;




/**
 * Remote Service implementation for CRUD based operations for Customer
 * @see com.showme.domain.Customer
 */
public class GWTCustomerServiceImpl extends RemoteServiceServlet implements GWTCustomerService {
		
	/**
	 * Default constructor.
 	 */
	public GWTCustomerServiceImpl() {
		java.util.Date defaultValue = null;
		java.util.Calendar defaultCalendarValue = null;
		java.math.BigDecimal defaultBigDecimalValue = null;
		
		DateConverter dateConverter = new DateConverter(defaultValue);
		CalendarConverter calendarConverter = new CalendarConverter (defaultCalendarValue);
		BigDecimalConverter bigDecimalConverter = new BigDecimalConverter (defaultBigDecimalValue);
			
		ConvertUtils.register(dateConverter, java.util.Date.class);
		ConvertUtils.register(calendarConverter, java.util.Calendar.class);
		ConvertUtils.register(bigDecimalConverter, java.math.BigDecimal.class);
	}

	/**
	 * Default execute method for Actions passed to this Service
	 * See the MVP approach and the use of the Command Pattern GWT Best Practices
	 *
	 */
	 @SuppressWarnings("unchecked")
	public  T execute(Action action) throws CrudException{
		if (action instanceof DeleteCustomer)
			return (T)execute ((DeleteCustomer)action);
		else if (action instanceof LoadCustomer)
			return (T)execute ((LoadCustomer)action);
		else if (action instanceof StoreCustomer)
			return (T)execute ((StoreCustomer)action);
		else if (action instanceof AddPaymentToRelated)
			return (T)execute ((AddPaymentToRelated)action);
		else if (action instanceof LoadRelatedPayment)
			return (T)execute ((LoadRelatedPayment)action);
		else if (action instanceof RemovePaymentFromRelated)
			return (T)execute ((RemovePaymentFromRelated)action);
	
	
		throw new CrudException ("Invalid Action, no handler specified:" + action); 
	}
	
	/**
	 * Get the DataUtil handler to convert Objects from GWT DTOs to their persisted version, and back
	*/
	private GWTCustomerUtil dataUtils() {
		return new GWTCustomerUtil(getDAO());
	}
	
	/**
	 * Default implementation loads all 
	 */
	private LoadCustomerResponse execute (gwt.customer.client.actions.LoadCustomer action) throws CrudException{
		return new LoadCustomerResponse (dataUtils().toGWT(getService().loadCustomers()));		}
	
	/**
	*/
	private DeleteCustomerResponse execute (gwt.customer.client.actions.DeleteCustomer action) throws CrudException {
		try {
			for (GWTCustomer customer: action.getCustomers()) {
				getService().deleteCustomer(dataUtils().toPersisted(customer));
			}
		}
		catch (javax.persistence.EntityExistsException ex) {
			throw new CrudException ("Unable to delete this Customer, it may be associated with a another record", ex);
		}
		catch (Exception e) {
			throw new CrudException ("Unable to delete this Customer", e);
		}
		return new DeleteCustomerResponse(action.getCustomers());
	}
	

//** This is only a partial listing of the generated file **//
Full listing of GWTCustomerServiceImpl.java

CustomerEditor

CustomerEditor is the Presenter in the MVP Pattern for Customer objects. At the top you will notice that the Presenter defines an interface called Display that defines the API that must be met to be a “View” for this presenter. Also notice the use of HasValue<x> instead of specific UI Components. This approach makes it easy to pass in mock displays and leaves the UI implementation a lot of flexibility for how it implements the contract with the presenter. The bindDisplay method takes care of getting elements from the Display and adding event handlers, etc where necessary. The edit and save methods do exactly what you would probably expect, they bind data in and out of the UI to the model, and take care of invoking the execute method on the server by passing in the StoreCustomer action.

package gwt.customer.client.components;

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.event.dom.client.HasClickHandlers;

import com.google.gwt.user.client.Window;

import com.google.gwt.user.client.ui.HasValue;

import gwt.common.client.EventBus;

import gwt.customer.client.CustomerMessages;
import gwt.customer.client.GWTCustomer;
import gwt.customer.client.GWTCustomerID;
import gwt.customer.client.GWTCustomerService;
import gwt.customer.client.GWTCustomerServiceAsync;

import gwt.customer.client.actions.StoreCustomer;
import gwt.customer.client.actions.StoredCustomer;

import gwt.customer.client.events.CustomerStoredEvent;
import gwt.customer.client.events.CustomerStoredHandler;

/**
 * Presenter for GWTCustomer editing
 */
public class CustomerEditor {
	// Display Interface that must be implemented to bind a Display to this
	// editor
	interface Display {
		public HasValue getCustomernumberBox();

		public HasValue getCustomernameBox();

		public HasValue getContactlastnameBox();

		public HasValue getContactfirstnameBox();

		public HasValue getPhoneBox();

		public HasValue getAddressline1Box();

		public HasValue getAddressline2Box();

		public HasValue getCityBox();

		public HasValue getStateBox();

		public HasValue getPostalcodeBox();

		public HasValue getCountryBox();

		public HasValue getSalesrepemployeenumberBox();

		public HasValue getCreditlimitBox();

		public HasClickHandlers getCancelButton();

		public HasClickHandlers getSaveButton();

		public void setIsNew(boolean isNew);

		public boolean isValid();
	}

	/**
	 * Reference to the Display that is bound to this presenter
	 */
	private Display display;

	/**
	 * The GWTCustomer being edited
	 */
	private GWTCustomer customer;

	/**
	 * The Crud Service used to handle saves from this Editor
	 */
	private GWTCustomerServiceAsync service;

	/**
	 * The I18N Messages
	 */
	private final static CustomerMessages messages = (CustomerMessages) GWT
			.create(CustomerMessages.class);

	/**
	 * Constructor that takes an instance of the Display to bind to this
	 * presenter
	 */
	public CustomerEditor(Display display) {
		this(display, GWTCustomerService.Util.getInstance());
	}

	/**
	 * Constructor that takes an instance of the Display to bind to this
	 * presenter, and an override to disable calls through to the server This
	 * constructor just avoids creating an instance of the Default crud Service.
	 * The Editor will only perform saves if the Async Crud Service is present
	 * 
	 */
	public CustomerEditor(Display display, boolean performSave) {
		bindDisplay(display);
		if (performSave)
			this.service = GWTCustomerService.Util.getInstance();
	}

	/**
	 * Constructor that takes the Display to bind to this presenter, and the
	 * Async Service to use for saving
	 */
	public CustomerEditor(Display display, GWTCustomerServiceAsync service) {
		bindDisplay(display);
		this.service = service;
	}

	/**
	 * Binds the view to the presenter
	 */
	void bindDisplay(final Display display) {
		this.display = display;

		if (display.getSaveButton() != null) {
			display.getSaveButton().addClickHandler(new ClickHandler() {
				public void onClick(ClickEvent event) {
					if (display.isValid())
						doSave();
					else
						doError();
				}
			});
		}

		if (display.getCancelButton() != null) {
			display.getCancelButton().addClickHandler(new ClickHandler() {
				public void onClick(ClickEvent event) {
					doCancel();
				}
			});
		}

		EventBus.get().addHandler(CustomerStoredEvent.TYPE,
				new CustomerStoredHandler() {
					public void onCustomerStored(CustomerStoredEvent event) {
						if (new GWTCustomerID(event.getCustomer())
								.equals(new GWTCustomerID(customer)))
							edit(event.getCustomer());
					}
				});
	}

//** This is only a partial listing of the generated file **//
Full listing of CustomerEditor.java

CustomerEditWidget

CustomerEditWidget is the class responsible for loading the UI using UIBinder. It implements the interface Display from CustomerEditor. You can see the code at the top of the class that specifies the UIBinder xml definition file and loads it through GWT.create(). The @UIField annotations allow UIElements defined in the XML file to be bound to the Java code in the CustomerEditWidget. The generated edit widgets offer “dumb” UI capabilities like enabling and disabling as well as very BASIC validation that the form fields are valid enough to be bound to the data model.

package gwt.customer.client.components;

import com.google.gwt.core.client.GWT;

import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiTemplate;

import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;

import gwt.common.client.DecimalBox;
import gwt.common.client.IntegerBox;


/**
 * The view for editing GWTCustomer
 * This view is constructed using UIBinder
 */
public class CustomerEditWidget extends Composite implements  CustomerEditor.Display {
	
	/**
	* Interface for UIBinder extension
	*/ 
	@UiTemplate("CustomerEditWidget.ui.xml")
	interface EditBinder extends UiBinder{}
	
	/**
	* Instance of EditBinder interface loaded through GWT.create
	*/ 
	private static EditBinder binder = GWT.create(EditBinder.class);
	
		/**
		* UI Input for customernumber
		*/ 
		@UiField
		IntegerBox customernumberBox;
		/**
		* UI Input for customername
		*/ 
		@UiField
		TextBox customernameBox;
		/**
		* UI Input for contactlastname
		*/ 
		@UiField
		TextBox contactlastnameBox;
		/**
		* UI Input for contactfirstname
		*/ 
		@UiField
		TextBox contactfirstnameBox;
		/**
		* UI Input for phone
		*/ 
		@UiField
		TextBox phoneBox;
		/**
		* UI Input for addressline1
		*/ 
		@UiField
		TextBox addressline1Box;
		/**
		* UI Input for addressline2
		*/ 
		@UiField
		TextBox addressline2Box;
		/**
		* UI Input for city
		*/ 
		@UiField
		TextBox cityBox;
		/**
		* UI Input for state
		*/ 
		@UiField
		TextBox stateBox;
		/**
		* UI Input for postalcode
		*/ 
		@UiField
		TextBox postalcodeBox;
		/**
		* UI Input for country
		*/ 
		@UiField
		TextBox countryBox;
		/**
		* UI Input for salesrepemployeenumber
		*/ 
		@UiField
		IntegerBox salesrepemployeenumberBox;
		/**
		* UI Input for creditlimit
		*/ 
		@UiField
		DecimalBox creditlimitBox;
	
	/**
	* Cancel button
	*/ 
	@UiField
	Button cancelButton;
	
	/**
	* Save button
	*/ 
	@UiField
	Button saveButton;

//** This is only a partial listing of the generated file **//
Full listing of CustomerEditWidget.java

CustomerEditWidget.ui.xml

This file defines the UI for the EditWidget using a blend of HTML and GWT Tags. This approach ensures a very clean separation between the UI definition and the java code. The default implementation takes care of dropping in the <ui:msg> tags which can be used by the GWT compiler to generate I18N files for localization.

<ui:UiBinder
  ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
  ui:generateKeys="com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator"
  ui:generateLocales="default"
  xmlns:ui='urn:ui:com.google.gwt.uibinder'
  xmlns:dp='urn:import:com.google.gwt.user.datepicker.client'
  xmlns:gc='urn:import:gwt.common.client'
	xmlns:payments='urn:import:gwt.payment.client.components'
  xmlns:g='urn:import:com.google.gwt.user.client.ui'>
  <ui:style src='../Customer.css'/>

  <g:HTMLPanel addStyleNames="{style.gwt-CrudEdit}">
  <table cellpadding="0" cellspacing="0" id="viewTable">
	<tr><td class="label" valign="top"><ui:msg description="customernumberLabel">Customernumber:</ui:msg></td><td><gc:IntegerBox ui:field="customernumberBox"></gc:IntegerBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="customernameLabel">Customername:</ui:msg></td><td><g:TextBox ui:field="customernameBox"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="contactlastnameLabel">Contactlastname:</ui:msg></td><td><g:TextBox ui:field="contactlastnameBox"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="contactfirstnameLabel">Contactfirstname:</ui:msg></td><td><g:TextBox ui:field="contactfirstnameBox"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="phoneLabel">Phone:</ui:msg></td><td><g:TextBox ui:field="phoneBox"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="addressline1Label">Addressline1:</ui:msg></td><td><g:TextBox ui:field="addressline1Box"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="addressline2Label">Addressline2:</ui:msg></td><td><g:TextBox ui:field="addressline2Box"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="cityLabel">City:</ui:msg></td><td><g:TextBox ui:field="cityBox"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="stateLabel">State:</ui:msg></td><td><g:TextBox ui:field="stateBox"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="postalcodeLabel">Postalcode:</ui:msg></td><td><g:TextBox ui:field="postalcodeBox"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="countryLabel">Country:</ui:msg></td><td><g:TextBox ui:field="countryBox"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="salesrepemployeenumberLabel">Salesrepemployeenumber:</ui:msg></td><td><gc:IntegerBox ui:field="salesrepemployeenumberBox"></gc:IntegerBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="creditlimitLabel">Creditlimit:</ui:msg></td><td><gc:DecimalBox ui:field="creditlimitBox"></gc:DecimalBox></td></tr>
  </table>
  <g:HorizontalPanel ui:field="buttonPanel">
  <g:Button addStyleNames="{style.gwt-CrudEditSaveButton}" ui:field="saveButton"><ui:msg description="SaveButton">Save</ui:msg></g:Button>
  <g:Button addStyleNames="{style.gwt-CrudEditCancelButton}" ui:field="cancelButton"><ui:msg description="CancelButton">Cancel</ui:msg></g:Button>
  </g:HorizontalPanel>
  </g:HTMLPanel>
</ui:UiBinder>
Full listing of CustomerEditWidget.ui.xml

Let us know what you think

We hope that you found this article informative and that you are able to get a good sense for the code that is being generated using MyEclipse For Spring, specifically as it relates to our GWT integration. As excited as we are about this new feature, we are eager to hear what the developer community thinks about our GWT scaffolding capability. You can download MyEclipse for Spring 8.6 here. Please post all feedback, questions and issues to the MyEclipse for Spring section of the MyEclipse forums.

Published at DZone with permission of its author, Jack Kennedy.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)