You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cayenne.apache.org by Kevin Menard <km...@servprise.com> on 2007/10/27 18:20:28 UTC

Cleaning up ROP

Hi all,

I've just gone back to work on a ROP application I have and am again running
into the issue of having to duplicate code between the server model
subclasses and the client model subclasses.  I've got a way of working
around that, but I'm thinking this is probably best handled by Cayenne.  In
particular, I really don't think that there should be two class hierarchies
for what amounts to a boolean field (remote/not remote).

I think the generated superclass should perform the duty of calling the
appropriate method for reading and writing properties.  I think this would
advocate adding an isRemote() method to the ObjectContext interface to avoid
having to do instanceof operations.

There would have to be some sort of unification between PersistentObject and
CayenneDataObject as well.  My naïve guess is that the latter could extend
the former, but I haven't investigated whether or not they are compatible.

Does this sound like a reasonable approach?  If so, I'll probably dig in on
that.

Thanks,
Kevin


Re: Cleaning up ROP

Posted by Kevin Menard <km...@servprise.com>.
Looks like the time away paid off.  I've made it farther now.

Turns out the problem was the ivar fields in subclass.  Since they get
loaded lazily, the serialized object differed as soon as I accessed a field.
So, serialization may not have been broken per se, but it definitely messed
with the Tapestry squeezer.  Modifying the velocity template to mark those
fields as transient has taken care of the problem for now.  I'll see where
that takes me this weekend.

Ari, I'll try to cobble together a patch as I clean things up.  I agree that
a new branch may be the best thing.  While there is certainly value in
waiting until the 1.4 and 1.5 modules merge, I don't think it's that
necessary before we start a new branch.

-- 
Kevin 


On 11/7/07 4:57 PM, "Aristedes Maniatis" <ar...@maniatis.org> wrote:

> 
> On 08/11/2007, at 5:33 AM, Kevin Menard wrote:
> 
>> At this point, I'm going to have to table the work for a bit.
>> After two
>> days, I'm not much further along than when I started :-/  Maybe the
>> time
>> away will help me think about the problem a bit more.  In the interim,
>> at least the copy & paste method works . . . just more invasive than I
>> was hoping for.
> 
> Perhaps you could post a patch for what you've done so far. This
> feature is important to me too and if there is any time for my team
> or I to work on it we will try. I guess we could even create a
> temporary svn branch for it if that helps, but probably after the
> impending java 5 move would be better.
> 
> Ari
> 
> 
> -------------------------->
> Aristedes Maniatis
> phone +61 2 9660 9700
> PGP fingerprint 08 57 20 4B 80 69 59 E2  A9 BF 2D 48 C2 20 0C C8
> 
> 

-- 



Re: Cleaning up ROP

Posted by Aristedes Maniatis <ar...@maniatis.org>.
On 08/11/2007, at 5:33 AM, Kevin Menard wrote:

> At this point, I'm going to have to table the work for a bit.   
> After two
> days, I'm not much further along than when I started :-/  Maybe the  
> time
> away will help me think about the problem a bit more.  In the interim,
> at least the copy & paste method works . . . just more invasive than I
> was hoping for.

Perhaps you could post a patch for what you've done so far. This  
feature is important to me too and if there is any time for my team  
or I to work on it we will try. I guess we could even create a  
temporary svn branch for it if that helps, but probably after the  
impending java 5 move would be better.

Ari


-------------------------->
Aristedes Maniatis
phone +61 2 9660 9700
PGP fingerprint 08 57 20 4B 80 69 59 E2  A9 BF 2D 48 C2 20 0C C8



RE: Cleaning up ROP

Posted by Kevin Menard <km...@servprise.com>.
Interestingly, I can't get the code to hit readObject() when using CDO.
writeObject() is definitely being called from the CDO, though.  So, it's
like something with the Tapesty data squeezer.

When using a PO, readObject and writeObject are both called (added by
me).

In no event does DataContext's readObject or writeObject execute.

At this point, I'm going to have to table the work for a bit.  After two
days, I'm not much further along than when I started :-/  Maybe the time
away will help me think about the problem a bit more.  In the interim,
at least the copy & paste method works . . . just more invasive than I
was hoping for.

-- 
Kevin

-----Original Message-----
From: Andrus Adamchik [mailto:andrus@objectstyle.org] 
Sent: Wednesday, November 07, 2007 11:23 AM
To: dev@cayenne.apache.org
Subject: Re: Cleaning up ROP

I think we can solve serialization problems... CDO should be  
reattached indirectly inside DataContext.readObject(..). I am  
surprised that didn't work for PersistentObject. You may add a  
breakpoint in DataContext.readObject(..) to see what really happens  
there.

