gwt enterprise edition
DESCRIPTION
GWT brings a lot to the table on the client side: the comprehensive browser compatibility and the ease of writing in java are just a few examples to name. But, when looking at the server side, GWT can be a bit lacking with the technologies it uses. Learn how to build powerful end to end Enterprise applications with GWT as your frontend and how to back it up with your favorite arsenal of tools, including, Spring, Guice and HibernateTRANSCRIPT
GWT Enterprise Edition
By: Gilad Garon
March 2012
#javaedge2012
» Vanilla GWT
» Improved RPC
» Enhanced Request Factory
» Request Builder
» Tying it all together
Overview:
Agenda
VANILLA GWT SERVER SIDE
» Communication is always asynchronous
» Client side code is Java translated to JavaScript
» Server side code is based on GWT Servlets
The Lowdown on GWT RMI:
Vanilla GWT Server Side
» GWT RPC
Service oriented
» RequestFactory
Data oriented
» RequestBuilder
Manual HTTP Requests
Three Flavors of RMI:
Vanilla GWT Server Side
Vanilla GWT RMI
RemoteServiceServlet
RequestFactoryServlet
HttpServlet
GWT Application WAR
HTTP (Async)
GWT server side application:
» GWT RPC
Standalone services: Authentication
» RequestFactory
Data services: Domain entity CRUD operations
» RequestBuilder
Cross site integration: Reading data from a remote server
Use case examples:
Vanilla GWT Server Side
IMPROVED RPC
» Each service is a standalone RPC Servlet
» Client invokes a generated interface
» Transports concrete java objects
GWT-RPC in a nutshell:
Vanilla RPC
» Use the command pattern
Use a single RPC Servlet for all of your services
» Integrate with an IoC container
Wire all of your services easily
» Don’t use interfaces, use only concrete classes
How can we improve it?
Vanilla RPC
» A Single RPC Servlet for all of your services
» Action class represents the client request
» Result class represents the server response
» ActionHandler class handles the Action and returns the Result
Meet the Command Pattern:
Command Pattern
Dispatcher RPC Servlet
Architecture
Client Browser
Action
Action Result
Dispatch Service
Action Handler
Async Dispatcher
Command Pattern
Action
Result
» Server side:
Single RPC Servlet for all services
» Client side:
Caching
Batching
Centralized failure handling
Undo / Redo support
Don’t reinvent the wheel, use existing frameworks:
GWTP
GWT-Dispatch
Benefits:
Command Pattern
Command Pattern, Example
public class GetDocumentByIDAction extends Action<GetDocumentByIDResult> { private String id; public String getId() { return id; } }
Action:
public class GetDocumentByIDResult implements Result { private String documentContents; public String getDocument() { return documentContents; } }
Result:
Command Pattern
public class GetDocummentByIDActionHandler implements ActionHandler<GetDocummentByIDAction, GetDocummentByIDResult> { DocumentService documentService; @Override public GetDocummentByIDResult execute(GetDocummentByIDAction action) { String doc = documentService.getDocumentById(action.getId()); return new GetDocummentByIDResult (doc); } }
ActionHandler:
» Central point for configuration:
Services
DAO
» Minimizes the amount of code in your application
Inject what you need
» Make your application more testable
Easy way to create mockup objects
Integrate with an IoC container:
IoC / DI
Simple Spring example:
Spring Integration
public class MyRPCServiceImpl extends RemoteServiceServlet implements MyRPCService { private ApplicationContext applicationContext; @Override public void init() { super.init() ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext (getServletContext()); } @Override public String doSomething() { return applicationContext.getBean(SpringRPCService.class).doSomething(); } }
Guiceified Servlet:
Guice Integration
public class MyRPCImpl extends RemoteServiceServlet implements MyRPC { private final SomeService service; public MyRPCImpl(){ this.service = MyGuiceFactory.getInjector().getInstance(SomeService.class); } @Override public String doSomething() { return service.doSomething(); } }
» Command pattern
Reduces number of RPC servlets
Allows for smart RMI
» IoC Integration
Enables single bootstrap
Allows Dependency Injection
So far we’ve seen:
Summary
Replace the entire RPC mechanism:
» GWT offers static methods to process RPC
» We can use it to create our own RPC Servlets implementations
What else can we do?
DIY RPC:
DIY RPC:
HttpServlet.doPost()
RPCServletUtils. readContentAsGwtRpc(request)
HttpServletRequest request
RPC.decodeRequest(payload,…)
String payload
Invoke Method by reflection using: • rpcRequest.getMethod() • rpcRequest.getParameters()
RPCRequest request
RPC.encodeResponseForSuccess (rpcRequest.getMethod(), result,
serializationPolicy, serializationFlags);
RPCServletUtils.writeResponse (getServletContext(), response,
payload, gzipEncode)
String payload
Object result
IMPROVED REQUEST FACTORY
Request Factory
RequestFactory in a nutshell:
» Designed for Data oriented services
» Lightweight
» Integrates with any ORM framework
Request Factory
Created for data oriented services:
» Uses proxies as Data Transfer objects
» Uses JSON as a transport layer
» Transfers shallow object graph
» Tracks your domain objects, transfers only delta changes
» Loosely coupled with your sever side
The main players:
Domain definition RequestFactory equivalent
Entity EntityProxy
POJO ValueProxy
Service definitions RequestContext
Service implementations N/A
Request Factory
Request Factory
Other players:
» Entity Locators
▪ A way to track domain entities
» Service Locators
▪ A way to instantiate domain services
@Entity public class Person { @Id private Long id; private Integer version; private String firstName private String lastName @Embedded private Address address; ... }
@ProxyFor(value=Person.class, locator=MyLocator.class) public interface PersonProxy extends EntityProxy { Long getId(); Integer getVersion(); String getFirstName(); String getLastName(); //ValueProxy AddressProxy getAddress(); ... }
Entity mappings:
JPA Entity: Entity Proxy:
Request Factory
@Service(value=PersonDAO.class,locator = MyServiceLocator.class) public interface PersonRequest extends RequestContext { Request<Void> saveOrUpdate(PersonProxy person); Request<List<PersonProxy>> findAllPersons(); Request<PersonProxy> findPerson(Long id); }
RequestContext:
public class PersonDAO { public void saveOrUpdate(Person person); public List<Person> findAllPersons(); public Person findPerson(Long id); }
Our actual service:
Request Factory
public class PersonLocator extends Locator<Person,Long> { @Override public Person create(Class<? extends Person> clazz) { return new Person(); } @Override public Person find(Class<? extends Person> clazz, Long id) { return PersonDAO.find(id); } //more abstract methods to override… }
Entity Locator:
Request Factory
To track entities, we need an entity locator:
To instantiate the services, we need a service locator:
public class MyServiceLocator implements ServiceLocator { @Override public Object getInstance(Class<?> clazz) { try { return clazz.newInstance(); } catch ( //Damn you checked exceptions!!! ){ } } }
Request Factory
ServiceLocator:
Summary:
» Entities
Your JPA/JPO entities
» Entity Proxy
RequestFactory equivalents for your entities
» Value Proxy
RequestFactory equivalents for your pojos
» Request Context
Interfaces declaring your exposed services
» Entity Locators
An API that allows RequestFactroy to interact with the domain world
» Service Locators
An API that allows RequestFactroy to instantiate your servcies
Request Factory
A Chain of decorators:
Determines how RF interacts with the Domain world
Creates objects
Handles domain / proxy / domain mappings
Handles method invocations
We can override it ;-)
How does it work? Meet the ServiceLayerDecorator:
ServiceLayerDecorator
» Override default behaviors such as:
Instantiating locators
Instantiating service locators
Instantiating services
Domain / Proxy / Domain mapping
Domain method invocations
AOP like behavior
What can we do with it?
ServiceLayerDecorator
» Extend RequestFactoryServlet
» Create a ServiceLayerDecorator with access to your Guice injector
» Override the createServiceInstance method
Instantiate a service with Guice:
ServiceLayerDecorator
Extended RequestFactory Servlet:
ServiceLayerDecorator
public class GuiceRequestFactoryServlet extends RequestFactoryServlet { public GuiceRequestFactoryServlet() { super(new DefaultExceptionHandler(), new GuiceServiceLayerDecorator()); } }
Guicified ServiceLayerDecorator:
ServiceLayerDecorator
public class GuiceServiceLayerDecorator extends ServiceLayerDecorator { private Injector injector = MyGuiceFactory.getInjector(); @Override public Object createServiceInstance (Class<? extends RequestContext> clazz) { Class<?> serviceClass = getTop().resolveServiceClass(clazz); try { return injector.getInstance(clazz); } catch (RuntimeException e) { return super.createServiceInstance(requestContext); } } }
» Extend RequestFactoryServlet
» Create a ServiceLayerDecorator with access to your Spring context
» Override the createLocator method
Instantiate an entity Locator with Spring:
ServiceLayerDecorator
Extended RequestFactory Servlet:
ServiceLayerDecorator
public class SpringRequestFactoryServlet extends RequestFactoryServlet { public SpringRequestFactoryServlet() { super(new DefaultExceptionHandler(), new SpringServiceLayerDecorator()); } }
Springified ServiceLayerDecorator:
public class SpringServiceLayerDecorator extends ServiceLayerDecorator private final ApplicationContext applicationContext; public SpringServiceLayerDecorator(){
this.applicationContext = WebApplicationContextUtils.getWebApplicationContext( SpringRequestFactoryServlet.getThreadLocalServletContext());
} @Override public <T extends Locator<?, ?>> T createLocator(Class<T> clazz) { try { return applicationContext.getBean(clazz); } catch (BeansException e) { return super.createLocator(clazz); } } }
ServiceLayerDecorator
» Every proxy method must have a domain entity equivalent method
» Override this behavior with the ServiceLayerDecorator
Execute business method on entity proxies
Calculated Fields
Calculated Fields
@Entity public class Person { @Id private Long id; private Integer version; private String firstName private String lastName @Embedded private Address address; ... }
@ProxyFor(value=Person.class, locator=SomeLocator.class) public interface PersonProxy extends EntityProxy { Long getId(); Integer getVersion(); String getFirstName(); String getLastName(); //Doesn’t exist in domain entity String getDisplayName(); //ValueProxy AddressProxy getAddress(); ... }
JPA Entity: Entity Proxy:
Proxy modification:
Calculated Fields
@SkipInterfaceValidation @ProxyFor(value=Person.class, locator=SomeLocator.class) public interface PersonProxy extends BaseEntityProxy { Long getId(); Integer getVersion(); String getFirstName(); String getLastName(); @CalculatedField(SomeOtherSerivce.class) String getDisplayName(); //ValueProxy AddressProxy getAddress(); ... }
SLD Modification:
Calculated Fields
public class MyServiceLayerDecorator extends ServiceLayerDecorator{ //code is simplified due to size limits… @Override public Object getProperty(Object domainObject, String property) { try { return super.getProperty(domainObject, property); } catch (Throwable t) {
Class<? extends BaseEntityProxy> clientClass =
super.resolveClientType(domainObject.getClass(), BaseEntityProxy.class, false);
Method proxyMethod = clientClass.getDeclaredMethod(methodName);
CalculatedField annotation = proxyMethod.getAnnotation(CalculatedField.class);
Class<?> value = annotation.value();
Method domainMethod =
value.getDeclaredMethod(methodName,domainObject.getClass());
domainMethod.setAccessible(true);
return domainMethod.invoke(injector.getInstance(value), domainObject);
}
» RequestContext Inheritance
» Generify the EntityLocator
» JSR 303
What else can we do?
Enhanced Request Factory
» You can inherit a RequestContext
» You can use Generics, but with limitations:
Method parameter cannot be generic, casting is required
Method return type can be generic
Remove the Boilerplate:
RequestContext Inheritance
RequestContext Inheritance
public interface BasicRequestContext<T extends BaseEntityProxy> extends RequestContext { Request<Void> saveOrUpdate (BaseEntityProxy entityProxy); Request<T> find(Long id); }
@Service(value=PersonDAO.class,locator = MyServiceLocator.class) public interface PersonRequest extends BasicRequestContext<PersonProxy>{ }
Generic RequestContext:
Actual RequestContext:
One EntityLocator for all of your entities:
Generic EntityLocator
@Component public class BaseEntityLocator extends Locator<BaseEntity, Long> { @Autowired private BaseDAO baseDAO; @Override public BaseEntity create(Class<? extends BaseEntity > clazz) { return clazz.newInstance(); } @Override public BaseEntity find(Class<? extends BaseEntity > clazz, Long id) { return (BaseEntity) baseDAO.find(aLong, aClass); } //more abstract methods to override…
Validations? Just add water:
JSR 303
@Entity public class Person { @Id private Long id; private Integer version; @NotNull private String firstName private String lastName @Embedded private Address address; ... }
PersonProxy person = personRequest.create(PersonProxy.class); someEntityProxy.setfName(null); someEntityProxy.setlName("Garon"); someRequest.save(someEntityProxy).fire(new Receiver<Void>() { @Override public void onSuccess(Void response) { } @Override public void onConstraintViolation(Set<ConstraintViolation<?>> violations) { //Handle the violations } });
Server entity: Client side invocation:
THE REQUESTBUILDER
» Generic HTTP Requests
» Transports text
» Same Origin Policy
RequestBuilder in a nutshell:
Request Builder
» Consume data from external site
» Interact with lightweight text based services
» Download files (creativity required)
What can we use if for?
Request Builder
Simple service example:
public void executeQuery(String url, AsyncCallback<String>callback){ String url = “http://javaedge.com/ws/getSpeakersList/json” RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url); builder.sendRequest(null, new RequestCallback() { public void onResponseReceived(Request request, Response response) { String response = response.getText() callback.onSuccess(response); } }); }
Request Builder
File download flow example:
Send Http GET Request Using
RequestBuilder
Fetch file from Filesystem and prepare a temporary url to file
Return URL to file in HTTP Response
Parse returned url from RequestBuilder Response
Open a new window with the file URL
(GET Request) Client Side
Server Side
Request Builder
» Support easy encoding of Pojos to JSON structures
» Usable in non-GWT code
com.google.web.bindery
» Used in RequestFactory
AutoBean Framework:
Request Builder
public interface Person { String getFirstName(); void setFirstName(String firstName); String getLastName(); void setLastName(String lastName); }
AutoBean declaration:
public interface MyFactory extends AutoBeanFactory { AutoBean<Person> person(); }
AutoBean Factory declaration:
Request Builder
//Creating an AutoBean public Person createPerson(){ MyFactory factory = GWT.create(MyFactory.class); AutoBean<Person> person = factory.person(); return person.as(); } //Serializing a bean to JSON String serializeToJson(Person person) { AutoBean<Person> bean = AutoBeanUtils.getAutoBean(person); return AutoBeanCodex.encode(bean).getPayload(); } // Deserializing a bean from JSON Person deserializeFromJson(String json) { AutoBean<Person> bean = AutoBeanCodex.decode(factory, Person.class, json); return bean.as(); }
Using the Framework:
Request Builder
WIRING IT ALL TOGETHER
» Server Side:
1 x Guice Servlet Context Listener
1 x Guice Servlet Module
1 x RequestFactory Servlet
1 x Dispatcher Servlet
» Web.xml:
1 x Guice Filter
Required ingredients:
Guice your GWT App
public class MyContextListener extends GuiceServletContextListener{ @Override protected Injector getInjector() { return Guice.createInjector(new MyServletModule()); } }
public class MyServletModule extends ServletModule { @Override protected void configureServlets() { serve(“rf_url”).with(MyRequestFactoryServlet.class); serve(“rpc_ul”).with(MyDisptacherSerlet.class); Multibinder<ServiceLayerDecorator> binder = Multibinder.newSetBinder(binder(), ServiceLayerDecorator.class); binder.addBinding().to(MyServiceLayerDecorator.class); } }
ContextListener:
ServletModule:
Guice your GWT App
<web-app> <filter> <filter-name>guiceFilter</filter-name> <filter-class>com.google.inject.servlet.GuiceFilter</filter-class> </filter> <filter-mapping> <filter-name>guiceFilter</filter-name> <url-pattern>/*</ url-pattern > </filter-class> <listener> <listener-class>com.alphacsp.theedge.server.MyContextListener</listener-class> </filter-class> … </ web-app >
Web.xml:
Guice your GWT App
» Server Side:
1 x RequestFactory Servlet
1 x Dispatcher Servlet
» Web.xml:
1 x Spring Dispatcher Servlet
Required ingredients:
Spring your GWT App
<web-app> <servlet> <servlet-name>SpringGWT</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringGWT</servlet-name> <url-pattern>your_url_mapping</url-pattern> </servlet-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </filter-class> … </ web-app >
Web.xml:
Spring your GWT App
Application context:
<beans> <context:component-scan base-package="com.alphacsp.theedge "/> <mvc:annotation-driven/> </beans>
@Controller public class SpringRequestFactoryServlet extends RequestFactoryServlet { @Autowired public SpringRequestFactoryServlet (ServiceLayerDecorator... serviceDecorators){ super(new DefaultExceptionHandler(), serviceDecorators); } @Override @RequestMapping("/rf") protected void doPost(HttpServletRequest request, HttpServletResponse response){ super.doPost(request, response); } }
RequestFactory Servlet:
Spring your GWT App
RESOURCES
Resources
» Official Docs : https://developers.google.com/web-toolkit/doc/latest/tutorial/RPC
» Command Pattern: ▪ http://www.google.com/events/io/2009/sessions/Go
ogleWebToolkitBestPractices.html (YouTube) ▪ http://code.google.com/p/gwt-
platform/wiki/GettingStartedDispatch ▪ http://code.google.com/p/gwt-dispatch/
GWT RPC
Resources
» Official Docs: https://developers.google.com/web-toolkit/doc/latest/tutorial/RPC
» Lifecycle (Great read): http://fascynacja.wordpress.com/2011/04/17/exciting-life-of-entity-proxies-in-contexts-of-requestfactory/
RequestFactory
Resources
» Official Docs: http://code.google.com/p/google-guice/wiki/ServletModule
GWT Guice Servlet Module
Resources
» Howto:
▪ http://krams915.blogspot.com/2012/02/spring-31-gwt-maven-plugin-and_3309.html
▪ http://crazygui.wordpress.com/2011/12/06/spring-gwt-software-architecture-for-scalable-applications-part-1/
GWT Spring Integration
THANK YOU!