Hi Ron,
For our implementation wrap is based on object identity not
equals()/hashCode(). Below is some JUnit code adapted from one of our
test cases to demonstrate.
assertEquals(p1, p2);
assertNotSame(p1, p2);
DataObject p1DO = jaxbHelperContext.wrap(p1);
DataObject p2DO = jaxbHelperContext.wrap(p2);
assertNotSame(p1DO, p2DO);
@XmlID (from JAXB) does not factor into identity past the scope of a
load/save operation. It is unlike @Id (from JPA) where POJOs with the
same ID value are strongly related in that they correspond to the same
row in some table.
Managing the scope of JAXBHelperContext is important. For our
implementation weak references are used where possible. We also see
the need for additional APIs to allow POJOs to be dereferenced under
the user control.
Some of the changes being proposed by Frank appear to be making it
easier for users to control HelperContexts. This work might alleviate
some of your concerns in that area.
I'm not sure I see the value in making JAXBHelperContext part of the
SDO specification. Again with potential changes to HelperContext
creation, it could become possible for implementors to offer special
purpose HelperContexts like this for handling of things like JAXB
objects, XML, EMF, etc.
-Blaise
Barack, Ron wrote:
7C3EF93EEBC6EB4A8B4470853DE865665EEDD6@dewdfe18.wdf.sap.corp"
type="cite">
Hi Blaise,
Some more questions on your
implementation of JaxbHelperContext
hc.wrap(Object) always
returns the same DataObject for the same pojo. Does this rely on the
equals() and hash() methods as implemented in the class? Imagine 2
pojos, where p1!=p2 but p1.equals(p2). Would
hc.wrap(p1)==hc.wrap(p2)? What about if p1.class has some @XmlID field?
Looking at your sample, it's
a little hard to discern the lifecycle of the JaxbHelperContext.
Because constructing the types is expensive (involving parsing XSDs,
etc), I normally think of HelperContexts as having to be thread-safe
and shared, like a JAXBContext, or like a classloader. For instance,
when a session bean uses SDO, there would be one HelperContext that are
shared by all instances of the bean. I believe this to be the common
understanding. The sample code is of course
a simple client; it's impossible to tell if the JaxbHelperContext is
meant to shared.
I think there is reason to
have maybe JaxbHelperContext be a sort of lightweight context, that is
meant to be owned by a single client. We could have a heavyweight
context, with types defined by the XSD. The heavyweight context
could have a method, something like
HelperContext.getJaxbHelperContext(JAXBContext). The JaxbHelperContext
would basically delegate to the heavyweight context, except when doing
things like constructing instances, or implementing wrap() or unwrap().
This gives me something like
a binder, a client object that I can use to control the scope and
lifetime of the POJO -> DataObject map. But without defining a new
"Binder" interface.
Any comments?
Ron
Hi Ron,
From our perspective JAXBHelper context is the POJO Helper Context.
Assuming the POJOs are also JPA entities, then wrapping (as opposed to
copying via an XML step) need not trigger all the lazy relationships
(of course this could be implementation dependent). With regards to
wrapping and the performance of getters/setters, there are some ways to
do this with reasonable performance.
The POJO as a DataObject, this is an interesting concept. One problem
I see with this is that DataObjects do a lot of relationship management
that most POJOs don't do (i.e. adding a DataObject to a new container
automatically removes it from the old container). Here the weaving is
not invisible to the user and may not match the users expecations.
The XSDHelper.define() call is necessary, because as you have
previously mentioned XML schemas generated from POJOs do not contain
all the information required by SDO. One example is specifying
datatype=false/containment=false properties, that require SDO
annotations on the XML schema.
Does
DataObject customerDO = hc.wrap(customer); always return the same
DataObject?
In our implementation wrap always returns the same DataObject. This
does require some management, but with proper attention to object
identity, and avoiding hard references it is doable. Not sure how a
"binder" solution avoids this as something JAXB like would require a
one-to-one correspondence between a POJO and a DataObject.
What happens if a wrapped POJO is modified?
Our customer base is not interacting with both
the POJO and the DataObject at the same time. If they were you are
correct that weaving could be used to apply the changes made to the
POJO back to the DataObject.
Is there any way to start with a DataObject, and generate a JAXB
from it?
Yes, this was the main reason for making the entry point its own
HelperContext. DataFactory.create() creates DataObjects that wrap
POJOs, and XMLHelper loads DataObjects that wrap POJOs.
Usage of StAX
The usage of StAX does not automatically create a Java SE 6
dependency. JAXB 2.0 based on Java SE 5 makes use of these APIs, it
just requires implementors to ship the StAX (JSR 173) API jar. I think
SDO should offer the same marshal/unmarshal options as JAXB. But the
decision to change the base Java SE version is independent from our
inclusion of StAX.
User Expectations of the XML representation of POJOs
You are correct in your model that the user must specify JAXB
information to get the desired XML representation. This is exactly the
role of JAXB in the larger world of Java EE. Today users are
retrieving data from relational databases as POJOs using JPA, and
exposing this data through services as XML using JAXB.
SDO competing with JAXB
If SDO takes a POJO graph and has its own XML representation then yes
it is competing with JAXB. Regardless of the algorithm it is
impossible to guess the XML schema representation of a set of POJOs, as
such annotations are required fix it. With your proposal SDO
annotations would be required to adjust the XML Schema representation
of a set of POJOs, again this competes with JAXB.
Advantages of our Approach
- A single integration point between SDO and Java EE (JAXB
annotated POJOs), as opposed to point integrations for
JAXB/JPA/POJO/etc.
- No SDO POJO annotations required. Instead the integration
point between JAXB/Java EE and SDO is the XML schema.
- SDO becomes a value add to JAXB/Java EE introducing enriched
metadata, relationship management, dynamic APIs, change summary, etc.
- No changes are required to existing Java EE applications (that
already expose data as XML) to make them compatible with SDO. This is
what our customers want, a mechanism to make their existing Java EE
applications compatible with SDO enabled middle-ware plumbing.
-Blaise
Barack, Ron wrote:
Hi Blaise,
Over the year, I've personally
become less convinced of the wrapping approach, as opposed to providing
some kind of optimized data transfer. What do you see as the advantage
of wrapping over some kind of convert operation? Instead of wrapping
the POJOs, I could mashal them in JAXB, and load them using XMLHelper.
Does wrapping give me something here I could not get through such a
mechanism (modulus performance)? And of course because of the second
point below, the getters and setters could become significantly more
expensive, offsetting the gains made in avoiding a conversion step.
There is a third approach, and
that is for the framework to weave the POJO classes, so that "new
Customer()", or at least ObjectFactory.createCustomer() returns an
object that already implements DataObject. It seems to me this is a
straightforward extension of JPAs approach. In our JPA integration,
you turn on this bytecode enhancement with a flag in persistence.xml.
Do you think it would be possible to get to this level of integration
with JAXB?
Either way, I have some
questions to the details:
Why is the call to
XSDHelper.define() necessary? Couldn't the metadata be introspected
from the POJOs (ala generateSchema())? Isn't there a danger of the XSD
being out of sync with the classes (similar to your comment regarding
the @schemaLoc annotation?
Does
DataObject customerDO = hc.wrap(customer); always
return the same DataObject? It's maybe a implementation detail,
but does wrap create the DataObjects lazilly or all at once? If you do
it lazilly, you need
to maintain a map from the POJO object to the DataObject wrapper,
right? Otherwise as we navigate around, we could wind up with the same
POJO having 2 DataObject wrappers. And such a map raises a bunch of
ugly lifecycle issues. This is where I thought something like a binder
could come come in handy, as a way of putting the lifecycle in the
hands of the client.
What happens if a wrapped POJO
is modified? Obviously, calls to DataObject.get() would see the
changes, but what about things like change tracking, containers, and
bi-directional properties, that, in SDO happen implicitly when a setter
is called? Are you "weaving" the byte code of the JAXB POJO?
Is there any way to start with a
DataObject, and generate a JAXB from it? Ie, can I unwrap something
that hasn't been wrapped? When I create a DataObject using
DataFactory.create(), does it automatically create an underlying POJO?
Regarding the StAX and JAXB
dependencies. I guess it will be 2010 before any SDO 3 implementations
are released as products... I imagine by that time pretty much
everyone will be on JSE 6. Maybe we should consider raising the
minimum compatible JSE to JSE 6. What do you think?
You
mention that customers have expections about the XML representation of
POJOs. What about POJOs that are not organized into a containment
structure? Eg, my example:
class School {
List<Student> getStudents() {...}
List<Course> getCourse() {...}
}
class Course {
School getSchool() {...}
List<Student>
getStudents() {...}
}
class Student {
School getSchool() {...}
List<Course> getCourses() {..}
}
what
do your customers expect from the XML representation of School? AFAIK,
the programmer has to specify the XML containment structure as part of
the model, He must annotate give the object ID properties, and he must
use @XmlID to indicate non-containment. I don't think SDO is competing
with JAXB if it specifies a mechanism through which such graphs can be
marshalled to XML. SDO 3 offers 2 such mechanisms: the graph could be
placed inside an container with orphan properties, and the containment
structure (or alternate structures) can be imposed on the model through
the project method. Offering this kind of fuctionality is one reason
to include an SDO-POJO binding in SDO 3.
Best
Regards,
Ron
Hi Ron,
I'm glad you raised this email thread. Integration with JAXB (POJOs)
is also an important use case to Oracle and in particular the
EclipseLink SDO implementation.
Both SDO and JAXB have an XML schema representation of their metadata.
For us this is the join point between these two technologies.
EclipseLink DataObjects are capable of wrapping POJOs, actions applied
to the DataObjects (set/unset/detach/etc.) are automatically applied to
the underlying POJO. I have included some code below to demonstrate:
// JAXB - Create the JAXB Context
JAXBContext jaxbContext =
JAXBContext.newInstance("com.example.customer");
// SDO - Create the JAXB aware HelperContext
HelperContext hc = new JAXBHelperConext(jaxbContext);
hc.getXSDHelper().define(customerXSD);
// SDO - Wrap the POJO in a DataObject
Customer customer = new Customer();
customer.setLastName("Doe");
DataObject customerDO = hc.wrap(customer);
// SDO - Create and modify DataObjects
customerDO.getString("last-name"); // returns "Doe"
customerDO.setString("first-name", "Jane");
DataObject addressDO customerDO.create("address");
addressDO.setString("street", "123 Any Street");
// SDO - Unwrap the POJOs
// Note: customer == hc.unwrap(customerDO)
Customer customer2 = (Customer) hc.unwrap(customerDO);
I would prefer to not invent a POJO to SDO binding. Our customers have
expectations about the XML representation of POJOs. As JAXB is
included in Java SE 6, I imagine many people share these expectations.
If Java objects save to XML one way, the act of wrapping them in (or
converting them to) DataObjects should not change the XML
representation. Of course if users choose to make use of concepts such
as ChangeSummary then the XML representation begins to diverge but in
predictable and explainable ways. For us it is imperative that SDO
provides a compatible value add to Java EE, and not become a competitor
to any of its technologies.
Other Points:
- I agree JAXB annotations do not contain enough metadata to
reproduce all SDO metadata. For example JAXB cannot represent the
property index of attribute properties.
- JAXB classes generated from XML schema can contain properties
that are different from SDO class generation. Especially wrt choices
and substitution groups.
- JAXB is primarily concerned with Java classes, while SDO is
concerned with Java interfaces.
- More load/save targets (such as StAX) would be great. Of
course StAX is not included with Java SE 5, and would introduce a new
dependency jar. JAXB has this same requirement and goes away with Java
SE 6, so this may not be a big concern.
- The JAXB Binder maintains a link between POJOs and DOM
nodes. Once linked you can make changes on one side and then make an
explicit call (it doesn't happen automatically) to apply the changes to
the other side. While useful the default JAXB algorithm isn't always
what users expect. Our implementation provides the user with the
choice of 3 different "binding" algorithms.
-Blaise
Barack, Ron wrote:
Hi Everyone,
The idea of using JAXB annotations
as a solution to ISSUE 22, even of consolidating static SDO and JAXB,
has come up repeatedly in the calls. I think we may be able to make
better progress in this regard by discussing the ideas per email, so
that there is a permament record of the arguments. Having this
discussion now will hopefully make the discussion during the F2F more
productive.
First I want to say that
integration with JAXB is a high priority issue for us, but this
integration may take several forms:
1. Transfering data between the
representations
2. JAXB annotations as a source of
SDO metadata
3. JAXB classes as static SDOs
Of course, it is desireable to
have as deep and integration as possible. When I first started
thinking about the problem, I set out to achieve all three types of
integration. After long consideration, I arrived at the conclusion
that only the first is really possible.
About the first point I hope there
is general agreement: Applications that use SDO as a data
representation must be able to transfer data with applications that use
JAXB. In some ways, this is already possible in 2.1: all you need to
do is mashall to XML. This approach introduces some performance costs,
and I think we should address steamlining the process in SDO 3. One
approach that we've found to be very helpful is for SDO to be able to
produce an XMLStreamReader which in turn can be fed into a JAXB
marshaller. For maintaining an active map between the representations,
I believe a DOMBinder is a very promising approach, and possibly we
could go a step further and have a JAXB binder. (Unfortunately, I
believe the JSE implementation of JAXB still has an incomplete version
of the DOMBinder functionality).
Similarly, clients can already use
JAXB annotations as a source of SDO metadata under SDO 2.1, at least
whereever JAXBContext.generateSchema works it is possible to use XSD as
an intermediate metadata format. However, it is important to realize
that JAXB does not provide the level of details we expect to see in a
resolution to ISSUE 22. JAXB annotations capture exactly the
information necessary to correctly marshal and unmarshal to XML.
Restrictions (facets), and all reference to the original simple type is
lost. JAXB creates String properties for xs:NCName, xs:AnyURI, etc.,
and there is no way to get back to the original type through reflection
on the JAXB object.
JAXB objects reflect the purpose
of JAXB, which is to create an XML binding for Java. XML models, and
therefore JAXB, do not contain (as a rule) bi-directional or
non-containment references. It's true that XML and JAX can represent
non-containment relationships using @XmlID, but in SDO non-containment
relationships are not limited to objects that have ID properties (or
even keys).
If we were to use JAXB annotations
as the standard way to represent SDO metadata in Java, then we are also
accepting the default behaviour associated with the *absence* of these
annotations. In JAXB, an unannotated POJO class that references a
second POJO class is intepreted as there being a containment
relationship between the associated types. I would argue that this
should not be the default behaviour for SDO. If it were, then we loose
the ability to to take an arbitrary model based on unannotated classes,
pack them in a DataGraph (or anything else that has an orphan property)
and being able to generate XML for the model. The assumption of
containment relationships means that we will get cycles and other
problems, making the whole approach unworkable.
A further problem is that I think
we would probably be misusing the JAXB annotations. Static SDOs may be
interfaces or generated classes. JAXBs are annotated POJOs (JavaBeans).
Finally, very pragmatically, the
JAXB spec is large and complex. Expecting implementations to make
sense of the annotations and their many combinations is a very high bar
indeed, even more so when we expect to enrich the annotation set with
some of our own annotations.
Having rejected the second point,
perhaps the discussion of the third point is moot, but I want to
discuss it because the appeal of the approach is so strong. The appeal
can be summarized as follows: "The JSE already defines a standard XML
to Java binding. By defining static SDOs, we are defining a competive
functionality that is therefore doomed to be rejected by the java
community. The problem with this argument is that static SDOs do not
represent an XSD, they represent SDO metadata. Although a large part
of the SDO spec contains the mapping between the metamodels, the models
are not the same, not is the SDO metamodel a subset of the XSD
metamodel (multiple inheritence, bi-directional relationships being two
examples of constructs that do not exist in XSD).
On a more practical level,
although for very simple XSDs the classes generated by JAXB and the
static SDOs defined in SDO 2.1 are compatible, we very quickly come to
places where the models diverge. SDO represents choices as several
parallel properties, and we expect the user to fill one or the other.
JAXB generates a sort of combined property (using the pattern
"getAorB"), and the user must create an JAXBElement and use this value
to set the property. It would be very odd to have this structure
reflected in the static SDO when it is missing from the SDO metamodel.
In cases such as those where we create a sequenced DataObject, JAXB
creates an class with a single getContent() method. Static SDOs, like
SDO itself, maintains in such cases a property oriented view of the
data in addition to the sequenced view.
I hope this gets the discussion
going so that we can decide what sort of integration with JAXB we are
aiming for, whether or not we we need to define our own annotations,
etc. Blaise, as a member of the JAXB EG, I'm particularly interested
in your comments.
Best Regards,
Ron
|