Date post: | 25-May-2015 |
Category: |
Technology |
Upload: | gilad-garon |
View: | 993 times |
Download: | 8 times |
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!