GWT 2.1.1 RequestFactory Strange Exception

153 views
Skip to first unread message

zixzigma

unread,
Dec 20, 2010, 5:45:17 PM12/20/10
to Google Web Toolkit
I am using GWT 2.1.1 new RequestFactory Service layer,
but I keep getting exceptions instructing me to declare my Service
methods "static" and in my domain(entity) classes. (instead of a
separate service layer [the point of 2.1.1])

the formatted code for easy reading can be find here

http://paste.pocoo.org/show/307617/

when I move the findPerson method to the Person entity class, and
declare it static, it works,
but I don't know why the new @Service doesn't work.

in debug statements, I also verified that the version I am using is in
fact 2.1.1

Would be grateful if you can provide some tips or comments on how I
can make my code work.

Thank You

zixzigma

unread,
Dec 20, 2010, 9:14:47 PM12/20/10
to Google Web Toolkit
tracked down the problem,
it has to do with validation: " RequestFactoryInterfaceValidator"

this validation fails:

RequestFactoryInterfaceValidator v = new
RequestFactoryInterfaceValidator(
logger, new RequestFactoryInterfaceValidator.

ClassLoaderLoader(PersonRequest.class.getClassLoader()));

v.validateRequestContext(PersonRequest.class.getName());
assertFalse(v.isPoisoned());


as stated in previous post, the source code can be found here:
http://paste.pocoo.org/show/307617/

I tried different combination of classes on @Service Annotation (also
locator attribute),
different combination of static/non-static. basically trial and error,
checked it with expenses sample, and RF wiki,
still can't get it to work.


I don't know what I am doing wrong, please Help : (

zixzigma

unread,
Dec 21, 2010, 3:33:37 AM12/21/10
to Google Web Toolkit
I realized I was using the 2.1 style to create my RequestFactory

I came across this, while digging into code:

* ServiceLayer serviceLayer = ServiceLayer.create();
* SimpleRequestProcessor processor = new
SimpleRequestProcessor(serviceLayer);
* EventBus eventBus = new SimpleEventBus();
* MyRequestFactory f =
RequestFactoryMagic.create(MyRequestFactory.class);
* f.initialize(eventBus, new InProcessRequestTransport(processor));

now I am going to experiment (in test mode) with this,
see if I am correct in my guess.

Henrik Schmidt

unread,
Dec 21, 2010, 4:42:09 AM12/21/10
to Google Web Toolkit
From the RequestFactory documentation:

Four special methods are required on all entities as they are used by
the RequestFactory servlet:
A no-arg constructor. This may be the implicit default constructor.
1. id_type getId() -- IDs can be String or Long
2. static entity_type findEntity(id_type id)
3. Integer getVersion()

You could argue that this means that you cannot easily make a separate
service and I believe you would be right. The only way to do this, I
believe, is to create a Locator for each proxy.

I asked the same question a couple of days ago. See
http://groups.google.com/group/google-web-toolkit/browse_thread/thread/c929fb1d4239f7e0

Matt Moriarity

unread,
Dec 21, 2010, 9:25:20 AM12/21/10
to google-we...@googlegroups.com
@ProxyFor(value = Person.class, locator = PersonLocator.class)

then having a PersonLocator which implements Locator<Person> would fix this for you.

But in order to get your static service methods (besides just the find method), you need a ServiceLocator for your service:

@Service(value = PersonService.class, locator = MyServiceLocator.class)

I'm not sure if the locator for the service is necessary if it has a default constructor. We use Spring, so we have a service locator that just pulls the bean out of spring.

zixzigma

unread,
Dec 21, 2010, 2:47:43 PM12/21/10
to Google Web Toolkit

On Dec 21, 6:25 am, Matt Moriarity <matt.moriar...@gmail.com> wrote:

> But in order to get your static service methods (besides just the find
> method), you need a ServiceLocator for your service:
>

do all these methods have to be static ?
I thought the idea behind 2.1.1 was to get rid of static ?


I am a bit confused on what is meant by the term "service" methods.

Normally we have a "Service Layer", and a "Data Access Layer"

Data Access Layer (all the DAOs), responsible for communicating with
persistent stores to find objects of a "given type"

