GWT manually serialize domain object on server
Asked Answered
B

4

5

The first thing my GWT app does when it loads is request the current logged in user from the server via RequestFactory. This blocks because I need properties of the User to know how to proceed. This only takes < 500ms, but it really annoys me that the app is blocked during this time. I already have the User on the server when the jsp is generated, so why not just add the serialized User to the jsp and eliminate this request altogether?

I have two problems keeping me from doing this:

  • I need to transform User to UserProxy
  • I need to serialize UserProxy in a way that is easy for GWT to deserialize.

I have not figured out a good way to do #1. This logic appears to be buried in ServiceLayerDecorator without an easy way to isolate? I may be wrong here.

The second one seems easier via ProxySerializer But how do I get my hands on the requestfactory when I am on the server? You cannot call GWT.create on the server.

I have been looking into AutoBeans but this does not handle #1 above. My UserProxy has references to collections of other EntityProxy's that I would like to maintain.

Baruch answered 17/3, 2012 at 18:40 Comment(0)
B
5

It is possible using AutoBeans if you create an AutoBeanFactory for your proxies:

  • To transform User to UserProxy: Create a server side RequestFactory and invoke the same normal request. Response will contain UserProxy (but on the server).

  • To serialize UserProxy:

    AutoBean<UserProxy> bean = AutoBeanUtils.getAutoBean(receivedUserProxy);

    String json = AutoBeanCodex.encode(bean).getPayload();

  • To deserialize UserProxy on client:

    AutoBean<UserProxy> bean = AutoBeanCodex.decode(userAutoBeanFactory, UserProxy.class, json);

Creating an in-process RequestFactory on the server (tutorial):

public static <T extends RequestFactory> T create( Class<T> requestFactoryClass ) {
  ServiceLayer serviceLayer = ServiceLayer.create();
  SimpleRequestProcessor processor = new SimpleRequestProcessor( serviceLayer );
  T factory = RequestFactorySource.create( requestFactoryClass );
  factory.initialize( new SimpleEventBus(), new InProcessRequestTransport(processor) );
  return factory;
}
Birdwatcher answered 29/3, 2012 at 19:0 Comment(0)
B
3

You could use AutoBeans for this as well if you are able to make User implements UserProxy. It works since Proxies are interfaces with getters/setters:

interface UserFactory implements AutoBeanFactory
{
  AutoBean<UserProxy> user(UserProxy toWrap); // wrap existing instance in an AutoBean
}

Then on server you can create the autobean and serialize to json:

UserFactory factory = AutoBeanFactorySource.create(UserFactory.class)
AutoBean<UserProxy> userProxyBean = factory.user( existingUserPojo );

// to convert AutoBean to JSON
String json = AutoBeanCodex.encode(userProxyBean).getPayload();

On the client you can just use AutoBeanCodex.decode to deserialize JSON back to a bean

Birdwatcher answered 27/3, 2012 at 13:12 Comment(4)
Unfortunately User could not implement UserProxy since it has accessors such as OrganizationProxy getOrganization() where the return type differs whether we are a proxy or domain model.Baruch
True, RF won't let you do it in this case.Birdwatcher
I am not willing to admit there is no solution :) Colin's solution above comes really close. I just havent had time to dive in deeper and find where its getting caught up. Thanks for the help though!Baruch
I'm sure its possible, not sure if the API is publicBirdwatcher
A
1

You cannot call GWT.create on the server (or from any real JVM), but in many cases you can call a JVM-compatible method designed for server use instead. In this case, take a look at RequestFactorySource.create.

It can be a little messy to get the server to read from itself and print out data using RequestFactory - here is a demo example of how this can work (using gwt 2.4, the main branch has the same thing for 2.3 or so) https://github.com/niloc132/tvguide-sample-parent/blob/gwt-2.4.0/tvguide-client/src/main/java/com/acme/gwt/server/TvViewerJsonBootstrap.java - not quite the same thing that you are after, but it may be possible to use this same idea to populate a string in a proxy store that can be read in the client (seen here https://github.com/niloc132/tvguide-sample-parent/blob/gwt-2.4.0/tvguide-client/src/main/java/com/acme/gwt/client/TvGuide.java).

The basic idea is to create a request (including ids, invocations, and with() arguments so the proxy builder makes all the right pieces in a consistent way), and pass it into a SimpleRequestProcessor instance, which will then run it through the server pieces it normally would. (Any entity management system probably should still have the entities cached to avoid an additional lookup, otherwise you need to model some of the work SRP doesn internally.) The ProxySerializer, which wraps a ProxyStore, expects to have full RF messages as sent from the server, so a fair bit of message bookkeeping needs to be done correctly.

Anastigmatic answered 17/3, 2012 at 19:10 Comment(2)
This got me pretty far, but proxySerializer.get(UserProxy.class, UserProxy.STORE_KEY) is null. However, proxyStore.get(UserProxy.STORE_KEY) returns a Splittable that appears correct (at least when it spits out as a string). Its difficult to track down since so much of this seems to be generated code.Baruch
I wrote most of that (trial and error) about a year ago, and apparently have done a poor job of keeping it up to date - i'll try it again to bring it up to date, see what pieces I am missing.Anastigmatic
M
1

I found the answer on the GWT Google Group. All credits go to Nisha Sowdri NM.

Server side encoding:

DefaultProxyStore store = new DefaultProxyStore();
ProxySerializer ser = requests.getSerializer(store);
final String key = ser.serialize(userProxy);
String message = key + ":" + store.encode();

Client side decoding:

String[] parts = message.split(":", 2);
ProxyStore store = new DefaultProxyStore(parts[1]);
ProxySerializer ser = requests.getSerializer(store);
UserProxy user = ser.deserialize(UserProxy.class, parts[0]);
Mercurio answered 8/4, 2012 at 17:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.