EntityProxy for an interface

141 views
Skip to first unread message

John Maitland

unread,
Jan 7, 2011, 1:37:27 PM1/7/11
to Google Web Toolkit
I’m having a problem using the new RequestFactory feature in 2.1.1
when trying to serialise an object, using the EntityProxy, with using
it's interface. If I have the following entity proxy, entity interface
and its implementation, a ‘UnexpectedException: The domain type
com.server.MyEntity cannot be sent to the client’.

@ProxyFor(value = MyEntity.class)
public interface MyEntityProxy extends EntityProxy {
String getAProperty();
}

public class MyEntityImpl implements MyEntity {
....
}

public interface MyEntity {
String getAProperty();
}

This doesn’t work, because the getSupertypes method in the
RequestFactoryInterfaceValidator return a list of the following types:
- Lcom/server/MyEntityImpl
- Ljava/lang/Object;
- Lcom/server/MyEntity

And getEntityProxyTypeName in RequestFactoryInterfaceValidator breaks
from the supertypes upcast loop if an object type is found before the
required type defined on the entityproxy (i.e. never reaches my
interface).

If I change the value of the ProxyFor from the interface to its
implementation then it works (i.e. @ProxyFor(MyEntityImpl.class),
which is not desirable.

Does anyone know if this is possible or a limitation?

Regards,
John

David Chandler

unread,
Jan 7, 2011, 2:09:48 PM1/7/11
to google-we...@googlegroups.com
John,

Currently, @ProxyFor must reference the Entity itself, which, in
2.1.0, required you to put static methods on the entity to do
persistence operations. However, you can use the new Locator and
ServiceLocator in 2.1.1 to move persistence methods out of the entity
into an entity service (perhaps a DAO). I just put up some example
code at http://code.google.com/p/listwidget. It's definitely a work in
progress (== not pretty yet), but the RF stuff there works.

/dmc

> --
> You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.
> To post to this group, send email to google-we...@googlegroups.com.
> To unsubscribe from this group, send email to google-web-tool...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.
>
>

--
David Chandler
Developer Programs Engineer, Google Web Toolkit
w: http://code.google.com/
b: http://googlewebtoolkit.blogspot.com/
t: @googledevtools

John Maitland

unread,
Jan 8, 2011, 5:33:29 AM1/8/11
to Google Web Toolkit
David,
Thank you for the reply. I already have a Locator and a
ServiceLocator, but left it out of the above example for simplicity.
After reading the source, all the entity proxies map to classes and
not interfaces. Hence, if your AppUser class was defined as an
interface and a implementation:

public interface AppUser extends DatastoreObject {
String getEmail()
void setEmail(String email)
}

public class AppUserImpl extends DatastoreObjectImpl implements
AppUser {
....
}

@ProxyFor(value=AppUser.class, locator=ObjectifyLocator.class)
public interface AppUserProxy extends DatastoreObjectProxy {
String getEmail();
}

Then I don’t think this arrangement would currently work because of
the getSupertypes in the RequestFactoryInterfaceValidator class.
Therefore, by ‘must reference the Entity itself’ you mean an
implementation (the type of the actual object from the service) and
it’s not possible to supply an interface in the @ProxyFor?

John

p.s. I ask because my implementations are in a separate data access
layer and are only exposes through its interfaces, called by my
services.

David Chandler

unread,
Jan 8, 2011, 10:38:10 AM1/8/11
to google-we...@googlegroups.com
Ah, thanks for the clarification. Your findings are correct: @ProxyFor is designed to specify an entity implementation only, not an interface. Ideally, it's just a POJO with getters and setters. However, Locator/ServiceLocator is designed to move data access methods out of entities into services. Is it a possibility for you to move data access methods in your entity impls into an entity service instead?

/dmc


--
You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.
To post to this group, send email to google-we...@googlegroups.com.
To unsubscribe from this group, send email to google-web-tool...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.

John Maitland

unread,
Jan 9, 2011, 11:42:01 AM1/9/11
to Google Web Toolkit
Frustratingly, as I know I'm so close, all my entities (both the
implementation and the interface) only contain getter and setter
(simple POJO for use in Hibernate). All my data access methods
currently reside in the entity service, as recommended. Now in the
entity service, I could do a horrible cast from the interface back to
its implementation and send that back to the client. However I have a
custom ServiceLayerDecorator, to use Spring to create my Locators (for
the find method), and hence I can override the following...

@Override
public <T> Class<? extends T> resolveClientType(Class<?>
domainClass, Class<T> clientType, boolean required) {
ProxyFor a = clientType.getAnnotation(ProxyFor.class);

if (a != null && a.value().isAssignableFrom(domainClass)) {
return super.resolveClientType(a.value(), clientType,
required);
}
return super.resolveClientType(domainClass, clientType,
required);
}

Do you see any problems with this approach? It works for the above
example.

John

P.S. Also using a ServiceLocator to intergrate with Spring. Spring and
GWT play nicely together now.

Thomas Broyer

unread,
Jan 9, 2011, 12:27:23 PM1/9/11
to google-we...@googlegroups.com


