Generating Enterprise Class GWT Applications for Spring
Join the DZone community and get the full member experience.
Join For Freemyeclipse for spring 8.6
genuitec and skyway software recently announced the production release of myeclipse for spring 8.6 . the latest release expands upon a set of advanced accelerators for spring development, providing new options for rapidly generating adobe flex, spring mvc, google web toolkit (gwt), spring web flow and iphone web applications. the release also introduces a new set of spring development editors, code assistants, that facilitate the annotation-based development of spring and jax-ws artifacts.
myeclipse for spring (me4s) 8.6 now supports the generation of reusable software components based on your data model. whether you are starting from rdbms tables, pojos, jpa entities, or wsdls, me4s can give you a lot of great technology to use in your project. you can see these components put to use in a basic, running crud-based application right after you finish filling out a simple wizard, but a lot of the value really comes from being able to leverage all of this technology as you build out other parts of your project. if you have an existing project and you want to get out of the business of maintaining the jpa/dao/service layer by hand, me4s can also take on this responsibility for you.
with the 8.6 release, we added a variety of new ui generation options because we are hearing consistently from java developers that as they explore the use of technologies like flex and google web toolkit (gwt), it is sometimes hard to find great examples. developers have told us that as they begin the process of evaluating these technologies, it would be nice to be able to demonstrate them running contextually against their own data for their internal and external partners. me4s lets you take an existing data model from an application that needs to be modernized, and very quickly generate the basic services you need on the back end, as well as building out nice clean ui components that are tailored for your data model.
if you have built software using almost anything that ends in wt, you’ll know that a lot of the time is spent integrating the windowing toolkit ui components to the data model and setting up the application to use best practices for that ui technology. me4s does a lot of this for you and as always, makes sure you have a running example at each stage of the process.
gwt
in this article, i am going to cover what is generated from the gwt generation capability in some detail. i will review the spring back-end generation options at a high level, since this stack is used by all of our generated ui’s, and then dive into the java code that is generated for the front-end. it is important to note, however, that the gwt front-end can also be generated to be agnostic to the persistence layer so that you can tie your gwt front-end to any back-end implementation that you wish.
prerequisites
if you have not already read the me4s tutorial on gwt scaffolding, you should read it now here . this tutorial takes you step-by-step through the process of using the software to generate your gwt application. once you are comfortable with this information, we can dive in a bit into the code that was actually generated.
design philosophy
the gwt integration in myeclipse for spring is targeted specifically at the ga version of gwt and the most widely adopted versions of spring (both 2.5 and 3.0). we wanted to make sure that developers had tooling options that supported the technologies that are available to them right now for use in production applications. me4s currently supports gwt 2.0.4, which is generally available at the time of the 8.6 release. we intend to support gwt 2.1 as quickly as possible as it moves towards general availability. the code that is generated from me4s is intended to follow the best practices for gwt 2.0 as prescribed by ray ryan ’s google i/o presentation .
we also thought it was important to include as few 3 rd party gwt projects as possible in the default implementation. there are a few great projects out there, and several great commercial gwt component providers, but we wanted to be sure that we didn’t make too many decisions for you in this case, and that what we built for gwt was based on what you could get straight from google.
summary of goals
goal | description |
use existing technology assets | java beans, rdbms, jpa, wsdl |
mvp architecture | follow a model view presenter approach |
uibinder | define ui’s using gwt’s uibinder |
command pattern | encapsulate using commands as described in ray ryan ’s google i/o presentation |
dto | use data transfer objects to encapsulate the data that should be sent to the client, and to control relationship management |
loose and lazy relationships | promote a design pattern that defers the loading of related data until it is needed with loose server based relationship management |
back end agnostic | tie the gwt front end to an automatically generated spring/jpa persistence layer, or just generate a service stub and then hand code the persistence to integrate with any back end. |
i18n support | generate ui’s that are already set up to be localized |
client bundles | use client bundles for optimization |
the application
in the gwt tutorial we used the sandbox derby database that comes with me4s to build out a gwt application that manages customers and payments using the customer and payment tables as inputs to the process. the resulting application looks something like this:
this application gives you the basic crud capabilities for managing your customers and payments and has a few nice capabilities built-in, including sortable, paginating lists, “pickers” for adding and removing elements from relationships, multi-tabbed editing, etc. this ui is really a composition of several nice, reusable components that can be re-purposed into other designs. because we are using a command pattern, event bus, and mvp based architecture, it is easy to wire these components together into different application patterns down the road.
understanding the architecture
after you have watched the google i/o presentation on architecting gwt applications, you will find the following illustration helpful in building a mental picture of the architecture that is being employed. the key architectural benefits are the clean separation between the model, which is dumb, and the view, which is also dumb, and the presenter, which isolates the logical (and testable) code in your application. the actions and events being used on the eventbus allow the code in your application to be extremely loosely coupled. the rpc service layer and server side gwt remote service act as an asynchronous information service. in this case, the gwt service is backed by the generated spring / jpa backend.
reusable software components
the following diagram illustrates the reusable software components that have been generated into your project as a result of the gwt scaffolding execution. the scaffolding wizard allows the developer to pick the layers that they wish to generate and those they wish to exclude. in this case, we generated a standard spring layered server side architecture with a service, dao and domain layer, as well as a web layer that is build upon gwt. the gwt components generated offer additional detail regarding the code that is used to implement the architecture as it was illustrated above.
project layout
the me4s scaffolding wizard gives you options for where you would like to put your code, how it is packaged, etc. let’s take a look at the default project layout for the application described above.
if we take the defaults, all of the generated code and configuration will go into a folder called “generated”. you can still build all of your own code in the existing src folder and leverage the code in the generated src folder. the generated code has absolutely no tie back to the tooling or generation technology. you can take this code and do what you like with it at this point. if you want to be able to run the scaffolding repetitively, you should avoid making changes in the generated .java files, although there are options to deal with re-generation which we will detail in a separate blog.
the “server side” components are put in the com.show. – (dao, domain, and service) packages. the packages that start with gwt.- are the gwt software components. me4s generates a separate gwt module for each domain object so that you can mix and match them later, plus a common module with utility classes, and an entry point module that rolls them all up into an example gwt application.
exploring the customer gwt model
let’s take a closer look at one of the generated gwt modules for the customer domain object. this module contains all of the boiler plate code that you would normally write to build a gwt application using the standards and best practices listed above.
top gwt module packages
the first two packages in the customer module are the gwt.customer and gwt.customer.client packages.
the gwt.customer package simply contains the gwt module.xml definition file for the customer module.
the gwt.customer.client module contains the dto object representation for customer, an id class for uniquely identifying a customer, the service and async service interfaces, and the css and i18n messages that are used by the customer ui.
the actions package
the actions package contains the actions or commands that can be passed back and forth to the server to accomplish specific tasks. there are actions for loading, storing, and deleting customers as well as actions for managing relationships to customer. this is an important point because the actions for managing relationships are generic such that you can use the same action to manage the relationship between salesrep and customer, and product and customer. this approach also allows us to make the screens for managing relationships to customer more oo. you will also notice that for each action, there is the action, a response object, and a action”ed” classes. this approach helps to encapsulate the code to pass these actions and receive their asynchronous callbacks. we will take a look at a specific example further on.
the events package
the events package declares a set of events that can be listened for on the eventbus. the ui components that are generated automatically register themselves to listen on the event bus for certain events in order to deal with changes to the data. if you want to hang some new behavior off the side of a customer being deleted for example, all you have to do is add a customerdeleted handler to the event bus and your code will be notified.
the server package
the server package contains the code that is executed on the server side of the application. this code can contain references to classes that cannot be compiled by gwt and allows us to integrate with the service , dao, and domain layers. if you choose not to scaffold the service and dao layers in the wizard, the service implementation will be generated with stubbed out methods that you can later implement to call your own back end implementation. the gwtcustomerutil class is a utility class that can do the conversion from your dto objects coming in from the gwt client back to the persistent version of those objects on the server, and back. the default implementation takes care of merging the data and using the dao and service layers to handle persistence. this approach does not require any 3 rd party projects that attempt to map jpa to gwt for example and leaves the developer open to implement other approaches to persistence.
the components package
the components package contains the gwt ui components that are designed to provide the developer with course grained java objects that can be used to build ui’s that work with customers in this case. these components generally use uibinder, are prewired to work with the eventbus, actions and events that are generated in the other packages, and they allow the implementing actions to be dependency injected into the presenter in order to allow them to remain loosely coupled and testable. there are paginated tables for both “listing” objects in a table and a table for “selecting” objects with only a few columns as you may see in a “picker”. there are general edit widgets which provide you with form based input and display of the objects fields and properties. there are relationship widgets for managing relationships to customer, both single or (one) sided relationships, or (many) sided relationships. there are also dialogs for your convenience for editing or picking customers. the tables are all specific implementations of the pagingscrolltable from the gwt incubator project.
the code
the best way to get a feel for the code is to download myeclipse for spring and walk through the gwt tutorial. let’s take a quick look at some of the generated code to get a feel for it. we are absolutely welcome to feedback from the community as to how we may continue to improve the product and so if you have thoughts please post them on our forums .
server side classes
customer
the customer class is the jpa entity that was generated in this case from our rdbms table definition. you can see that we automatically generate @namedquery annotations for the common named queries you may need to search for customers. each field has been annotated based on information derived from the database, generally pretty standard stuff.
package com.showme.domain; import java.io.serializable; import java.lang.stringbuilder; import java.math.bigdecimal; import java.util.linkedhashset; import java.util.set; import javax.persistence.id; import javax.persistence.namedqueries; import javax.persistence.namedquery; import javax.xml.bind.annotation.*; import javax.persistence.*; /** */ @entity @namedqueries( { @namedquery(name = "findallcustomers", query = "select mycustomer from customer mycustomer"), @namedquery(name = "findcustomerbyaddressline1", query = "select mycustomer from customer mycustomer where mycustomer.addressline1 = ?1"), @namedquery(name = "findcustomerbyaddressline1containing", query = "select mycustomer from customer mycustomer where mycustomer.addressline1 like ?1"), @namedquery(name = "findcustomerbyaddressline2", query = "select mycustomer from customer mycustomer where mycustomer.addressline2 = ?1"), @namedquery(name = "findcustomerbyaddressline2containing", query = "select mycustomer from customer mycustomer where mycustomer.addressline2 like ?1"), @namedquery(name = "findcustomerbycity", query = "select mycustomer from customer mycustomer where mycustomer.city = ?1"), @namedquery(name = "findcustomerbycitycontaining", query = "select mycustomer from customer mycustomer where mycustomer.city like ?1"), @namedquery(name = "findcustomerbycontactfirstname", query = "select mycustomer from customer mycustomer where mycustomer.contactfirstname = ?1"), @namedquery(name = "findcustomerbycontactfirstnamecontaining", query = "select mycustomer from customer mycustomer where mycustomer.contactfirstname like ?1"), @namedquery(name = "findcustomerbycontactlastname", query = "select mycustomer from customer mycustomer where mycustomer.contactlastname = ?1"), @namedquery(name = "findcustomerbycontactlastnamecontaining", query = "select mycustomer from customer mycustomer where mycustomer.contactlastname like ?1"), @namedquery(name = "findcustomerbycountry", query = "select mycustomer from customer mycustomer where mycustomer.country = ?1"), @namedquery(name = "findcustomerbycountrycontaining", query = "select mycustomer from customer mycustomer where mycustomer.country like ?1"), @namedquery(name = "findcustomerbycreditlimit", query = "select mycustomer from customer mycustomer where mycustomer.creditlimit = ?1"), @namedquery(name = "findcustomerbycustomername", query = "select mycustomer from customer mycustomer where mycustomer.customername = ?1"), @namedquery(name = "findcustomerbycustomernamecontaining", query = "select mycustomer from customer mycustomer where mycustomer.customername like ?1"), @namedquery(name = "findcustomerbycustomernumber", query = "select mycustomer from customer mycustomer where mycustomer.customernumber = ?1"), @namedquery(name = "findcustomerbyphone", query = "select mycustomer from customer mycustomer where mycustomer.phone = ?1"), @namedquery(name = "findcustomerbyphonecontaining", query = "select mycustomer from customer mycustomer where mycustomer.phone like ?1"), @namedquery(name = "findcustomerbypostalcode", query = "select mycustomer from customer mycustomer where mycustomer.postalcode = ?1"), @namedquery(name = "findcustomerbypostalcodecontaining", query = "select mycustomer from customer mycustomer where mycustomer.postalcode like ?1"), @namedquery(name = "findcustomerbyprimarykey", query = "select mycustomer from customer mycustomer where mycustomer.customernumber = ?1"), @namedquery(name = "findcustomerbysalesrepemployeenumber", query = "select mycustomer from customer mycustomer where mycustomer.salesrepemployeenumber = ?1"), @namedquery(name = "findcustomerbystate", query = "select mycustomer from customer mycustomer where mycustomer.state = ?1"), @namedquery(name = "findcustomerbystatecontaining", query = "select mycustomer from customer mycustomer where mycustomer.state like ?1") }) @table(schema = "classiccars", name = "customer") @xmlaccessortype(xmlaccesstype.field) @xmltype(namespace = "showmethemoney/com/showme/domain", name = "customer") @xmlrootelement(namespace = "showmethemoney/com/showme/domain") public class customer implements serializable { private static final long serialversionuid = 1l; /** */ @column(name = "customernumber", nullable = false) @basic(fetch = fetchtype.eager) @id @xmlelement integer customernumber; /** */ @column(name = "customername", length = 50) @basic(fetch = fetchtype.eager) @xmlelement string customername; //** this is only a partial listing of the generated file **//full listing of customer.java
customerdaoimpl
the customerdaoimpl class is the implementation of the customerdao interface which defines the behaviors required to persist customers. this class is an annotated spring @repository and which has @transactional annotations for transaction management. the entitymanager is injected by spring. there are methods for each of the namedqueries from the customer jpa object. this class is used by the service layer to manage the persistence of customers.
package com.showme.dao; import com.showme.domain.customer; import java.util.arrays; import java.util.hashset; import java.util.linkedhashset; import java.util.set; import javax.persistence.entitymanager; import javax.persistence.noresultexception; import javax.persistence.persistencecontext; import javax.persistence.query; import org.skyway.spring.util.dao.abstractjpadao; import org.springframework.dao.dataaccessexception; import org.springframework.stereotype.repository; import org.springframework.transaction.annotation.transactional; /** * dao to manage customer entities. * */ @repository("customerdao") @transactional public class customerdaoimpl extends abstractjpadao implements customerdao { /** * set of entity classes managed by this dao. typically a dao manages a single entity. * */ private final static setfull listing of customerdaoimpl.java> datatypes = new hashset >(arrays.aslist(new class>[] { customer.class })); /** * entitymanager injected by spring for persistence unit myeclipse_derby * */ @persistencecontext(unitname = "myeclipse_derby") private entitymanager entitymanager; /** * instantiates a new customerdaoimpl * */ public customerdaoimpl() { super(); } /** * get the entity manager that manages persistence unit myeclipse_derby * */ public entitymanager getentitymanager() { return entitymanager; } /** * returns the set of entity classes managed by this dao. * */ public set > gettypes() { return datatypes; } /** * jpql query - findcustomerbycreditlimit * */ @transactional public set findcustomerbycreditlimit(java.math.bigdecimal creditlimit) throws dataaccessexception { return findcustomerbycreditlimit(creditlimit, -1, -1); } /** * jpql query - findcustomerbycreditlimit * */ @suppresswarnings("unchecked") @transactional public set findcustomerbycreditlimit(java.math.bigdecimal creditlimit, int startresult, int maxrows) throws dataaccessexception { query query = createnamedquery("findcustomerbycreditlimit", startresult, maxrows, creditlimit); return new linkedhashset (query.getresultlist()); }
customerserviceimpl
the customerserviceimpl class is the implementation for the customerservice interface which defines the behavior for a crud service for customer. this class is a session scoped bean and is annotated as a spring @service. this class has the dao’s it needs injected by spring and has methods to manage the basic crud operations as well as dependency management for customer.
package com.showme.service; import com.showme.dao.customerdao; import com.showme.dao.paymentdao; import com.showme.domain.customer; import com.showme.domain.payment; import java.util.set; import org.springframework.beans.factory.annotation.autowired; import org.springframework.context.annotation.scope; import org.springframework.stereotype.service; import org.springframework.transaction.annotation.transactional; /** * spring service that handles crud requests for customer entities * */ @scope("session") @service("customerservice") @transactional public class customerserviceimpl implements customerservice { /** * dao injected by spring that manages customer entities * */ @autowired private customerdao customerdao; /** * dao injected by spring that manages payment entities * */ @autowired private paymentdao paymentdao; /** * instantiates a new customerserviceimpl. * */ public customerserviceimpl() { } /** * load an existing customer entity * */ @transactional public setfull listing of customerserviceimpl.javaloadcustomers() { return customerdao.findallcustomers(); } /** * save an existing customer entity * */ @transactional public void savecustomer(customer customer) { customer = customerdao.store(customer); customerdao.flush(); } //** this is only a partial listing of the generated file **//
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") publicfull listing of gwtcustomerserviceimpl.javat 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 **//
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 hasvaluefull listing of customereditor.javagetcustomernumberbox(); 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 **//
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 uibinderfull listing of customereditwidget.java{} /** * 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 **//
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 .
Opinions expressed by DZone contributors are their own.
Trending
-
Avoiding Pitfalls With Java Optional: Common Mistakes and How To Fix Them [Video]
-
RBAC With API Gateway and Open Policy Agent (OPA)
-
How to Implement Istio in Multicloud and Multicluster
-
Seven Steps To Deploy Kedro Pipelines on Amazon EMR
Comments