Andrus


Re: Cleaning up ROP

Posted by Andrus Adamchik <an...@objectstyle.org>.
I think we can solve serialization problems... CDO should be  
reattached indirectly inside DataContext.readObject(..). I am  
surprised that didn't work for PersistentObject. You may add a  
breakpoint in DataContext.readObject(..) to see what really happens  
there.

Andrus


On Nov 7, 2007, at 6:14 PM, Kevin Menard wrote:

> Well, I've made marginal progress there as well.  I am wrestling  
> with a
> serialization problem right now.  I know you had mentioned issues with
> this earlier, but it's the object context that's behaving differently
> from CayenneDataObject.
>
> I've had a Tapestry 4 app that's used lists of CDOs in @For loops and
> has just worked.  As PersistentObjects, they lose their data  
> context on
> deserialization.  No custom squeezers are involved.  What's really
> interesting is that the contexts are marked as transient in both  
> CDO and
> PO.
>
> I suspect once that's squared away, the rest won't be nearly as bad.
>
> -- 
> Kevin
>
> -----Original Message-----
> From: Andrus Adamchik [mailto:andrus@objectstyle.org]
> Sent: Wednesday, November 07, 2007 3:53 AM
> To: dev@cayenne.apache.org
> Subject: Re: Cleaning up ROP
>
> Hi Kevin,
>
> CayenneDataObject approach is fundamentally different from other
> persistent object implementations that are all variations of POJO. So
> I think a different approach may work better - using current client
> objects on the server (instead of using current server objects on the
> client).
>
> Andrus
>


RE: Cleaning up ROP

Posted by Kevin Menard <km...@servprise.com>.
Well, I've made marginal progress there as well.  I am wrestling with a
serialization problem right now.  I know you had mentioned issues with
this earlier, but it's the object context that's behaving differently
from CayenneDataObject.

I've had a Tapestry 4 app that's used lists of CDOs in @For loops and
has just worked.  As PersistentObjects, they lose their data context on
deserialization.  No custom squeezers are involved.  What's really
interesting is that the contexts are marked as transient in both CDO and
PO.

I suspect once that's squared away, the rest won't be nearly as bad.

-- 
Kevin

-----Original Message-----
From: Andrus Adamchik [mailto:andrus@objectstyle.org] 
Sent: Wednesday, November 07, 2007 3:53 AM
To: dev@cayenne.apache.org
Subject: Re: Cleaning up ROP

Hi Kevin,

CayenneDataObject approach is fundamentally different from other  
persistent object implementations that are all variations of POJO. So  
I think a different approach may work better - using current client  
objects on the server (instead of using current server objects on the  
client).

Andrus

Re: Cleaning up ROP

Posted by Andrus Adamchik <an...@objectstyle.org>.
On Nov 11, 2007, at 9:28 AM, Andrus Adamchik wrote:

>
>> 2) Runtime relationships -- Need to have accessor methods in  
>> superclass.
>> Likely requires change to context.
>
> Yes - probably need special noop property descriptors

Sorry - I answered that differently in a separate thread after  
thinking about it some more. I think the best solution is adding  
missing fields via class generation.

Andrus


Re: Cleaning up ROP

Posted by Kevin Menard <km...@servprise.com>.
On 11/11/07 9:28 AM, "Andrus Adamchik" <an...@objectstyle.org> wrote:

> 
> On Nov 8, 2007, at 10:19 AM, Kevin Menard wrote:
> 
>> 
>> 1) Serialization -- Fields are controlled by getters/setters and as
>> such
>> should probably be marked transient, allowing a fault to resolve
>> values
>> upon deserialization.
> 
> I don't understand this one. Why?

Consider the following Tapestry syntax:

<tr jwcid="@For" source="ognl:paintings" value="ognl:painting">
   <span jwcid="@Insert" value="ognl:painting.title"/>
</tr>

I've simplified this because it's not a form and thus no rewind cycle, but
that's really when the problem occures.  The basic issue is that when
"painting" is assigned and thus serialized (via the Squeezer), the "title"
field is null.  In the pass through the loop, the "title" field is resolved
and the local field changed from null to whatever the title is.  On a form
rewind, Tapestry reserializes all values to see if anything has changed.  It
essentially has a map keyed on serialized values to objects.  When the
serialized value changes, as is the case here due to the change in the
"title" field, it can't find it in the map and attempts to reconstruct the
object.

This is how the context was carried along using CDO.  As it turns out, the
context was never serialized in my case -- the fully registered object was
just pulled out of Tapestry's map.

Hopefully that made some sense.

