OASIS Mailing List ArchivesView the OASIS mailing list archive below
or browse/search using MarkMail.

 


Help: OASIS Mailing Lists Help | MarkMail Help

sdo message

[Date Prev] | [Thread Prev] | [Thread Next] | [Date Next] -- [Date Index] | [Thread Index] | [List Home]


Subject: ISSUE 119: Modified Proposal


Title: ISSUE 119: Modified Proposal

Hi Guys,
I've tried my best to reword the proposal in a way to make everyone happy.  I've taken out the motivation, and separated the discussion of round tripping to a separate chapter.  Also, returning the original entity as a result of roundtripping is described as "SHOULD" behavior (ie, not required, but I've convinced myself that it is the right thing to do, if an implementation can manage it) and only when the mapped entity can be easilly identified (ie, there are no conflicts).

It's all a complex description for something that is actually very simple, but it's the best I can do.
Have a look, and prepare comments, because I guess this will be on tomorrow's agenda.

Ron


Keys and Projection
A type and its keyType are compatible with each other.  In other words, an object with a reference to an entity in one context can be projected onto an object with a reference to the key type in another context. For example, the metamodel  

HelperContext #1

Type Employee
* property - id (Integer) - KEY
* property - name (String)
* property - direct-report (List of Employee)

is compatible with the metamodel
HelperContext #2

Type Employee
* property - id (Integer) - KEY
* property - name (String)
* property - direct-report (List of Integer)

Here we see the type of the direct-report property has been replaced in the second HelperContext with the corresponding key type.

As described in the section on projection, it is possible to project from one context to another, and then back again to the original context.  This means it is also possible to project from a context with entity references to a context with keys and back again.  The behavior of such round-trip applications is described in the next section.  Here, we consider only one-way projection (which is, of course, the behavior during the first half of a round-trip).

Conceptually, keys represent references to entities that lie outside the data graph (and whose types are potentially not even defined in the context through which the data graph was created).  Changing the value of a key changes the target of the reference, and impacts neither the referenced entity itself nor any other references to that object.  This is of course the exact behavior we have when primitives or immutable data values (such as Integers or Strings) are used as key types.  The behavior when the key type extends DataObject (as required whenever compound keys are used) must also be consistent.  This means, when projecting from a context with entities to a content with complex keys, a new DataObject will be created for every reference.  The DataObjects representing the keys are never shared, no matter how often any single entity is referenced in the original model. When projecting from an entity to a key, a new instance of the key is always created.  By contrast, when projecting between contexts that do not map entities to keys, we get the same number of objects in the target HelperContext that we had in the source HelperContext.

Conversely, when projecting from a key to an entity then for each distinct key value within the graph being projected, all references to that key must resolve to the same entity.  It will be necessary for the projection operation to create a single DataObject for each entity.  The created DataObject must have all default values, except for ist key properties, which must be set to match the key.

Projection between entities and keys is useful when the relationship through which the entities tie into the graph are non-containment. The following example shows how a complex model that lacks containment relationships can be projected onto a context that requires a specific XML serialization, which implicitly prunes the orignal domain to the requirements of a specific client.

HelperContext #1

Type School
* property - name (String) - KEY
* property - students (Student) - many=true
* property - courses (Course) - many=true

Type Student
* property - name (String) - KEY
* property - courses (Course) - many=true, containment=false, opposite=students
* property - school (School) - containment=false, opposite=students

Type Course
* property - name (String) - KEY
* property - students (Students) - many=true, containment=false, opposite=courses
* property - school (School) - containment=false, opposite=courses

Notice the m:n relationship between Student and Course. If we imagine a service that should expose this domain model to clients, it is possible that some clients will wish to obtain the list of students participating in a particular course, while other clients may wish to obtain the list of courses in which a particular student is enrolled. The application cannot determine based on the structure of the data which of the two possible containment structures is "correct". Notice also that returning the transitive closure would return all the data associated with the entire school.

In this example the client wants the data structured according to the following XSD
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://projection" xmlns:tns="http://projection" elementFormDefault="qualified">
<complexType name="School">
<sequence>
<element name="students" type="tns:Student" maxOccurs="unbounded"/>
</sequence>
<attribute name="name" type="string"/>
</complexType>
<complexType name="Student">
<sequence>
<element name="courses" type="string" maxOccurs="unbounded"/>
</sequence>
<attribute name="name" type="string"/>
</complexType>

<element name="school" type="tns:School"/>
</schema>
Notice that we are imposing a containment structure on the original context, as well as pruning it by replacing the course entity by the corresponding key. The following code illustrates the behavior of the project method.

DataObject cal = _helperContext.getDataFactory().create(School.class);
// Create the SDO graph
cal.set("name","Berkeley");
DataObject billy = cal.createDataObject("students");
billy.set("name", "Billy Bear");
DataObject bob = cal.createDataObject("students");
bob.set("name", "Bobbie Bear");
DataObject basketWeaving = cal.createDataObject("courses");
basketWeaving.set("name", "Basket Weaving");
DataObject algol = cal.createDataObject("courses");
algol.set("name", "Algol");
DataObject revolution = cal.createDataObject("courses");
revolution.set("name", "Revolution");
// hook things up
billy.getList("courses").add(basketWeaving);
billy.getList("courses").add(algol);
bob.getList("courses").add(basketWeaving);
bob.getList("courses").add(revolution);
// Create a second context defined by an XSD
HelperContext hc2 = HelperProvider.getNewContext();
hc2.getXSDHelper().define(getClass().getClassLoader().getResourceAsStream("com/sap/sdo/testcase/internal/pojo/ex/projection.xsd"),
null);
// Project from the java context to the XSD context
DataObject projection = hc2.getDataFactory().project(cal);
// Produce XML based on the XSD

String xml = hc2.getXMLHelper().save(projection, "http://projection", "school");
// I'm imagining here that sending the XML out over the wire (eg, using it as a response to a
// WebService request. On the client side, we go from the XML back to SDO. We use the context
// based on the XSD.
DataObject projection2 = hc2.getXMLHelper().load(xml).getRootObject();
// We can make some changes. We can add a new course...
projection2.getList("students.0/courses").add("Fortran and You");
// So, now the trip back to the server…
//I'm skipping the XML step, and simply projecting the modified (XML oriented) data back into
// my java context
DataObject cal2 = _helperContext.getDataFactory().project(projection2);

// Test that there is one entity per key value
DataObject billy2 = (DataObject)cal2.getList("students").get(0);
DataObject basketWeavingBill = (DataObject)billy2.getList("courses")
.get(0);
DataObject bob2 = (DataObject)cal2.getList("students").get(1);
DataObject basketWeavingBob = (DataObject)bob2.getList("courses").get(0);
assertSame(basketWeaving2, basketWeavingBob);
X.X  Round Trip Projections
In many scenarios data will round-trip between contexts, including between contexts in which entities map to keys. Let us consider two context Ce and Ck, representing the entity and the key context, respectively, and a DataObject Oe, in context Ce. Projecting Oe into Ck returns a DataObject, Ok. Let Gk be the transitive closure reachable from Ok.  Every key value in Gk maps to an single entity in Ce, and it is this entity SHOULD be found when Ok (and effective, all of Gk) is projected back into Ce, provided such an entity can be uniquely identified.  In cases where the user has set a key property to a value that is not found in Gk, then, as a result of projecting Ok into Ce, a new entity will be created, as describe in the one-way projection, above.  In cases where conflicting entities map to the same key, the behavior is undefined.

We illustrate with an example. Note that the example uses containment relationships in both contexts. This is done for clarity, since it allows an XML representation of the data. The use-case, however, is stronger when the contexts have different (or perhaps no) containment structures. Imagine the following data in HelperContext #1

<employee id="11">
<name>Foo Bar</name>
<direct-report id="21">
<name>Jane Doe</name>
</direct-report>
<direct-report id="31">
<name>Jim Jones</name>
</direct-report>
<direct-report id="22">
<name>John Smith</name>
</direct-report>
</employee>


After projecting to HelperContext #2, we have the following data

<employee id="11">
<name>Foo Bar</name
<direct-report>21</direct-report>
<direct-report>31</direct-report>
<direct-report>22</direct-report>
</employee>


If we imagine the client changes the list of direct reports, so that the second item in the list has value “41” instead of “31”, then the meaning of the change is not that the employee with name “Jim Jones” now has a new ID, but that “Jim Jones” has been replaced by another employee.

On projecting from context 2 back into context 1, the instance of Employee with id="22" would be detached from the Employee with id="11". Also a new Employee with id="41" would be created and added as a direct report to the Employee with id="11". If the graph in context 1 were in scope of a ChangeSummary, then because the "direct-report" property in context1 is a containment property, the change would be tracked as a delete or a create. If the relationship were non-containment, the change would be tracked as a modification to the employee with id=11, not as a delete to the employee with id=22 or as the creation of a new employee with id=41; semantically, it is only the reference to entity 22 that has changed, the entity itself still exists, and is unaltered. Projecting this data from context 2:

<employee id="11">
<name>Foo Bar</name>
<direct-report>21</direct-report>
<direct-report>41</direct-report>
<direct-report>22</direct-report>
</employee>

Into context 1 yields:
<employee id="11">
<name>Foo Bar</name>
<direct-report id="21">
<name>Jane Doe</name>
</direct-report>
<direct-report id="41"/>
<direct-report id="22">
<name>John Smith</name>
</direct-report>
</employee>



[Date Prev] | [Thread Prev] | [Thread Next] | [Date Next] -- [Date Index] | [Thread Index] | [List Home]