use of org.apache.jackrabbit.core.state.StaleItemStateException in project jackrabbit by apache.
the class ItemSaveOperation method perform.
public Object perform(SessionContext context) throws RepositoryException {
SessionItemStateManager stateMgr = context.getItemStateManager();
/**
* build list of transient (i.e. new & modified) states that
* should be persisted
*/
Collection<ItemState> dirty;
try {
dirty = getTransientStates(context.getItemStateManager());
} catch (ConcurrentModificationException e) {
String msg = "Concurrent modification; session is closed";
log.error(msg, e);
context.getSessionImpl().logout();
throw e;
}
if (dirty.size() == 0) {
// no transient items, nothing to do here
return this;
}
/**
* build list of transient descendants in the attic
* (i.e. those marked as 'removed')
*/
Collection<ItemState> removed = getRemovedStates(context.getItemStateManager());
// All affected item states. The keys are used to look up whether
// an item is affected, and the values are iterated through below
Map<ItemId, ItemState> affected = new HashMap<ItemId, ItemState>(dirty.size() + removed.size());
for (ItemState state : dirty) {
affected.put(state.getId(), state);
}
for (ItemState state : removed) {
affected.put(state.getId(), state);
}
/**
* make sure that this save operation is totally 'self-contained'
* and independent; items within the scope of this save operation
* must not have 'external' dependencies;
* (e.g. moving a node requires that the target node including both
* old and new parents are saved)
*/
for (ItemState transientState : affected.values()) {
if (transientState.isNode()) {
NodeState nodeState = (NodeState) transientState;
Set<NodeId> dependentIDs = new HashSet<NodeId>();
if (nodeState.hasOverlayedState()) {
NodeState overlayedState = (NodeState) nodeState.getOverlayedState();
NodeId oldParentId = overlayedState.getParentId();
NodeId newParentId = nodeState.getParentId();
if (oldParentId != null) {
if (newParentId == null) {
// to dependencies
if (overlayedState.isShareable()) {
dependentIDs.addAll(overlayedState.getSharedSet());
} else {
dependentIDs.add(oldParentId);
}
} else {
if (!oldParentId.equals(newParentId)) {
// node has been moved to a new location,
// add old and new parent to dependencies
dependentIDs.add(oldParentId);
dependentIDs.add(newParentId);
} else {
// the node has been renamed (JCR-1034)
if (!affected.containsKey(newParentId) && stateMgr.hasTransientItemState(newParentId)) {
try {
NodeState parent = (NodeState) stateMgr.getTransientItemState(newParentId);
// check parent's renamed child node entries
for (ChildNodeEntry cne : parent.getRenamedChildNodeEntries()) {
if (cne.getId().equals(nodeState.getId())) {
// node has been renamed,
// add parent to dependencies
dependentIDs.add(newParentId);
}
}
} catch (ItemStateException ise) {
// should never get here
log.warn("failed to retrieve transient state: " + newParentId, ise);
}
}
}
}
}
}
// removed child node entries
for (ChildNodeEntry cne : nodeState.getRemovedChildNodeEntries()) {
dependentIDs.add(cne.getId());
}
// added child node entries
for (ChildNodeEntry cne : nodeState.getAddedChildNodeEntries()) {
dependentIDs.add(cne.getId());
}
// are within the scope of this save operation
for (NodeId id : dependentIDs) {
if (!affected.containsKey(id)) {
// otherwise ignore them
if (stateMgr.hasTransientItemState(id) || stateMgr.hasTransientItemStateInAttic(id)) {
// need to save dependency as well
String msg = context.getItemManager().safeGetJCRPath(id) + " needs to be saved as well.";
log.debug(msg);
throw new ConstraintViolationException(msg);
}
}
}
}
}
// validate access and node type constraints
// (this will also validate child removals)
validateTransientItems(context, dirty, removed);
// start the update operation
try {
stateMgr.edit();
} catch (IllegalStateException e) {
throw new RepositoryException("Unable to start edit operation", e);
}
boolean succeeded = false;
try {
// process transient items marked as 'removed'
removeTransientItems(context.getItemStateManager(), removed);
// process transient items that have change in mixins
processShareableNodes(context.getRepositoryContext().getNodeTypeRegistry(), dirty);
// initialize version histories for new nodes (might generate new transient state)
if (initVersionHistories(context, dirty)) {
// re-build the list of transient states because the previous call
// generated new transient state
dirty = getTransientStates(context.getItemStateManager());
}
// process 'new' or 'modified' transient states
persistTransientItems(context.getItemManager(), dirty);
// item state which is not referenced by any node instance.
for (ItemState transientState : dirty) {
// dispose the transient state, it is no longer used
stateMgr.disposeTransientItemState(transientState);
}
// end update operation
stateMgr.update();
// update operation succeeded
succeeded = true;
} catch (StaleItemStateException e) {
throw new InvalidItemStateException("Unable to update a stale item: " + this, e);
} catch (ItemStateException e) {
throw new RepositoryException("Unable to update item: " + this, e);
} finally {
if (!succeeded) {
// update operation failed, cancel all modifications
stateMgr.cancel();
// JCR-288: if an exception has been thrown during
// update() the transient changes have already been
// applied by persistTransientItems() and we need to
// restore transient state, i.e. undo the effect of
// persistTransientItems()
restoreTransientItems(context, dirty);
}
}
// items in store().
for (ItemState transientState : removed) {
// dispose the transient state, it is no longer used
stateMgr.disposeTransientItemStateInAttic(transientState);
}
return this;
}
use of org.apache.jackrabbit.core.state.StaleItemStateException in project jackrabbit by apache.
the class ConcurrentVersioningWithTransactionsTest method testConcurrentRestoreInTransaction.
public void testConcurrentRestoreInTransaction() throws RepositoryException {
runTask(new Task() {
public void execute(Session session, Node test) throws RepositoryException {
int i = 0;
try {
Node n = test.addNode("test");
n.addMixin(mixVersionable);
session.save();
// create 3 version
List versions = new ArrayList();
for (i = 0; i < 3; i++) {
n.checkout();
versions.add(n.checkin());
}
// do random restores
Random rand = new Random();
for (i = 0; i < NUM_OPERATIONS / CONCURRENCY; i++) {
Version v = (Version) versions.get(rand.nextInt(versions.size()));
n.restore(v, true);
}
n.checkout();
} catch (Exception e) {
final String threadName = Thread.currentThread().getName();
final Throwable deepCause = getLevel2Cause(e);
if (deepCause != null && deepCause instanceof StaleItemStateException) {
// ignore
} else {
throw new RepositoryException(threadName + ", i=" + i + ":" + e.getClass().getName(), e);
}
}
}
}, CONCURRENCY);
}
use of org.apache.jackrabbit.core.state.StaleItemStateException in project jackrabbit by apache.
the class ConcurrentLockingWithTransactionsTest method testConcurrentCreateAndLockUnLockInTransaction.
public void testConcurrentCreateAndLockUnLockInTransaction() throws RepositoryException {
runTask(new Task() {
public void execute(Session session, Node test) throws RepositoryException {
// add versionable nodes
for (int i = 0; i < NUM_OPERATIONS / CONCURRENCY; i++) {
try {
final UserTransaction utx = new UserTransactionImpl(test.getSession());
utx.begin();
Node n = test.addNode("test" + i);
n.addMixin(mixLockable);
session.save();
Lock l = n.lock(false, true);
n.unlock();
utx.commit();
} catch (Exception e) {
final String threadName = Thread.currentThread().getName();
final Throwable deepCause = getLevel2Cause(e);
if (deepCause != null && deepCause instanceof StaleItemStateException) {
// ignore
} else {
throw new RepositoryException(threadName + ", i=" + i + ":" + e.getClass().getName(), e);
}
}
}
}
}, CONCURRENCY);
}
use of org.apache.jackrabbit.core.state.StaleItemStateException in project jackrabbit by apache.
the class ItemSaveOperation method removeTransientItems.
/**
* walk through list of transient items marked 'removed' and
* definitively remove each one
*/
private void removeTransientItems(SessionItemStateManager sism, Iterable<ItemState> states) throws StaleItemStateException {
for (ItemState transientState : states) {
ItemState persistentState = transientState.getOverlayedState();
// permanently invalidate all Item instances wrapping it
assert persistentState != null;
if (transientState.getModCount() != persistentState.getModCount()) {
throw new StaleItemStateException(transientState.getId() + " has been modified externally");
}
sism.destroy(persistentState);
}
}
use of org.apache.jackrabbit.core.state.StaleItemStateException in project jackrabbit by apache.
the class ConcurrentLockingWithTransactionsTest method testConcurrentRefreshInTransaction.
public void testConcurrentRefreshInTransaction() throws RepositoryException {
runTask(new Task() {
public void execute(Session session, Node test) throws RepositoryException {
int i = 0;
try {
Node n = test.addNode("test");
n.addMixin(mixLockable);
session.save();
for (i = 0; i < NUM_OPERATIONS / CONCURRENCY; i++) {
Lock lock = n.lock(false, true);
final UserTransaction utx = new UserTransactionImpl(test.getSession());
utx.begin();
lock.refresh();
utx.commit();
n.unlock();
}
} catch (Exception e) {
final String threadName = Thread.currentThread().getName();
final Throwable deepCause = getLevel2Cause(e);
if (deepCause != null && deepCause instanceof StaleItemStateException) {
// ignore
} else {
throw new RepositoryException(threadName + ", i=" + i + ":" + e.getClass().getName(), e);
}
}
}
}, CONCURRENCY);
}
Aggregations