Adding a HibernateFilter to RPC

55 views
Skip to first unread message

jdwy

unread,
Dec 14, 2007, 1:27:25 PM12/14/07
to Google Web Toolkit Contributors
Hi all.
I was the one at the back of the room at Rob's GWT RPC talk who had a
HibernateFilter that he wanted to integrate into GWT. It's a quick and
dirty solution, but it works for me. All it really requires is one
line in ServerSerializationStreamWriter in order to activate it, but
with the current methods there's no way to override this functionality
without replacing all the RPC classes with tweaked versions of my
own.

Here's the 1 line change I need in
ServerSerializationStreamWriter.java
private enum ValueWriter {
OBJECT {
@Override
void write(ServerSerializationStreamWriter1529 stream,
Object instance) throws SerializationException {
stream.writeObject(HibernateFilter.filter(instance));
}
}
}

And here's the HibernateFilter.
public class HibernateFilter {
public static Object filter(Object instance) {
if (instance == null) {
return instance;
}

if (instance instanceof Date) {
return new java.util.Date(((java.util.Date) instance).getTime());
}

if (instance instanceof PersistentSet) {
HashSet<Object> hashSet = new HashSet<Object>();
PersistentSet persSet = (PersistentSet) instance;
if (persSet.wasInitialized()) {
hashSet.addAll(persSet);
}
return hashSet;
}
//... similar code as above for PersistentList, Bag, Map, etc...

if (instance.getClass().getName().contains("CGLIB")) {
if (Hibernate.isInitialized(instance)) {
CGLIBLazyInitializer cg = (CGLIBLazyInitializer) instance;
log.warn("On The Fly initialization: "+ cg.getEntityName());
return cg.getImplementation();
} else {
log.debug("Uninitialized CGLIB");
return null;
}
}
return instance;
}
}

I'd prefer to be able to integrate this simple filter into the
serialization stream without copying the RPC and
ServerSerializationStreamWriter classes into my project.

Playing around with a solution to this I've come up with something
that will allow me to write code such as the following.

ServerSerializationStreamWriter writer = new
ServerSerializationStreamWriter(
rpcRequest.getSerializationPolicy());

writer.setValueWriter(Object.class, new ValueWriter() {
public void write(
ServerSerializationStreamWriter stream,
Object instance) throws SerializationException
{

stream.writeObject(HibernateFilter.filter(instance));
}
});

return RPC.invokeAndEncodeResponse(
this, rpcRequest.getMethod(), rpcRequest
.getParameters(), writer);

The major changes are:
1) RPC.invokeAndEncodeResponse() now takes a
ServerSerializationStreamWriter instead of a SerializationPolicy
2) ValueWriter is now a public interface instead of an enumeration
3) ServerSerializationStreamWriter has a setValueWriter(Class,
ValueWriter) method for overriding defaults.


I'd love to hear another idea for how to integrate this. An
alternative would be to make RPC not final and allow encodeResponse()
to be overriden so I can setup my own SerializationWriter there, but I
assumed that that would be less popular. I know Hibernate4GWT includes
an RPCCopy class as well, so it's definitely something people are
dealing with now.

Oh and to be clear, I'm not advocating that my HibernateFilter is
included, just that there's some sort of RPC filter extension point
that's easily accesible.

Cheers,

-Jeff

http://www.myhippocampus.com

jdwy

unread,
Dec 14, 2007, 2:03:55 PM12/14/07
to Google Web Toolkit Contributors
Hi All,
I'm the guy from the back of Rob's great GWT RPC talk that had a
HibernateFilter he wanted to integrate into RPC more easily.
Essentially I've got a simple little filter that I'd like to pass all
my objects through before they hit the serializer. It's easy enough to
do, but with the current RPC and ServerSerializationWriter classes
it's difficult to hook in.

Here's the 1 line change I currently make to
ServerSerializationStreamWriter.java
OBJECT {
@Override
void write(ServerSerializationStreamWriter1529 stream,
Object instance) throws SerializationException {
stream.writeObject(HibernateFilter.filter(instance));
}
},