-- 
Kevin


Re: Cleaning up ROP

Posted by Andrus Adamchik <an...@objectstyle.org>.
On Nov 8, 2007, at 10:19 AM, Kevin Menard wrote:

>
> 1) Serialization -- Fields are controlled by getters/setters and as  
> such
> should probably be marked transient, allowing a fault to resolve  
> values
> upon deserialization.

I don't understand this one. Why?


> 2) Runtime relationships -- Need to have accessor methods in  
> superclass.
> Likely requires change to context.

Yes - probably need special noop property descriptors

> 3) Reverse relationships -- Need some way of handling this in the
> superclass that doesn't produce a recursive loop.

Can't say anything offhand.

Andrus


RE: Cleaning up ROP

Posted by Kevin Menard <km...@servprise.com>.
Having played with this a bit, I have a better handle on the problem
now.  I also have a much richer appreciation for what CDO does for me
;-)

Making the client objects work on the server seems doable, but does
require either changes to the velocity templates or the context provided
to them.  Right now, the issues I see are:

1) Serialization -- Fields are controlled by getters/setters and as such
should probably be marked transient, allowing a fault to resolve values
upon deserialization.

2) Runtime relationships -- Need to have accessor methods in superclass.
Likely requires change to context.

3) Reverse relationships -- Need some way of handling this in the
superclass that doesn't produce a recursive loop.

#1 I've addressed locally by marking the fields transient.  There's
probably a better way of handling this, but it works for now.

#2 I've addressed locally by modifying the data map to specify all
reverse relationships.

#3 I haven't dealt with yet.  A rough approach is to have public
accessors that take the single target parameter and a set of private
setters that take the target plus a Boolean that indicates whether to
set the reverse relationship.  This could be done all in the velocity
template and would take care of the recursive call problem.

-- 
Kevin

-----Original Message-----
From: Andrus Adamchik [mailto:andrus@objectstyle.org] 
Sent: Wednesday, November 07, 2007 3:53 AM
To: dev@cayenne.apache.org
Subject: Re: Cleaning up ROP

Hi Kevin,

CayenneDataObject approach is fundamentally different from other  
persistent object implementations that are all variations of POJO. So  
I think a different approach may work better - using current client  
objects on the server (instead of using current server objects on the  
client).

Andrus


Re: Cleaning up ROP

Posted by Andrus Adamchik <an...@objectstyle.org>.
Hi Kevin,

CayenneDataObject approach is fundamentally different from other  
persistent object implementations that are all variations of POJO. So  
I think a different approach may work better - using current client  
objects on the server (instead of using current server objects on the  
client).

Andrus


On Nov 7, 2007, at 8:07 AM, Kevin Menard wrote:

> I just spent the better part of this evening trying to get  
> something going.
> Unfortunately, I'm stuck at the moment and it's getting pretty late  
> (or
> early) here.  So, I'm just posting what I tried and what my results  
> were.
> If anyone else wants to suggest something, great, if not, I'll prod  
> at it
> more when I can get some time.  I'll likely have to defer it and  
> come up
> with some workaround, because I need this portion of the app  
> relying on the
> fix done.
>
> Anyway . . .
>
> I took a really naïve approach to start because I just wanted to get
> something going.
>
> * Made CayenneDataObject extends PersistentObject
> ** Removed any code duplication
>
> * Updated my data map to use the same class name for the server and  
> client
> classes for all obj entities.
>
> * Modified the velocity template to combine both server and client  
> class
> info:
>
> * Static property name fields appear as before
> * All client fields appear
> * All methods perform an instanceof check on the context to choose the
> appropriate code to call like so:
>
> public BillingInfo getDefaultBillingInfo() {
>         if (objectContext instanceof CayenneContext) {
>             if(objectContext != null) {
>                 objectContext.prepareForAccess(this,  
> "defaultBillingInfo",
> true);
>             }
>
>             return (BillingInfo) defaultBillingInfo.getValue();
>         }
>         else {
>             return (BillingInfo)readProperty("defaultBillingInfo");
>         }
>     }
>
>
> Problems:
>
> * Retrieving data from client fails during deep merge on toMany
> relationships.  The read property is null causing most of the rest  
> of the
> method to fail.
>
> * Adding NPE checks for the above gets me to a workable state, but the
> generated class does not work as expected.  In particular, all the  
> values
> are in the map (like they should for a server) and all the fields  
> are blank
> (which should not be the case for the client).
>
> My initial thoughts were that the failures were due to the above code
> snippet failing to do the right thing if the context is null (is  
> there a
> better way to tell if we're executing on the client?).  But, the first
> failure occurs well before any of those accessors are called.  So,  
> I am a
> bit at a loss.
>
> Anyway, if Ari or someone else wants to help out with this, I'd  
> certainly
> welcome it.
>
> -- 
> Kevin
>
>
> On 10/27/07 1:01 PM, "Andrus Adamchik" <an...@objectstyle.org> wrote:
>
>> Would be nice if we could make it work... IIRC back in the day when
>> ROP was first implemented there were a number of limitations
>> preventing DataContext and CayenneDataObject from being used on the
>> client, mostly related to the fact that both had direct dependencies
>> on the structure of the underlying stack (e.g. referencing DataDomain
>> and DataNode). Now we are *mostly* free of those. So I'd be curious
>> to try and use DataContext on the client.
>>
>> A few issues to pay attention to:
>>
>> * Serialization. Current client objects are more lightweight as they
>> store their properties in Java fields, vs. a Map used by
>> CayenneDataObject. So probably a custom serializer is needed.
>>
>> * While supporting a single class hierarchy is a good idea,
>> preserving the ability for separate hierarchies is very important -
>> users may not want to expose some server methods to the client, and
>> generally may want to have two sets of objects with different  
>> behavior.
>>
>> In any event it is a good idea to reevaluate our options now.
>>
>> Andrus
>>
>>
>>
>> On Oct 27, 2007, at 7:20 PM, Kevin Menard wrote:
>>
>>> Hi all,
>>>
>>> I've just gone back to work on a ROP application I have and am
>>> again running
>>> into the issue of having to duplicate code between the server model
>>> subclasses and the client model subclasses.  I've got a way of  
>>> working
>>> around that, but I'm thinking this is probably best handled by
>>> Cayenne.  In
>>> particular, I really don't think that there should be two class
>>> hierarchies
>>> for what amounts to a boolean field (remote/not remote).
>>>
>>> I think the generated superclass should perform the duty of calling
>>> the
>>> appropriate method for reading and writing properties.  I think
>>> this would
>>> advocate adding an isRemote() method to the ObjectContext interface
>>> to avoid
>>> having to do instanceof operations.
>>>
>>> There would have to be some sort of unification between
>>> PersistentObject and
>>> CayenneDataObject as well.  My naïve guess is that the latter could
>>> extend
>>> the former, but I haven't investigated whether or not they are
>>> compatible.
>>>
>>> Does this sound like a reasonable approach?  If so, I'll probably
>>> dig in on
>>> that.
>>>
>>> Thanks,
>>> Kevin


Re: Cleaning up ROP

Posted by Kevin Menard <km...@servprise.com>.
I just spent the better part of this evening trying to get something going.
Unfortunately, I'm stuck at the moment and it's getting pretty late (or
early) here.  So, I'm just posting what I tried and what my results were.
If anyone else wants to suggest something, great, if not, I'll prod at it
more when I can get some time.  I'll likely have to defer it and come up
with some workaround, because I need this portion of the app relying on the
fix done.

Anyway . . .

I took a really naïve approach to start because I just wanted to get
something going.

* Made CayenneDataObject extends PersistentObject
** Removed any code duplication

* Updated my data map to use the same class name for the server and client
classes for all obj entities.

* Modified the velocity template to combine both server and client class
info:

* Static property name fields appear as before
* All client fields appear
* All methods perform an instanceof check on the context to choose the
appropriate code to call like so:

public BillingInfo getDefaultBillingInfo() {
        if (objectContext instanceof CayenneContext) {
            if(objectContext != null) {
                objectContext.prepareForAccess(this, "defaultBillingInfo",
true);
            }

            return (BillingInfo) defaultBillingInfo.getValue();
        }
        else {
            return (BillingInfo)readProperty("defaultBillingInfo");
        }
    }


Problems:

* Retrieving data from client fails during deep merge on toMany
relationships.  The read property is null causing most of the rest of the
method to fail.

* Adding NPE checks for the above gets me to a workable state, but the
generated class does not work as expected.  In particular, all the values
are in the map (like they should for a server) and all the fields are blank
(which should not be the case for the client).

My initial thoughts were that the failures were due to the above code
snippet failing to do the right thing if the context is null (is there a
better way to tell if we're executing on the client?).  But, the first
failure occurs well before any of those accessors are called.  So, I am a
bit at a loss.

Anyway, if Ari or someone else wants to help out with this, I'd certainly
welcome it.

-- 
Kevin


On 10/27/07 1:01 PM, "Andrus Adamchik" <an...@objectstyle.org> wrote:

> Would be nice if we could make it work... IIRC back in the day when
> ROP was first implemented there were a number of limitations
> preventing DataContext and CayenneDataObject from being used on the
> client, mostly related to the fact that both had direct dependencies
> on the structure of the underlying stack (e.g. referencing DataDomain
> and DataNode). Now we are *mostly* free of those. So I'd be curious
> to try and use DataContext on the client.
> 
> A few issues to pay attention to:
> 
> * Serialization. Current client objects are more lightweight as they
> store their properties in Java fields, vs. a Map used by
> CayenneDataObject. So probably a custom serializer is needed.
> 
> * While supporting a single class hierarchy is a good idea,
> preserving the ability for separate hierarchies is very important -
> users may not want to expose some server methods to the client, and
> generally may want to have two sets of objects with different behavior.
> 
> In any event it is a good idea to reevaluate our options now.
> 
> Andrus
> 
> 
> 
> On Oct 27, 2007, at 7:20 PM, Kevin Menard wrote:
> 
>> Hi all,
>> 
>> I've just gone back to work on a ROP application I have and am
>> again running
>> into the issue of having to duplicate code between the server model
>> subclasses and the client model subclasses.  I've got a way of working
>> around that, but I'm thinking this is probably best handled by
>> Cayenne.  In
>> particular, I really don't think that there should be two class
>> hierarchies
>> for what amounts to a boolean field (remote/not remote).
>> 
>> I think the generated superclass should perform the duty of calling
>> the
>> appropriate method for reading and writing properties.  I think
>> this would
>> advocate adding an isRemote() method to the ObjectContext interface
>> to avoid
>> having to do instanceof operations.
>> 
>> There would have to be some sort of unification between
>> PersistentObject and
>> CayenneDataObject as well.  My naïve guess is that the latter could
>> extend
>> the former, but I haven't investigated whether or not they are
>> compatible.
>> 
>> Does this sound like a reasonable approach?  If so, I'll probably
>> dig in on
>> that.
>> 
>> Thanks,
>> Kevin
>> 
>> 
> 

-- 



Re: Cleaning up ROP

Posted by Kevin Menard <km...@servprise.com>.
On 10/27/07 1:01 PM, "Andrus Adamchik" <an...@objectstyle.org> wrote:

> * While supporting a single class hierarchy is a good idea,
> preserving the ability for separate hierarchies is very important -
> users may not want to expose some server methods to the client, and
> generally may want to have two sets of objects with different behavior.

I think I'd like to see this done via the DataMap to tie in a bit more with
the validation framework.  Granted, it'd still have to be supported for
different sets of utility methods, but it would be worthwhile to be able to
mark some attributes/relationships only available on server or client.  Same
thing with the read only state.

-- 
Kevin


Re: Cleaning up ROP

Posted by Andrus Adamchik <an...@objectstyle.org>.
Would be nice if we could make it work... IIRC back in the day when  
ROP was first implemented there were a number of limitations  
preventing DataContext and CayenneDataObject from being used on the  
client, mostly related to the fact that both had direct dependencies  
on the structure of the underlying stack (e.g. referencing DataDomain  
and DataNode). Now we are *mostly* free of those. So I'd be curious  
to try and use DataContext on the client.

A few issues to pay attention to:

* Serialization. Current client objects are more lightweight as they  
store their properties in Java fields, vs. a Map used by  
CayenneDataObject. So probably a custom serializer is needed.

* While supporting a single class hierarchy is a good idea,  
preserving the ability for separate hierarchies is very important -  
users may not want to expose some server methods to the client, and  
generally may want to have two sets of objects with different behavior.

In any event it is a good idea to reevaluate our options now.

Andrus



On Oct 27, 2007, at 7:20 PM, Kevin Menard wrote:

> Hi all,
>
> I've just gone back to work on a ROP application I have and am  
> again running
> into the issue of having to duplicate code between the server model
> subclasses and the client model subclasses.  I've got a way of working
> around that, but I'm thinking this is probably best handled by  
> Cayenne.  In
> particular, I really don't think that there should be two class  
> hierarchies
> for what amounts to a boolean field (remote/not remote).
>
> I think the generated superclass should perform the duty of calling  
> the
> appropriate method for reading and writing properties.  I think  
> this would
> advocate adding an isRemote() method to the ObjectContext interface  
> to avoid
> having to do instanceof operations.
>
> There would have to be some sort of unification between  
> PersistentObject and
> CayenneDataObject as well.  My naïve guess is that the latter could  
> extend
> the former, but I haven't investigated whether or not they are  
> compatible.
>
> Does this sound like a reasonable approach?  If so, I'll probably  
> dig in on
> that.
>
> Thanks,
> Kevin
>
>