findCustomerById, ByEmail, AllCustomers, AllCustomersWithCriteriaX
all of this goes into CustomerDAO

then you need a Service Layer, that uses many of these DAOs to satisfy
a use case.
PersonService, might use PersonDAO, AccountDAO, and more ..., performs
a business logic on them and take some action.

in RequestFactory 2.1.1, do you think the ServiceLayer and
ServiceLocator is just
for locating the DAOs or the Service objects as I described ?
(because in RequestFactory 2.1 even the DataAccess (DAOs) where inside
Entity class.

to achieve the complete isolation we need a ServiceLayer +
DataAccessLayer on top of our Entities.

with RF 2.1.1, can we specify just the Service method in GWT, and
leaving the job of dealing with DAOS to the Service object ?







> @ProxyFor(value = Person.class, locator = PersonLocator.class)
>
> then having a PersonLocator which implements Locator<Person> would fix this
> for you.
>
> @Service(value = PersonService.class, locator = MyServiceLocator.class)
>
> I'm not sure if the locator for the service is necessary if it has a default
> constructor. We use Spring, so we have a service locator that just pulls the
> bean out of spring.


I am planning to use it with Spring too.

in your case (spring), you need all 4 attributes ?

on PersonProxy we use @ProxyFor(EntityObject, EntityLocator)

on PersonService we use @Service(ServiceObject, ServiceObjectLocator)

so the EntityLocator is the DAO ? and ServiceLocator queries spring
context
to find the Service method to handle the EntityLocator ?

yet, all of these methods have to be static ?

Thank You

zixzigma

unread,
Dec 21, 2010, 3:35:15 PM12/21/10
to Google Web Toolkit
in RequestFactory 2.1.1 documentations (source code),
there are many references to Domain and Domain Environment,

I am unclear what is meant by Domain Environment.
is it on the server side where RequestFactory work stops and our work
begins ?

it helps if the documentaton clarify the vocabulary,
Locator, Service, ServiceLocator, Domain, Domain Environment,
ServiceLayer

is ServiceLayer a DAO layer or a true Service Layer.

ServiceLayer vs Domain Environment ? what is the distinction between
the two ?

zixzigma

unread,
Dec 21, 2010, 6:10:56 PM12/21/10
to Google Web Toolkit
Thank you guys for the tips.
I got it to work!
Thank you very much !

I noticed a VERY STRANGE behaviour.

following your suggestion, I declared:

@ProxyFor(MyPersonEntity, EntityLocator)
PersonProxy

@Service(MyPersonService, MyServiceLocator)
PersonService


I noticed when methods in PersonService are declared "static",
ServiceLocator is "bypassed", and is NEVER used by RequestFactory to
look up "PersonService"
RF directly goes to PersonService.
This was strange, because what is the point of ServiceLocator then, if
it is going to be bypassed ?

to force RF to go through ServiceLocator, you have to remove "static"
from your Service methods.

This is where things get even more STRANGE:

if I do not define a Locator,
I get exceptions complaining PersonRequest did not pass validation.

however if I define a Locator (by extending Locator Interface)
I then have to implement bunch of methods: find/create/getId/
getVersion

I return NULL from all methods, and it still works !

why is that? and why we are forced to define a Locator, if we already
have a Service defined ?
my Locator does nothing !

you can see the code here:
http://paste.pocoo.org/show/308153/


from the debug statements, I see these methods being invoked:
[1022][INFO ][main][EntityLocator:<init>:22] - #####EntityLocator
constructor called ####
[1022][INFO ][main][EntityLocator:getId:45] - ######### get Id
#################
[1023][INFO ][main][EntityLocator:getVersion:57] - ########### get
Version ###############

however, I returned null for all of them,
still the client receives the correct Id/Version as I set in my
PersonService#find method.

could someone from GWT Team please explain.

I am beginning to love the new RequestFactory, Thank You !

Thomas Broyer

unread,
Dec 21, 2010, 6:38:03 PM12/21/10
to google-we...@googlegroups.com
The getId method is used generate a "stable id" that's transmitted to the client so that the object can be identified when it's later updated, and the find() method (see below) can be called with the appropriate identifier. You don't have to define a getId() in your EntityProxy, and if you do, it'll be resolved as a property, not by calling getId.
The getVersion method is used to send to the client the version of the object it "knows about", so that when it send it back to the the server (generally with changes), the server can quickly bail if it knows that the server-side version is different (i.e. getVersion is called to send the version to the client, and also at the very beginning of the server-side processing to check that the version known by the client is not out-dated, i.e. an optimistic lock). It's also used, just before returning to the client, to detect if a given object has been changed by the service methods (RF then generates the appropriate payload in the response so that an UPDATE event is dispatched on the client-side)

The find method is called to update the domain entity when you send one from the client to the server (RF will actually only send deltas of your changes, not the whole object, that's (one of) the point(s) of RF), e.g. to pass it to a persist() service method.

The create method is called when you send to the server an object that you RequestContext#create()-d on the client-side.

The isLive method is called for each object sent over the wire on a given request (from client to server and from server to client) to possibly generate the payload in the response so that a DELETE event is dispatched client-side.

Finally, getDomainType is useless, it's never called.

zixzigma

unread,
Dec 21, 2010, 6:59:16 PM12/21/10
to Google Web Toolkit
Thank you, it all makes sense now.

However what you described is how GWT RF uses those details
to do its magic behind the scenes.

I am not clear what our responsibility is then ?
What should go inside the Locator ?

From the code I posted,
http://paste.pocoo.org/show/308153/

I don't know what I should put in there.

I have a ServiceLocator ( one per application),
that locates Services. those Services are independent from GWT RF,
they can connect to persistent stores, do whatever they want.
and return a retult/perform action that is expected by RequestContext.

and what you described for Locators
@ProxyFor(value=PersonEntity, locator=EntityLocator)
public interface PersonProxy {


from your explanation I understood why Locator is important,
but what should we/the developers put in it ?
and do we need one per Entity or one per Application ?

sorry I repeated myself, I am still a bit confused :" )

Thomas Broyer

unread,
Dec 22, 2010, 5:19:01 AM12/22/10
to google-we...@googlegroups.com


On Wednesday, December 22, 2010 12:59:16 AM UTC+1, zixzigma wrote:
Thank you, it all makes sense now.

However what you described is how GWT RF uses those details
to do its magic behind the scenes.

I am not clear what our responsibility is then ?

Provide the "right" values so RF can "do its magic" ?

What should go inside the Locator ?

From the code I posted,
http://paste.pocoo.org/show/308153/

I don't know what I should put in there.

I have a ServiceLocator ( one per application),
that locates Services. those Services are independent from GWT RF,
they can connect to persistent stores, do whatever they want.
and return a retult/perform action that is expected by RequestContext.

and what you described for Locators
@ProxyFor(value=PersonEntity, locator=EntityLocator)
public interface PersonProxy {


from your explanation I understood why Locator is important,
but what should we/the developers put in it ?
and do we need one per Entity or one per Application ?
 
Well, as always, it depends what you need and what you can do.

If you can implement a single Locator class that works for all your entities, then go with it; and otherwise make one for each entity.
For instance, if you have a base class for your entities that provides an ID and version, then you can easily cast any entity to that class to implement getId and getVersion, and you probably can implement getIdType by returning a fixed type.
You can clazz.newInstance() in the create() or use PersistenceManager#newInstace with JDO.
And you can easily "find" using the Class and ID with JPA using EntityManager#find(clazz,id), or with JDO using PersistenceManager#getObjectById(clazz,id).

Basically, you could very well have only one Locator class per "id type".

Oh, and something to keep in mind: the Locator and *your services* instances (not the ServiceLocator instances though) are cached aggressively and reused for all subsequent requests, so make sure they are thread-safe !
(have a look at the ServiceLayerCache class to see all "memoized" methods)
Message has been deleted

zixzigma

unread,
Dec 23, 2010, 4:11:42 PM12/23/10
to Google Web Toolkit

from your explanation I get the feeling that Locators are DAOs,
is this correct ?

placing calls like these: EntityManager#find(clazz,id) inside a
Locator,
means Locator is now responsible for accessing datastore to perform
actions.

it also means that our DAOs are somehow directly coupled with
RequestFactoryServlet.

A common design approach is to have a Service Layer and a
DataAccessLayer,
that communicate through interfaces.

when the request comes from the client, it gets routed to the Service
object responsible
for performing a business logic to fulfill clients request.

In doing so, the Service object, may interact with "a number of" DAOs,
to perform the task.
and if Required, this "Service object" returns the data that has to be
given to the client.

in GWT 2.1.1 RequestFactory Servlet,

ServiceLocator can be used to locate the Service Object responsible
for fulfilling client request.
However, how the ServiceObject communicates with server-side DAO to
get its data is the ServiceObject concern,
not the RequestFactoryServlet.
In otherwords, our Service Object itself knows how to locate DAOs,
therefore why do we need this Locator for our EntityObjects ?

Thomas Broyer

unread,
Dec 23, 2010, 4:33:37 PM12/23/10
to google-we...@googlegroups.com
RequestFactory sends diffs on the wire (client-to-server), so how would your Service Object (as you call it) would receive a "complete" domain object if the diff isn't applied to an object retrieved from the database/datastore?
Things are a bit more "blury" than "service layer on top of data access layer". If you want to name things, RequestFactoryServlet is part of your service layer, and delegates a few (a very few) things to DAOs (Locator, or static findXxx methods), and all the "business logic" to other pieces (services, as referenced in @Service/@ServiceName, or instance methods on your domain objects)

(as for the SL vs. DAL, I never bought that; the few times I used that "strict" approach, one was almost useless; JPA/JDO/whatever are the DAL, you hardly need an additional layer, and each layer removes some ways of optimizing things, notably database/datastore requests; but that's another debate)

zixzigma

unread,
Dec 23, 2010, 5:38:47 PM12/23/10
to Google Web Toolkit
Thank you so much for your feedback on this.
I am going to experiment more, using your guidelines.

as for the need for additional layer,
a valid use case could be this:

lets say you are going to deploy your app on GAE.
for the persistence, there are several options:

- JDO/JPA
- DataStore LowLevel APIs
- Objectify, Twig Persist

many of these technologies are new and subject to (rapid) change,
it is a good idea to use interfaces to declare our
DataAccess "contract" without exposing infrastructure related detail
that is specific
to each of those technologies.


if one has 20 domain objects(entities),
and for each entity,
certain requests/methods : findX, findXInRange, findByX,

it can get out of hand quickly.


it would be very difficult to swap one persistence technology for
another,
sometime down the road.

the idea is,
- loose coupling
- easily swapping infrastructure specific code, without affecting
other part of system
(easy refactoring, testing,)
- clearly defined contracts in the form of interfaces,
when you look at an interface you can see what is expected of this
DAO,
rather than going through hundreds of lines of codes, some of which
scattered with try/catch/finally blocks
(i am exaggerating a bit, IDEs can provide a quick overview)

having said that,
if one is working on a project that
uses a legacy database, a database that's been in that environment for
years,
where DBAs have developed custom scripts, and its been used in day to
day operation of that business/organization for many years,
and there are policies within that business/organization to continue
using that database in the future,
I agree, using abstractions might be an overkill.


As with most things, I believe it all depends on the requirements/
constraints.



zixzigma

unread,
Dec 23, 2010, 5:51:13 PM12/23/10
to Google Web Toolkit
SL vs DAL,

one example of how they are different is that:

let's say
we have a DAO for accessing a Customer entity
a DAO for accessing Account entity
a DAO for accessing History/Trend entity
// maybe not a good example, but just three separate entities

a Service(object) living in a ServiceLayer,
needs to access these three entities,
analyze them and based on a current condition, apply a policy,
and then notify the interested parties.

the Logic for sending Notification is in another Object.
the Logic for Accessing/Finding those entities are in another object.

our Service(object) responsibility is to perform some operation,
with the help of those other objects. (through collaboration).

in this case, our Service is not just a basic CRUD.

to complicate things further, (in a real world scenario),
not all of the data might come from same DataStore,
one using a local DataStore, one a WebService to retrieve realtime
data,

so when going beyond basic CRUD,
the need for a Separate(isolated) ServiceLayer and DataAccessLayer,
becomes more obvious.

Reply all
Reply to author
Forward
Message has been deleted
0 new messages