And here's the HibernateFilter I'm trying to include.
public class HibernateFilter {
private static final Logger log = Logger
.getLogger(HibernateFilter.class);

public static Object filter(Object instance) {
if (instance == null) {
return instance;
}
if (instance instanceof Date) {
return new java.util.Date(((java.util.Date) instance)
.getTime());
}

if (instance instanceof PersistentSet) {
HashSet<Object> hashSet = new HashSet<Object>();
PersistentSet persSet = (PersistentSet) instance;
if (persSet.wasInitialized()) {
hashSet.addAll(persSet);
}
return hashSet;
}
//Similar Code for PersistentList,Map,Bag, etc
if (instance.getClass().getName().contains("CGLIB")) {
if (Hibernate.isInitialized(instance)) {
CGLIBLazyInitializer cg = (CGLIBLazyInitializer)
instance;
log.warn("On The Fly initialization: "
+ cg.getEntityName());
return cg.getImplementation();
} else {
log.debug("Uninitialized CGLIB");
return null;
}
}
return instance;
}
}

I've started playing around with a different way of including the code
above that doesn't require making copies of both RPC and
ServerSerializationWriter. The changes I made let me write code such
as the following, from my RemoteServiceServlet


ServerSerializationStreamWriter writer = new
ServerSerializationStreamWriter(
rpcRequest.getSerializationPolicy());

writer.setValueWriter(Object.class, new ValueWriter() {
public void write(
ServerSerializationStreamWriter stream,
Object instance) throws SerializationException
{

stream.writeObject(HibernateFilter.filter(instance));
}
});

return
RPCWithHibernateSupport1529.invokeAndEncodeResponse(
this, rpcRequest.getMethod(), rpcRequest
.getParameters(), writer);

The 3 major changes are:
1) ValueWriter is now a public interface instead of a private
Enumeration
2) ServerSerializationWriter has a setValueWriter(Class, ValueWriter)
method for overriding.
3) RPCWithHibernateSupport now takes a ServerSerializationWriter
instead of a SerializationPolicy.

Just to be clear, I'm not lobbying that my HibernateFilter is
included, just that there's an easier extension point for
functionality of this kind. It seems like a legitimate place to add
extension as it's really the last gasp for java to have a say in what
get's passed to javascript. I know Hibernate4GWT ends up making an
RPCCopy as well, so it's definitely something people are doing.

I'd love to hear what people think of this solution, there's a number
of different approaches one could take. Making RPC no longer final
could be part of of a solution, but I assumed that would be less
popular. There could also be some SerializationFilter interface to
make this more explicit, but I figured I'd keep the changes more
general.

Cheers and sorry if this double posts. The first seems to be stuck in
limbo.

-Jeff
http://www.myhippocampus.com

ps Oh and anybody at the conference get the business card of those
fellows from Portland? The 'Bold' gwt guys?

Rob Jellinghaus

unread,
Dec 17, 2007, 6:17:11 PM12/17/07
to Google Web Toolkit Contributors
Thanks for posting this, Jeff. We are moving soon and my life has
been obliterated by a frenzy of packing, but I would really like
Miguel to take a quick look at this. In other words: +1 from me on
this general concept. Bruno might have something to say, too.

Cheers!
Rob
> -Jeffhttp://www.myhippocampus.com

mP

unread,
Dec 19, 2007, 4:43:42 PM12/19/07
to Google Web Toolkit Contributors
You might want to try rocket's new serialization and rpc module, which
takes a different approach to assigning custom serializers, amongst
other additional features.

http://rocket-gwt.googlecode.com/files/Rocket-0.39.zip

Rather than just registering a Serializer for a single class, the
approach taken is iterate thru a list of handlers. Each one is asked
in turn and the one that answers positively is responsible for
serializing that particular instance.

With this approach its quite easy to handle the hibernate instrumented
classes, such as PersistedSet or Timestamp etc.

A SetObjectWriter exists that takes any incoming Set (on the server)
and writes out a java.util.HashSet with the same elements.

Another ObjectWriter exists that writes out an CGLIB instrumented
classes so they appear as the uninstrumented supertype on the client.

I have not gotten around to reattaching incoming dtos during the
deserializing phase but that shouldnt be too hard (just need to copy
some code from hibernate4gwt :), and everything should be transparent
to hibernate users.

noon

unread,
Dec 22, 2007, 1:59:14 PM12/22/07
to Google Web Toolkit Contributors
Hello mP,

I am the author of hibernate4gwt. I am quite surprised that you are
talking of copying my code without having ever talked about it with
me...
I think we should cooperate to make GWT-Hibernate integration easier,
and I would like to be associated if hibernate4gwt code is used in the
HibernateFilter.

