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>