gwt enterprise edition

69
GWT Enterprise Edition By: Gilad Garon March 2012 #javaedge2012

Upload: gilad-garon

Post on 25-May-2015

992 views

Category:

Technology


8 download

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 Hibernate

TRANSCRIPT

Page 1: GWT Enterprise Edition

GWT Enterprise Edition

By: Gilad Garon

March 2012

#javaedge2012

Page 2: GWT Enterprise Edition

» Vanilla GWT

» Improved RPC

» Enhanced Request Factory

» Request Builder

» Tying it all together

Overview:

Agenda

Page 3: GWT Enterprise Edition

VANILLA GWT SERVER SIDE

Page 4: GWT Enterprise Edition

» 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

Page 5: GWT Enterprise Edition

» GWT RPC

Service oriented

» RequestFactory

Data oriented

» RequestBuilder

Manual HTTP Requests

Three Flavors of RMI:

Vanilla GWT Server Side

Page 6: GWT Enterprise Edition

Vanilla GWT RMI

RemoteServiceServlet

RequestFactoryServlet

HttpServlet

GWT Application WAR

HTTP (Async)

GWT server side application:

Page 7: GWT Enterprise Edition

» 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

Page 8: GWT Enterprise Edition

IMPROVED RPC

Page 9: GWT Enterprise Edition

» Each service is a standalone RPC Servlet

» Client invokes a generated interface

» Transports concrete java objects

GWT-RPC in a nutshell:

Vanilla RPC

Page 10: GWT Enterprise Edition

» 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

Page 11: GWT Enterprise Edition

» 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

Page 12: GWT Enterprise Edition

Dispatcher RPC Servlet

Architecture

Client Browser

Action

Action Result

Dispatch Service

Action Handler

Async Dispatcher

Command Pattern

Action

Result

Page 13: GWT Enterprise Edition

» 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

Page 14: GWT Enterprise Edition

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:

Page 15: GWT Enterprise Edition

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:

Page 16: GWT Enterprise Edition

» 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

Page 17: GWT Enterprise Edition

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(); } }

Page 18: GWT Enterprise Edition

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(); } }

Page 19: GWT Enterprise Edition

» 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

Page 20: GWT Enterprise Edition

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:

Page 21: GWT Enterprise Edition

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

Page 22: GWT Enterprise Edition

IMPROVED REQUEST FACTORY

Page 23: GWT Enterprise Edition

Request Factory

RequestFactory in a nutshell:

» Designed for Data oriented services

» Lightweight

» Integrates with any ORM framework

Page 24: GWT Enterprise Edition

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

Page 25: GWT Enterprise Edition

The main players:

Domain definition RequestFactory equivalent

Entity EntityProxy

POJO ValueProxy

Service definitions RequestContext

Service implementations N/A

Request Factory

Page 26: GWT Enterprise Edition

Request Factory

Other players:

» Entity Locators

▪ A way to track domain entities

» Service Locators

▪ A way to instantiate domain services

Page 27: GWT Enterprise Edition

@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

Page 28: GWT Enterprise Edition

@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

Page 29: GWT Enterprise Edition

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:

Page 30: GWT Enterprise Edition

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:

Page 31: GWT Enterprise Edition

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

Page 32: GWT Enterprise Edition

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

Page 33: GWT Enterprise Edition

» 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

Page 34: GWT Enterprise Edition

» Extend RequestFactoryServlet

» Create a ServiceLayerDecorator with access to your Guice injector

» Override the createServiceInstance method

Instantiate a service with Guice:

ServiceLayerDecorator

Page 35: GWT Enterprise Edition

Extended RequestFactory Servlet:

ServiceLayerDecorator

public class GuiceRequestFactoryServlet extends RequestFactoryServlet { public GuiceRequestFactoryServlet() { super(new DefaultExceptionHandler(), new GuiceServiceLayerDecorator()); } }

Page 36: GWT Enterprise Edition

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); } } }

Page 37: GWT Enterprise Edition

» Extend RequestFactoryServlet

» Create a ServiceLayerDecorator with access to your Spring context

» Override the createLocator method

Instantiate an entity Locator with Spring:

ServiceLayerDecorator

Page 38: GWT Enterprise Edition

Extended RequestFactory Servlet:

ServiceLayerDecorator

public class SpringRequestFactoryServlet extends RequestFactoryServlet { public SpringRequestFactoryServlet() { super(new DefaultExceptionHandler(), new SpringServiceLayerDecorator()); } }

Page 39: GWT Enterprise Edition

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

Page 40: GWT Enterprise Edition

» Every proxy method must have a domain entity equivalent method

» Override this behavior with the ServiceLayerDecorator

Execute business method on entity proxies

Calculated Fields

Page 41: GWT Enterprise Edition

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:

Page 42: GWT Enterprise Edition

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(); ... }

Page 43: GWT Enterprise Edition

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);

}

Page 44: GWT Enterprise Edition

» RequestContext Inheritance

» Generify the EntityLocator

» JSR 303

What else can we do?

Enhanced Request Factory

Page 45: GWT Enterprise Edition

» 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

Page 46: GWT Enterprise Edition

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:

Page 47: GWT Enterprise Edition

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…

Page 48: GWT Enterprise Edition

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:

Page 49: GWT Enterprise Edition

THE REQUESTBUILDER

Page 50: GWT Enterprise Edition

» Generic HTTP Requests

» Transports text

» Same Origin Policy

RequestBuilder in a nutshell:

Request Builder

Page 51: GWT Enterprise Edition

» Consume data from external site

» Interact with lightweight text based services

» Download files (creativity required)

What can we use if for?

Request Builder

Page 52: GWT Enterprise Edition

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

Page 53: GWT Enterprise Edition

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

Page 54: GWT Enterprise Edition

» Support easy encoding of Pojos to JSON structures

» Usable in non-GWT code

com.google.web.bindery

» Used in RequestFactory

AutoBean Framework:

Request Builder

Page 55: GWT Enterprise Edition

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

Page 56: GWT Enterprise Edition

//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

Page 57: GWT Enterprise Edition

WIRING IT ALL TOGETHER

Page 58: GWT Enterprise Edition

» 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

Page 59: GWT Enterprise Edition

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

Page 60: GWT Enterprise Edition

<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

Page 61: GWT Enterprise Edition

» Server Side:

1 x RequestFactory Servlet

1 x Dispatcher Servlet

» Web.xml:

1 x Spring Dispatcher Servlet

Required ingredients:

Spring your GWT App

Page 62: GWT Enterprise Edition

<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

Page 63: GWT Enterprise Edition

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

Page 64: GWT Enterprise Edition

RESOURCES

Page 66: GWT Enterprise Edition

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

Page 67: GWT Enterprise Edition

Resources

» Official Docs: http://code.google.com/p/google-guice/wiki/ServletModule

GWT Guice Servlet Module

Page 68: GWT Enterprise Edition

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

Page 69: GWT Enterprise Edition

THANK YOU!