/*******************************************************************************
* Copyright (c) 2007, 2014 compeople AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* compeople AG - initial API and implementation
*******************************************************************************/
package org.eclipse.riena.internal.objecttransaction.impl;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.osgi.service.log.LogService;
import org.eclipse.core.runtime.Assert;
import org.eclipse.equinox.log.Logger;
import org.eclipse.riena.core.Log4r;
import org.eclipse.riena.internal.objecttransaction.Activator;
import org.eclipse.riena.objecttransaction.IObjectId;
import org.eclipse.riena.objecttransaction.IObjectTransaction;
import org.eclipse.riena.objecttransaction.IObjectTransactionExtract;
import org.eclipse.riena.objecttransaction.ITransactedObject;
import org.eclipse.riena.objecttransaction.InvalidTransactionFailure;
import org.eclipse.riena.objecttransaction.ObjectTransactionFailure;
import org.eclipse.riena.objecttransaction.ObjectTransactionManager;
import org.eclipse.riena.objecttransaction.delta.AbstractBaseChange;
import org.eclipse.riena.objecttransaction.delta.MultipleChange;
import org.eclipse.riena.objecttransaction.delta.MultipleChangeEntry;
import org.eclipse.riena.objecttransaction.delta.SingleChange;
import org.eclipse.riena.objecttransaction.delta.TransactionDelta;
import org.eclipse.riena.objecttransaction.state.Action;
import org.eclipse.riena.objecttransaction.state.State;
import org.eclipse.riena.objecttransaction.state.StateMachine;
/**
* Implements the IObjectTransaction interface. Contains all the logic for
* maintaining property values, references between object, state of objects.
*
*/
public class ObjectTransactionImpl implements IObjectTransaction {
// Map of the TransactionDelta entries in this objectTransaction, key is
// IObjectId
private Map<IObjectId, TransactionDelta> changesInTransaction;
// Map of the ITransactedObjects registered in this objectTransaction key is
// IObjectId
private Map<IObjectId, ITransactedObject> involvedTransactedObjects;
// set to true if this objectTransaction is the root transaction
private boolean rootTransaction = true;
/**
* contains a reference to the next higher object transaction. Usually this
* field is null, if this is a object transaction. However for root
* transactions this field is used to reference a hidden transaction that
* contains all the changes that were committed against the database.
*/
private ObjectTransactionImpl parentTransaction;
// is this objectTransaction is cleanModus ? then changes do not get
// recorded in this objectTransaction
private boolean cleanModus;
// is this objectTransaction invalid ? (because it was committed) then no
// calls are possible
private boolean invalidTransaction = false;
/**
* do we have preRegistered ITransactedObjects ? The application program
* tried to register them at a time where no IObjectId was set in the
* business object. The preregistered objects are registered as soon as
* their IObjectId is set.
*/
private List<ITransactedObject> preRegisteredCleanObjects = null;
private boolean strictModus = false;
private boolean allowRegister = true;
private static final Logger LOGGER = Log4r.getLogger(Activator.getDefault(), ObjectTransactionImpl.class);
/**
* constructor that creates a new objectTransaction
*/
public ObjectTransactionImpl() {
super();
changesInTransaction = new HashMap<IObjectId, TransactionDelta>();
involvedTransactedObjects = new HashMap<IObjectId, ITransactedObject>();
this.cleanModus = false;
}
/**
* constructor that creates a new objectTransaction with setting a
* difference objectTransaction as parent
*
* @param parentTransaction
*/
public ObjectTransactionImpl(final IObjectTransaction parentTransaction) {
this();
this.parentTransaction = (ObjectTransactionImpl) parentTransaction;
this.rootTransaction = false;
}
/**
* create a new objectTransaction as interface method
*
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#createSubObjectTransaction()
*/
public IObjectTransaction createSubObjectTransaction() {
final IObjectTransaction objectTransaction = new ObjectTransactionImpl(this);
ObjectTransactionManager.getInstance().setCurrent(objectTransaction);
return objectTransaction;
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#register(org.eclipse.riena.objecttransaction.ITransactedObject)
*/
public void register(final ITransactedObject object) {
if (!allowRegister) {
return;
}
Assert.isNotNull(object, "object must not be null"); //$NON-NLS-1$
// objects can only be registered once, so check if is already
// registered
Assert.isTrue((object.getObjectId() != null && !isRegistered(object)) || object.getObjectId() == null,
"object cannot be registered a second time"); //$NON-NLS-1$
if (isInvalid()) {
throw new InvalidTransactionFailure("object transaction is invalid"); //$NON-NLS-1$
}
keepReferenceOf(object);
// if ObjectId is not set, preregister, otherwise set object state to
// CLEAN (unchanged)
if (object.getObjectId() == null) {
addPreRegisteredClean(object);
} else {
setObjectState(object, State.CLEAN);
}
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#registerNew(org.eclipse.riena.objecttransaction.ITransactedObject)
*/
public void registerNew(final ITransactedObject object) {
if (!allowRegister) {
return;
}
Assert.isNotNull(object, "object must not be null"); //$NON-NLS-1$
Assert.isTrue(object.getObjectId() != null, "ObjectId of Transacted Object must not be null"); //$NON-NLS-1$
Assert.isTrue(!isRegistered(object), "Transacted Object must not be already registered"); //$NON-NLS-1$
if (isInvalid()) {
throw new InvalidTransactionFailure("object transaction is invalid"); //$NON-NLS-1$
}
notifyObjectChange(object, Action.NEW);
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#registerAsDeleted(org.eclipse.riena.objecttransaction.ITransactedObject)
*/
public void registerAsDeleted(final ITransactedObject object) {
if (!allowRegister) {
return;
}
Assert.isNotNull(object, "object must not be null"); //$NON-NLS-1$
Assert.isNotNull(object.getObjectId(), "ObjectId of object must not be null"); //$NON-NLS-1$
if (isInvalid()) {
throw new InvalidTransactionFailure("object transaction is invalid"); //$NON-NLS-1$
}
notifyObjectChange(object, Action.DELETE);
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#allowRegister(boolean)
*/
public void allowRegister(final boolean parmAllowRegister) {
this.allowRegister = parmAllowRegister;
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#exportExtract()
*/
public IObjectTransactionExtract exportExtract() {
return exportExtractInternal(true);
}
/*
* @seeorg.eclipse.riena.objecttransaction.IObjectTransaction#
* exportOnlyModifedObjectsToExtract()
*/
public IObjectTransactionExtract exportOnlyModifedObjectsToExtract() {
// first export regular modified objects
final IObjectTransactionExtract extract = exportExtractInternal(false);
// then include all targets of a reference change in the extract
for (final TransactionDelta delta : extract.getDeltas()) {
final Collection<?> changes = delta.getChanges().values();
for (final Object change : changes) {
if (change instanceof SingleChange) {
final SingleChange sChange = (SingleChange) change;
Object child = sChange.getChildObject();
if (child instanceof IObjectId) {
child = lookupObjectById((IObjectId) child);
}
if (child instanceof ITransactedObject
&& !(extract.contains(((ITransactedObject) child).getObjectId()))) {
extract.addCleanTransactedObject((ITransactedObject) child);
}
} else {
if (change instanceof MultipleChange) {
final MultipleChange mChange = (MultipleChange) change;
for (final MultipleChangeEntry mEntry : mChange.getEntries()) {
Object mChild = mEntry.getChildObject();
if (mChild instanceof IObjectId) {
mChild = lookupObjectById((IObjectId) mChild);
}
if (mChild instanceof ITransactedObject) {
if (!extract.contains(((ITransactedObject) mChild).getObjectId())) {
extract.addCleanTransactedObject((ITransactedObject) mChild);
}
} else {
throw new ObjectTransactionFailure("non ITransactionObject in multi reference ???"); //$NON-NLS-1$
}
}
} else {
throw new ObjectTransactionFailure(
"unknown change typ (not single and not multi reference change)"); //$NON-NLS-1$
}
}
}
}
return extract;
}
private IObjectTransactionExtract exportExtractInternal(final boolean exportClean) {
if (isInvalid()) {
throw new InvalidTransactionFailure("object transaction is invalid"); //$NON-NLS-1$
}
// copies the TransactionDeltas into an Array
final ObjectTransactionExtractImpl extract = new ObjectTransactionExtractImpl();
try {
for (TransactionDelta temp : changesInTransaction.values()) {
if (!(temp.getState().equals(State.VANISHED))) {
if (exportClean || !(temp.getState().equals(State.CLEAN))) {
temp = (TransactionDelta) temp.clone();
extract.addDelta(temp);
}
}
}
} catch (final CloneNotSupportedException e) {
throw new ObjectTransactionFailure("error cloning TransactionDelta ", e); //$NON-NLS-1$
}
// and create an objectTransactionextract from it
return extract;
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#importExtract(org.eclipse.riena.objecttransaction.IObjectTransactionExtract)
*/
public void importExtract(final IObjectTransactionExtract extract) throws InvalidTransactionFailure {
checkForRegisteredObjects();
importExtractInternal(extract, true);
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#importOnlyModifedObjectsFromExtract(org.eclipse.riena.objecttransaction.IObjectTransactionExtract)
*/
public void importOnlyModifedObjectsFromExtract(final IObjectTransactionExtract extract)
throws InvalidTransactionFailure {
checkForRegisteredObjects();
// check that all targets of reference changes are registered
for (final TransactionDelta delta : extract.getDeltas()) {
final Collection<?> changes = delta.getChanges().values();
for (final Object change : changes) {
if (change instanceof SingleChange) {
final SingleChange sChange = (SingleChange) change;
final Object child = sChange.getChildObject();
boolean isRegistered = true;
if (child instanceof IObjectId) {
isRegistered = this.isRegistered((IObjectId) child);
} else {
if (child instanceof ITransactedObject) {
isRegistered = this.isRegistered((ITransactedObject) child);
}
}
if (!isRegistered) {
throw new InvalidTransactionFailure("reference target object " + child //$NON-NLS-1$
+ " must be registered before import"); //$NON-NLS-1$
}
} else {
if (change instanceof MultipleChange) {
final MultipleChange mChange = (MultipleChange) change;
for (final MultipleChangeEntry mEntry : mChange.getEntries()) {
final Object mChild = mEntry.getChildObject();
boolean isRegistered = true;
if (mChild instanceof IObjectId) {
isRegistered = isRegistered((IObjectId) mChild);
} else {
if (mChild instanceof ITransactedObject) {
isRegistered = isRegistered((ITransactedObject) mChild);
} else {
throw new InvalidTransactionFailure("invalid object in multi-ref " + mChild); //$NON-NLS-1$
}
}
if (!isRegistered) {
throw new InvalidTransactionFailure("reference target object " + mChild //$NON-NLS-1$
+ " must be registered before import"); //$NON-NLS-1$
}
}
} else {
throw new ObjectTransactionFailure(
"unknown change typ (not single and not multi reference change)"); //$NON-NLS-1$
}
}
}
}
importExtractInternal(extract, false);
}
/**
* Check that objects that own a delta record are registered in the object
* transaction
*
* @throws InvalidTransactionFailure
*/
private void checkForRegisteredObjects() throws InvalidTransactionFailure {
if (isInvalid()) {
throw new InvalidTransactionFailure("object transaction is invalid"); //$NON-NLS-1$
}
if (!isRootTransaction()) {
throw new InvalidTransactionFailure("object transaction must be the root transaction"); //$NON-NLS-1$
}
// import means adding changes to the objectTransaction and cannot
// happen in clean modus
if (isCleanModus()) {
throw new InvalidTransactionFailure("object transaction must NOT be in clean modus when importing"); //$NON-NLS-1$
}
// this will add all preregistered objects with ObjectIds to the
// objectTransaction
checkPreRegisteredClean();
if (preRegisteredCleanObjects != null && preRegisteredCleanObjects.size() > 0) {
for (final ITransactedObject object : preRegisteredCleanObjects) {
if (object.getObjectId() != null) {
throw new InvalidTransactionFailure("internal error, preregistered object found with ObjectId " //$NON-NLS-1$
+ object);
} else {
throw new InvalidTransactionFailure("missing object id for object " + object); //$NON-NLS-1$
}
}
}
}
private void importExtractInternal(final IObjectTransactionExtract extract, final boolean importClean)
throws InvalidTransactionFailure {
if (isInvalid()) {
throw new InvalidTransactionFailure("object transaction is invalid"); //$NON-NLS-1$
}
if (!isRootTransaction()) {
throw new InvalidTransactionFailure("object transaction must be the root transaction"); //$NON-NLS-1$
}
// import means adding changes to the objectTransaction and cannot
// happen in clean modus
if (isCleanModus()) {
throw new InvalidTransactionFailure("object transaction must NOT be in clean modus when importing"); //$NON-NLS-1$
}
// check that all object ids referenced in the deltas, exist in the
// transaction, if they exist copy them over
final TransactionDelta[] deltas = extract.getDeltas();
final ObjectTransactionImpl tempSubTransaction = (ObjectTransactionImpl) createSubObjectTransaction();
for (final TransactionDelta delta : deltas) {
if (importClean || delta.getState() != State.CLEAN) {
IObjectId objectId = delta.getObjectId();
IObjectId newObjectId = null;
String versionChange = null;
// check if the extract contains an object id change
if (delta.getSingleRefObject("sys::oldoid") != null) { //$NON-NLS-1$
objectId = (IObjectId) delta.getSingleRefObject("sys::oldoid"); //$NON-NLS-1$
newObjectId = (IObjectId) delta.getSingleRefObject("sys::oid"); //$NON-NLS-1$
}
if (delta.getSingleRefObject("sys::version") != null) { //$NON-NLS-1$
versionChange = (String) delta.getSingleRefObject("sys::version"); //$NON-NLS-1$
}
if (this.involvedTransactedObjects.get(objectId) == null) {
throw new ObjectTransactionFailure(
"ObjectIds for all imported deltas must exist in objectTransaction, not found=" + delta); //$NON-NLS-1$
} else {
if (newObjectId != null) {
changeObjectId(objectId, newObjectId);
objectId = newObjectId;
}
if (versionChange != null) {
final ITransactedObject transObject = this.involvedTransactedObjects.get(objectId);
transObject.setVersion(versionChange);
}
// copy involved objects from this transaction and add
// deltas to subtransaction
tempSubTransaction.involvedTransactedObjects.put(objectId,
this.involvedTransactedObjects.get(objectId));
tempSubTransaction.changesInTransaction.put(objectId, delta);
}
}
}
// commit the temporary sub transaction and forget it. this way the
// extract is merged into the current transaction
tempSubTransaction.commit();
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#isRootTransaction()
*/
public boolean isRootTransaction() {
if (isInvalid()) {
throw new InvalidTransactionFailure("object transaction is invalid"); //$NON-NLS-1$
}
return rootTransaction;
}
/**
* sets the object state of a transacted object
*
* @param object
* transacted object for which to set the state
* @param state
* state that should be set
*/
private void setObjectState(final ITransactedObject object, final State state) {
Assert.isNotNull(object, "object must not be null"); //$NON-NLS-1$
Assert.isNotNull(object.getObjectId(), "ObjectId of object must not be null"); //$NON-NLS-1$
if (isInvalid()) {
throw new InvalidTransactionFailure("object transaction is invalid"); //$NON-NLS-1$
}
keepReferenceOf(object);
// check if a delta exists and if not create one, then set the state
TransactionDelta delta = changesInTransaction.get(object.getObjectId());
if (delta == null) {
// we have to set the version at the end, because retrieving the
// version
// (sometimes) requires that the object is registered so it has to
// have
// a changesInTransaction entry
delta = new TransactionDelta(object.getObjectId(), state, object.getVersion());
changesInTransaction.put(object.getObjectId(), delta);
} else {
delta.setState(state);
}
return;
}
/**
* return the object state for a given transacted object
*
* @param object
* transacted object for which to retrieve the object state
* @return state of the transacted objcet
*/
private State getObjectState(final ITransactedObject object) {
Assert.isNotNull(object, "object must not be null"); //$NON-NLS-1$
Assert.isNotNull(object.getObjectId(), "ObjectId of object must not be null"); //$NON-NLS-1$
if (isInvalid()) {
throw new InvalidTransactionFailure("object transaction is invalid"); //$NON-NLS-1$
}
// find the delta for an oid
final TransactionDelta delta = changesInTransaction.get(object.getObjectId());
// if not found
if (delta == null) {
// if a parenttransaction exist, ask the parent transaction for the
// state
if (parentTransaction != null) {
return parentTransaction.getObjectState(object);
}
return null;
}
return delta.getState();
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#setReference(org.eclipse.riena.objecttransaction.ITransactedObject,
* java.lang.String, java.lang.Object)
*/
public void setReference(final ITransactedObject object, final String refName, final Object newValue) {
Assert.isNotNull(object, "object must not be null"); //$NON-NLS-1$
Assert.isNotNull(object.getObjectId(), "ObjectId of object must not be null"); //$NON-NLS-1$
if (newValue instanceof ITransactedObject) {
setReference(object, refName, (ITransactedObject) newValue);
return;
}
checkPreRegisteredClean();
if (!isCleanModus()) {
Assert.isTrue(isRegistered(object), "object must be registered"); //$NON-NLS-1$
}
if (isInvalid()) {
throw new InvalidTransactionFailure("object transaction is invalid"); //$NON-NLS-1$
}
// change nothing if in clean modus
if (isCleanModus()) {
return;
}
// find the delta entry for this transacted objects ObjectId, create new
// entry if it does not exist
TransactionDelta delta = changesInTransaction.get(object.getObjectId());
if (delta == null) {
delta = new TransactionDelta(object.getObjectId(), State.CREATED, object.getVersion());
changesInTransaction.put(object.getObjectId(), delta);
} else {
// if the object was clean, set it to modified
if (delta.getState().equals(State.CLEAN)) {
delta.setState(State.MODIFIED);
}
}
// set the newValue for the refName
delta.setSingleRefObject(refName, newValue);
return;
}
public Object getReference(final ITransactedObject object, final String refName, final Object defaultValue) {
Assert.isNotNull(object, "object must not be null"); //$NON-NLS-1$
Assert.isNotNull(object.getObjectId(), "ObjectId of object must not be null"); //$NON-NLS-1$
Assert.isTrue(!(defaultValue instanceof ITransactedObject),
"getReference for Object must not pass a \"hidden\" ITransactedObject for defaultValue"); //$NON-NLS-1$
checkPreRegisteredClean();
if (isCleanModus() || (!isStrictModus() && !isRegistered(object))) {
return defaultValue;
}
Assert.isTrue(isRegistered(object), "object must be registered"); //$NON-NLS-1$
if (isInvalid()) {
throw new InvalidTransactionFailure("object transaction is invalid"); //$NON-NLS-1$
}
// find delta entry, look for a single change entry with the requested
// refName
// if it is a property (not instanceof IObjectId) return the value,
// if is a IObjectId, lookup the original transactedobject and return it
final TransactionDelta delta = changesInTransaction.get(object.getObjectId());
if (delta != null) {
if (delta.hasSingleRefObject(refName)) {
final Object propValue = delta.getSingleRefObject(refName);
if (propValue instanceof IObjectId) {
return lookupObjectById((IObjectId) propValue);
} else {
return propValue; // includes the case where propValue is
// null
}
}
}
// if I can't find an entry, ask the parenttransaction if it exists
if (parentTransaction != null) {
return parentTransaction.getReference(object, refName, defaultValue);
}
// if nothing is found, return the defaultValue
return defaultValue;
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#setReference(org.eclipse.riena.objecttransaction.ITransactedObject,
* java.lang.String,
* org.eclipse.riena.objecttransaction.ITransactedObject)
*/
public void setReference(final ITransactedObject object, final String relationName,
final ITransactedObject refObject) {
Assert.isNotNull(object, "object must not be null"); //$NON-NLS-1$
Assert.isNotNull(object.getObjectId(), "ObjectId of object must not be null"); //$NON-NLS-1$
checkPreRegisteredClean();
if (!isCleanModus()) {
Assert.isTrue(isRegistered(object), "object must be registered"); //$NON-NLS-1$
Assert.isTrue(refObject == null || (refObject.getObjectId() != null && isRegistered(refObject)),
"invalid refObject"); //$NON-NLS-1$
}
if (isInvalid()) {
throw new InvalidTransactionFailure("object transaction is invalid"); //$NON-NLS-1$
}
// do nothing if in clean modus
if (isCleanModus()) {
return;
}
// find current delta entry, create one if it does not exist
TransactionDelta delta = changesInTransaction.get(object.getObjectId());
if (delta == null) {
delta = new TransactionDelta(object.getObjectId(), State.CREATED, object.getVersion());
changesInTransaction.put(object.getObjectId(), delta);
} else {
// if it was clean, change it to modified
if (delta.getState().equals(State.CLEAN)) {
delta.setState(State.MODIFIED);
}
}
// add a reference to the ObjectId object of the referenced
// transactedobject
IObjectId id2 = null;
if (refObject != null) {
id2 = refObject.getObjectId();
}
delta.setSingleRefObject(relationName, id2);
return;
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#getReference(org.eclipse.riena.objecttransaction.ITransactedObject,
* java.lang.String,
* org.eclipse.riena.objecttransaction.ITransactedObject)
*/
public ITransactedObject getReference(final ITransactedObject object, final String relationName,
final ITransactedObject defaultRefObject) {
Assert.isNotNull(object, "object must not be null"); //$NON-NLS-1$
Assert.isNotNull(object.getObjectId(), "ObjectId of object must not be null"); //$NON-NLS-1$
checkPreRegisteredClean();
if (isCleanModus() || (!isStrictModus() && !isRegistered(object))) {
return defaultRefObject;
}
Assert.isTrue(isRegistered(object), "object must be registered"); //$NON-NLS-1$
Assert.isTrue(defaultRefObject == null
|| (defaultRefObject.getObjectId() != null && isRegistered(defaultRefObject)),
"invalid defaultRefObject (is null, has no ObjectId or is not registered)"); //$NON-NLS-1$
if (isInvalid()) {
throw new InvalidTransactionFailure("object transaction is invalid"); //$NON-NLS-1$
}
// find the delta entry for this transacted object, look for a single
// ref object with that reference name
// if is instanceof IObjectId, lookup the transacted object and return
// it, otherwise throw an exception
final TransactionDelta delta = changesInTransaction.get(object.getObjectId());
if (delta != null) {
if (delta.hasSingleRefObject(relationName)) {
final Object refObject = delta.getSingleRefObject(relationName);
if (refObject != null) {
if (refObject instanceof IObjectId) {
return lookupObjectById((IObjectId) refObject);
} else {
throw new InvalidTransactionFailure("how did I ever get here ???"); //$NON-NLS-1$
}
} else {
return null;
}
}
}
// if no delta entry was found, ask the parentTransaction, if it exists
if (parentTransaction != null) {
return parentTransaction.getReference(object, relationName, defaultRefObject);
}
// nothing found, return the default
return defaultRefObject;
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#addReference(org.eclipse.riena.objecttransaction.ITransactedObject,
* java.lang.String,
* org.eclipse.riena.objecttransaction.ITransactedObject)
*/
public void addReference(final ITransactedObject object, final String referenceName,
final ITransactedObject refObject) {
Assert.isNotNull(object, "object must not be null"); //$NON-NLS-1$
Assert.isNotNull(object.getObjectId(), "ObjectId of object must not be null"); //$NON-NLS-1$
checkPreRegisteredClean();
if (!isCleanModus()) {
Assert.isTrue(isRegistered(object), "object must be registered"); //$NON-NLS-1$
}
Assert.isNotNull(refObject, "refObject must not be null"); //$NON-NLS-1$
Assert.isNotNull(refObject.getObjectId(), "ObjectId of refObject must not be null"); //$NON-NLS-1$
if (!isCleanModus()) {
Assert.isTrue(isRegistered(refObject), "refObject must be registered"); //$NON-NLS-1$
}
if (isInvalid()) {
throw new InvalidTransactionFailure("object transaction is invalid"); //$NON-NLS-1$
}
// do nothing if in clean modus
if (isCleanModus()) {
return;
}
// find delta entry for the transacted object, add one if it was not
// found
TransactionDelta delta = changesInTransaction.get(object.getObjectId());
if (delta == null) {
delta = new TransactionDelta(object.getObjectId(), State.CREATED, object.getVersion());
changesInTransaction.put(object.getObjectId(), delta);
} else {
// if state was clean, change it to modified
if (delta.getState().equals(State.CLEAN)) {
delta.setState(State.MODIFIED);
}
}
// add a reference to the refname into the MultiRefObject (a set of
// multiobjects for one refName)
delta.addMultiRefObject(referenceName, refObject.getObjectId());
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#removeReference(org.eclipse.riena.objecttransaction.ITransactedObject,
* java.lang.String,
* org.eclipse.riena.objecttransaction.ITransactedObject)
*/
public void removeReference(final ITransactedObject object, final String referenceName,
final ITransactedObject refObject) {
Assert.isNotNull(object, "object must not be null"); //$NON-NLS-1$
Assert.isNotNull(object.getObjectId(), "ObjectId of object must not be null"); //$NON-NLS-1$
checkPreRegisteredClean();
if (!isCleanModus()) {
Assert.isTrue(isRegistered(object), "object must be registered"); //$NON-NLS-1$
}
Assert.isNotNull(refObject, "object must not be null"); //$NON-NLS-1$
Assert.isNotNull(refObject.getObjectId(), "ObjectId of object must not be null"); //$NON-NLS-1$
if (!isCleanModus() || isStrictModus()) {
Assert.isTrue(isRegistered(refObject), "refObject must be registered"); //$NON-NLS-1$
}
if (isInvalid()) {
throw new InvalidTransactionFailure("object transaction is invalid"); //$NON-NLS-1$
}
// if in clean modus don't do anything in the transaction
if (isCleanModus()) {
return;
}
// find delta entry for transacted object, if not found, create one
TransactionDelta delta = changesInTransaction.get(object.getObjectId());
if (delta == null) {
delta = new TransactionDelta(object.getObjectId(), State.CREATED, object.getVersion());
changesInTransaction.put(object.getObjectId(), delta);
} else {
// if the state of the transacted object was clean, change it to
// modified
if (delta.getState().equals(State.CLEAN)) {
delta.setState(State.MODIFIED);
}
}
// add a "remove" entry to the multirefobject
delta.removeMultiRefObject(referenceName, refObject.getObjectId());
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#listReference(org.eclipse.riena.objecttransaction.ITransactedObject,
* java.lang.String, java.util.Set)
*/
public <T> Set<T> listReference(final ITransactedObject object, final String referenceName, final Set<T> initialSet) {
Assert.isNotNull(object, "object must not be null"); //$NON-NLS-1$
Assert.isNotNull(object.getObjectId(), "ObjectId of object must not be null"); //$NON-NLS-1$
checkPreRegisteredClean();
if (isCleanModus() || (!isStrictModus() && !isRegistered(object))) {
return initialSet;
}
Assert.isTrue(isRegistered(object), "object must be registered"); //$NON-NLS-1$
Set<T> newSet;
// if the passed Set is not a LinkedHashSet, make it one
if (initialSet == null) {
newSet = new LinkedHashSet<T>();
} else {
newSet = new LinkedHashSet<T>(initialSet);
}
fillReference(object, referenceName, newSet);
return newSet;
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#listReference(org.eclipse.riena.objecttransaction.ITransactedObject,
* java.lang.String, java.util.List)
*/
public <T> List<T> listReference(final ITransactedObject object, final String referenceName,
final List<T> initialList) {
Assert.isNotNull(object, "object must not be null"); //$NON-NLS-1$
Assert.isNotNull(object.getObjectId(), "ObjectId of object must not be null"); //$NON-NLS-1$
checkPreRegisteredClean();
if (isCleanModus() || (!isStrictModus() && !isRegistered(object))) {
return initialList;
}
Assert.isTrue(isRegistered(object), "object must be registered"); //$NON-NLS-1$
List<T> newList;
if (initialList == null) {
newList = new ArrayList<T>();
} else {
newList = new ArrayList<T>(initialList);
}
fillReference(object, referenceName, newList);
return newList;
}
@SuppressWarnings({ "unchecked" })
// TODO SuppressWarnings: Collection<T> is not Type safety
private <T> void fillReference(final ITransactedObject object, final String referenceName,
final Collection<T> initialCollection) {
// if this transaction has a parentTransaction, ask it first to add or
// remove their entries
if (parentTransaction != null) {
parentTransaction.fillReference(object, referenceName, initialCollection);
}
// find the delta object
final TransactionDelta delta = changesInTransaction.get(object.getObjectId());
if (delta != null) {
// iterate over the changes
for (final AbstractBaseChange cEntry : delta.getChanges().values()) {
// check if is a MultiSetChangeEntry and the referenceName
// matches
if (cEntry.getRelationName().equals(referenceName) && cEntry instanceof MultipleChange) {
// get the set of changes
final List<MultipleChangeEntry> changes = ((MultipleChange) cEntry).getEntries();
for (final MultipleChangeEntry singleEntry : changes) {
final Object tObject = lookupObjectById((IObjectId) singleEntry.getChildObject());
// if added, add it to the result set
if (singleEntry.getState().equals(State.ADDED)) {
initialCollection.add((T) tObject);
// if removed, remove it from the result set
} else if (singleEntry.getState().equals(State.REMOVED)) {
initialCollection.remove(tObject);
} else {
throw new InvalidTransactionFailure("state was not ADDED or REMOVED for " + singleEntry); //$NON-NLS-1$
}
}
}
}
}
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#setVersionUpdate(org.eclipse.riena.objecttransaction.ITransactedObject,
* java.lang.String)
*/
public void setVersionUpdate(final ITransactedObject object, final String version) {
Assert.isNotNull(object, "object must not be null"); //$NON-NLS-1$
Assert.isNotNull(object.getObjectId(), "object ObjectId must not be null"); //$NON-NLS-1$
Assert.isTrue(!isInvalid(), "must not be an invalid transaction"); //$NON-NLS-1$
Assert.isTrue(!isCleanModus(), "must not be in clean modus"); //$NON-NLS-1$
this.setReference(object, "sys::version", version); //$NON-NLS-1$
object.setVersion(version);
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#getVersionUpdate(org.eclipse.riena.objecttransaction.ITransactedObject,
* java.lang.String)
*/
// private String getVersionUpdate( ITransactedObject object, String
// defaultVersion ) {
// Assert.isNotNull( "object must not be null", object );
// Assert.isNotNull( "object ObjectId must not be null",
// object.getObjectId() );
// Assert.isTrue( "must not be an invalid transaction",
// !isInvalid() );
// if ( !isRegistered( object ) ) {
// return defaultVersion;
// }
// return (String) this.getReference( object, "sys::version", defaultVersion
// );
// }
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#setObjectIdUpdate(org.eclipse.riena.objecttransaction.IObjectId,
* org.eclipse.riena.objecttransaction.IObjectId)
*/
public void setObjectIdUpdate(final IObjectId oldObjectId, final IObjectId newObjectId) {
Assert.isTrue(oldObjectId != null && newObjectId != null, "old and new ObjectId must not be null"); //$NON-NLS-1$
Assert.isTrue(oldObjectId != newObjectId, "instance of oldObjectId and newObjectId must not be the same"); //$NON-NLS-1$
checkPreRegisteredClean();
changeObjectId(oldObjectId, newObjectId);
}
private void changeObjectId(final IObjectId oldObjectId, final IObjectId newObjectId) {
Assert.isNotNull(oldObjectId, "oldObjectId must not be null"); //$NON-NLS-1$
Assert.isNotNull(newObjectId, "newObjectId must not be null"); //$NON-NLS-1$
final ITransactedObject transObject = lookupObjectById(oldObjectId);
if (transObject == null) {
throw new ObjectTransactionFailure("oldObjectId is not registered"); //$NON-NLS-1$
}
// change the ObjectId in the transactedobject
transObject.setObjectId(newObjectId);
involvedTransactedObjects.remove(oldObjectId);
// change the ObjectId in the list of involvedobjects
involvedTransactedObjects.put(newObjectId, transObject);
// change the delta record for this ObjectId (if exist)
final TransactionDelta delta = changesInTransaction.get(oldObjectId);
if (delta != null) {
changesInTransaction.remove(oldObjectId);
changesInTransaction.put(newObjectId, delta);
delta.setObjectId(newObjectId);
}
// search deltas and find reference to changed ObjectId
for (final TransactionDelta delta2 : changesInTransaction.values()) {
for (final AbstractBaseChange cEntry : delta2.getChanges().values()) {
if (cEntry instanceof SingleChange) {
final Object child = ((SingleChange) cEntry).getChildObject();
if (child instanceof ITransactedObject) {
if (((ITransactedObject) child).getObjectId().equals(oldObjectId)) {
((ITransactedObject) child).setObjectId(newObjectId);
}
}
} else {
final List<MultipleChangeEntry> changes = ((MultipleChange) cEntry).getEntries();
for (final MultipleChangeEntry singleEntry : changes) {
if (singleEntry.getChildObject() instanceof ITransactedObject) {
if (((ITransactedObject) singleEntry.getChildObject()).getObjectId().equals(oldObjectId)) {
((ITransactedObject) singleEntry.getChildObject()).setObjectId(newObjectId);
}
}
}
}
}
}
// add change record so that extract contain a notification of the
// change
this.setReference(transObject, "sys::oid", newObjectId); //$NON-NLS-1$
this.setReference(transObject, "sys::oldoid", oldObjectId); //$NON-NLS-1$
}
/**
*
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#isRegistered(org.eclipse.riena.objecttransaction.ITransactedObject)
*/
public boolean isRegistered(final ITransactedObject object) {
Assert.isNotNull(object, "object must not be null"); //$NON-NLS-1$
Assert.isNotNull(object.getObjectId(), "object ObjectId must not be null"); //$NON-NLS-1$
Assert.isTrue(!isInvalid(), "must not be an invalid transaction"); //$NON-NLS-1$
checkPreRegisteredClean();
// check if a delta entry was found
if (changesInTransaction.get(object.getObjectId()) != null) {
return true;
}
// look for delta entry in parent and add business object to this transaction's references
if (parentTransaction != null) {
keepReferenceOf(object);
return parentTransaction.isRegistered(object);
}
// nothing was found -> object was not registered
return false;
}
private boolean isRegistered(final IObjectId object) {
Assert.isNotNull(object, "object must not be null"); //$NON-NLS-1$
Assert.isTrue(!isInvalid(), "must not be an invalid transaction"); //$NON-NLS-1$
checkPreRegisteredClean();
// check if a delta entry was found
if (changesInTransaction.get(object) != null) {
return true;
}
// look for delta entry in parent and add business object to this transaction's references
if (parentTransaction != null) {
return parentTransaction.isRegistered(object);
}
// nothing was found -> object was not registered
return false;
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#notifyObjectChange(org.eclipse.riena.objecttransaction.IObjectId,
* int)
*/
private void notifyObjectChange(final ITransactedObject object, final Action action) {
Assert.isNotNull(object, "object must not be null"); //$NON-NLS-1$
Assert.isNotNull(object.getObjectId(), "ObjectId of object must not be null"); //$NON-NLS-1$
if (isInvalid()) {
throw new InvalidTransactionFailure("object transaction is invalid"); //$NON-NLS-1$
}
keepReferenceOf(object);
// don't do anything if in clean modus
if (isCleanModus()) {
return;
}
// find the delta entry, create one if it does not exist, apply the
// action using the StateMachine
TransactionDelta delta = changesInTransaction.get(object.getObjectId());
if (delta == null) {
delta = new TransactionDelta(object.getObjectId(), StateMachine.initAction(action), object.getVersion());
changesInTransaction.put(object.getObjectId(), delta);
} else {
delta.setState(StateMachine.processAction(delta.getState(), action));
}
return;
}
/**
* add object to a hashmap of involvedObjects in this objectTransaction
*
* @param object
* object of which to keep the reference
*/
private void keepReferenceOf(final ITransactedObject object) {
if (object == null || object.getObjectId() == null) {
return;
}
involvedTransactedObjects.put(object.getObjectId(), object);
}
/**
* add a new object to the list of preregistered transacted objects (those
* with no IObjectId)
*
* @param object
* @pre isCleanModus()
* @pre object.getObjectId()==null
*/
private void addPreRegisteredClean(final ITransactedObject object) {
Assert.isTrue(isCleanModus(), "can only register objects with no IObjectId in clean modus"); //$NON-NLS-1$
Assert.isTrue(object.getObjectId() == null, "can only preregister object where the IObjectId is null"); //$NON-NLS-1$
if (preRegisteredCleanObjects == null) {
preRegisteredCleanObjects = new ArrayList<ITransactedObject>();
}
preRegisteredCleanObjects.add(object);
return;
}
/**
* preregistered transactedobjects are those that called in a register call,
* but dont had a IObjectId yet this method checks all entries, whether they
* now have a IObjectId. if so they are removed and added into the normal
* hashmaps (which index by IObjectId)
*/
private void checkPreRegisteredClean() {
if (preRegisteredCleanObjects == null || preRegisteredCleanObjects.size() == 0) {
return;
}
for (int i = 0; i < preRegisteredCleanObjects.size(); i++) {
final ITransactedObject object = preRegisteredCleanObjects.get(i);
if (object.getObjectId() != null) {
preRegisteredCleanObjects.remove(object);
changesInTransaction.put(object.getObjectId(), new TransactionDelta(object.getObjectId(), State.CLEAN,
object.getVersion()));
keepReferenceOf(object);
i = i - 1;
}
}
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#setCleanModus(boolean)
*/
public void setCleanModus(final boolean cleanModus) {
if (isInvalid()) {
throw new InvalidTransactionFailure("object transaction is invalid"); //$NON-NLS-1$
}
checkPreRegisteredClean();
this.cleanModus = cleanModus;
if (!cleanModus) {
if (preRegisteredCleanObjects != null && preRegisteredCleanObjects.size() > 0) {
LOGGER.log(
LogService.LOG_WARNING,
"The cleanModus is set to false in objectTransaction with the consequence that 'preregistered' object (objects with a 'null' oid) are removed and no longe registered. Your ObjectTransaction has " //$NON-NLS-1$
+ preRegisteredCleanObjects.size() + " of such objects."); //$NON-NLS-1$
for (final ITransactedObject transObject : preRegisteredCleanObjects) {
LOGGER.log(LogService.LOG_INFO, "removing " + transObject); //$NON-NLS-1$
}
preRegisteredCleanObjects = new ArrayList<ITransactedObject>();
}
}
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#isCleanModus()
*/
public boolean isCleanModus() {
if (isInvalid()) {
throw new InvalidTransactionFailure("object transaction is invalid"); //$NON-NLS-1$
}
return cleanModus;
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#setStrictModus(boolean)
*/
public void setStrictModus(final boolean strictModus) {
checkPreRegisteredClean();
this.strictModus = strictModus;
}
public boolean isStrictModus() {
return strictModus;
}
/**
* internal implementation of the commit functionality. There is an internal
* method that performs the actual task, so that it can be used internally
* for other features
*/
private void internalCommit() {
for (final ITransactedObject transObject : involvedTransactedObjects.values()) {
if (!(parentTransaction.involvedTransactedObjects.get(transObject.getObjectId()) != null)) {
parentTransaction.involvedTransactedObjects.put(transObject.getObjectId(), transObject);
}
if (!(parentTransaction.changesInTransaction.get(transObject.getObjectId()) != null)) {
parentTransaction.changesInTransaction.put(transObject.getObjectId(),
new TransactionDelta(transObject.getObjectId(), State.CLEAN, transObject.getVersion()));
}
}
for (final TransactionDelta delta : changesInTransaction.values()) {
final ITransactedObject usedObject = lookupObjectById(delta.getObjectId());
// if not registered in parent, register and set same state as in
// this transaction
if (!parentTransaction.isRegistered(usedObject)) {
parentTransaction.setObjectState(usedObject, getObjectState(usedObject));
}
for (final AbstractBaseChange cEntry : delta.getChanges().values()) {
if (cEntry instanceof SingleChange) {
parentTransaction.setReference(usedObject, ((SingleChange) cEntry).getRelationName(),
((SingleChange) cEntry).getChildObject());
} else {
final List<MultipleChangeEntry> changes = ((MultipleChange) cEntry).getEntries();
for (final MultipleChangeEntry singleEntry : changes) {
if (singleEntry.getState().equals(State.ADDED)) {
parentTransaction.addReference(usedObject, ((MultipleChange) cEntry).getRelationName(),
lookupObjectById((IObjectId) singleEntry.getChildObject()));
} else if (singleEntry.getState().equals(State.REMOVED)) {
parentTransaction.removeReference(usedObject, ((MultipleChange) cEntry).getRelationName(),
lookupObjectById((IObjectId) singleEntry.getChildObject()));
} else {
throw new InvalidTransactionFailure("state is not ADD and not REMOVED for " + singleEntry); //$NON-NLS-1$
}
}
}
}
if (!(delta.getState().equals(State.CLEAN))) {
parentTransaction.setObjectState(usedObject,
StateMachine.mergeStates(parentTransaction.getObjectState(usedObject), delta.getState()));
}
}
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#commit()
*/
public void commit() {
Assert.isTrue(!isInvalid(), "must not be an invalid transaction"); //$NON-NLS-1$
Assert.isTrue(!isRootTransaction(), "must not be root transaction to commit"); //$NON-NLS-1$
// call the internal commit method
internalCommit();
// clean the deltas, this mainly is good so that it releases all
// references to external objects
// however the objectTransaction becomes invalid anyway and cannot be
// used further on
// UPDATE: no longer true since the objecttransaction is no longer
// invalidated
clearChanges();
// make the transaction invalid (UPDATE: no longer !!!)
// invalidate();
// make the current transaction the parent transaction of this
// transaction
ObjectTransactionManager.getInstance().setCurrent(parentTransaction);
}
private Method findMethod(final Class<?> clazz, final String name, final String methodPrefix, final Object arg) {
final String methodName = methodPrefix + name.substring(0, 1).toUpperCase() + name.substring(1);
// find methods in this class
for (final Method method : clazz.getDeclaredMethods()) {
if (method.getName().equals(methodName)) {
final Class<?>[] parmType = method.getParameterTypes();
if (parmType.length == 1) {
if ((arg != null && parmType[0].isAssignableFrom(arg.getClass())) || arg == null) {
method.setAccessible(true);
return method;
}
} else {
if (parmType.length == 0 && arg == null) {
method.setAccessible(true);
return method;
}
}
}
}
// if no matching methods where found try superclass
final Class<?> superClass = clazz.getSuperclass();
if (!superClass.equals(Object.class)) {
return findMethod(superClass, name, methodPrefix, arg);
}
throw new ObjectTransactionFailure("ITransactedObject " + clazz + " must have method " + methodName //$NON-NLS-1$ //$NON-NLS-2$
+ " but lookup fails."); //$NON-NLS-1$
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#commitToObjects()
*/
public void commitToObjects() {
Assert.isTrue(!isInvalid(), "must not be an invalid transaction"); //$NON-NLS-1$
Assert.isTrue(isRootTransaction(), "must be rootTransaction"); //$NON-NLS-1$
final boolean savedCleanModus = cleanModus;
cleanModus = false;
// iterate through all changes in the objectTransaction
for (final TransactionDelta delta : changesInTransaction.values()) {
final ITransactedObject usedObject = lookupObjectById(delta.getObjectId());
// iterate through all properties or relation changes for one
// transactedobject
for (final AbstractBaseChange cEntry : delta.getChanges().values()) {
final String refName = cEntry.getRelationName();
try {
if (cEntry instanceof SingleChange) {
if (refName.equals("sys::oid") || refName.equals("sys::oldoid") || refName.equals("sys::version")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
continue;
}
applySingleChangeToBean(usedObject, cEntry, refName);
} else {
applyMultipleChangeToBean(usedObject, cEntry, refName);
}
} catch (final IllegalAccessException e) {
throw new ObjectTransactionFailure("access to field blocked field " + refName + " in object " //$NON-NLS-1$ //$NON-NLS-2$
+ usedObject, e);
} catch (final InvocationTargetException e2) {
throw new ObjectTransactionFailure("problem while accessing field blocked field " + refName //$NON-NLS-1$
+ " in object " + usedObject, e2); //$NON-NLS-1$
}
}
}
// do a second iterate and remove deleted objects from involved object
// list
for (final TransactionDelta delta : changesInTransaction.values()) {
if (delta.getState().equals(State.DELETED)) {
final ITransactedObject usedObject = lookupObjectById(delta.getObjectId());
involvedTransactedObjects.remove(usedObject.getObjectId());
}
}
cleanModus = savedCleanModus;
// only clear the delta changes, keep the referenced transacted objects
changesInTransaction = new HashMap<IObjectId, TransactionDelta>();
final Collection<ITransactedObject> transactedObject = involvedTransactedObjects.values();
if (transactedObject != null) {
final ITransactedObject[] arrayValues = transactedObject.toArray(new ITransactedObject[transactedObject
.size()]);
for (final ITransactedObject object : arrayValues) {
setObjectState(object, State.CLEAN);
}
}
}
private void applyMultipleChangeToBean(final ITransactedObject usedObject, final AbstractBaseChange cEntry,
final String refName) throws IllegalAccessException, InvocationTargetException {
cleanModus = true;
Method addMethod = null;
Method removeMethod = null;
for (final MultipleChangeEntry singleEntry : ((MultipleChange) cEntry).getEntries()) {
if (singleEntry.getState().equals(State.ADDED)) {
final Object value = lookupObjectById((IObjectId) singleEntry.getChildObject());
if (addMethod == null) {
addMethod = findMethod(usedObject.getClass(), refName, "add", value); //$NON-NLS-1$
}
addMethod.invoke(usedObject, new Object[] { value });
} else if (singleEntry.getState().equals(State.REMOVED)) {
final Object value = lookupObjectById((IObjectId) singleEntry.getChildObject());
if (removeMethod == null) {
removeMethod = findMethod(usedObject.getClass(), refName, "remove", value); //$NON-NLS-1$
}
removeMethod.invoke(usedObject, new Object[] { value });
}
}
cleanModus = false;
}
private void applySingleChangeToBean(final ITransactedObject usedObject, final AbstractBaseChange cEntry,
final String refName) {
try {
// get the current value from the bean, which should ask
// the objectTransaction
// Method getMethod = findMethod( usedObject.getClass(),
// refName, "get", null );
// Object value = getMethod.invoke( usedObject, new
// Object[] {} );
Object value = ((SingleChange) cEntry).getChildObject();
if (value instanceof IObjectId) {
value = lookupObjectById((IObjectId) value);
}
// set it into the bean using clean modus, so no
// objectTransaction is involved
final Method setMethod = findMethod(usedObject.getClass(), refName, "set", value); //$NON-NLS-1$
cleanModus = true;
setMethod.invoke(usedObject, new Object[] { value });
cleanModus = false;
} catch (final IllegalAccessException e) {
throw new ObjectTransactionFailure("access to field blocked field " + refName + " in object " //$NON-NLS-1$ //$NON-NLS-2$
+ usedObject, e);
} catch (final InvocationTargetException e2) {
throw new ObjectTransactionFailure("problem while accessing field blocked field " + refName //$NON-NLS-1$
+ " in object " + usedObject, e2); //$NON-NLS-1$
}
}
public void rollback() {
if (isInvalid()) {
throw new InvalidTransactionFailure("object transaction is invalid"); //$NON-NLS-1$
}
clearChanges();
if (!isRootTransaction()) {
// UPDATE: no longer invalidated so that it can be reused
// invalidate();
ObjectTransactionManager.getInstance().setCurrent(parentTransaction);
}
}
/**
* clear all changes in this transaction
*
*/
private void clearChanges() {
changesInTransaction = new HashMap<IObjectId, TransactionDelta>();
involvedTransactedObjects = new HashMap<IObjectId, ITransactedObject>();
}
/**
* find a transacted object instance by its IObjectId, also check
* parentTransactions
*
* @param objectId
* object id of the transacted object that we are looking for
* @return ITransactedObject of the found object or null if no entry was
* found
*/
public ITransactedObject lookupObjectById(final IObjectId objectId) {
checkPreRegisteredClean();
// find object by oid
ITransactedObject tObject = involvedTransactedObjects.get(objectId);
// if not found, ask parentTransaction, if found there add to this
// transactions list of involvedobjects
if (tObject == null && parentTransaction != null) {
tObject = parentTransaction.lookupObjectById(objectId);
keepReferenceOf(tObject);
}
return tObject;
}
/**
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#replaceRegisteredObject(org.eclipse.riena.objecttransaction.IObjectId,
* org.eclipse.riena.objecttransaction.ITransactedObject)
*/
public void replaceRegisteredObject(final IObjectId objectId, final ITransactedObject transactedObject) {
Assert.isTrue(objectId.equals(transactedObject.getObjectId()),
"oldObjectId and new transactedobject must have the an 'equal' OID"); //$NON-NLS-1$
final ITransactedObject tObject = involvedTransactedObjects.get(objectId);
Assert.isTrue(
transactedObject != tObject,
"object instances of the existing registered transacted object and the new object must not be the same or this call is meaningless"); //$NON-NLS-1$
if (tObject != null) {
involvedTransactedObjects.put(tObject.getObjectId(), transactedObject);
}
if (parentTransaction != null) {
parentTransaction.replaceRegisteredObject(objectId, transactedObject);
}
}
/**
* invalidate this objectTransaction
*/
@SuppressWarnings("unused")
// currently unused thats why it is private but maybe we need it later
private void invalidate() {
invalidTransaction = true;
}
/**
*
* @see org.eclipse.riena.objecttransaction.IObjectTransaction#isInvalid()
*/
public boolean isInvalid() {
return invalidTransaction;
}
/**
* Internal method for testcases to test if the state is correct
*
* @param object
* @param state
* @return
*/
public boolean isState(final ITransactedObject object, final State state) {
final TransactionDelta delta = changesInTransaction.get(object.getObjectId());
if (delta == null) {
return false;
}
return delta.getState().equals(state);
}
/**
* pretty printing of the content of this object transaction
*
* @return String
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("----------transaction----------------\n"); //$NON-NLS-1$
if (!isInvalid()) {
for (final TransactionDelta delta : this.exportExtract().getDeltas()) {
sb.append(delta);
}
} else {
sb.append("Transaction is invalid and cannot be displayed."); //$NON-NLS-1$
}
sb.append("----------transaction----------------\n"); //$NON-NLS-1$
return sb.toString();
}
}