To be more constructive, the HibernateFilter code could be better :
- it does not tale care of javaassist proxy generation (which can
be used instead of CGLIB)
- converting Timestamp to Date can be harmful
- merge operation is missing : just removing the CGLIB enhancement
is insufficient, because Hibernate needs to know which properties are
lazy but not loaded when the POJO comes back to the server side.

Regards
Bruno

Miroslav Pokorny

unread,
Dec 22, 2007, 9:09:35 PM12/22/07
to Google-Web-Tool...@googlegroups.com
Hi

I didnt copy your code, sorry if it sounds that way.

I was attempting to make a more generic approach to handle translating instances on the server to instances that exist on the client.

On Dec 23, 2007 5:59 AM, noon <bruno.ma...@gmail.com> wrote:

Hello mP,

I am the author of hibernate4gwt. I am quite surprised that you are
talking of copying my code without having ever talked about it with
me...
I think we should cooperate to make GWT-Hibernate integration easier,
and I would like to be associated if hibernate4gwt code is used in the
HibernateFilter.

The *Hibernate* problem is just a symptom of the limits of how types are mapped by gwt rpc custom serializers. Solving the hibernate problem so instances are autmatically reattached when instances come from the client to the server or detaching on the way out doesnt take care of some other issues.

My comments about the hibernate problem were an attempt to highlight that the approach to mapping custom serializers can be improved. The 1:1 mapping justint powerful enough.
 

The special attention for cglib instrumented classes was a 5 min hack, and not as conclusive as your stuff. It was just a poc to show how a richer custom serializer mapping system can help solve problems like this automatically.






--
mP

noon

unread,
Dec 23, 2007, 9:24:12 AM12/23/07
to Google Web Toolkit Contributors
Hi Miroslav,

Thanks for the clarification.
I haven't look RPC enhancement in GWT 1.5 but it looks promising : I
will probably adapt my existing code to it and publish it when done.

Regards
Bruno

On Dec 23, 3:09 am, "Miroslav Pokorny" <miroslav.poko...@gmail.com>
wrote:
> Hi
>
> I didnt copy your code, sorry if it sounds that way.
>
> I was attempting to make a more generic approach to handle translating
> instances on the server to instances that exist on the client.
>

jdwy

unread,
Dec 23, 2007, 4:38:18 PM12/23/07
to Google Web Toolkit Contributors
@bruno
I agree that my HibernateFilter is a bit lacking. Actually I think the
code above is old anyway and the CGLib bit up there isn't what I
actually use, but it's pretty similar. I've found that relying on
merges typically cause me nothing but heartache over time, so I use
the command pattern for most everything from the client and this
simplifies the problems I need to solve quite a bit.

I think the goal of this discussion should be to make sure that GWT
RPC can easily support some sort of more advanced custom
serialization. I'm not sure that we're going to come to a consensus on
the _best_ way to deal with Hibernate, so just finding a way for me to
plug in my super simple version, but also something that would allow
you to plug in your more comprehensive solution seems to me like the
best step for now.

@mp
It seems like the approach you've taken does a good job of allowing
for custom serialization in a much more extensible way than the
current system. I know you've tweaked a bunch of the actual
serialization bits as well. I'm hesitant to switch away from the
google code for those bits, though I thought your blog posts made a
decently compelling case.

@google
Any input on how we could get some of miroslav's ease of custom
serialization into gwt-rpc? This is definitely an ugly duckling.

-Jeff

Miroslav Pokorny

unread,
Dec 24, 2007, 2:06:05 AM12/24/07
to Google-Web-Tool...@googlegroups.com
On Dec 24, 2007 8:38 AM, jdwy <jdw...@gmail.com> wrote:

@bruno
I agree that my HibernateFilter is a bit lacking. Actually I think the
code above is old anyway and the CGLib bit up there isn't what I
actually use, but it's pretty similar. I've found that relying on
merges typically cause me nothing but heartache over time, so I use
the command pattern for most everything from the client and this
simplifies the problems I need to solve quite a bit.

I think the goal of this discussion should be to make sure that GWT
RPC can easily support some sort of more advanced custom
serialization. I'm not sure that we're going to come to a consensus on
the _best_ way to deal with Hibernate, so just finding a way for me to
plug in my super simple version, but also something that would allow
you to plug in your more comprehensive solution seems to me like the
best step for now.

Pray tell, how did you approach the hibernate problem ?
 

@mp
It seems like the approach you've taken does a good job of allowing
for custom serialization in a much more extensible way than the
current system. I know you've tweaked a bunch of the actual
serialization bits as well. I'm hesitant to switch away from the
google code for those bits, though I thought your blog posts made a
decently compelling case.

