Download pdf - GWT Enterprise Edition

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!


Recommended