On Sunday, January 9, 2011 5:42:01 PM UTC+1, John Maitland wrote:
Frustratingly, as I know I'm so close, all my entities (both the
implementation and the interface) only contain getter and setter
(simple POJO for use in Hibernate). All my data access methods
currently reside in the entity service, as recommended. Now in the
entity service, I could do a horrible cast from the interface back to
its implementation and send that back to the client. However I have a
custom ServiceLayerDecorator, to use Spring to create my Locators (for
the find method), and hence I can override the following...

    @Override
    public <T> Class<? extends T> resolveClientType(Class<?>
domainClass, Class<T> clientType, boolean required) {
        ProxyFor a = clientType.getAnnotation(ProxyFor.class);

        if (a != null && a.value().isAssignableFrom(domainClass)) {
            return super.resolveClientType(a.value(), clientType,
required);

Because you change the value, you should use getTop().resolveClientType instead of super.resolveClientType.

John Maitland

unread,
Jan 15, 2011, 8:28:57 AM1/15/11
to Google Web Toolkit
Thanks for the advise. After using getTop().resolveClientType this
open up further compilications, not least a circular dependency in the
layers. However, the next problem was intergrating this with a custom
Locator, which resolves the domain class with the BaseProxy and not
the actual proxy for the domain class (therefore above code doesnt
work with BaseProxy). Therefore I put in a further cache map the
ProxyFor class to the client class.

Problem solved!

har_shan

unread,
Mar 18, 2011, 3:09:20 AM3/18/11
to John Maitland, google-we...@googlegroups.com
i have got the same problem to solve and inspired by John's solution,
i have done something like this on top of code provided above and it
seem to solve the problem atleast for a sample that i tried. would be
great if John or Thomas or anyone could review it and point me if
anything is wrong. Also John, would be great if you could share your
complete code.

This comes in the custom Service Locator that i hooked in via custom
Request Factory Servlet class:

Also see it formatted here: http://pastie.org/1685351

private final static Map<Class<?>, Class<? extends BaseProxy>>
s_proxyTypeCache = new HashMap<Class<?>, Class<? extends
BaseProxy>>();

@SuppressWarnings("unchecked")


@Override
public <T> Class<? extends T> resolveClientType(Class<?> domainClass,
Class<T> clientType, boolean required) {

/*-
* Inspired by
* http://groups.google.com/group/google-web-toolkit/browse_thread/thread/dddd36d0ed4f87be/45af985914ac1780?lnk=gst&q=maitland
* as we mostly use interface type of domain in ProxyFor annotation.
* Assumption:
* Type <T> should be one that is/extends BaseProxy
*/
Class<? extends T> c = null;
ProxyFor p = clientType.getAnnotation(ProxyFor.class);
if (p != null && p.value().isAssignableFrom(domainClass)) {
// job made simple as clientType passed was the real client proxy
// that we defined
c = super.resolveClientType(p.value(), clientType, required);
} else {
// ProxyFor won't be present obviously if clientType param was
// BaseProxy itself
c = super.resolveClientType(domainClass, clientType, required);
if (c != null) {
// we might need/use it later
s_proxyTypeCache.put(domainClass,
(Class<? extends BaseProxy>) c);
} else {
// client type couldn't be retrieved if we had given interface
// type of domain obj in ProxyFor anno and if Locator had
// resolved it to the actual impl type of domain obj and passed
// it as domainClass; See if we already had gotten and stored
// the client type for the same domain's corresponding interface
// type
Set<Class<?>> domainTypes = s_proxyTypeCache.keySet();
for (Iterator<Class<?>> iterator = domainTypes.iterator();
iterator
.hasNext();) {
Class<?> domType = iterator.next();
if (domType.isAssignableFrom(domainClass)) {
// good, we have an entry for domain obj's
// interface/class type
c = (Class<? extends T>) s_proxyTypeCache.get(domType);
break;
}
}
}
}
if (null == c) {
throw new YourApplicationRuntimeException(
"Unable to resolve the client side custom defined proxy class
given the i/p {domain class : "
+ domainClass
+ ", "
+ "client type : "
+ clientType + "}");
}
return c;
}

Ignacio Baca Moreno-Torres

unread,
Nov 16, 2012, 9:53:15 AM11/16/12
to google-we...@googlegroups.com, jfgma...@googlemail.com
Hi! I have the same problem and after solving the issue and reading this posts I have some opinions I will like to share.

I think this is a conceptual problem. RequestFactory cant solve the parent hierarchy because of multiple inheritance of interfaces. If you have a class "class A implements B,C", which domain type will be the parent? If you assume that the solution must be to define a concrete superclass. So I try two options.

1. Change the interface and use an abstract class with all methods abstract. This is quite simple and solves the problem instantly. But, if you need an interface because of multiple inheritance in some class of your code this is wont be an option.

2. Explicitly define wich of the interfaces is the parten of the domain object. This is my preferred option. I create a annotation named @Parent (also @ChildOf might be used), exactly like @ProxyOf but used in the server-side. So you can define the implementation class as "@Parent(SomeEntity.class) class SomeEntityImpl implements SomeEntity, SomeOtherInterface...". Finally I extends the resolveClientType method in my application service decorator.

@Override
public <T> Class<? extends T> resolveClientType(Class<?> domainClass, Class<T> clientType, boolean required) {
    Parent parent = domainClass.getAnnotation(Parent.class);
    if (parent != null) {
        return getTop().resolveClientType(parent.value(), clientType, required);
    } else {
        return super.resolveClientType(domainClass, clientType, required);
Reply all
Reply to author
Forward
0 new messages