7C3EF93EEBC6EB4A8B4470853DE86566DEEDF2@dewdfe18.wdf.sap.corp"
type="cite">
4.3 ChangeSummary
A ChangeSummary
provides access to the changes made to the DataObjects in a data graph,
comparing the data graph’s “before” state to its “after” state. The
“before” state is the state of the data graph at the time when logging
was activated. If logging is no longer active, the “after” state is the
state of the graph when logging was deactivated. This means that only
changes that were made up to the point when logging was deactivated are
included in the ChangeSummary. Otherwise, the ChangeSummary includes
all changes up to the point at which the it is interrogated. The
ChangeSummary contains only net changes, transient values that are
assigned to properties after logging begins, but which are overwritten
before logging is deactivated are not visible throught the change
summary. Although change information is only gathered when logging is
on, the ChangeSummary can be queried whether logging is on or off. All
of the information returned is read-only.
The ChangeSummary
interface has methods that:
- Activate and
deactivate logging.
- Restore a tree
of DataObjects to the state it was in when logging began and clear the
log.
- Query the
logging status.
- Get the
ChangeSummary’s root DataObject.
- Get the list of
changed DataObjects.
- Indicate
whether a DataObject in the list of changed DataObjects has been
created, deleted or modified.
- Get a
DataObject’s container DataObject at the point when logging began.
- Get a
DataObject’s containment property at the point when logging began.
- Get a
DataObject’s Sequence at the point when logging began.
- Get a specific
old value.
- Get a list of
old values.
4.3.1 Starting and
Stopping Change Logging
ChangeSummary.beginLogging()
clears the ChangeSummary’s list of changed DataObjects and starts
change logging. ChangeSummary.endLogging() stops change logging.
ChangeSummary.undoChanges() restores the data graph to its state when
logging began. ChangeSummary.undoChanges() also clears the log, but
does not affect isLogging().
NOTE: The beginLogging(), endLogging() and
undoChanges() methods are intended primarily for the use of service
implementations since services define how the processing of a
ChangeSummary relates to external resources. Making changes that are
not captured in the ChangeSummary may cause services that drive updates
from a ChangeSummary to act on incomplete information.
4.3.2 ChangeSummary
Root
ChangeSummaries become
associated with DataObjects when the DataObject includes a Property
with type ChangeSummaryType. The ChangeSummary root is the DataObject
having the ChangeSummary property. ChangeSummary.getRootObject() MUST
return the ChangeSummary root. Calling DataObject.getChangeSummary()
on the ChangeSummary root, or on any DataObject contained, directly or
indirectly, by the ChangeSummary root, MUST return the same
ChangeSummary object. It MUST also be possible to retrieve this object
using DataObject.get(“changeSummaryProperty”) where
“changeSummaryProperty” is the name of a property whose Type is
ChangeSummaryType. The following rules and restrictions apply to
ChangeSummary properties:
- Types are
allowed to contain at most one property with type ChangeSummaryType.
- A property with
type ChangeSummaryType must have many=false and readOnly=true.
- The scope of
ChangeSummaries may never overlap. If a DataObject has a property of
type ChangeSummary, it cannot be directly or indirectly contained or
otherwised referenced by any other DataObjects that have a property of
type ChangeSummay.
- When the
DataObject containing the ChangeSummary is created, logging is by
default off. Before any changes will be logged,
ChangeSummary.beginLogging() must be called.
- The
ChangeSummary will not contain the creation or deletion of its
containing DataObject.
- The
ChangeSummaryType MAY NOT be used to define OpenContent properties.
4.3.3 ChangeSummary
Scope
The scope of a
ChangeSummary is defined as the set of DataObjects reachable from the
ChangeSummary root either through containment or over any orphanHolder
properties. In other words the scope of the change summary is the same
set of DataObjects that would be in the sub-tree whose root node is
ChangeSummary root, if the graph were serialized to XML
DataObjects that
were in the ChangeSummary scope when logging was activated but are not
in the scope when logging was deactivated (or when
ChangeSummary.getChangedObjects() was called, if logging is still
active) and which are not themselves contained by a deleted DataObject
MUST be appear in the ChangeSummary as deleted. DataObjects that were
not in the ChangeSummary scope when logging was activated , but are in
scope when logging was deactivated (or when
ChangeSummary.getChangedObjects() was called, if logging is still
active) and which are not themselves contained by a created DataObject
MUST be appear in the ChangeSummary as created. Other DataObjects
within the ChangeSummary scope whose property values changed during the
time that logging was activated MUST appear in the ChangeSummary as
modified.
4.3.4 OrphanHolder
Properties
Although orphanHolder
properties play a role in determining the scope of the ChangeSummary,
changes to the orphanHolder properties themselves are not tracked. If
the only change to the ChangeSummary root is to the contents of its
orphanHolder properties, the object itself MUST NOT be contained in the
ChangeSummary as a modified.
OrphanHolder
properties MUST NOT appear in the getOldValues, getOldSequence lists.
The following
restrictions apply to the use of orphanHolder properties together with
ChangeSummaries.
1. If an orphan DataObject is
referenced from within the scope of a ChangeSummary, and if an object
with a matching orphanHolder property is contained, either directly
then the object is in scope of the ChangeSummary. When the change
summary root is serialized using XMLHelper.save, then the orphan
DataObject must be serialized as being “contained” by the ChangeSummary
root’s orphanHolder property.
2. All orphanHolders associated
with the ChangeSummary root or any DataObjects contained, directly or
indirectly by the ChangeSummary root MUST be considered in determining
the scope.
3. Implementations MAY ignore
orphanHolder properties on DataObjects that themselves are orphans.
4.3.5 Old Values
A List of old values
can be retrieved using the getOldValues(DataObject dataObject) method.
The order of old values returned is implementation dependent. For a
deleted DataObject, the old values List contains all the properties of
the DataObject. For a DataObject that has been modified, the old values
List consists of the modified properties only. For a DataObject that
has not been deleted or modified, the List of old values is empty.
Individual languages
MAY return a list of changed properties or a list of objects which
contain the old value, the property, and other information if desired.
If a list of objects is returned, then getOldValue and isOldSet are
methods on the returned objects.
4.3.6 Sequenced
DataObject
getOldSequence(DataObject
dataObject) returns the entire value of a DataObject’s Sequence, at the
point when logging began. This return value can be null. If
DataObject.getSequence() returns null then getOldSequence(DataObject
dataObject) will return null.
4.3.7 Serialization
and Deserialization
When a ChangeSummary is
deserialized, the logging state will be on if a <changeSummary>
element is present in the XML unless the changeSummary marks logging as
off. A serializer must produce a <changeSummary> element in the
XML if either of the following conditions applies:
- Changes have been
logged (getChangedDataObjects().size() > 0).
- No changes have
been logged but isLogging() is true at the time of
serialization. In this case, an empty <changeSummary/> or
<changeSummary logging="true"/> element must be produced.
The state of logging
is recorded in the logging attribute of the changeSummary element.
The serialization of
a ChangeSummary includes enough information to reconstruct the original
state of all DataObjects in its scope, at the point when logging was
turned on. Each individual object removed from the data graph must be
recorded in the ChangeSummary serialization in order to perform this
reconstruction. The create attribute labels DataObjects currently in
the data graph that were not present when logging started, and the
delete attribute labels objects contained in the change summary that
are no longer in the data graph. Labels are space-separated lists of
either IDs, if available, or XPath expressions.
The contents of a
ChangeSummary element are either deep copies of the objects at the
point they were deleted, or a prototype of an object that has had only
data type changes, with values for the properties that have changed
value.
4.3.8 ChangeSummary
Interface
The type returned by
getOldValue, and the types in the List returned by getOldValues is
implementation language dependent.
_______________________________________________________________________________________________________________
6.4 SDO Type and
Property constraints
There are several
restrictions on SDO Types and Properties. These restrictions ensure
Types and Properties for DataObjects are consistent with their API
behavior. Behavior of ChangeSummaryType Properties is defined.
Instances of Types
with dataType=false must implement the DataObject interface.
Values of bidirectional
Properties with type.dataType=false and many=true must be unique
objects within the same list.
Values of Properties
with type.dataType=false and many=true cannot contain null.
Property.containment
has no effect unless type.dataType=false.
Property.default!=null
requires type.dataType=true and many=false
Types with
dataType=true cannot contain properties, and must have open=false and
sequenced=false.
Type.dataType and
sequenced must have the same value as their base Types' dataType and
sequenced.
Type.open may only be
false when the base Types' open are also false.
An implementation is
not required to modify pre-existing types when derived types are
defined.
Properties that are
bidirectional require type.dataType=false
Properties that are
bidirectional require that no more than one end has containment=true
Properties that are
bidirectional require that both ends have the same value for readOnly
Properties that are
bidirectional with containment require that the non-containment
Property has many=false.
Names and aliasNames
must all be unique within Type.getProperties()
_______________________________________________________________________________________________________________
11.2
Inclusion of Orphaned Objects
As discussed in section
4.3.3, if the ChangeSummary root contains orphanHolder properties, the
orphaned objects that are associated with these holders are also in the
scope of the ChangeSummary.
As discussed in
section 4.3.4, changes to the orphanHolder properties themselves are
not tracked as modifications.
This has the
consequence that modified and deleted orphans always appear as top
level elements in the changeSummary XML. Other than this, there are no
special rules for the serialization the changes associated with
orphaned DataObjects.
11.3
Example Use of ChangeSummary on a DataObject
We begin with a model
that is specified without a containment structure. In this example, we
use a simple model of an organization, with types representing the
company, its departments and their employees. For clarity, we specify
the model using XSD:
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://sdo.example"
xmlns:tns="http://sdo.example" xmlns:sdox="commonj.sdo/xml">
<element name="Company" type="tns:Company" />
<complexType name="Company">
<sequence>
<element name="department" type="string" sdox:propertyType="tns:Department"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
<attribute name="name" type="string" sdox:key="true" />
</complexType>
<complexType name="Department">
<sequence>
<element name="employee" type="string" sdox:propertyType="tns:Employee"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
<attribute name="name" type="string" sdox:key="true" />
</complexType>
<complexType name="Employee">
<attribute name="name" type="string" sdox:key="true" />
<attribute name="salary" type="int" />
</complexType>
</schema>
Let us suppose that
the client receives the following document from the server.
<?xml version="1.0" encoding="UTF-8"?>
<sdo:datagraph xmlns:sdo="commonj.sdo" xmlns:ex="http://sdo.example" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<changeSummary logging="true"/>
<ex:Company name="Acme">
<department>Sales</department>
<department>Manufacturing</department>
</ex:Company>
<orphanHolder xsi:type="ex:Department" name="Sales">
<employee>Ron</employee>
<employee>Stefan</employee>
</orphanHolder>
<orphanHolder xsi:type="ex:Department" name="Manufacturing">
<employee>Stefan</employee>
<employee>Ulf</employee>
</orphanHolder>
<orphanHolder xsi:type="ex:Employee" name="Ron" salary="2"/>
<orphanHolder xsi:type="ex:Employee" name="Ulf" salary="2"/>
<orphanHolder xsi:type="ex:Employee" name="Stefan" salary="2"/>
</sdo:datagraph>
After deleting the
"Sales" department, changing a salary and adding a new employee to
Manufacturing, calling XMLHelper.save returns the following document:
<?xml version="1.0" encoding="UTF-8"?>
<sdo:datagraph xmlns:sdo="commonj.sdo" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ex="http://sdo.example">
<changeSummary create="#/orphanHolder.3" delete="#/changeSummary/orphanHolder.2
#/changeSummary/orphanHolder.3">
<ex:Company xsi:type="ex:Company" sdo:ref="Acme">
<department sdo:ref="Sales"></department>
<department sdo:ref="Manufacturing"></department>
</ex:Company>
<orphanHolder xsi:type="ex:Department" sdo:ref="Manufacturing">
<employee sdo:ref="Stefan"></employee>
<employee sdo:ref="Ulf"></employee>
</orphanHolder>
<orphanHolder xsi:type="ex:Employee" sdo:ref="Stefan" salary="2"></orphanHolder>
<orphanHolder xsi:type="ex:Employee" name="Ron" salary="2"></orphanHolder>
<orphanHolder xsi:type="ex:Department" name="Sales">
<employee>Ron</employee>
<employee>Stefan</employee>
</orphanHolder>
</changeSummary>
<orphanHolder xsi:type="ex:Department" name="Manufacturing">
<employee>Stefan</employee>
<employee>Ulf</employee>
<employee>Frank</employee>
</orphanHolder>
<orphanHolder xsi:type="ex:Employee" name="Stefan" salary="3"></orphanHolder>
<orphanHolder xsi:type="ex:Employee" name="Ulf" salary="2"></orphanHolder>
<orphanHolder xsi:type="ex:Employee" name="Frank" salary="2"></orphanHolder>
<ex:Company name="Acme">
<department>Manufacturing</department>
</ex:Company>
</sdo:datagraph>