Re: [Simple-support] Using constructors with arguments
Brought to you by:
niallg
|
From: Kevin D. <for...@tr...> - 2009-01-26 18:51:57
|
Thanks for taking a look. I may be trying to use this framework for more than what it is intented to do. If you are interested in pursuing some use-cases, I'd be happy to work with you on aspects of the design, etc... But I also understand that different projects have different goals, and direct encapsulation of business layer objects (which are more likely to be required to be immutable) may not be part of the goal of Simple-XML.
Using the @Commit or a combination of @Replace/@Resolve requires an aweful lot of coding that it seems a persistence framework could address... Coding with the @Commit, etc... style is roughly similar to using a JAXB generated object structure for persistence, then having a bunch of translators written to convert persistent values into business objects and vice-versa. It can be done, but I'm looking for something a lot more elegant.
I'm also fine with using a Strategy to achieve the goal (I've implemented a really rough first cut at this already), but there may be a couple of challenges.
As an example, my work so far indicates that the Strategy interface can provide a hook for instantiation of objects (i.e. via a factory that could use injected parameters from a DI container) - however, unless I'm missing something, it doesn't look like Strategy has access to the Type objects generated from parsing the XML tree, which means that I can't use objects recovered from the deserialization during instantiation. If the objects are simple (String or something easily handled with the *Transform classes), then this is no big deal (we can just instantiate from the Type object in the node map), but for complicated objects, I'm not sure how to handle it.
This makes me wonder if I'm trying to get Strategy to do something that it just isn't designed to do (maybe this kind of thing should be implemented using a different layer of the API), or if I can somehow use the map passed into the getElement method to make this work.
So, is it possible that the Instantiator class is the place where this functionality needs to take place? If so, then the Instantiator would need to have access to the deserialization's context during the call to getType(). For example, if there were some way to obtain named Type objects for the fields/values of the object being deserialized? These could be used to instantiate values that could then be passed to a factory or constructor. I suspect this would involve making a read-only view of the Collector available to Instantiator.
I'm not sure how much control Simple has over the *order* that instantiation occurs in, or whether this would impact things. It appears that Simple builds the Type hierarchy before it starts instantiating objects, so that should be OK (unless there is a cyclic reference in the constructor parameters - but that won't happen in a well designed object hierarchy :-) )
I'm wondering if you have a philosophical overview of the Simple architecture captured anywhere? The JavaDocs don't really provide a high level overview, and the API has a *lot* of layers to it that make it difficult for someone new the source to get a handle on what types of behavior should be implemented at which level. My assumption is that the system takes several logical passes over the object hierarchy:
Parse XML into nodes
For each node, obtain it's Type (this is the job of Strategy I think?)
Determine the class for each Type and determine it's fields/parameters/etc... and create Contacts for those (this is the job of Scanner I think? And that interacts with the Collector somehow)
Use each Type to instantiate a concrete object and inject it into the Contacts (I think the Collector does this)
Is that approximately correct?
- K
----------------------- Original Message -----------------------
From: <Nia...@ub...>
To: <for...@tr...>, <sim...@li...>
Cc:
Date: Mon, 26 Jan 2009 09:11:54 -0000
Subject: RE: [Simple-support] Using constructors with arguments
Hi,
Ill take a look at the read resolve and write replace issues you
mention. The reason @Replace and @Resolve do not work in lists is they
go through the Traverser object, rather than the Composite object
directly. This is something I will have to address. So there are two
options, the first is to use a strategy (there should be plenty of
examples in the test cases), the second is to use @Commit to build the
Tuples like so.
public class TupleDatabase {
private List<Tuple> tuples;
@ElementList
private List<TupleEntry> entries;
@Commit
private void commit() {
for(TupleEntry entry : entries) {
tuples.add(entry.name, entry.value);
}
}
@Root
private static class TupleEntry {
@Element String name;
@Element String value;
}
}
Here when building the list you can create the tuples without a no-arg
constructor.
Hope this helps,
Niall
-----Original Message-----
From: Kevin Day [mailto:for...@tr...]
Sent: 24 January 2009 00:21
To: sim...@li...
Subject: Re: [Simple-support] Using constructors with arguments
Thanks for the suggestion, this type of strategy is exactly what we do
with our current Java serialization based persistence, so it's easy to
relate to (even though there's a lot of duplicate code).
For some reason, though, @Replace isn't working (the getSubstitute()
method is never called).
After a bit of digging into the source, I think that the problem is that
the only place that Caller#replace() is actually called is in
Composite#writeReplace(). And that method is only called for composite
objects. We are dealing with a list of objects, so it seems that maybe
writeReplace() hasn't been properly implemented for ElementList
handlers?
If I place my object outside the list (as part of a composite element),
then it serializes fine.
Another issue I'm running into is during deserialization. In this case,
I am using @Resolve on the replaced object (which is part of a composite
object structure). I set a break point on Composite.readResolve(), but
readResolve never gets called. The framework throws an
InstantiationException from Factory#getOverride().
I'm not sure if the problem here is my lack of understanding of the API,
or if this is just a corner case that didn't get unit testing written
for it. I am attaching a self contained unit test case that exhibits
all of the above behavior. Please let me know if you have any
pointers/suggestions, or if I may just be missing something!
Cheers,
- Kevin
PS - I suspect that the use of @Replace and @Resolve may not solve our
entire use case, but it is a great start at a solution and is helping me
understand the Simple framework better. Our ultimate need also includes
injection of resources via a DI framework, and I'm having a hard time
figuring out how that's going to happen without using some sort of
factory instantiation strategy (I suppose that we could use a singleton
to get the DI hook, but that's an anti-design pattern that I really
would prefer to avoid at all costs). Hopefully we can discuss this
aspect a bit more after I understand why my @Replace and @Resolve aren't
working!
----------------------- Original Message -----------------------
From: <Nia...@ub...>
To: <for...@tr...>, <sim...@li...>
Cc:
Date: Thu, 22 Jan 2009 16:45:15 -0000
Subject: RE: [Simple-support] Using constructors with arguments
Hi,
There is no need to do this. You can do the following.
@Root
public class Tuple {
final private String from;
final private String to;
public Tuple(String from, String to){
this.from = from;
this.to = to;
}
@Replace
private TupleSubstitute getSubstitute() {
return new TupleSubstitute(from, to);
}
public String getFrom() {
return from;
}
public String getTo() {
return to;
}
public String toString() {
return from + " -> " + to;
}
}
@Root
private class TupleSubstitute {
@Element
private String from;
@Element
private String to;
public TupleSubstitute() {
super();
}
public TupleSubstitute(String from, String to) {
this.from = from;
this.to = to;
}
@Resolve
public Tuple getTuple() {
return new Tuple();
}
}
This should work, for you. The @Resolve annotation resolves the
deserialized object before assigning it to the field or method. The
@Replace object replaces the object in the stream. Just like java object
serialization. Let me know how this works out for you.
Hope this helps,
Niall
-----Original Message-----
From: Kevin Day [mailto:for...@tr...]
Sent: 21 January 2009 22:56
To: Simple (Java Project)
Subject: Re: [Simple-support] Using constructors with arguments
I've done some additional work on handling classes that do not have
zero-arg constructors. First, I've put together some simple sample code
(attached, start with TryIt#main) that shows one of the situations we
are facing: we have a number of immutable Tuple objects that we need to
serialize/deserialze. The values used in the Tuple class' constructor
come from attributes (or elements) from the XML. The attached
serializes fine, but we can't deserialize it b/c of the lack of
no-argument constructor. We can't add a no-arg constructor because the
class is intended to be immutable (in fact, the fields are marked as
final). In addition, the sample code shows the Tuple values as holding
simple strings - but in our application, the values are going to be
complex objects.
I have put together a Strategy and Type implementation that *kind* of
works, but I'm running into some issues (may be related to my lack of
familiarity with the code base).
The approach that I'm working on now creates a new Strategy that allows
us to register special Type objects that can handle object instantiation
based on state of the deserialization process (for now, I'm calling this
FactoryGeneratedStrategy). Ideally, FactoryGeneratedStrategy would be
able to inter-operate with any other Strategy (DefaultStrategy,
CycleStrategy, TreeStrategy) - after all, it's only purpose is to help
instantiate objects during deserialization - the behavior of the other
strategies is still desirable.
I ran into a couple of problems with this type of Strategy chaining
approach:
1. DefaultStrategy is not instantiable due to package scope visibility
(this is the smaller issue) 2. The other strategies appear to be
implemented in a way that assumes use of zero-arg constructors. For
example, CycleStrategy#getElement returns an Allocate object depending
on the deserialization state. The Allocate object is ultimately backed
by an Instance object as it's delegate Type. Instance assumes a
zero-arg constructor.
Short of re-implementing all of the *Strategy classes, I can't see how
to go about providing a factory to create new object instances (without
losing functionality provided by various Strategy classes).
My initial impression based on the above is that Strategy may not be the
appropriate place in the hierarchy to add object instantiation behavior.
If not, where? The Instantiator class seems a likely candidate, except
that it's getInstace() method doesn't have access to deserialization
context information.
Maybe I'm just going about this all wrong... If anyone can provide some
suggestions, I'd love to hear them!
Thanks,
- K
----------------------- Original Message -----------------------
From: Kevin Day <ke...@tr...>
To: Simple (Java Project) <sim...@li...>
Cc:
Date: Tue, 20 Jan 2009 17:28:05 -0700
Subject: [Simple-support] Using constructors with arguments
For starters, I really like what I see in this project (just started
reviewing it this afternoon). I'm hoping that I can get some pointers
on a specific use-case.
I would like to use Simple in conjunction with a dependency injection
system (probably Guice) and List implementations from the GlazedLists
project.
We have to use a constructor on our list implementations that contains
arguments (some of the arguments provided by Guice, some from element or
attribute data in the XML).
What is the best way to approach this with Simple?
It seems that some sort of Strategy mixed with a custom Type is in
order, but I'm not entirely clear on the best approach. The
architecture of the framework looks powerful enough to do what we need,
but I can't see where to get started just yet.
To give a pseudo-code example of what we are trying to do (in real life,
we'll be injecting SomeInjectedObject using Guice or a factory/service
provider call):
class SpecialList<E>{
public SpecialList(SomeInjectedObject injected, String
valueFromAttribute, SomeObject valueFromElement){ ...
}
}
class MyObject{
@ElementList
SpecialList<ElementObject> list = new
SpecialList<ElementObject>(SomeInjectedObject.defaultInstance, "test",
new SomeObject());
}
When we serialize this with Simple, we'd like to see something
approximately like the following:
<myObject valueFromAttribute="test">
<someObject> ... </someObject>
<list>
<elementObject> ... whatever goes into this ... </elementObject>
<elementObject> ... whatever goes into this ... </elementObject> ...
</list>
</myObject>
Note that:
1. The <list> element doesn't have a class attribute 2. When we
construct the list during deserialization, we need to be able to pull
the 'valueFromAttribute' and 'someObject' values to use during
construction of the SpecialList object 3. The SpecialList object needs
to have the 3 argument constructor called (not a zero-arg constructor).
And it is not possible to call a zero arg constructor, then modify the
object afterwards to get the same result.
Any pointers? I may be pushing the framework too hard (some of the
above requirements can be relaxed - if it isn't possible to obtain
valueFromAttribute or someObject at construction time, we can find
workarounds - but we have to be able to create the SpecialList instance
using values from a factory/Guice/etc...). For what it's worth, we have
this type of behavior working in Betwixt using custom BeanCreator
implementations.
Thanks much,
- Kevin
------------------------------------------------------------------------
------
This SF.net email is sponsored by:
SourcForge Community
SourceForge wants to tell your story.
http://p.sf.net/sfu/sf-spreadtheword
_______________________________________________
Simple-support mailing list
Sim...@li...
https://lists.sourceforge.net/lists/listinfo/simple-support
Visit our website at http://www.ubs.com
This message contains confidential information and is intended only for
the individual named. If you are not the named addressee you should not
disseminate, distribute or copy this e-mail. Please notify the sender
immediately by e-mail if you have received this e-mail by mistake and
delete this e-mail from your system.
E-mails are not encrypted and cannot be guaranteed to be secure or
error-free as information could be intercepted, corrupted, lost,
destroyed, arrive late or incomplete, or contain viruses. The sender
therefore does not accept liability for any errors or omissions in the
contents of this message which arise as a result of e-mail transmission.
If verification is required please request a hard-copy version. This
message is provided for informational purposes and should not be
construed as a solicitation or offer to buy or sell any securities or
related financial instruments.
UBS Limited is a company registered in England & Wales under company
number 2035362, whose registered office is at 1 Finsbury Avenue, London,
EC2M 2PP, United Kingdom.
UBS AG (London Branch) is registered as a branch of a foreign company
under number BR004507, whose registered office is at
1 Finsbury Avenue, London, EC2M 2PP, United Kingdom.
UBS Clearing and Execution Services Limited is a company registered in
England & Wales under company number 03123037, whose registered office
is at 1 Finsbury Avenue, London, EC2M 2PP, United Kingdom.
Visit our website at http://www.ubs.com
This message contains confidential information and is intended only
for the individual named. If you are not the named addressee you
should not disseminate, distribute or copy this e-mail. Please
notify the sender immediately by e-mail if you have received this
e-mail by mistake and delete this e-mail from your system.
E-mails are not encrypted and cannot be guaranteed to be secure or
error-free as information could be intercepted, corrupted, lost,
destroyed, arrive late or incomplete, or contain viruses. The sender
therefore does not accept liability for any errors or omissions in the
contents of this message which arise as a result of e-mail transmission.
If verification is required please request a hard-copy version. This
message is provided for informational purposes and should not be
construed as a solicitation or offer to buy or sell any securities
or related financial instruments.
UBS Limited is a company registered in England & Wales under company
number 2035362, whose registered office is at 1 Finsbury Avenue,
London, EC2M 2PP, United Kingdom.
UBS AG (London Branch) is registered as a branch of a foreign company
under number BR004507, whose registered office is at
1 Finsbury Avenue, London, EC2M 2PP, United Kingdom.
UBS Clearing and Execution Services Limited is a company registered
in England & Wales under company number 03123037, whose registered
office is at 1 Finsbury Avenue, London, EC2M 2PP, United Kingdom.
|