@google
Any input on how we could get some of miroslav's ease of custom
serialization into gwt-rpc? This is definitely an ugly duckling.

You also want blacklisting types from serializing, because once a type is marked as Serializable theres no way to reverse that :(.

-Jeff

noon

unread,
Dec 24, 2007, 8:26:29 AM12/24/07
to Google Web Toolkit Contributors
Hi all,

I agree with Jeff : the first thing is to define a way to *properly*
extend the GWT-RPC mechanism (by defining a list of filter to apply
for example).
I think it is not technically very difficult, but it is probably a
question of willing from the GWT team...

Regards
Bruno

On Dec 24, 8:06 am, "Miroslav Pokorny" <miroslav.poko...@gmail.com>
> --
> mP

jdwy

unread,
Dec 29, 2007, 7:51:04 PM12/29/07
to Google Web Toolkit Contributors
Miroslav,
As I mentioned above, I have just been copy-pasting
ServerSerializationStreamWriter and RPC (to call the new copy pasted
SSSW) and then changing 1 line in SSSW to run object through my filter
before serialization. The filter is essentially the code above. I just
wack hibernate things and change hibernate collections to standard
java ones. I'd written solutions that did all of this themselves, but
since GWT serialization is already doing a good job of all of the
recursive, reflective bits it seemed to make far more sense to just
insert the necessary logic there.

What I have now works well for me and makes for a pretty seemless
programming experience where I don't need to worry about what goes
over the wire. No calling getRidOfHibernate() on all objects or
anything like that. I still need to remember not to go off the end of
the object graph on the client, but this is pretty much going to be
the case no matter what. Your code allows for re-attachments of
objects, but I've been happy to steer clear of this. Hibernate merging
has always led to more problems than it's solved for me, so I heavily
favor the command pattern. The only problem with what I have now is
that it's a maintenance bummer and downright ugly to have to copy
these classes.

I feel like this is a pretty standard sticking point for anyone using
hibernate and possibly others libraries as well. I think offering GWT
developers a last chance to tweak their object before they enter
JavaScript-land seems like a pretty logical addition to RPC.

-Jeff

On Dec 24, 2:06 am, "Miroslav Pokorny" <miroslav.poko...@gmail.com>
> --
> mP- Hide quoted text -
>
> - Show quoted text -- Hide quoted text -
>
> - Show quoted text -

Rob Jellinghaus

unread,
Dec 31, 2007, 4:20:12 AM12/31/07
to Google Web Toolkit Contributors
On Dec 29, 4:51 pm, jdwy <jdw...@gmail.com> wrote:
> I feel like this is a pretty standard sticking point for anyone using
> hibernate and possibly others libraries as well. I think offering GWT
> developers a last chance to tweak their object before they enter
> JavaScript-land seems like a pretty logical addition to RPC.

Hey Jeff, thanks for carrying on the conversation. I'm so
unbelievably swamped in house-moving stuff that I will probably not do
any more open source hacking until July. But I'll channel Miguel (the
lead RPC developer) for a minute.

I think what you should do now is create a GWT issue in the GWT issue
tracker for this change, and then submit -- in that issue -- a
specific patch against the latest revision of the GWT trunk. (If you
lack the tools to make a proper patch -- I did when I did my GWT work
-- then just submit a comment which attaches your modified SSSW class
and proposed filter interface. You should probably also think through
how you want to extend the SSSW API to allow customizing the attached
filter, and include that in the patch also.)

The GWT team seems to be very quiet -- no doubt happily enjoying the
holidays before the final 1.5 release crunch -- so pushing this into
the issue tracker is the best way to ensure it doesn't fall off the
radar.

Cheers!
Rob

John Tamplin

unread,
Dec 31, 2007, 1:46:22 PM12/31/07
to Google-Web-Tool...@googlegroups.com
On Dec 31, 2007 4:20 AM, Rob Jellinghaus <rjelli...@gmail.com> wrote:
The GWT team seems to be very quiet -- no doubt happily enjoying the
holidays before the final 1.5 release crunch -- so pushing this into
the issue tracker is the best way to ensure it doesn't fall off the
radar.

Yes, I think only two of us didn't take off from before Christmas until New Years, and several people won't be back until Thursday.

--
John A. Tamplin
Software Engineer, Google
Reply all
Reply to author
Forward
0 new messages