Re: Cleaning up ROP
On 11/11/07 9:28 AM, "Andrus Adamchik" <[EMAIL PROTECTED]> 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: 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
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
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
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:[EMAIL PROTECTED] Sent: Wednesday, November 07, 2007 3:53 AM To: [email protected] 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
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" <[EMAIL PROTECTED]> 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
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
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:[EMAIL PROTECTED] Sent: Wednesday, November 07, 2007 11:23 AM To: [email protected] 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
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:[EMAIL PROTECTED] Sent: Wednesday, November 07, 2007 3:53 AM To: [email protected] 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
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:[EMAIL PROTECTED] Sent: Wednesday, November 07, 2007 3:53 AM To: [email protected] 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
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" <[EMAIL PROTECTED]> 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
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" <[EMAIL PROTECTED]> 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
On 10/27/07 1:01 PM, "Andrus Adamchik" <[EMAIL PROTECTED]> 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
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
