blob: f299d0383b85300ea3c0a0dddf9172ed853b2e40 [file] [log] [blame]
/*
* Copyright (c) 2009-2020 Eike Stepper (Loehne, Germany) 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:
* Eike Stepper - initial API and implementation
* Simon McDuff - maintenance
* Victor Roldan Betancort - maintenance
* Gonzague Reydet - bug 298334
* Andre Dietisheim - bug 256649
* Caspar De Groot - bug 290032 (Sticky views)
*/
package org.eclipse.emf.internal.cdo.transaction;
import org.eclipse.emf.cdo.CDOObject;
import org.eclipse.emf.cdo.CDOObjectReference;
import org.eclipse.emf.cdo.CDOState;
import org.eclipse.emf.cdo.common.CDOCommonRepository;
import org.eclipse.emf.cdo.common.branch.CDOBranch;
import org.eclipse.emf.cdo.common.branch.CDOBranchManager;
import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
import org.eclipse.emf.cdo.common.commit.CDOChangeSet;
import org.eclipse.emf.cdo.common.commit.CDOChangeSetData;
import org.eclipse.emf.cdo.common.commit.CDOCommitData;
import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
import org.eclipse.emf.cdo.common.commit.CDOCommitInfoManager;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.id.CDOIDGenerator;
import org.eclipse.emf.cdo.common.id.CDOIDProvider;
import org.eclipse.emf.cdo.common.id.CDOIDReference;
import org.eclipse.emf.cdo.common.id.CDOIDTemp;
import org.eclipse.emf.cdo.common.id.CDOIDUtil;
import org.eclipse.emf.cdo.common.id.CDOIdentifiable;
import org.eclipse.emf.cdo.common.lob.CDOLob;
import org.eclipse.emf.cdo.common.lob.CDOLobStore;
import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo;
import org.eclipse.emf.cdo.common.lock.CDOLockOwner;
import org.eclipse.emf.cdo.common.lock.CDOLockState;
import org.eclipse.emf.cdo.common.lock.CDOLockUtil;
import org.eclipse.emf.cdo.common.model.CDOModelUtil;
import org.eclipse.emf.cdo.common.model.CDOPackageRegistry;
import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
import org.eclipse.emf.cdo.common.model.EMFUtil;
import org.eclipse.emf.cdo.common.protocol.CDODataInput;
import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
import org.eclipse.emf.cdo.common.protocol.CDOProtocol;
import org.eclipse.emf.cdo.common.protocol.CDOProtocol.CommitData;
import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion;
import org.eclipse.emf.cdo.common.revision.CDOList;
import org.eclipse.emf.cdo.common.revision.CDOListFactory;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.common.revision.CDORevisionFactory;
import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
import org.eclipse.emf.cdo.common.revision.CDORevisionManager;
import org.eclipse.emf.cdo.common.revision.CDORevisionProvider;
import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
import org.eclipse.emf.cdo.common.revision.delta.CDOOriginSizeProvider;
import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
import org.eclipse.emf.cdo.common.util.CDOException;
import org.eclipse.emf.cdo.common.util.CDOResourceNodeNotFoundException;
import org.eclipse.emf.cdo.eresource.CDOBinaryResource;
import org.eclipse.emf.cdo.eresource.CDOFileResource;
import org.eclipse.emf.cdo.eresource.CDOResource;
import org.eclipse.emf.cdo.eresource.CDOResourceFolder;
import org.eclipse.emf.cdo.eresource.CDOResourceNode;
import org.eclipse.emf.cdo.eresource.CDOTextResource;
import org.eclipse.emf.cdo.eresource.EresourceFactory;
import org.eclipse.emf.cdo.eresource.EresourcePackage;
import org.eclipse.emf.cdo.eresource.impl.CDOResourceImpl;
import org.eclipse.emf.cdo.eresource.impl.CDOResourceNodeImpl;
import org.eclipse.emf.cdo.internal.common.commit.FailureCommitInfo;
import org.eclipse.emf.cdo.internal.common.revision.CDOListWithElementProxiesImpl;
import org.eclipse.emf.cdo.session.CDORepositoryInfo;
import org.eclipse.emf.cdo.session.CDOSession;
import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil;
import org.eclipse.emf.cdo.spi.common.commit.CDOCommitInfoUtil;
import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager;
import org.eclipse.emf.cdo.spi.common.lock.InternalCDOLockState;
import org.eclipse.emf.cdo.spi.common.model.InternalCDOClassInfo;
import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
import org.eclipse.emf.cdo.spi.common.protocol.CDODataInputImpl;
import org.eclipse.emf.cdo.spi.common.protocol.CDODataOutputImpl;
import org.eclipse.emf.cdo.spi.common.revision.CDOIDMapper;
import org.eclipse.emf.cdo.spi.common.revision.CDORevisionUnchunker;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
import org.eclipse.emf.cdo.spi.common.revision.ManagedRevisionProvider;
import org.eclipse.emf.cdo.spi.common.revision.SyntheticCDORevision;
import org.eclipse.emf.cdo.transaction.CDOCommitContext;
import org.eclipse.emf.cdo.transaction.CDOConflictResolver;
import org.eclipse.emf.cdo.transaction.CDOConflictResolver2;
import org.eclipse.emf.cdo.transaction.CDOConflictResolver3;
import org.eclipse.emf.cdo.transaction.CDODefaultTransactionHandler1;
import org.eclipse.emf.cdo.transaction.CDOMerger;
import org.eclipse.emf.cdo.transaction.CDOSavepoint;
import org.eclipse.emf.cdo.transaction.CDOStaleReferenceCleaner;
import org.eclipse.emf.cdo.transaction.CDOTransaction;
import org.eclipse.emf.cdo.transaction.CDOTransaction.Options.AutoReleaseLocksEvent.AutoReleaseLocksEnabledEvent;
import org.eclipse.emf.cdo.transaction.CDOTransaction.Options.AutoReleaseLocksEvent.AutoReleaseLocksExemptionsEvent;
import org.eclipse.emf.cdo.transaction.CDOTransactionConflictEvent;
import org.eclipse.emf.cdo.transaction.CDOTransactionFinishedEvent;
import org.eclipse.emf.cdo.transaction.CDOTransactionHandler;
import org.eclipse.emf.cdo.transaction.CDOTransactionHandler1;
import org.eclipse.emf.cdo.transaction.CDOTransactionHandler2;
import org.eclipse.emf.cdo.transaction.CDOTransactionHandler3;
import org.eclipse.emf.cdo.transaction.CDOTransactionHandlerBase;
import org.eclipse.emf.cdo.transaction.CDOTransactionStartedEvent;
import org.eclipse.emf.cdo.transaction.CDOUndoDetector;
import org.eclipse.emf.cdo.transaction.CDOUserSavepoint;
import org.eclipse.emf.cdo.util.CDOURIUtil;
import org.eclipse.emf.cdo.util.CDOUtil;
import org.eclipse.emf.cdo.util.CommitException;
import org.eclipse.emf.cdo.util.ConcurrentAccessException;
import org.eclipse.emf.cdo.util.DanglingIntegrityException;
import org.eclipse.emf.cdo.util.DanglingReferenceException;
import org.eclipse.emf.cdo.util.LocalCommitConflictException;
import org.eclipse.emf.cdo.util.ObjectNotFoundException;
import org.eclipse.emf.cdo.view.CDOQuery;
import org.eclipse.emf.internal.cdo.CDOObjectImpl;
import org.eclipse.emf.internal.cdo.bundle.OM;
import org.eclipse.emf.internal.cdo.messages.Messages;
import org.eclipse.emf.internal.cdo.object.CDONotificationBuilder;
import org.eclipse.emf.internal.cdo.object.CDOObjectMerger;
import org.eclipse.emf.internal.cdo.object.CDOObjectReferenceImpl;
import org.eclipse.emf.internal.cdo.object.CDOObjectWrapper;
import org.eclipse.emf.internal.cdo.query.CDOQueryImpl;
import org.eclipse.emf.internal.cdo.util.CommitIntegrityCheck;
import org.eclipse.emf.internal.cdo.util.CompletePackageClosure;
import org.eclipse.emf.internal.cdo.util.IPackageClosure;
import org.eclipse.emf.internal.cdo.view.CDOStateMachine;
import org.eclipse.emf.internal.cdo.view.CDOViewImpl;
import org.eclipse.net4j.util.CheckUtil;
import org.eclipse.net4j.util.ObjectUtil;
import org.eclipse.net4j.util.WrappedException;
import org.eclipse.net4j.util.collection.AbstractCloseableIterator;
import org.eclipse.net4j.util.collection.ByteArrayWrapper;
import org.eclipse.net4j.util.collection.CloseableIterator;
import org.eclipse.net4j.util.collection.ComposedIterator;
import org.eclipse.net4j.util.collection.ConcurrentArray;
import org.eclipse.net4j.util.collection.DelegatingCloseableIterator;
import org.eclipse.net4j.util.collection.Pair;
import org.eclipse.net4j.util.concurrent.IRWLockManager;
import org.eclipse.net4j.util.concurrent.TimeoutRuntimeException;
import org.eclipse.net4j.util.event.IEvent;
import org.eclipse.net4j.util.event.IListener;
import org.eclipse.net4j.util.io.ExtendedDataInputStream;
import org.eclipse.net4j.util.io.ExtendedDataOutputStream;
import org.eclipse.net4j.util.om.OMPlatform;
import org.eclipse.net4j.util.om.monitor.MonitorCanceledException;
import org.eclipse.net4j.util.om.trace.ContextTracer;
import org.eclipse.net4j.util.options.OptionsEvent;
import org.eclipse.net4j.util.transaction.TransactionException;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.EClassImpl;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EContentsEList;
import org.eclipse.emf.ecore.util.ECrossReferenceEList;
import org.eclipse.emf.spi.cdo.CDOSessionProtocol;
import org.eclipse.emf.spi.cdo.CDOSessionProtocol.CommitTransactionResult;
import org.eclipse.emf.spi.cdo.CDOTransactionStrategy;
import org.eclipse.emf.spi.cdo.FSMUtil;
import org.eclipse.emf.spi.cdo.InternalCDOObject;
import org.eclipse.emf.spi.cdo.InternalCDOSavepoint;
import org.eclipse.emf.spi.cdo.InternalCDOSession;
import org.eclipse.emf.spi.cdo.InternalCDOSession.CommitToken;
import org.eclipse.emf.spi.cdo.InternalCDOSession.InvalidationData;
import org.eclipse.emf.spi.cdo.InternalCDOSession.MergeData;
import org.eclipse.emf.spi.cdo.InternalCDOTransaction;
import org.eclipse.emf.spi.cdo.InternalCDOViewSet;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubMonitor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
/**
* @author Eike Stepper
*/
public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransaction
{
private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_TRANSACTION, CDOTransactionImpl.class);
private static final boolean X_COMPRESSION = OMPlatform.INSTANCE.isProperty("org.eclipse.emf.cdo.transaction.X_COMPRESSION");
private Object transactionHandlersLock = new Object();
private ConcurrentArray<CDOTransactionHandler1> transactionHandlers1 = new ConcurrentArray<CDOTransactionHandler1>()
{
@Override
protected CDOTransactionHandler1[] newArray(int length)
{
return new CDOTransactionHandler1[length];
}
};
private ConcurrentArray<CDOTransactionHandler2> transactionHandlers2 = new ConcurrentArray<CDOTransactionHandler2>()
{
@Override
protected CDOTransactionHandler2[] newArray(int length)
{
return new CDOTransactionHandler2[length];
}
};
private InternalCDOSavepoint lastSavepoint = createSavepoint(null);
private InternalCDOSavepoint firstSavepoint = lastSavepoint;
private boolean dirty;
private int conflict;
private CDOTransactionStrategy transactionStrategy;
private CDOIDGenerator idGenerator;
private volatile long lastCommitTime = UNSPECIFIED_DATE;
private String commitComment;
private CommitToken commitToken;
private CDOBranchPoint commitMergeSource;
// Bug 283985 (Re-attachment)
private final ThreadLocal<Boolean> providingCDOID = new InheritableThreadLocal<Boolean>()
{
@Override
protected Boolean initialValue()
{
return false;
}
};
/**
* An optional set to specify which objects in this TX are to be committed by {@link #commit()}
*/
private Set<? extends EObject> committables;
/**
* A map to hold a clean (i.e. unmodified) revision for objects that have been modified or detached.
*/
private Map<InternalCDOObject, InternalCDORevision> cleanRevisions = new CleanRevisionsMap();
public CDOTransactionImpl(CDOSession session, CDOBranch branch)
{
super(session, branch, UNSPECIFIED_DATE);
}
public CDOTransactionImpl(CDOSession session, String durableLockingID)
{
super(session, durableLockingID);
}
/**
* @since 2.0
*/
@Override
public OptionsImpl options()
{
return (OptionsImpl)super.options();
}
/**
* @since 2.0
*/
@Override
protected OptionsImpl createOptions()
{
return new OptionsImpl();
}
@Override
public boolean isReadOnly()
{
return false;
}
@Override
public boolean setBranchPoint(CDOBranchPoint branchPoint)
{
synchronized (getViewMonitor())
{
lockView();
try
{
if (branchPoint.getTimeStamp() != UNSPECIFIED_DATE)
{
throw new IllegalArgumentException("Changing the target time is not supported by transactions");
}
if (isDirty() && !getBranch().equals(branchPoint.getBranch()))
{
throw new IllegalStateException("Changing the target branch is impossible while transaction is dirty");
}
return super.setBranchPoint(branchPoint);
}
finally
{
unlockView();
}
}
}
@Override
public void addTransactionHandler(CDOTransactionHandlerBase handler)
{
synchronized (transactionHandlersLock)
{
if (handler instanceof CDOTransactionHandler1)
{
transactionHandlers1.add((CDOTransactionHandler1)handler);
}
if (handler instanceof CDOTransactionHandler2)
{
transactionHandlers2.add((CDOTransactionHandler2)handler);
}
}
}
@Override
public void removeTransactionHandler(CDOTransactionHandlerBase handler)
{
synchronized (transactionHandlersLock)
{
if (handler instanceof CDOTransactionHandler1)
{
transactionHandlers1.remove((CDOTransactionHandler1)handler);
}
if (handler instanceof CDOTransactionHandler2)
{
transactionHandlers2.remove((CDOTransactionHandler2)handler);
}
}
}
@Override
public CDOTransactionHandler[] getTransactionHandlers()
{
Set<CDOTransactionHandler> result = new HashSet<>();
synchronized (transactionHandlersLock)
{
CDOTransactionHandler1[] handlers1 = transactionHandlers1.get();
if (handlers1 != null)
{
for (CDOTransactionHandler1 handler : handlers1)
{
if (handler instanceof CDOTransactionHandler)
{
result.add((CDOTransactionHandler)handler);
}
}
}
CDOTransactionHandler2[] handlers2 = transactionHandlers2.get();
if (handlers2 != null)
{
for (CDOTransactionHandler2 handler : handlers2)
{
if (handler instanceof CDOTransactionHandler)
{
result.add((CDOTransactionHandler)handler);
}
}
}
}
return result.toArray(new CDOTransactionHandler[result.size()]);
}
@Override
public CDOTransactionHandler1[] getTransactionHandlers1()
{
synchronized (transactionHandlersLock)
{
return transactionHandlers1.get();
}
}
@Override
public CDOTransactionHandler2[] getTransactionHandlers2()
{
synchronized (transactionHandlersLock)
{
return transactionHandlers2.get();
}
}
@Override
public boolean isDirty()
{
if (isClosed())
{
return false;
}
return dirty;
}
@Override
public void setDirty(boolean dirty)
{
if (this.dirty != dirty)
{
this.dirty = dirty;
IListener[] listeners = getListeners();
if (listeners != null)
{
IEvent event = dirty ? new StartedEvent() : new FinishedEvent(false);
fireEvent(event, listeners);
}
}
}
@Override
public boolean hasConflict()
{
checkActive();
return conflict > 0;
}
@Override
public void setConflict(InternalCDOObject object)
{
IEvent event = null;
synchronized (getViewMonitor())
{
lockView();
try
{
event = new ConflictEvent(object, conflict == 0);
++conflict;
}
finally
{
unlockView();
}
}
fireEvent(event);
}
@Override
public void removeConflict(InternalCDOObject object)
{
synchronized (getViewMonitor())
{
lockView();
try
{
if (conflict > 0)
{
--conflict;
}
}
finally
{
unlockView();
}
}
}
/**
* @since 2.0
*/
@Override
public Set<CDOObject> getConflicts()
{
synchronized (getViewMonitor())
{
lockView();
try
{
Set<CDOObject> conflicts = new HashSet<>();
for (CDOObject object : getDirtyObjects().values())
{
if (object.cdoConflict())
{
conflicts.add(object);
}
}
for (CDOObject object : getDetachedObjects().values())
{
if (object.cdoConflict())
{
conflicts.add(object);
}
}
return conflicts;
}
finally
{
unlockView();
}
}
}
@Override
public CDOChangeSetData getChangeSetData()
{
checkActive();
synchronized (getViewMonitor())
{
lockView();
try
{
return lastSavepoint.getAllChangeSetData();
}
finally
{
unlockView();
}
}
}
@Override
public CDOChangeSetData merge(CDOBranch source, CDOMerger merger)
{
return merge(source.getHead(), merger);
}
@Override
public CDOChangeSetData merge(CDOBranchPoint source, CDOMerger merger)
{
return merge(source, CDOBranchUtil.AUTO_BRANCH_POINT, merger);
}
@Override
public CDOChangeSetData merge(CDOBranchPoint source, CDOBranchPoint sourceBase, CDOMerger merger)
{
return merge(source, sourceBase, null, merger);
}
@Override
public CDOChangeSetData merge(CDOBranchPoint source, CDOBranchPoint sourceBase, CDOBranchPoint targetBase, CDOMerger merger)
{
synchronized (getViewMonitor())
{
lockView();
try
{
if (isDirty())
{
throw new IllegalStateException("Merging into dirty transactions not yet supported");
}
long now = getLastUpdateTime();
CDOBranchPoint target = getBranch().getPoint(now);
if (source.getTimeStamp() == UNSPECIFIED_DATE)
{
source = source.getBranch().getPoint(now);
}
if (CDOBranchUtil.isContainedBy(source, target))
{
throw new IllegalArgumentException("Source is already contained in " + target);
}
if (sourceBase != CDOBranchUtil.AUTO_BRANCH_POINT)
{
if (sourceBase != null && !CDOBranchUtil.isContainedBy(sourceBase, source))
{
throw new IllegalArgumentException("Source base is not contained in " + source);
}
if (targetBase != null && !CDOBranchUtil.isContainedBy(targetBase, target))
{
throw new IllegalArgumentException("Target base is not contained in " + target);
}
}
InternalCDOSession session = getSession();
MergeData mergeData = session.getMergeData(target, source, targetBase, sourceBase, true);
CDOChangeSet targetChanges = mergeData.getTargetChanges();
CDOChangeSet sourceChanges = mergeData.getSourceChanges();
CDOChangeSetData result = merger.merge(targetChanges, sourceChanges);
if (result == null)
{
return null;
}
CDORevisionProvider targetProvider = mergeData.getTargetInfo();
CDORevisionProvider resultBaseProvider;
CDORevisionManager revisionManager = session.getRevisionManager();
CDOBranchPoint resultBase = mergeData.getResultBase();
if (resultBase != null)
{
resultBaseProvider = new ManagedRevisionProvider(revisionManager, resultBase);
}
else
{
resultBaseProvider = mergeData.getTargetBaseInfo();
}
ApplyChangeSetResult changeSet = applyChangeSet(result, resultBaseProvider, targetProvider, source, false);
commitMergeSource = source;
return changeSet.getChangeSetData();
}
finally
{
unlockView();
}
}
}
public CDOChangeSetData remerge(CDOBranchPoint source, CDOMerger merger)
{
return merge(source, CDOBranchUtil.AUTO_BRANCH_POINT, merger);
}
@Override
@Deprecated
public Pair<CDOChangeSetData, Pair<Map<CDOID, CDOID>, List<CDOID>>> applyChangeSetData(CDOChangeSetData changeSetData, CDORevisionProvider targetBaseProvider,
CDORevisionProvider targetProvider, CDOBranchPoint source)
{
throw new UnsupportedOperationException();
}
@Override
public ApplyChangeSetResult applyChangeSet(CDOChangeSetData changeSetData, CDORevisionProvider resultBaseProvider, CDORevisionProvider targetProvider,
CDOBranchPoint source, boolean keepVersions) throws ChangeSetOutdatedException
{
synchronized (getViewMonitor())
{
lockView();
try
{
ApplyChangeSetResult result = new ApplyChangeSetResult();
// Merges from local offline branches may require additional ID mappings: localID -> tempID
if (source != null && source.getBranch().isLocal()
&& getSession().getRepositoryInfo().getIDGenerationLocation() == CDOCommonRepository.IDGenerationLocation.STORE)
{
applyLocalIDMapping(changeSetData, result);
}
CDOChangeSetData resultData = result.getChangeSetData();
// New objects
applyNewObjects(changeSetData.getNewObjects(), resultData.getNewObjects());
// Detached objects
Set<CDOObject> detachedSet = applyDetachedObjects(changeSetData.getDetachedObjects(), resultData.getDetachedObjects());
// Changed objects
Map<CDOID, InternalCDORevision> oldRevisions = applyChangedObjects(changeSetData.getChangedObjects(), resultBaseProvider, targetProvider, keepVersions,
resultData.getChangedObjects());
// Delta notifications
Collection<CDORevisionDelta> notificationDeltas = lastSavepoint.getRevisionDeltas2().values();
if (!notificationDeltas.isEmpty() || !detachedSet.isEmpty())
{
sendDeltaNotifications(notificationDeltas, detachedSet, oldRevisions);
}
return result;
}
finally
{
unlockView();
}
}
}
private void applyLocalIDMapping(CDOChangeSetData changeSetData, ApplyChangeSetResult result)
{
Map<CDOID, CDOID> idMappings = result.getIDMappings();
// Collect needed ID mappings
for (CDOIDAndVersion key : changeSetData.getNewObjects())
{
InternalCDORevision revision = (InternalCDORevision)key;
if (revision.getBranch().isLocal())
{
CDOID oldID = revision.getID();
CDOID newID = createIDForNewObject(null);
idMappings.put(oldID, newID);
revision.setID(newID);
revision.setVersion(0);
}
}
if (!idMappings.isEmpty())
{
// Apply collected ID mappings
CDOIDMapper idMapper = new CDOIDMapper(idMappings);
idMapper.setAllowUnmappedTempIDs(true);
for (CDOIDAndVersion key : changeSetData.getNewObjects())
{
InternalCDORevision revision = (InternalCDORevision)key;
revision.adjustReferences(idMapper);
}
for (CDORevisionKey key : changeSetData.getChangedObjects())
{
InternalCDORevisionDelta revisionDelta = (InternalCDORevisionDelta)key;
if (revisionDelta.adjustReferences(idMapper))
{
result.getAdjustedObjects().add(revisionDelta.getID());
}
}
}
}
private void applyNewObjects(List<CDOIDAndVersion> newObjects, List<CDOIDAndVersion> result)
{
List<InternalCDOObject> objects = new ArrayList<>();
for (CDOIDAndVersion key : newObjects)
{
InternalCDORevision revision = (InternalCDORevision)key;
CDOID id = revision.getID();
if (getObjectIfExists(id) == null)
{
revision = revision.copy();
revision.setBranchPoint(getBranchPoint());
revision.setVersion(0);
InternalCDOObject object = newInstance(revision.getEClass());
object.cdoInternalSetView(this);
object.cdoInternalSetRevision(revision);
object.cdoInternalSetState(CDOState.NEW);
objects.add(object);
registerObject(object);
result.add(revision);
}
}
if (!objects.isEmpty())
{
for (InternalCDOObject object : objects)
{
object.cdoInternalPostLoad();
registerAttached(object, true);
}
setDirty(true);
}
}
private Set<CDOObject> applyDetachedObjects(List<CDOIDAndVersion> detachedObjects, List<CDOIDAndVersion> result)
{
Set<CDOObject> detachedSet = new HashSet<>();
for (CDOIDAndVersion key : detachedObjects)
{
CDOID id = key.getID();
InternalCDOObject object = getObjectIfExists(id);
if (object != null)
{
result.add(CDOIDUtil.createIDAndVersion(id, CDOBranchVersion.UNSPECIFIED_VERSION));
CDOStateMachine.INSTANCE.detach(object);
detachedSet.add(object);
setDirty(true);
}
}
return detachedSet;
}
private Map<CDOID, InternalCDORevision> applyChangedObjects(List<CDORevisionKey> changedObjects, CDORevisionProvider resultBaseProvider,
CDORevisionProvider targetProvider, boolean keepVersions, List<CDORevisionKey> result) throws ChangeSetOutdatedException
{
Map<CDOID, InternalCDORevision> oldRevisions = CDOIDUtil.createMap();
Map<CDOID, CDOObject> detachedObjects = lastSavepoint.getDetachedObjects();
Map<CDOID, CDOObject> dirtyObjects = lastSavepoint.getDirtyObjects();
Map<CDOID, CDORevisionDelta> revisionDeltas = lastSavepoint.getRevisionDeltas2();
for (CDORevisionKey key : changedObjects)
{
InternalCDORevisionDelta resultBaseGoalDelta = (InternalCDORevisionDelta)key;
resultBaseGoalDelta.setTarget(null);
CDOID id = resultBaseGoalDelta.getID();
InternalCDORevision resultBaseRevision = (InternalCDORevision)resultBaseProvider.getRevision(id);
InternalCDOObject object = getObject(id);
boolean revisionChanged = false;
InternalCDORevision targetRevision = object.cdoRevision();
if (targetRevision == null)
{
targetRevision = (InternalCDORevision)targetProvider.getRevision(id);
object.cdoInternalSetRevision(targetRevision);
revisionChanged = true;
}
oldRevisions.put(id, targetRevision);
InternalCDORevision goalRevision = resultBaseRevision.copy();
goalRevision.setBranchPoint(this);
if (!keepVersions)
{
goalRevision.setVersion(targetRevision.getVersion());
}
goalRevision.setRevised(UNSPECIFIED_DATE);
resultBaseGoalDelta.applyTo(goalRevision);
InternalCDORevisionDelta targetGoalDelta = goalRevision.compare(targetRevision);
targetGoalDelta.setTarget(null);
if (!targetGoalDelta.isEmpty())
{
if (keepVersions && targetGoalDelta.getVersion() != resultBaseRevision.getVersion())
{
throw new ChangeSetOutdatedException();
}
revisionDeltas.put(id, targetGoalDelta);
result.add(targetGoalDelta);
// handle reattached objects.
if (detachedObjects.containsKey(id))
{
CDOStateMachine.INSTANCE.internalReattach(object, this);
}
object.cdoInternalSetRevision(goalRevision);
object.cdoInternalSetState(CDOState.DIRTY);
revisionChanged = true;
dirtyObjects.put(id, object);
setDirty(true);
}
if (revisionChanged)
{
object.cdoInternalPostLoad();
}
}
return oldRevisions;
}
private InternalCDOObject getObjectIfExists(CDOID id)
{
try
{
return getObject(id);
}
catch (ObjectNotFoundException ex)
{
return null;
}
}
/*
* Synchronized through InvalidationRunnable.run()
*/
@Override
protected void handleConflicts(long lastUpdateTime, Map<CDOObject, Pair<CDORevision, CDORevisionDelta>> conflicts, List<CDORevisionDelta> deltas)
{
synchronized (getViewMonitor())
{
lockView();
try
{
CDOConflictResolver[] resolvers = options().getConflictResolvers();
if (resolvers.length == 0)
{
return;
}
if (conflicts == null)
{
for (CDOConflictResolver resolver : resolvers)
{
if (resolver instanceof CDOConflictResolver.NonConflictAware)
{
((CDOConflictResolver.NonConflictAware)resolver).handleNonConflict(lastUpdateTime);
}
}
return;
}
// Remember original state to be able to restore it after an exception
List<CDOState> states = new ArrayList<>(conflicts.size());
List<CDORevision> revisions = new ArrayList<>(conflicts.size());
for (CDOObject conflict : conflicts.keySet())
{
states.add(conflict.cdoState());
revisions.add(conflict.cdoRevision());
}
int resolved = 0;
try
{
Map<CDOObject, Pair<CDORevision, CDORevisionDelta>> remaining = new HashMap<>(conflicts);
for (CDOConflictResolver resolver : resolvers)
{
if (resolver instanceof CDOConflictResolver2)
{
((CDOConflictResolver2)resolver).resolveConflicts(Collections.unmodifiableMap(remaining), deltas);
}
else
{
resolver.resolveConflicts(Collections.unmodifiableSet(remaining.keySet()));
}
for (Iterator<CDOObject> it = remaining.keySet().iterator(); it.hasNext();)
{
CDOObject object = it.next();
if (!object.cdoConflict())
{
++resolved;
it.remove();
}
}
}
}
catch (Exception ex)
{
// Restore original state
Iterator<CDOState> state = states.iterator();
Iterator<CDORevision> revision = revisions.iterator();
for (CDOObject object : conflicts.keySet())
{
((InternalCDOObject)object).cdoInternalSetRevision(revision.next());
((InternalCDOObject)object).cdoInternalSetState(state.next());
}
throw WrappedException.wrap(ex);
}
conflict -= resolved;
Map<CDOID, CDOObject> dirtyObjects = getDirtyObjects();
setDirty(!dirtyObjects.isEmpty());
}
finally
{
unlockView();
}
}
}
/**
* @deprecated {@link #createIDForNewObject(EObject object)} is called since 4.1.
*/
@Override
@Deprecated
public CDOIDTemp getNextTemporaryID()
{
throw new UnsupportedOperationException();
}
@Override
public CDOID createIDForNewObject(EObject object)
{
return idGenerator.generateCDOID(object);
}
@Override
public CDOResourceFolder createResourceFolder(String path) throws CDOResourceNodeNotFoundException
{
synchronized (getViewMonitor())
{
lockView();
try
{
if (path.endsWith(CDOURIUtil.SEGMENT_SEPARATOR))
{
path = path.substring(0, path.length() - 1);
}
CDOResourceFolder folder = EresourceFactory.eINSTANCE.createCDOResourceFolder();
int pos = path.lastIndexOf(CDOURIUtil.SEGMENT_SEPARATOR_CHAR);
if (pos <= 0)
{
String name = path.substring(pos == 0 ? 1 : 0);
folder.setName(name);
getRootResource().getContents().add(folder);
}
else
{
String name = path.substring(pos + 1);
folder.setName(name);
path = path.substring(0, pos);
CDOResourceNode parent = null;
try
{
parent = getResourceNode(path);
}
catch (Exception ex)
{
parent = createResourceFolder(path);
}
if (parent instanceof CDOResourceFolder)
{
((CDOResourceFolder)parent).getNodes().add(folder);
}
else
{
throw new CDOException("Parent is not a folder: " + parent);
}
}
return folder;
}
finally
{
unlockView();
}
}
}
@Override
public CDOResource createResource(String path)
{
checkActive();
synchronized (getViewMonitor())
{
lockView();
try
{
URI uri = CDOURIUtil.createResourceURI(this, path);
return (CDOResource)getResourceSet().createResource(uri);
}
finally
{
unlockView();
}
}
}
@Override
public CDOResource getOrCreateResource(String path)
{
checkActive();
synchronized (getViewMonitor())
{
lockView();
try
{
try
{
CDOID id = getResourceNodeID(path);
if (!CDOIDUtil.isNull(id))
{
return (CDOResource)getObject(id);
}
}
catch (CDOResourceNodeNotFoundException ignore)
{
// Just create the missing resource.
}
return createResource(path);
}
finally
{
unlockView();
}
}
}
@Override
public CDOTextResource createTextResource(String path)
{
synchronized (getViewMonitor())
{
lockView();
try
{
CDOTextResource resource = EresourceFactory.eINSTANCE.createCDOTextResource();
createFileResource(path, resource);
return resource;
}
finally
{
unlockView();
}
}
}
@Override
public CDOTextResource getOrCreateTextResource(String path)
{
synchronized (getViewMonitor())
{
lockView();
try
{
try
{
CDOID id = getResourceNodeID(path);
if (!CDOIDUtil.isNull(id))
{
return (CDOTextResource)getObject(id);
}
}
catch (CDOResourceNodeNotFoundException ignore)
{
// Just create the missing resource.
}
return createTextResource(path);
}
finally
{
unlockView();
}
}
}
@Override
public CDOBinaryResource createBinaryResource(String path)
{
synchronized (getViewMonitor())
{
lockView();
try
{
CDOBinaryResource resource = EresourceFactory.eINSTANCE.createCDOBinaryResource();
createFileResource(path, resource);
return resource;
}
finally
{
unlockView();
}
}
}
@Override
public CDOBinaryResource getOrCreateBinaryResource(String path)
{
synchronized (getViewMonitor())
{
lockView();
try
{
try
{
CDOID id = getResourceNodeID(path);
if (!CDOIDUtil.isNull(id))
{
return (CDOBinaryResource)getObject(id);
}
}
catch (CDOResourceNodeNotFoundException ignore)
{
// Just create the missing resource
}
return createBinaryResource(path);
}
finally
{
unlockView();
}
}
}
private void createFileResource(String path, CDOFileResource<?> resource)
{
int lastSlash = path.lastIndexOf('/');
if (lastSlash == -1)
{
resource.setName(path);
getRootResource().getContents().add(resource);
}
else
{
resource.setName(path.substring(lastSlash + 1));
CDOResourceFolder folder = getOrCreateResourceFolder(path.substring(0, lastSlash));
folder.getNodes().add(resource);
}
}
/**
* @since 2.0
*/
@Override
public void attachResource(CDOResourceImpl resource)
{
synchronized (getViewMonitor())
{
lockView();
try
{
if (resource.isExisting())
{
super.attachResource(resource);
}
else
{
// ResourceSet.createResource(uri) was called!!
attachNewResource(resource);
}
}
finally
{
unlockView();
}
}
}
private void attachNewResource(CDOResourceImpl resource)
{
URI uri = resource.getURI();
List<String> names = CDOURIUtil.analyzePath(uri);
String resourceName = names.isEmpty() ? null : names.remove(names.size() - 1);
CDOResourceFolder folder = getOrCreateResourceFolder(names);
attachNewResourceNode(folder, resourceName, resource);
}
@Override
public CDOResourceFolder getOrCreateResourceFolder(String path)
{
checkActive();
synchronized (getViewMonitor())
{
lockView();
try
{
try
{
CDOID id = getResourceNodeID(path);
if (!CDOIDUtil.isNull(id))
{
return (CDOResourceFolder)getObject(id);
}
}
catch (CDOResourceNodeNotFoundException ignore)
{
// Just create the missing resource.
}
return createResourceFolder(path);
}
finally
{
unlockView();
}
}
}
/**
* @return never <code>null</code>;
* @since 2.0
*/
@Override
public CDOResourceFolder getOrCreateResourceFolder(List<String> names)
{
synchronized (getViewMonitor())
{
lockView();
try
{
CDOResourceFolder folder = null;
for (String name : names)
{
CDOResourceNode node;
try
{
CDOID folderID = folder == null ? null : folder.cdoID();
node = getResourceNode(folderID, name);
}
catch (CDOResourceNodeNotFoundException ex)
{
node = EresourceFactory.eINSTANCE.createCDOResourceFolder();
attachNewResourceNode(folder, name, node);
}
if (node instanceof CDOResourceFolder)
{
folder = (CDOResourceFolder)node;
}
else
{
throw new CDOException(MessageFormat.format(Messages.getString("CDOTransactionImpl.0"), node)); //$NON-NLS-1$
}
}
return folder;
}
finally
{
unlockView();
}
}
}
private void attachNewResourceNode(CDOResourceFolder folder, String name, CDOResourceNode newNode)
{
CDOResourceNodeImpl node = (CDOResourceNodeImpl)newNode;
node.basicSetName(name, false);
if (folder == null)
{
if (node.isRoot())
{
CDOStateMachine.INSTANCE.attach(node, this);
}
else
{
getRootResource().getContents().add(node);
}
}
else
{
node.basicSetFolder(folder, false);
}
}
/**
* @since 2.0
*/
public void detach(CDOResourceImpl cdoResource)
{
synchronized (getViewMonitor())
{
lockView();
try
{
CDOStateMachine.INSTANCE.detach(cdoResource);
}
finally
{
unlockView();
}
}
}
/**
* @since 4.1
*/
@Override
public InternalCDOSavepoint getFirstSavepoint()
{
return firstSavepoint;
}
/**
* @since 2.0
*/
@Override
public InternalCDOSavepoint getLastSavepoint()
{
checkActive();
return lastSavepoint;
}
/**
* @since 2.0
*/
@Override
public CDOTransactionStrategy getTransactionStrategy()
{
synchronized (getViewMonitor())
{
lockView();
try
{
if (transactionStrategy == null)
{
transactionStrategy = CDOTransactionStrategy.DEFAULT;
transactionStrategy.setTarget(this);
}
return transactionStrategy;
}
finally
{
unlockView();
}
}
}
/**
* @since 2.0
*/
@Override
public void setTransactionStrategy(CDOTransactionStrategy transactionStrategy)
{
synchronized (getViewMonitor())
{
lockView();
try
{
if (this.transactionStrategy != null)
{
this.transactionStrategy.unsetTarget(this);
}
this.transactionStrategy = transactionStrategy;
if (this.transactionStrategy != null)
{
this.transactionStrategy.setTarget(this);
}
}
finally
{
unlockView();
}
}
}
/**
* @since 2.0
*/
@Override
protected CDOID getRootOrTopLevelResourceNodeID(String name) throws CDOResourceNodeNotFoundException
{
synchronized (getViewMonitor())
{
lockView();
try
{
if (dirty)
{
CDOResourceNode node = getRootResourceNode(name, getDirtyObjects().values());
if (node != null)
{
return node.cdoID();
}
node = getRootResourceNode(name, getNewObjects().values());
if (node != null)
{
return node.cdoID();
}
}
CDOID id = super.getRootOrTopLevelResourceNodeID(name);
if (isObjectDetached(id) || isObjectDirty(id))
{
throw new CDOException(MessageFormat.format(Messages.getString("CDOTransactionImpl.1"), name)); //$NON-NLS-1$
}
return id;
}
finally
{
unlockView();
}
}
}
private CDOResourceNode getRootResourceNode(String name, Collection<? extends CDOObject> objects)
{
for (CDOObject object : objects)
{
if (object instanceof CDOResourceNode)
{
CDOResourceNode node = (CDOResourceNode)object;
if (node.getFolder() == null && ObjectUtil.equals(name, node.getName()))
{
return node;
}
}
}
return null;
}
@Override
protected InternalCDOObject getObjectUnsynced(CDOID id, boolean loadOnDemand)
{
if (isObjectNew(id) && isObjectDetached(id))
{
throw new ObjectNotFoundException(id, this);
}
return super.getObjectUnsynced(id, loadOnDemand);
}
@Override
public boolean isObjectNew(CDOID id)
{
return lastSavepoint.isNewObject(id);
}
private boolean isObjectDirty(CDOID id)
{
return lastSavepoint.getDirtyObject(id) != null;
}
private boolean isObjectDetached(CDOID id)
{
return lastSavepoint.getDetachedObject(id) != null;
}
/**
* @since 2.0
*/
@Override
public InternalCDOCommitContext createCommitContext()
{
synchronized (getViewMonitor())
{
lockView();
try
{
return new CDOCommitContextImpl(this);
}
finally
{
unlockView();
}
}
}
@Override
public CDOCommitInfo commit() throws CommitException
{
return commit(null);
}
/**
* @since 2.0
*/
@Override
public CDOCommitInfo commit(IProgressMonitor monitor) throws CommitException
{
CDOConflictResolver[] conflictResolvers = options().getConflictResolvers();
if (conflictResolvers.length != 0)
{
try
{
checkActive();
// Reach out to conflict resolvers before synchronizing on this transaction to avoid deadlocks in UI thread.
for (CDOConflictResolver resolver : conflictResolvers)
{
if (resolver instanceof CDOConflictResolver3)
{
if (!((CDOConflictResolver3)resolver).preCommit())
{
return null;
}
}
}
}
catch (Exception ex)
{
throw new CommitException(ex);
}
}
CDOCommitInfo info = commitSynced(monitor);
if (info != null)
{
waitForCommitInfo(info.getTimeStamp());
}
return info;
}
private void waitForCommitInfo(long timeStamp)
{
long timeout = options().getCommitInfoTimeout();
if (!waitForUpdate(timeStamp, timeout))
{
throw new TimeoutRuntimeException("Did not receive an update: " + this);
}
}
/**
* Overridden by Bugzilla_547640_Test to resume the repository's commit notification sending.
*/
protected void waitForBaseline(long timeStamp)
{
waitForCommitInfo(timeStamp);
}
private CDOCommitInfo commitSynced(IProgressMonitor progressMonitor) throws DanglingIntegrityException, CommitException
{
synchronized (getViewMonitor())
{
lockView();
try
{
InternalCDOSession session = getSession();
try
{
checkActive();
if (hasConflict())
{
throw new LocalCommitConflictException(Messages.getString("CDOTransactionImpl.2")); //$NON-NLS-1$
}
commitToken = (CommitToken)session.startLocalCommit();
CDOTransactionStrategy transactionStrategy = getTransactionStrategy();
CDOCommitInfo info = transactionStrategy.commit(this, progressMonitor);
if (info != null)
{
lastCommitTime = info.getTimeStamp();
}
return info;
}
catch (DanglingReferenceException ex)
{
throw new DanglingIntegrityException(ex);
}
catch (CommitException ex)
{
throw ex;
}
catch (Throwable t)
{
try
{
// The commit may have succeeded on the server, but after that network problems or timeouts have hit us.
// Let's see if we can recover...
CDOCommitInfo info = session.getSessionProtocol().resetTransaction(getViewID(), commitToken.getCommitNumber());
if (info != null)
{
lastCommitTime = info.getTimeStamp();
InvalidationData invalidationData = new InvalidationData();
invalidationData.setCommitInfo(info);
invalidationData.setSender(this);
invalidationData.setClearResourcePathCache(true);
invalidationData.setSecurityImpact(CDOProtocol.CommitNotificationInfo.IMPACT_NONE);
invalidationData.setNewPermissions(null);
invalidationData.setLockChangeInfo(null);
session.invalidate(invalidationData);
// At this point the session (invalidator) is recovered.
// Continue to rethrow the exception and let the client call rollback()...
}
}
catch (Throwable ex)
{
if (TRACER.isEnabled())
{
TRACER.trace(ex);
}
}
Throwable cause = t.getCause();
if (cause instanceof MonitorCanceledException)
{
if (!(cause.getCause() instanceof TimeoutRuntimeException))
{
throw new OperationCanceledException("CDOTransactionImpl.7");//$NON-NLS-1$
}
}
throw new CommitException(t);
}
finally
{
session.endLocalCommit(commitToken);
commitToken = null;
clearResourcePathCacheIfNecessary(null);
}
}
finally
{
unlockView();
}
}
}
@Override
@Deprecated
public <T> CommitResult<T> commit(final Callable<T> callable, org.eclipse.net4j.util.Predicate<Long> retry, IProgressMonitor monitor)
throws ConcurrentAccessException, CommitException, Exception
{
return commit(callable, org.eclipse.net4j.util.Predicates.toJava8(retry), monitor);
}
@Override
public <T> CommitResult<T> commit(Callable<T> callable, java.util.function.Predicate<Long> retry, IProgressMonitor monitor)
throws ConcurrentAccessException, CommitException, Exception
{
final Object[] result = { null };
final Exception[] exception = { null };
Runnable runnable = new Runnable()
{
@Override
public void run()
{
try
{
result[0] = callable.call();
}
catch (Exception ex)
{
exception[0] = ex;
}
}
};
CDOCommitInfo info = commit(runnable, retry, monitor);
if (exception[0] != null)
{
throw exception[0];
}
if (info == null)
{
return null;
}
@SuppressWarnings("unchecked")
T r = (T)result[0];
return new CommitResult<>(r, info);
}
@Override
public <T> CommitResult<T> commit(Callable<T> callable, int attempts, IProgressMonitor monitor) throws ConcurrentAccessException, CommitException, Exception
{
return commit(callable, new CountedRetryPredicate(attempts), monitor);
}
@Override
@Deprecated
public CDOCommitInfo commit(Runnable runnable, org.eclipse.net4j.util.Predicate<Long> retry, IProgressMonitor monitor)
throws ConcurrentAccessException, CommitException
{
return commit(runnable, org.eclipse.net4j.util.Predicates.toJava8(retry), monitor);
}
@Override
public CDOCommitInfo commit(Runnable runnable, java.util.function.Predicate<Long> retry, IProgressMonitor monitor)
throws ConcurrentAccessException, CommitException
{
long start = System.currentTimeMillis();
SubMonitor subMonitor = SubMonitor.convert(monitor);
for (;;)
{
subMonitor.setWorkRemaining(100);
synchronized (getViewMonitor())
{
lockView();
runnable.run();
try
{
return commit(subMonitor.split(1));
}
catch (ConcurrentAccessException ex)
{
if (retry.test(System.currentTimeMillis() - start))
{
rollback();
continue;
}
throw ex;
}
finally
{
unlockView();
}
}
}
}
@Override
public CDOCommitInfo commit(Runnable runnable, int attempts, IProgressMonitor monitor) throws ConcurrentAccessException, CommitException
{
return commit(runnable, new CountedRetryPredicate(attempts), monitor);
}
@Override
public CommitToken getCommitToken()
{
return commitToken;
}
/**
* @since 2.0
*/
@Override
public void rollback()
{
checkActive();
synchronized (getViewMonitor())
{
lockView();
try
{
CDOTransactionStrategy strategy = getTransactionStrategy();
strategy.rollback(this, firstSavepoint);
cleanUp(null);
}
finally
{
unlockView();
}
}
}
private void removeObject(CDOID id, final CDOObject object)
{
removeObject(id);
if (object instanceof CDOResource)
{
InternalCDOViewSet viewSet = getViewSet();
viewSet.executeWithoutNotificationHandling(new Callable<Boolean>()
{
@Override
public Boolean call() throws Exception
{
EList<Resource> resources = getResourceSet().getResources();
resources.remove(object);
return true;
}
});
}
}
/**
* @since 2.0
*/
@Override
public void detachObject(InternalCDOObject object)
{
synchronized (getViewMonitor())
{
lockView();
try
{
CDOTransactionHandler1[] handlers = getTransactionHandlers1();
for (int i = 0; i < handlers.length; i++)
{
CDOTransactionHandler1 handler = handlers[i];
handler.detachingObject(this, object);
}
// deregister object
CDOID id = object.cdoID();
if (object.cdoState() == CDOState.NEW)
{
Map<CDOID, CDOObject> map = lastSavepoint.getNewObjects();
// Determine if we added object
if (map.containsKey(id))
{
map.remove(id);
}
else
{
lastSavepoint.getDetachedObjects().put(id, object);
}
// deregister object
deregisterObject(object);
}
else
{
if (!cleanRevisions.containsKey(object))
{
cleanRevisions.put(object, object.cdoRevision());
}
lastSavepoint.getDetachedObjects().put(id, object);
// Object may have been reattached previously, in which case it must
// here be removed from the collection of reattached objects
lastSavepoint.getReattachedObjects().remove(id);
}
getUnitManager().removeObject(object);
}
finally
{
unlockView();
}
}
}
/**
* @since 2.0
*/
@Override
public void handleRollback(InternalCDOSavepoint savepoint)
{
if (savepoint == null)
{
throw new IllegalArgumentException(Messages.getString("CDOTransactionImpl.3")); //$NON-NLS-1$
}
if (savepoint.getTransaction() != this)
{
throw new IllegalArgumentException(MessageFormat.format(Messages.getString("CDOTransactionImpl.4"), savepoint)); //$NON-NLS-1$
}
if (!savepoint.isValid())
{
throw new IllegalArgumentException(MessageFormat.format(Messages.getString("CDOTransactionImpl.6"), savepoint)); //$NON-NLS-1$
}
if (TRACER.isEnabled())
{
TRACER.trace("handleRollback()"); //$NON-NLS-1$
}
synchronized (getViewMonitor())
{
lockView();
try
{
// Remember current revisions
Map<CDOObject, CDORevision> oldRevisions = new HashMap<>();
collectRevisions(oldRevisions, getDirtyObjects());
collectRevisions(oldRevisions, getNewObjects());
// Rollback objects
Map<CDOObject, CDORevision> newRevisions = new HashMap<>();
Set<CDOID> idsOfNewObjectWithDeltas = rollbackCompletely(savepoint, newRevisions);
lastSavepoint = savepoint;
lastSavepoint.setNextSavepoint(null);
lastSavepoint.clear();
// Load from first savepoint up to current savepoint
loadSavepoint(lastSavepoint, idsOfNewObjectWithDeltas);
if (lastSavepoint == firstSavepoint && options().isAutoReleaseLocksEnabled())
{
CDORepositoryInfo repositoryInfo = getSession().getRepositoryInfo();
if (isDurableView() && repositoryInfo.getState() == CDOCommonRepository.State.ONLINE || repositoryInfo.getType() == CDOCommonRepository.Type.MASTER)
{
// Unlock all objects
unlockObjects(null, null);
}
}
// Send notifications.
for (Entry<CDOObject, CDORevision> entry : oldRevisions.entrySet())
{
InternalCDOObject object = (InternalCDOObject)entry.getKey();
InternalCDORevision oldRevision = (InternalCDORevision)entry.getValue();
InternalCDORevision newRevision = object.cdoRevision();
if (newRevision == null)
{
newRevision = (InternalCDORevision)newRevisions.get(object);
}
if (newRevision == null && !FSMUtil.isTransient(object))
{
// This can happen for example for conflicting objects which have been set to PROXY in rollbackCompletely().
CDOID id = oldRevision.getID();
newRevision = getRevision(id, true);
object.cdoInternalSetRevision(newRevision);
object.cdoInternalSetState(CDOState.CLEAN);
}
if (newRevision != null)
{
InternalCDORevisionDelta delta = newRevision.compare(oldRevision);
if (!delta.isEmpty())
{
Set<CDOObject> detachedObjects = Collections.emptySet();
CDONotificationBuilder builder = new CDONotificationBuilder(this);
NotificationChain notification = builder.buildNotification(object, oldRevision, delta, detachedObjects);
if (notification != null)
{
notification.dispatch();
}
}
}
}
IListener[] listeners = getListeners();
if (listeners != null)
{
fireEvent(new FinishedEvent(true), listeners);
}
CDOTransactionHandler2[] handlers = getTransactionHandlers2();
for (int i = 0; i < handlers.length; i++)
{
CDOTransactionHandler2 handler = handlers[i];
try
{
handler.rolledBackTransaction(this);
}
catch (RuntimeException ex)
{
OM.LOG.error(ex);
}
}
}
catch (RuntimeException ex)
{
throw ex;
}
catch (Exception ex)
{
throw new TransactionException(ex);
}
finally
{
unlockView();
}
}
}
private Set<CDOID> rollbackCompletely(CDOUserSavepoint savepoint, Map<CDOObject, CDORevision> newRevisions)
{
Set<CDOID> idsOfNewObjectsWithDeltas = new HashSet<>();
Set<InternalCDOObject> newObjects = new HashSet<>();
// Start from the last savepoint and come back up to the active
for (InternalCDOSavepoint itrSavepoint = lastSavepoint; itrSavepoint != null; itrSavepoint = itrSavepoint.getPreviousSavepoint())
{
Set<Object> toBeDetached = new HashSet<>();
// Rollback new objects attached after the save point
Map<CDOID, CDOObject> newObjectsMap = itrSavepoint.getNewObjects();
for (CDOID id : newObjectsMap.keySet())
{
InternalCDOObject object = (InternalCDOObject)newObjectsMap.get(id);
toBeDetached.add(id);
toBeDetached.add(object);
toBeDetached.add(object.cdoInternalInstance());
removeObject(id, object);
newObjects.add(object);
}
// Rollback new objects re-attached after the save point
Map<CDOID, CDOObject> reattachedObjectsMap = itrSavepoint.getReattachedObjects();
Set<CDOID> detachedIDs = itrSavepoint.getDetachedObjects().keySet();
for (CDOObject reattachedObject : reattachedObjectsMap.values())
{
CDOID id = reattachedObject.cdoID();
if (!detachedIDs.contains(id))
{
InternalCDOObject internal = (InternalCDOObject)reattachedObject;
toBeDetached.add(id);
toBeDetached.add(internal);
toBeDetached.add(internal.cdoInternalInstance());
removeObject(id, internal);
internal.cdoInternalSetView(null);
internal.cdoInternalSetID(null);
internal.cdoInternalSetState(CDOState.TRANSIENT);
}
}
Map<CDOID, CDORevision> attachedRevisions = options().getAttachedRevisionsMap();
for (Object idOrObject : toBeDetached)
{
InternalCDORevision revision = null;
if (idOrObject instanceof InternalCDOObject)
{
InternalCDOObject object = (InternalCDOObject)idOrObject;
CDOID id = object.cdoID();
if (attachedRevisions != null)
{
revision = (InternalCDORevision)attachedRevisions.get(id);
}
if (revision == null)
{
revision = object.cdoRevision();
if (revision != null)
{
// Copy the revision, so that the revision modifications below don't apply to the oldRevisions,
// which have been remembered in handleRollback() for the computation of delta notifications.
revision = revision.copy();
}
}
if (revision != null)
{
newRevisions.put((CDOObject)idOrObject, revision);
}
Resource.Internal directResource = object.eDirectResource();
EObject container = object.eContainer();
boolean topLevel = !toBeDetached.contains(directResource) && !toBeDetached.contains(container);
if (topLevel)
{
if (revision != null)
{
// Unset direct resource and container in the revision.
// Later cdoInternalPostDetach() will migrate these values into the EObject.
// See CDOStateMachine.RollbackTransition.execute().
revision.setResourceID(null);
revision.setContainerID(null);
revision.setContainingFeatureID(0);
}
}
if (idOrObject instanceof CDOObjectImpl)
{
CDOObjectImpl impl = (CDOObjectImpl)idOrObject;
if (revision != null)
{
impl.cdoInternalSetRevision(revision);
}
else if (topLevel)
{
// Unset direct resource and eContainer in the EObject.
impl.cdoInternalSetResource(null);
}
}
else if (idOrObject instanceof CDOObjectWrapper)
{
CDOObjectWrapper wrapper = (CDOObjectWrapper)idOrObject;
if (revision != null)
{
wrapper.cdoInternalRollback(revision);
}
if (topLevel)
{
wrapper.setInstanceResource(null);
wrapper.setInstanceContainer(null, 0);
}
}
}
}
Map<CDOID, CDORevisionDelta> revisionDeltas = itrSavepoint.getRevisionDeltas2();
if (!revisionDeltas.isEmpty())
{
for (CDORevisionDelta dirtyObject : revisionDeltas.values())
{
CDOID id = dirtyObject.getID();
if (isObjectNew(id))
{
idsOfNewObjectsWithDeltas.add(id);
}
}
}
// Rollback all detached objects.
Map<CDOID, CDOObject> detachedObjectsMap = itrSavepoint.getDetachedObjects();
if (!detachedObjectsMap.isEmpty())
{
for (Entry<CDOID, CDOObject> detachedObjectEntry : detachedObjectsMap.entrySet())
{
CDOID id = detachedObjectEntry.getKey();
if (isObjectNew(id))
{
idsOfNewObjectsWithDeltas.add(id);
}
else
{
InternalCDOObject detachedObject = (InternalCDOObject)detachedObjectEntry.getValue();
InternalCDORevision cleanRev = cleanRevisions.get(detachedObject);
cleanObject(detachedObject, cleanRev);
}
}
}
// Rollback dirty objects.
for (Entry<CDOID, CDOObject> entryDirtyObject : itrSavepoint.getDirtyObjects().entrySet())
{
CDOID id = entryDirtyObject.getKey();
if (!isObjectNew(id))
{
InternalCDOObject internalDirtyObject = (InternalCDOObject)entryDirtyObject.getValue();
// Bug 283985 (Re-attachment): Skip objects that were reattached, because
// they were already reset to TRANSIENT earlier in this method
if (!reattachedObjectsMap.values().contains(internalDirtyObject))
{
CDOStateMachine.INSTANCE.rollback(internalDirtyObject, this);
}
}
}
if (savepoint == itrSavepoint)
{
break;
}
}
// Rollback new objects.
for (InternalCDOObject internalCDOObject : newObjects)
{
if (FSMUtil.isNew(internalCDOObject))
{
CDOStateMachine.INSTANCE.rollback(internalCDOObject, this);
internalCDOObject.cdoInternalSetID(null);
internalCDOObject.cdoInternalSetView(null);
}
}
return idsOfNewObjectsWithDeltas;
}
private void loadSavepoint(CDOSavepoint savepoint, Set<CDOID> idsOfNewObjectWithDeltas)
{
Map<CDOID, CDOObject> dirtyObjects = getDirtyObjects();
Map<CDOID, CDOObject> newObjMaps = getNewObjects();
Map<CDOID, CDORevision> newBaseRevision = getBaseNewObjects();
Map<CDOID, CDOObject> detachedObjects = getDetachedObjects();
// Reload the objects (NEW) with their base.
for (CDOID id : idsOfNewObjectWithDeltas)
{
if (detachedObjects.containsKey(id))
{
continue;
}
InternalCDOObject object = (InternalCDOObject)newObjMaps.get(id);
CDORevision revision = newBaseRevision.get(id);
if (revision != null)
{
object.cdoInternalSetRevision(revision.copy());
object.cdoInternalSetView(this);
object.cdoInternalSetState(CDOState.NEW);
// Load the object from revision to EObject
object.cdoInternalPostLoad();
if (super.getObject(object.cdoID(), false) == null)
{
registerObject(object);
}
}
}
// We need to register back new objects that are not removed anymore there.
for (Entry<CDOID, CDOObject> entryNewObject : newObjMaps.entrySet())
{
InternalCDOObject object = (InternalCDOObject)entryNewObject.getValue();
// Go back to the previous state
cleanObject(object, object.cdoRevision());
object.cdoInternalSetState(CDOState.NEW);
}
for (Entry<CDOID, CDOObject> entryDirtyObject : dirtyObjects.entrySet())
{
if (detachedObjects.containsKey(entryDirtyObject.getKey()))
{
continue;
}
// Rollback every persisted objects
InternalCDOObject internalDirtyObject = (InternalCDOObject)entryDirtyObject.getValue();
cleanObject(internalDirtyObject, getRevision(entryDirtyObject.getKey(), true));
}
CDOObjectMerger merger = new CDOObjectMerger();
for (InternalCDOSavepoint itrSavepoint = firstSavepoint; itrSavepoint != savepoint; itrSavepoint = itrSavepoint.getNextSavepoint())
{
for (CDORevisionDelta delta : itrSavepoint.getRevisionDeltas2().values())
{
CDOID id = delta.getID();
boolean isNew = isObjectNew(id);
if (isNew && !idsOfNewObjectWithDeltas.contains(id) || detachedObjects.containsKey(id))
{
continue;
}
Map<CDOID, CDOObject> map = isNew ? newObjMaps : dirtyObjects;
InternalCDOObject object = (InternalCDOObject)map.get(id);
// Change state of the objects
merger.merge(object, delta);
// Load the object from revision to EObject
object.cdoInternalPostLoad();
}
}
dirty = savepoint.wasDirty();
}
private void collectRevisions(Map<CDOObject, CDORevision> revisions, Map<CDOID, CDOObject> objects)
{
for (CDOObject object : objects.values())
{
CDORevision oldRevision = object.cdoRevision();
if (oldRevision != null)
{
revisions.put(object, oldRevision);
}
}
}
/**
* @since 2.0
*/
@Override
public InternalCDOSavepoint handleSetSavepoint()
{
synchronized (getViewMonitor())
{
lockView();
try
{
addToBase(lastSavepoint.getNewObjects());
lastSavepoint = createSavepoint(lastSavepoint);
return lastSavepoint;
}
finally
{
unlockView();
}
}
}
private CDOSavepointImpl createSavepoint(InternalCDOSavepoint lastSavepoint)
{
return new CDOSavepointImpl(this, lastSavepoint);
}
/**
* @since 2.0
*/
@Override
public InternalCDOSavepoint setSavepoint()
{
checkActive();
synchronized (getViewMonitor())
{
lockView();
try
{
return (InternalCDOSavepoint)getTransactionStrategy().setSavepoint(this);
}
finally
{
unlockView();
}
}
}
@Override
public boolean hasMultipleSavepoints()
{
return lastSavepoint != firstSavepoint;
}
private void addToBase(Map<CDOID, CDOObject> objects)
{
for (CDOObject object : objects.values())
{
// Load instance to revision
((InternalCDOObject)object).cdoInternalPreCommit();
lastSavepoint.getBaseNewObjects().put(object.cdoID(), object.cdoRevision().copy());
}
}
@Override
protected String getClassName()
{
return "CDOTransaction"; //$NON-NLS-1$
}
@Override
public void registerAttached(InternalCDOObject object, boolean isNew)
{
if (TRACER.isEnabled())
{
TRACER.format("Registering new object {0}", object); //$NON-NLS-1$
}
synchronized (getViewMonitor())
{
lockView();
try
{
if (isNew)
{
registerNewPackage(object.eClass().getEPackage());
}
CDOTransactionHandler1[] handlers = getTransactionHandlers1();
for (int i = 0; i < handlers.length; i++)
{
CDOTransactionHandler1 handler = handlers[i];
handler.attachingObject(this, object);
}
if (isNew)
{
registerNew(lastSavepoint.getNewObjects(), object);
}
}
finally
{
unlockView();
}
}
}
private void registerNewPackage(EPackage ePackage)
{
CDOPackageRegistry packageRegistry = getSession().getPackageRegistry();
if (!packageRegistry.containsKey(ePackage.getNsURI()))
{
packageRegistry.putEPackage(ePackage);
}
}
private CDOOriginSizeProvider getOriginSizeProvider(InternalCDOObject object, CDOFeatureDelta featureDelta, InternalCDORevision cleanRevision)
{
EStructuralFeature feature = featureDelta.getFeature();
if (feature.isMany())
{
if (cleanRevision == null)
{
cleanRevision = cleanRevisions.get(object);
if (cleanRevision == null)
{
cleanRevision = object.cdoRevision();
}
}
CDOList list = cleanRevision.getListOrNull(feature);
final int originSize = list == null ? 0 : list.size();
return new CDOOriginSizeProvider()
{
@Override
public int getOriginSize()
{
return originSize;
}
};
}
return null;
}
/**
* Receives notification for new and dirty objects
*/
@Override
public void registerFeatureDelta(InternalCDOObject object, CDOFeatureDelta featureDelta)
{
synchronized (getViewMonitor())
{
lockView();
try
{
registerFeatureDelta(object, featureDelta, null);
}
finally
{
unlockView();
}
}
}
@Override
public void registerFeatureDelta(InternalCDOObject object, CDOFeatureDelta featureDelta, InternalCDORevision cleanRevision)
{
synchronized (getViewMonitor())
{
lockView();
try
{
CDOID id = object.cdoID();
boolean needToSaveFeatureDelta = true;
if (object.cdoState() == CDOState.NEW)
{
// Register Delta for new objects only if objectA doesn't belong to this savepoint
if (lastSavepoint.getPreviousSavepoint() == null || featureDelta == null)
{
needToSaveFeatureDelta = false;
}
else
{
Map<CDOID, CDOObject> map = lastSavepoint.getNewObjects();
needToSaveFeatureDelta = !map.containsKey(id);
}
}
if (needToSaveFeatureDelta)
{
Map<CDOID, CDORevisionDelta> revisionDeltas = lastSavepoint.getRevisionDeltas2();
InternalCDORevisionDelta revisionDelta = (InternalCDORevisionDelta)revisionDeltas.get(id);
if (revisionDelta == null)
{
InternalCDORevision revision = object.cdoRevision();
revisionDelta = (InternalCDORevisionDelta)CDORevisionUtil.createDelta(revision);
revisionDeltas.put(id, revisionDelta);
}
CDOOriginSizeProvider originSizeProvider = getOriginSizeProvider(object, featureDelta, cleanRevision);
revisionDelta.addFeatureDelta(featureDelta, originSizeProvider);
}
CDOTransactionHandler1[] handlers = getTransactionHandlers1();
for (int i = 0; i < handlers.length; i++)
{
CDOTransactionHandler1 handler = handlers[i];
handler.modifyingObject(this, object, featureDelta);
}
}
finally
{
unlockView();
}
}
}
@Override
public void registerRevisionDelta(CDORevisionDelta revisionDelta)
{
synchronized (getViewMonitor())
{
lockView();
try
{
Map<CDOID, CDORevisionDelta> revisionDeltas = lastSavepoint.getRevisionDeltas2();
CDOID id = revisionDelta.getID();
if (!revisionDeltas.containsKey(id))
{
revisionDeltas.put(id, revisionDelta);
}
}
finally
{
unlockView();
}
}
}
@Override
public void registerDirty(InternalCDOObject object, CDOFeatureDelta featureDelta)
{
registerDirty(object, featureDelta, null);
}
@Override
public void registerDirty(InternalCDOObject object, CDOFeatureDelta featureDelta, InternalCDORevision cleanRevision)
{
if (TRACER.isEnabled())
{
TRACER.format("Registering dirty object {0}", object); //$NON-NLS-1$
}
if (featureDelta != null)
{
registerFeatureDelta(object, featureDelta, cleanRevision);
}
registerNew(lastSavepoint.getDirtyObjects(), object);
}
/**
* TODO Simon: Should this method go to CDOSavePointImpl?
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private void registerNew(Map map, InternalCDOObject object)
{
Object old = map.put(object.cdoID(), object);
if (old != null)
{
throw new IllegalStateException(MessageFormat.format(Messages.getString("CDOTransactionImpl.10"), object)); //$NON-NLS-1$
}
setDirty(true);
}
public List<CDOPackageUnit> analyzeNewPackages()
{
synchronized (getViewMonitor())
{
lockView();
try
{
CDOPackageRegistry packageRegistry = getSession().getPackageRegistry();
Set<EPackage> usedPackages = new HashSet<>();
Set<EPackage> usedNewPackages = new HashSet<>();
for (CDOObject object : getNewObjects().values())
{
EPackage ePackage = object.eClass().getEPackage();
if (usedPackages.add(ePackage))
{
EPackage topLevelPackage = EMFUtil.getTopLevelPackage(ePackage);
if (ePackage == topLevelPackage || usedPackages.add(topLevelPackage))
{
// if (!CDOModelUtil.isSystemPackage(topLevelPackage))
{
CDOPackageUnit packageUnit = packageRegistry.getPackageUnit(topLevelPackage);
if (packageUnit.getState() == CDOPackageUnit.State.NEW)
{
usedNewPackages.add(topLevelPackage);
}
}
}
}
}
if (usedNewPackages.size() > 0)
{
Set<CDOPackageUnit> result = new HashSet<>();
for (EPackage usedNewPackage : analyzeNewPackages(usedNewPackages, packageRegistry))
{
CDOPackageUnit packageUnit = packageRegistry.getPackageUnit(usedNewPackage);
result.add(packageUnit);
}
return new ArrayList<>(result);
}
return Collections.emptyList();
}
finally
{
unlockView();
}
}
}
private static List<EPackage> analyzeNewPackages(Collection<EPackage> usedTopLevelPackages, CDOPackageRegistry packageRegistry)
{
// Determine which of the corresponding EPackages are new
List<EPackage> newPackages = new ArrayList<>();
IPackageClosure closure = new CompletePackageClosure();
usedTopLevelPackages = closure.calculate(usedTopLevelPackages);
for (EPackage usedPackage : usedTopLevelPackages)
{
// if (!CDOModelUtil.isSystemPackage(usedPackage))
{
CDOPackageUnit packageUnit = packageRegistry.getPackageUnit(usedPackage);
if (packageUnit == null)
{
throw new CDOException(MessageFormat.format(Messages.getString("CDOTransactionImpl.11"), usedPackage)); //$NON-NLS-1$
}
if (packageUnit.getState() == CDOPackageUnit.State.NEW)
{
newPackages.add(usedPackage);
}
}
}
return newPackages;
}
private void cleanUp(CDOCommitContext commitContext)
{
if (commitContext == null || !commitContext.isPartialCommit())
{
if (commitContext != null)
{
for (CDOObject object : commitContext.getDetachedObjects().values())
{
cleanUpLockState(object);
}
}
lastSavepoint = firstSavepoint;
firstSavepoint.clear();
firstSavepoint.setNextSavepoint(null);
cleanRevisions.clear();
Map<CDOID, CDORevision> attachedRevisions = options().getAttachedRevisionsMap();
if (attachedRevisions != null)
{
attachedRevisions.clear();
}
dirty = false;
conflict = 0;
idGenerator.reset();
}
else
{
collapseSavepoints(commitContext);
for (CDOObject object : commitContext.getDetachedObjects().values())
{
cleanRevisions.remove(object);
cleanUpLockState(object);
}
for (CDOObject object : commitContext.getDirtyObjects().values())
{
cleanRevisions.remove(object);
}
}
// Reset partial-commit filter
committables = null;
}
private void cleanUpLockState(CDOObject object)
{
InternalCDOLockState lockState = (InternalCDOLockState)removeLockState(object);
if (lockState != null)
{
lockState.dispose();
}
}
private void collapseSavepoints(CDOCommitContext commitContext)
{
InternalCDOSavepoint newSavepoint = createSavepoint(null);
copyUncommitted(lastSavepoint.getAllNewObjects(), commitContext.getNewObjects(), newSavepoint.getNewObjects());
copyUncommitted(lastSavepoint.getAllDirtyObjects(), commitContext.getDirtyObjects(), newSavepoint.getDirtyObjects());
copyUncommitted(lastSavepoint.getAllRevisionDeltas(), commitContext.getRevisionDeltas(), newSavepoint.getRevisionDeltas2());
copyUncommitted(lastSavepoint.getAllDetachedObjects(), commitContext.getDetachedObjects(), newSavepoint.getDetachedObjects());
lastSavepoint = newSavepoint;
firstSavepoint = lastSavepoint;
}
private <T> void copyUncommitted(Map<CDOID, T> oldSavepointMap, Map<CDOID, T> commitContextMap, Map<CDOID, T> newSavepointMap)
{
for (Entry<CDOID, T> entry : oldSavepointMap.entrySet())
{
if (!commitContextMap.containsKey(entry.getKey()))
{
newSavepointMap.put(entry.getKey(), entry.getValue());
}
}
}
@Override
public CDOSavepoint[] exportChanges(OutputStream stream) throws IOException
{
synchronized (getViewMonitor())
{
lockView();
try
{
CDODataOutput out = new CDODataOutputImpl(new ExtendedDataOutputStream(stream))
{
@Override
public CDOIDProvider getIDProvider()
{
return CDOTransactionImpl.this;
}
@Override
public CDOPackageRegistry getPackageRegistry()
{
return getSession().getPackageRegistry();
}
@Override
public CDORevisionUnchunker getRevisionUnchunker()
{
return getSession();
}
@Override
protected boolean isXCompression()
{
return X_COMPRESSION;
}
};
List<CDOSavepoint> savepoints = new ArrayList<>();
int totalNewObjects = 0;
InternalCDOSavepoint savepoint = firstSavepoint;
while (savepoint != null)
{
Collection<CDOObject> newObjects = savepoint.getNewObjects().values();
totalNewObjects += newObjects.size();
savepoint = savepoint.getNextSavepoint();
}
out.writeXInt(totalNewObjects);
savepoint = firstSavepoint;
while (savepoint != null)
{
Collection<CDOObject> newObjects = savepoint.getNewObjects().values();
Collection<CDORevisionDelta> revisionDeltas = savepoint.getRevisionDeltas2().values();
if (newObjects.isEmpty() && revisionDeltas.isEmpty())
{
savepoint = savepoint.getNextSavepoint();
continue;
}
savepoints.add(savepoint);
out.writeBoolean(true);
out.writeXInt(newObjects.size());
for (CDOObject newObject : newObjects)
{
out.writeCDORevision(newObject.cdoRevision(), CDORevision.UNCHUNKED);
}
out.writeXInt(revisionDeltas.size());
for (CDORevisionDelta revisionDelta : revisionDeltas)
{
out.writeCDORevisionDelta(revisionDelta);
}
savepoint = savepoint.getNextSavepoint();
}
out.writeBoolean(false);
return savepoints.toArray(new CDOSavepoint[savepoints.size()]);
}
finally
{
unlockView();
}
}
}
@Override
public CDOSavepoint[] importChanges(InputStream stream, boolean reconstructSavepoints) throws IOException
{
synchronized (getViewMonitor())
{
lockView();
try
{
List<CDOSavepoint> savepoints = new ArrayList<>();
if (stream.available() > 0)
{
CDODataInput in = new CDODataInputImpl(new ExtendedDataInputStream(stream))
{
@Override
public CDOPackageRegistry getPackageRegistry()
{
return getSession().getPackageRegistry();
}
@Override
protected CDOBranchManager getBranchManager()
{
return getSession().getBranchManager();
}
@Override
protected CDOCommitInfoManager getCommitInfoManager()
{
return getSession().getCommitInfoManager();
}
@Override
protected CDORevisionFactory getRevisionFactory()
{
return getSession().getRevisionManager().getFactory();
}
@Override
protected CDOLobStore getLobStore()
{
return getSession().getLobStore();
}
@Override
protected CDOListFactory getListFactory()
{
return CDOListWithElementProxiesImpl.FACTORY;
}
@Override
protected boolean isXCompression()
{
return X_COMPRESSION;
}
};
// Increase the internal tempID counter to prevent ID collisions during mapping
int totalNewObjects = in.readXInt();
for (int i = 0; i < totalNewObjects; i++)
{
createIDForNewObject(null);
}
Map<CDOID, CDOID> idMappings = CDOIDUtil.createMap();
while (in.readBoolean())
{
if (reconstructSavepoints)
{
InternalCDOSavepoint savepoint = setSavepoint();
savepoints.add(savepoint);
}
// Import revisions and deltas
List<InternalCDORevision> revisions = new ArrayList<>();
importNewRevisions(in, revisions, idMappings);
List<InternalCDORevisionDelta> revisionDeltas = importRevisionDeltas(in);
if (!idMappings.isEmpty())
{
// Re-map temp IDs
CDOIDMapper idMapper = new CDOIDMapper(idMappings);
for (InternalCDORevision revision : revisions)
{
revision.adjustReferences(idMapper);
}
for (InternalCDORevisionDelta delta : revisionDeltas)
{
delta.adjustReferences(idMapper);
}
}
if (!revisions.isEmpty())
{
// Create new objects
List<InternalCDOObject> newObjects = new ArrayList<>();
for (InternalCDORevision revision : revisions)
{
InternalCDOObject object = newInstance(revision);
registerObject(object);
registerAttached(object, true);
newObjects.add(object);
}
// Post-load new objects (important for legacy objects!)
for (InternalCDOObject object : newObjects)
{
object.cdoInternalPostLoad();
}
}
// Apply deltas
CDOObjectMerger merger = new CDOObjectMerger();
for (InternalCDORevisionDelta delta : revisionDeltas)
{
InternalCDOObject object = getObject(delta.getID());
int oldVersion = object.cdoRevision().getVersion();
merger.merge(object, delta);
registerRevisionDelta(delta);
registerDirty(object, null);
if (delta.getVersion() < oldVersion)
{
setConflict(object);
}
}
}
}
return savepoints.toArray(new CDOSavepoint[savepoints.size()]);
}
finally
{
unlockView();
}
}
}
private void importNewRevisions(CDODataInput in, List<InternalCDORevision> revisions, Map<CDOID, CDOID> idMappings) throws IOException
{
int size = in.readXInt();
for (int i = 0; i < size; i++)
{
InternalCDORevision revision = (InternalCDORevision)in.readCDORevision(false);
CDOID oldID = revision.getID();
if (oldID.isTemporary())
{
CDOID newID = createIDForNewObject(null);
idMappings.put(oldID, newID);
revision.setID(newID);
}
revisions.add(revision);
}
}
private List<InternalCDORevisionDelta> importRevisionDeltas(CDODataInput in) throws IOException
{
int size = in.readXInt();
List<InternalCDORevisionDelta> deltas = new ArrayList<>(size);
for (int i = 0; i < size; i++)
{
InternalCDORevisionDelta delta = (InternalCDORevisionDelta)in.readCDORevisionDelta();
deltas.add(delta);
}
return deltas;
}
private InternalCDOObject newInstance(InternalCDORevision revision)
{
InternalCDOObject object = newInstance(revision.getEClass());
object.cdoInternalSetRevision(revision);
object.cdoInternalSetView(this);
object.cdoInternalSetState(CDOState.NEW);
return object;
}
@Override
public Map<CDOID, CDOObject> getDirtyObjects()
{
checkActive();
synchronized (getViewMonitor())
{
lockView();
try
{
return lastSavepoint.getAllDirtyObjects();
}
finally
{
unlockView();
}
}
}
@Override
public Map<CDOID, CDOObject> getNewObjects()
{
checkActive();
synchronized (getViewMonitor())
{
lockView();
try
{
return lastSavepoint.getAllNewObjects();
}
finally
{
unlockView();
}
}
}
/**
* @since 2.0
*/
public Map<CDOID, CDORevision> getBaseNewObjects()
{
checkActive();
synchronized (getViewMonitor())
{
lockView();
try
{
return lastSavepoint.getAllBaseNewObjects();
}
finally
{
unlockView();
}
}
}
@Override
public Map<CDOID, CDORevisionDelta> getRevisionDeltas()
{
checkActive();
synchronized (getViewMonitor())
{
lockView();
try
{
return lastSavepoint.getAllRevisionDeltas();
}
finally
{
unlockView();
}
}
}
/**
* @since 2.0
*/
@Override
public Map<CDOID, CDOObject> getDetachedObjects()
{
checkActive();
synchronized (getViewMonitor())
{
lockView();
try
{
return lastSavepoint.getAllDetachedObjects();
}
finally
{
unlockView();
}
}
}
@Override
protected CloseableIterator<CDOResourceNode> queryResourcesUnsynced(CDOResourceFolder folder, final String name, final boolean exactMatch)
{
if (!isDirty())
{
return super.queryResourcesUnsynced(folder, name, exactMatch);
}
CDOState state;
CDOList list = null;
if (folder == null)
{
CDOResource rootResource = getRootResource();
state = rootResource.cdoState();
if (state.isLocal())
{
InternalCDORevision revision = (InternalCDORevision)rootResource.cdoRevision();
if (revision != null)
{
list = revision.getListOrNull(EresourcePackage.Literals.CDO_RESOURCE__CONTENTS);
}
}
}
else
{
state = folder.cdoState();
if (state == CDOState.TRANSIENT)
{
throw new CDOException("Folder " + folder + " is transient");
}
if (state.isLocal())
{
InternalCDORevision revision = (InternalCDORevision)folder.cdoRevision();
if (revision != null)
{
list = revision.getListOrNull(EresourcePackage.Literals.CDO_RESOURCE_FOLDER__NODES);
}
}
}
if (state.isRemote())
{
Set<CDOID> validIDs = null;
List<CDOResourceNode> addedNodes = null;
if (list != null)
{
for (Object element : list)
{
if (element instanceof CDOResourceNode)
{
CDOResourceNode node = (CDOResourceNode)element;
if (addedNodes == null)
{
addedNodes = new ArrayList<>();
}
addedNodes.add(node);
}
else
{
CDOID id = (CDOID)element;
if (isObjectNew(id))
{
CDOResourceNode node = (CDOResourceNode)getObject(id);
addedNodes.add(node);
}
else
{
if (validIDs == null)
{
validIDs = new HashSet<>();
}
validIDs.add(id);
}
}
}
}
final Set<CDOID> finalValidIDs = validIDs;
final List<CDOResourceNode> finalAddedNodes = addedNodes;
final CloseableIterator<CDOResourceNode> delegate = super.queryResourcesUnsynced(folder, name, exactMatch);
return new AbstractCloseableIterator<CDOResourceNode>()
{
private Iterator<CDOResourceNode> addedNodesIterator = finalAddedNodes == null ? null : finalAddedNodes.iterator();
@Override
protected Object computeNextElement()
{
if (addedNodesIterator != null)
{
if (isClosed())
{
return END_OF_DATA;
}
while (addedNodesIterator.hasNext())
{
CDOResourceNode node = addedNodesIterator.next();
// Locally added node is not matched by the server; match it now.
if (isResourceMatch(node.getName(), name, exactMatch))
{
return node;
}
}
addedNodesIterator = null;
}
if (finalValidIDs != null)
{
while (delegate.hasNext())
{
CDOResourceNode node = delegate.next();
if (finalValidIDs.contains(node.cdoID()))
{
if (node.cdoState().isLocal())
{
// Locally changed node is not matched by the server; match it now.
if (isResourceMatch(node.getName(), name, exactMatch))
{
return node;
}
}
else
{
// Locally unchanged node is matched by the server already; just return it.
return node;
}
}
}
}
return END_OF_DATA;
}
@Override
public void close()
{
delegate.close();
}
@Override
public boolean isClosed()
{
return delegate.isClosed();
}
};
}
final CDOList finalList = list;
return new AbstractCloseableIterator<CDOResourceNode>()
{
private Iterator<Object> listIterator = finalList == null ? null : finalList.iterator();
@Override
protected Object computeNextElement()
{
if (listIterator != null)
{
while (listIterator.hasNext())
{
Object element = listIterator.next();
CDOResourceNode node = getResourceNode(element);
// Locally added node is not matched by the server; match it now.
if (isResourceMatch(node.getName(), name, exactMatch))
{
return node;
}
}
listIterator = null;
}
return END_OF_DATA;
}
private CDOResourceNode getResourceNode(Object element)
{
if (element instanceof CDOResourceNode)
{
return (CDOResourceNode)element;
}
return (CDOResourceNode)getObject((CDOID)element);
}
@Override
public void close()
{
listIterator = null;
}
@Override
public boolean isClosed()
{
return listIterator == null;
}
};
}
@Override
protected <T extends EObject> CloseableIterator<T> queryInstancesUnsynced(final EClass type, final boolean exact)
{
if (!isDirty())
{
return super.queryInstancesUnsynced(type, exact);
}
final Map<CDOID, CDOObject> newObjects = lastSavepoint.getAllNewObjects();
final CloseableIterator<T> delegate = super.queryInstancesUnsynced(type, exact);
return new AbstractCloseableIterator<T>()
{
private Iterator<CDOObject> newObjectsIterator = newObjects.isEmpty() ? null : newObjects.values().iterator();
@Override
protected Object computeNextElement()
{
if (newObjectsIterator != null)
{
if (isClosed())
{
return END_OF_DATA;
}
while (newObjectsIterator.hasNext())
{
EObject newObject = CDOUtil.getEObject(newObjectsIterator.next());
// Locally added node is not matched by the server; match it now.
if (exact ? type == newObject.eClass() : type.isInstance(newObject))
{
return newObject;
}
}
newObjectsIterator = null;
}
while (delegate.hasNext())
{
T object = delegate.next();
CDOObject cdoObject = CDOUtil.getCDOObject(object);
System.out.println(cdoObject);
if (!FSMUtil.isTransient(cdoObject))
{
return object;
}
}
return END_OF_DATA;
}
@Override
public void close()
{
delegate.close();
}
@Override
public boolean isClosed()
{
return delegate.isClosed();
}
};
}
@Override
protected CloseableIterator<CDOObjectReference> queryXRefsUnsynced(Set<CDOObject> targetObjects, EReference... sourceReferences)
{
if (!isDirty())
{
return super.queryXRefsUnsynced(targetObjects, sourceReferences);
}
final Set<CDOID> targetIDs = new HashSet<>();
final Set<EReference> relevantReferences = toSet(sourceReferences);
final CDOQuery query = createXRefsQuery(false, targetIDs, targetObjects, sourceReferences);
if (query.getQueryString() != null)
{
final Set<CDOID> localIDs = new HashSet<>();
final List<CDOObjectReference> localXRefs = queryXRefsLocal(localIDs, targetIDs, relevantReferences);
final CloseableIterator<CDOObjectReference> delegate = query.getResultAsync(CDOObjectReference.class);
return new AbstractCloseableIterator<CDOObjectReference>()
{
private Iterator<CDOObjectReference> localXRefsIterator = localXRefs == null ? null : localXRefs.iterator();
@Override
protected Object computeNextElement()
{
if (localXRefsIterator != null)
{
if (isClosed())
{
return END_OF_DATA;
}
if (localXRefsIterator.hasNext())
{
return localXRefsIterator.next();
}
localXRefsIterator = null;
}
while (delegate.hasNext())
{
CDOObjectReference ref = delegate.next();
if (!localIDs.contains(ref.getSourceID()))
{
return ref;
}
}
return END_OF_DATA;
}
@Override
public void close()
{
delegate.close();
}
@Override
public boolean isClosed()
{
return delegate.isClosed();
}
};
}
List<CDOObjectReference> localXRefs = queryXRefsLocal(null, targetIDs, relevantReferences);
if (localXRefs != null)
{
return new DelegatingCloseableIterator<>(localXRefs.iterator());
}
return AbstractCloseableIterator.emptyCloseable();
}
private List<CDOObjectReference> queryXRefsLocal(Set<CDOID> localIDs, Set<CDOID> targetIDs, Set<EReference> relevantReferences)
{
List<CDOObjectReference> refs = null;
Map<CDOID, CDOObject> newObjects = lastSavepoint.getAllNewObjects();
Map<CDOID, CDOObject> dirtyObjects = lastSavepoint.getAllDirtyObjects();
if (localIDs != null)
{
localIDs.addAll(newObjects.keySet());
localIDs.addAll(dirtyObjects.keySet());
localIDs.addAll(lastSavepoint.getAllDetachedObjects().keySet());
}
Iterator<CDOObject> it = new ComposedIterator<>( //
newObjects.values().iterator(), //
dirtyObjects.values().iterator());
while (it.hasNext())
{
CDOObject object = it.next();
InternalCDORevision revision = (InternalCDORevision)object.cdoRevision();
for (EReference reference : revision.getClassInfo().getAllPersistentReferences())
{
if (!reference.isContainer() && !reference.isContainment() && (relevantReferences == null || relevantReferences.contains(reference)))
{
if (reference.isMany())
{
CDOList list = revision.getListOrNull(reference);
if (list != null)
{
int index = 0;
for (Object value : list)
{
refs = addXRefLocal(refs, targetIDs, object, reference, index, value);
++index;
}
}
}
else
{
Object value = revision.getValue(reference);
refs = addXRefLocal(refs, targetIDs, object, reference, 0, value);
}
}
}
}
return refs;
}
private List<CDOObjectReference> addXRefLocal(List<CDOObjectReference> refs, Set<CDOID> targetIDs, CDOObject object, EReference reference, int index,
Object value)
{
if (value != null)
{
CDOID targetID = value instanceof CDOID ? (CDOID)value : getXRefID(CDOUtil.getCDOObject((EObject)value));
if (!CDOIDUtil.isNull(targetID) && targetIDs.contains(targetID))
{
CDOIDReference delegateRef = new CDOIDReference(targetID, object.cdoID(), reference, index);
CDOObjectReference ref = new CDOObjectReferenceImpl(this, delegateRef);
if (refs == null)
{
refs = new ArrayList<>();
}
refs.add(ref);
}
}
return refs;
}
private CDOID getXRefID(CDOObject object)
{
CDOID id = object.cdoID();
if (id == null)
{
CDORevisionKey key = cleanRevisions.get(object);
if (key != null)
{
id = key.getID();
}
}
return id;
}
@Override
protected CDOID getXRefTargetID(CDOObject target)
{
CDORevisionKey key = cleanRevisions.get(target);
if (key != null)
{
return key.getID();
}
return super.getXRefTargetID(target);
}
@Override
protected CDOID getID(InternalCDOObject object, boolean onlyPersistedID)
{
synchronized (getViewMonitor())
{
lockView();
try
{
CDOID id = super.getID(object, onlyPersistedID);
if (id != null)
{
return id;
}
// Don't perform the trickery that follows later in this method, if we are being called
// indirectly through provideCDOID. This occurs when deltas or revisions are
// being written out to a stream; in which case null must be returned (for transients) so that
// the caller will detect a dangling reference
if (providingCDOID.get() == Boolean.TRUE)
{
return null;
}
// The super implementation returns null for a transient (unattached) object;
// but in a transaction, a transient object may have been attached previously.
// So we consult the cleanRevisions if that's the case.
CDORevisionKey revisionKey = cleanRevisions.get(object);
if (revisionKey != null)
{
CDOID revisionID = revisionKey.getID();
if (isObjectDetached(revisionID))
{
return revisionID;
}
}
return null;
}
finally
{
unlockView();
}
}
}
@Override
public CDOID provideCDOID(Object idOrObject)
{
synchronized (getViewMonitor())
{
lockView();
try
{
try
{
providingCDOID.set(Boolean.TRUE);
return super.provideCDOID(idOrObject);
}
finally
{
providingCDOID.remove();
}
}
finally
{
unlockView();
}
}
}
@Override
public CDOQueryImpl createQuery(String language, String queryString, Object context)
{
return createQuery(language, queryString, context, false);
}
@Override
public CDOQueryImpl createQuery(String language, String queryString, boolean considerDirtyState)
{
return createQuery(language, queryString, null, considerDirtyState);
}
@Override
public CDOQueryImpl createQuery(String language, String queryString, Object context, boolean considerDirtyState)
{
synchronized (getViewMonitor())
{
lockView();
try
{
CDOQueryImpl query = super.createQuery(language, queryString, context);
if (considerDirtyState && isDirty())
{
query.setChangeSetData(getChangeSetData());
}
return query;
}
finally
{
unlockView();
}
}
}
@Override
protected void doActivate() throws Exception
{
super.doActivate();
InternalCDOSession session = getSession();
if (session.getRepositoryInfo().getIDGenerationLocation() == CDOCommonRepository.IDGenerationLocation.STORE)
{
idGenerator = new TempIDGenerator();
}
else
{
idGenerator = session.getIDGenerator();
if (idGenerator == null)
{
idGenerator = CDOIDGenerator.UUID;
}
}
}
/**
* @since 2.0
*/
@Override
protected void doDeactivate() throws Exception
{
providingCDOID.remove();
options().disposeConflictResolvers();
lastSavepoint = null;
firstSavepoint = null;
transactionStrategy = null;
idGenerator = null;
super.doDeactivate();
}
/**
* Bug 298561: This override removes references to remotely detached objects that are present in any DIRTY or NEW
* objects.
*
* @since 3.0
*/
@Override
protected Map<CDOObject, Pair<CDORevision, CDORevisionDelta>> invalidate( //
List<CDORevisionKey> allChangedObjects, //
List<CDOIDAndVersion> allDetachedObjects, //
List<CDORevisionDelta> deltas, //
Map<CDOObject, CDORevisionDelta> revisionDeltas, //
Set<CDOObject> detachedObjects, //
Map<CDOID, InternalCDORevision> oldRevisions)
{
if (!allDetachedObjects.isEmpty())
{
// Remove stale references from locally changed or new objects to remotely detached objects
Set<CDOObject> remotelyDetachedObjects = getObjects(allDetachedObjects);
removeCrossReferences(remotelyDetachedObjects, getDirtyObjects().values());
removeCrossReferences(remotelyDetachedObjects, getNewObjects().values());
invalidateNewChildrenOfRemotelyDetached(remotelyDetachedObjects);
}
// Bug 290032 - Sticky views
InternalCDOSession session = getSession();
if (session.isSticky())
{
session.clearCommittedSinceLastRefresh();
}
Map<CDOObject, Pair<CDORevision, CDORevisionDelta>> conflicts = super.invalidate( //
allChangedObjects, //
allDetachedObjects, //
deltas, //
revisionDeltas, //
detachedObjects, //
oldRevisions);
if (!allChangedObjects.isEmpty())
{
// Remove stale references from remotely changed objects to locally detached objects
Set<CDOObject> remotelyChangedObjects = getObjects(allChangedObjects);
removeCrossReferences(getDetachedObjects().values(), remotelyChangedObjects);
}
return conflicts;
}
private void invalidateNewChildrenOfRemotelyDetached(Set<CDOObject> remotelyDetachedObjects)
{
// Test EObject.eInternalContainer()/eDirectResource() implicit references to have them return null in legacy for
// objects in NEW state whose a direct/indirect parent has been remotelly detached
for (CDOObject referencer : getNewObjects().values())
{
if (CDOUtil.isLegacyObject(referencer))
{
InternalEObject referencerInternalEObject = (InternalEObject)referencer;
InternalEObject eContainer = referencerInternalEObject.eInternalContainer();
if (eContainer != null)
{
CDOObject containerCDOObject = CDOUtil.getCDOObject(eContainer);
if (remotelyDetachedObjects.contains(containerCDOObject))
{
CDOStateMachine.INSTANCE.invalidate((InternalCDOObject)referencer, referencer.cdoRevision());
}
}
Resource.Internal eDirectResource = referencerInternalEObject.eDirectResource();
if (eDirectResource instanceof CDOResource)
{
CDOObject cdoResource = (CDOObject)eDirectResource;
if (remotelyDetachedObjects.contains(cdoResource))
{
CDOStateMachine.INSTANCE.invalidate((InternalCDOObject)referencer, referencer.cdoRevision());
}
}
}
}
}
@Override
protected Map<String, CDOResourceNode> collectNewResourceNodes()
{
Map<String, CDOResourceNode> result = new HashMap<>();
for (CDOObject object : getNewObjects().values())
{
if (object instanceof CDOResourceNode)
{
CDOResourceNode node = (CDOResourceNode)object;
String path = node.getPath();
result.put(path, node);
}
}
return result;
}
private Set<CDOObject> getObjects(Collection<? extends CDOIdentifiable> identifiables)
{
Set<CDOObject> result = new HashSet<>();
for (CDOIdentifiable identifiable : identifiables)
{
CDOID id = identifiable.getID();
InternalCDOObject object = getObject(id, false);
if (object != null)
{
result.add(object);
}
}
return result;
}
private void removeCrossReferences(Collection<CDOObject> possibleTargets, Collection<CDOObject> referencers)
{
CDOStaleReferenceCleaner staleReferenceCleaner = options().getStaleReferenceCleaner();
Collection<Pair<EStructuralFeature.Setting, EObject>> staleReferencesToClean = new LinkedList<>();
for (CDOObject referencer : referencers)
{
InternalCDOObject internalReferencer = (InternalCDOObject)referencer;
InternalCDOClassInfo referencerClassInfo = internalReferencer.cdoClassInfo();
EContentsEList.FeatureIterator<EObject> it = getChangeableCrossReferences(referencer);
while (it.hasNext())
{
EObject referencedObject = it.next();
CDOObject referencedCDOObject = CDOUtil.getCDOObject(referencedObject);
if (possibleTargets.contains(referencedCDOObject))
{
EReference reference = (EReference)it.feature();
// In the case of DIRTY, we must investigate further: Is the referencer dirty
// because a reference to the referencedObject was added? Only in this case
// should we remove it. If this is not the case (i.e. it is dirty in a different
// way), we skip it. (If the reference is not persistent, then this exception
// doesn't apply: it must be removed for sure.)
if (referencer.cdoState() == CDOState.DIRTY && referencerClassInfo.isPersistent(reference))
{
InternalCDORevision cleanRevision = cleanRevisions.get(referencer);
if (reference.isMany())
{
CDOList list = cleanRevision.getListOrNull(reference);
if (list != null)
{
for (Object value : list)
{
if (value == referencedCDOObject.cdoID() || value == referencedObject)
{
continue;
}
}
}
}
else
{
Object value = cleanRevision.getValue(reference);
if (value == referencedCDOObject.cdoID() || value == referencedObject)
{
continue;
}
}
}
EStructuralFeature.Setting setting = internalReferencer.eSetting(reference);
staleReferencesToClean.add(Pair.create(setting, referencedObject));
}
}
}
if (!staleReferencesToClean.isEmpty())
{
staleReferenceCleaner.cleanStaleReferences(staleReferencesToClean);
}
}
private EContentsEList.FeatureIterator<EObject> getChangeableCrossReferences(EObject object)
{
EClassImpl.FeatureSubsetSupplier features = (EClassImpl.FeatureSubsetSupplier)object.eClass().getEAllStructuralFeatures();
EStructuralFeature[] crossReferences = features.crossReferences();
if (crossReferences != null)
{
List<EStructuralFeature> changeableReferences = new ArrayList<>();
for (int i = 0; i < crossReferences.length; i++)
{
EStructuralFeature reference = crossReferences[i];
// Filter out derived references
if (reference.isDerived())
{
continue;
}
// Filter out unchangeable references
if (!reference.isChangeable())
{
continue;
}
changeableReferences.add(reference);
}
if (!changeableReferences.isEmpty())
{
EStructuralFeature[] collectedStructuralFeatures = changeableReferences.toArray(new EStructuralFeature[changeableReferences.size()]);
return new EContentsEList.ResolvingFeatureIteratorImpl<>(object, collectedStructuralFeatures);
}
}
return (EContentsEList.FeatureIterator<EObject>)ECrossReferenceEList.<EObject> emptyContentsEList().iterator();
}
@Override
public long getLastCommitTime()
{
synchronized (getViewMonitor())
{
lockView();
try
{
return lastCommitTime;
}
finally
{
unlockView();
}
}
}
@Override
public String getCommitComment()
{
synchronized (getViewMonitor())
{
lockView();
try
{
return commitComment;
}
finally
{
unlockView();
}
}
}
@Override
public void setCommitComment(String comment)
{
synchronized (getViewMonitor())
{
lockView();
try
{
commitComment = comment;
}
finally
{
unlockView();
}
}
}
@Override
public CDOBranchPoint getCommitMergeSource()
{
synchronized (getViewMonitor())
{
lockView();
try
{
return commitMergeSource;
}
finally
{
unlockView();
}
}
}
@Override
public void setCommitMergeSource(CDOBranchPoint mergeSource)
{
synchronized (getViewMonitor())
{
lockView();
try
{
commitMergeSource = mergeSource;
}
finally
{
unlockView();
}
}
}
@Override
public void setCommittables(Set<? extends EObject> committables)
{
synchronized (getViewMonitor())
{
lockView();
try
{
this.committables = committables;
}
finally
{
unlockView();
}
}
}
@Override
public Set<? extends EObject> getCommittables()
{
synchronized (getViewMonitor())
{
lockView();
try
{
return committables;
}
finally
{
unlockView();
}
}
}
@Override
public Map<InternalCDOObject, InternalCDORevision> getCleanRevisions()
{
synchronized (getViewMonitor())
{
lockView();
try
{
return cleanRevisions;
}
finally
{
unlockView();
}
}
}
@Override
protected InternalCDORevision getViewedRevision(InternalCDOObject object)
{
InternalCDORevision rev = super.getViewedRevision(object);
// Bug 336590: If we have a clean revision for this object, return that instead
if (rev != null)
{
InternalCDORevision cleanRev = cleanRevisions.get(object);
if (cleanRev != null)
{
return cleanRev;
}
}
return rev;
}
@Override
protected InternalCDORevision getRevision(CDOObject object)
{
if (object.cdoState() == CDOState.TRANSIENT)
{
InternalCDORevision revision = cleanRevisions.get(object);
if (revision == null)
{
throw new IllegalStateException("No revision for transient object " + object);
}
return revision;
}
return super.getRevision(object);
}
@Override
protected InternalCDOLockState createUpdatedLockStateForNewObject(CDOObject object, IRWLockManager.LockType lockType, boolean on)
{
CheckUtil.checkState(FSMUtil.isNew(object), "Object is not in NEW state");
CheckUtil.checkArg(lockType, "lockType");
InternalCDOLockState lockState = (InternalCDOLockState)getLockState(object);
if (lockState == null)
{
CheckUtil.checkArg(on == true, "on != true");
Object lockTarget = getLockTarget(object);
lockState = (InternalCDOLockState)CDOLockUtil.createLockState(lockTarget);
}
else
{
lockState = (InternalCDOLockState)CDOLockUtil.copyLockState(lockState);
}
CDOLockOwner lockOwner = CDOLockUtil.createLockOwner(this);
if (on)
{
switch (lockType)
{
case READ:
lockState.addReadLockOwner(lockOwner);
break;
case WRITE:
lockState.setWriteLockOwner(lockOwner);
break;
case OPTION:
lockState.setWriteOptionOwner(lockOwner);
break;
default:
throw new IllegalArgumentException("Unknown lock type " + lockType);
}
}
else
{
switch (lockType)
{
case READ:
lockState.removeReadLockOwner(lockOwner);
break;
case WRITE:
lockState.setWriteLockOwner(null);
break;
case OPTION:
lockState.setWriteOptionOwner(null);
break;
default:
throw new IllegalArgumentException("Unknown lock type " + lockType);
}
}
return lockState;
}
@Override
protected List<CDOLockState> createUnlockedLockStatesForAllNewObjects()
{
List<CDOLockState> locksOnNewObjects = new LinkedList<>();
for (CDOObject object : getNewObjects().values())
{
Object lockTarget = getLockTarget(object);
CDOLockState lockState = CDOLockUtil.createLockState(lockTarget);
locksOnNewObjects.add(lockState);
}
return locksOnNewObjects;
}
public static boolean isResourceMatch(String nodeName, String name, boolean exactMatch)
{
boolean useEquals = exactMatch || nodeName == null || name == null;
return useEquals ? ObjectUtil.equals(nodeName, name) : nodeName.startsWith(name);
}
public static void resurrectObject(CDOObject object, CDOID id)
{
if (object.cdoState() != CDOState.NEW)
{
throw new IllegalStateException("Object is not new: " + object);
}
CDOID oldID = object.cdoID();
if (oldID == id)
{
return;
}
InternalCDORevision revision = (InternalCDORevision)object.cdoRevision(false);
revision.setID(id);
InternalCDOTransaction transaction = (InternalCDOTransaction)object.cdoView();
transaction.remapObject(oldID);
Map<CDOID, CDOObject> newObjects = transaction.getLastSavepoint().getNewObjects();
newObjects.remove(oldID);
newObjects.put(id, object);
CDORevision detachedRevision = getDetachedRevision(transaction, id);
if (detachedRevision != null)
{
revision.setVersion(detachedRevision.getVersion());
}
}
private static CDORevision getDetachedRevision(InternalCDOTransaction transaction, CDOID id)
{
SyntheticCDORevision[] synthetics = new SyntheticCDORevision[1];
InternalCDORevisionManager revisionManager = transaction.getSession().getRevisionManager();
InternalCDORevision result = revisionManager.getRevision(id, transaction, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true, synthetics);
if (result != null)
{
throw new IllegalStateException("An object with the same id already exists on this branch");
}
return synthetics[0];
}
@SafeVarargs
private static <T> Set<T> toSet(T... objects)
{
Set<T> result = null;
if (objects.length != 0)
{
result = new HashSet<>();
for (T object : objects)
{
result.add(object);
}
}
return result;
}
/**
* @author Eike Stepper
*/
private final class CleanRevisionsMap extends HashMap<InternalCDOObject, InternalCDORevision>
{
private static final long serialVersionUID = 1L;
public CleanRevisionsMap()
{
}
@Override
public InternalCDORevision get(Object key)
{
if (key instanceof EObject)
{
CDOObject cdoObject = CDOUtil.getCDOObject((EObject)key);
InternalCDORevision revision = super.get(cdoObject);
if (revision != null)
{
getSession().resolveAllElementProxies(revision);
}
return revision;
}
return null;
}
@Override
public InternalCDORevision remove(Object key)
{
if (key instanceof EObject)
{
CDOObject cdoObject = CDOUtil.getCDOObject((EObject)key);
return super.remove(cdoObject);
}
return null;
}
@Override
public boolean containsKey(Object key)
{
if (key instanceof EObject)
{
CDOObject cdoObject = CDOUtil.getCDOObject((EObject)key);
return super.containsKey(cdoObject);
}
return false;
}
@Override
public Set<InternalCDOObject> keySet()
{
throw new UnsupportedOperationException();
}
}
/**
* Generates {@link CDOIDTemp temporary} ID values.
*
* @author Eike Stepper
*/
private static final class TempIDGenerator implements CDOIDGenerator
{
private AtomicInteger lastTemporaryID = new AtomicInteger();
public TempIDGenerator()
{
}
@Override
public CDOID generateCDOID(EObject object)
{
return CDOIDUtil.createTempObject(lastTemporaryID.incrementAndGet());
}
@Override
public void reset()
{
lastTemporaryID.set(0);
}
}
/**
* @author Simon McDuff
*/
private final class CDOCommitContextImpl implements InternalCDOCommitContext
{
private InternalCDOTransaction transaction;
/**
* Tracks whether this commit is *actually* partial or not. (Having tx.committables != null does not in itself mean
* that the commit will be partial, because the committables could cover all dirty/new/detached objects. But this
* boolean gets set to reflect whether the commit will really commit less than all dirty/new/detached objects.)
*/
private boolean isPartialCommit;
private CDOCommitData commitData;
private Map<CDOID, CDOObject> newObjects;
private Map<CDOID, CDOObject> detachedObjects;
private Map<CDOID, CDORevisionDelta> revisionDeltas;
private Map<CDOID, CDOObject> dirtyObjects;
private Map<ByteArrayWrapper, CDOLob<?>> lobs = new HashMap<>();
private List<CDOLockState> locksOnNewObjects = new ArrayList<>();
private List<CDOID> idsToUnlock = new ArrayList<>();
public CDOCommitContextImpl(InternalCDOTransaction transaction)
{
this.transaction = transaction;
calculateCommitData();
}
private void calculateCommitData()
{
OptionsImpl options = (OptionsImpl)transaction.options();
List<CDOPackageUnit> newPackageUnits = analyzeNewPackages();
newObjects = filterCommittables(transaction.getNewObjects());
List<CDOIDAndVersion> revisions = new ArrayList<>(newObjects.size());
for (CDOObject newObject : newObjects.values())
{
revisions.add(newObject.cdoRevision());
CDOLockState lockState = getLockState(newObject);
if (lockState != null)
{
if (!options.isEffectiveAutoReleaseLock(newObject))
{
locksOnNewObjects.add(lockState);
}
}
}
revisionDeltas = filterCommittables(transaction.getRevisionDeltas());
List<CDORevisionKey> deltas = new ArrayList<>(revisionDeltas.size());
for (CDORevisionDelta delta : revisionDeltas.values())
{
deltas.add(delta);
}
detachedObjects = filterCommittables(transaction.getDetachedObjects());
List<CDOIDAndVersion> detached = new ArrayList<>(detachedObjects.size());
for (Entry<CDOID, CDOObject> entry : detachedObjects.entrySet())
{
CDOObject object = entry.getValue();
InternalCDORevision cleanRevision = cleanRevisions.get(object);
if (cleanRevision == null)
{
// Can happen after merged detachments
CDORevision revision = object.cdoRevision();
detached.add(CDOIDUtil.createIDAndVersion(revision));
}
else if (cleanRevision.getBranch() == getBranch())
{
detached.add(CDOIDUtil.createIDAndVersion(cleanRevision));
}
else
{
detached.add(cleanRevision);
}
}
dirtyObjects = filterCommittables(transaction.getDirtyObjects());
for (CDOObject lockedObject : getLockStates().keySet())
{
if (!FSMUtil.isTransient(lockedObject))
{
if (options.isEffectiveAutoReleaseLock(lockedObject))
{
idsToUnlock.add(lockedObject.cdoID());
}
}
}
commitData = CDOCommitInfoUtil.createCommitData(newPackageUnits, revisions, deltas, detached);
}
private <T> Map<CDOID, T> filterCommittables(Map<CDOID, T> map)
{
if (committables == null)
{
// No partial commit filter -- nothing to do
return map;
}
Map<CDOID, T> newMap = CDOIDUtil.createMap();
for (Entry<CDOID, T> entry : map.entrySet())
{
CDOID id = entry.getKey();
CDOObject o = getObject(id);
if (committables.contains(o))
{
newMap.put(id, entry.getValue());
}
else
{
isPartialCommit = true;
}
}
return newMap;
}
@Override
public String getUserID()
{
return transaction.getSession().getUserID();
}
@Override
public int getViewID()
{
return transaction.getViewID();
}
@Override
public CDOBranch getBranch()
{
return transaction.getBranch();
}
@Override
public InternalCDOTransaction getTransaction()
{
return transaction;
}
@Override
public boolean isPartialCommit()
{
return isPartialCommit;
}
@Override
public String getCommitComment()
{
return transaction.getCommitComment();
}
@Override
public CDOBranchPoint getCommitMergeSource()
{
return transaction.getCommitMergeSource();
}
@Override
public CDOCommitData getCommitData()
{
return commitData;
}
@Override
public Map<CDOID, CDOObject> getDirtyObjects()
{
return dirtyObjects;
}
@Override
public Map<CDOID, CDOObject> getNewObjects()
{
return newObjects;
}
@Override
public List<CDOPackageUnit> getNewPackageUnits()
{
return commitData.getNewPackageUnits();
}
@Override
public Map<CDOID, CDOObject> getDetachedObjects()
{
return detachedObjects;
}
@Override
public Map<CDOID, CDORevisionDelta> getRevisionDeltas()
{
return revisionDeltas;
}
@Override
public Collection<CDOLob<?>> getLobs()
{
return lobs.values();
}
@Override
@Deprecated
public boolean isAutoReleaseLocks()
{
return transaction.options().isAutoReleaseLocksEnabled();
}
@Override
public Collection<CDOLockState> getLocksOnNewObjects()
{
return locksOnNewObjects;
}
@Override
public List<CDOID> getIDsToUnlock()
{
return idsToUnlock;
}
@Override
public void preCommit()
{
if (isDirty())
{
if (TRACER.isEnabled())
{
TRACER.trace("commit()"); //$NON-NLS-1$
}
CDOTransactionHandler2[] handlers = getTransactionHandlers2();
if (handlers.length != 0)
{
final boolean[] modifiedAgain = { false };
CDOTransactionHandler1 modifiedAgainHandler = new CDODefaultTransactionHandler1()
{
@Override
public void modifyingObject(CDOTransaction transaction, CDOObject object, CDOFeatureDelta featureChange)
{
modifiedAgain[0] = true;
}
};
addTransactionHandler(modifiedAgainHandler);
try
{
for (int i = 0; i < handlers.length; i++)
{
modifiedAgain[0] = false;
CDOTransactionHandler2 handler = handlers[i];
handler.committingTransaction(getTransaction(), this);
if (modifiedAgain[0])
{
calculateCommitData();
}
}
}
finally
{
removeTransactionHandler(modifiedAgainHandler);
}
}
try
{
// TODO (CD) It might be wise to always do the checks,
// instead of only for partial commits
if (isPartialCommit)
{
new CommitIntegrityCheck(this, CommitIntegrityCheck.Style.EXCEPTION_FAST).check();
}
preCommit(getNewObjects(), lobs);
preCommit(getDirtyObjects(), lobs);
if (!lobs.isEmpty())
{
CDOSessionProtocol sessionProtocol = getSession().getSessionProtocol();
List<byte[]> alreadyKnown = sessionProtocol.queryLobs(ByteArrayWrapper.toByteArray(lobs.keySet()));
for (byte[] id : alreadyKnown)
{
lobs.remove(new ByteArrayWrapper(id));
}
}
}
catch (RuntimeException ex)
{
throw ex;
}
catch (Exception ex)
{
throw new TransactionException(ex);
}
}
}
@Override
public void postCommit(CommitTransactionResult result)
{
try
{
InternalCDOSession session = getSession();
long timeStamp = result.getTimeStamp();
long previousTimeStamp = result.getPreviousTimeStamp();
boolean clearResourcePathCache = result.isClearResourcePathCache();
if (result.getRollbackMessage() != null)
{
InternalCDOCommitInfoManager commitInfoManager = session.getCommitInfoManager();
CDOCommitInfo commitInfo = new FailureCommitInfo(commitInfoManager, timeStamp, previousTimeStamp);
InvalidationData invalidationData = new InvalidationData();
invalidationData.setCommitInfo(commitInfo);
invalidationData.setSender(transaction);
invalidationData.setClearResourcePathCache(clearResourcePathCache);
invalidationData.setSecurityImpact(CDOProtocol.CommitNotificationInfo.IMPACT_NONE);
invalidationData.setNewPermissions(null);
invalidationData.setLockChangeInfo(null);
session.invalidate(invalidationData);
return;
}
CDOBranch oldBranch = getBranch();
CDOBranch branch = result.getBranch();
boolean branchChanged = branch != getBranch();
if (branchChanged)
{
basicSetBranchPoint(branch.getHead());
}
for (CDOPackageUnit newPackageUnit : getNewPackageUnits())
{
((InternalCDOPackageUnit)newPackageUnit).setState(CDOPackageUnit.State.LOADED);
}
CDOLockChangeInfo lockChangeInfo = makeUnlockChangeInfo(result);
CDOCommitInfo commitInfo = null;
CommitData newCommitData = result.getNewCommitData();
if (newCommitData == null)
{
Map<CDOID, CDOObject> newObjects = getNewObjects();
postCommit(newObjects, result);
Map<CDOID, CDOObject> dirtyObjects = getDirtyObjects();
postCommit(dirtyObjects, result);
for (CDORevisionDelta delta : getRevisionDeltas().values())
{
((InternalCDORevisionDelta)delta).adjustReferences(result.getReferenceAdjuster());
}
for (CDOID id : getDetachedObjects().keySet())
{
removeObject(id);
}
commitInfo = makeCommitInfo(timeStamp, previousTimeStamp);
if (!commitInfo.isEmpty())
{
InvalidationData invalidationData = new InvalidationData();
invalidationData.setCommitInfo(commitInfo);
invalidationData.setSender(transaction);
invalidationData.setClearResourcePathCache(clearResourcePathCache);
invalidationData.setSecurityImpact(result.getSecurityImpact());
invalidationData.setNewPermissions(result.getNewPermissions());
invalidationData.setLockChangeInfo(lockChangeInfo);
session.invalidate(invalidationData);
}
// Bug 290032 - Sticky views
if (session.isSticky())
{
CDOBranchPoint commitBranchPoint = CDOBranchUtil.copyBranchPoint(result);
// Note: keySet() does not work because ID mappings are not applied there!
for (CDOObject object : newObjects.values())
{
session.setCommittedSinceLastRefresh(object.cdoID(), commitBranchPoint);
}
for (CDOID id : dirtyObjects.keySet())
{
session.setCommittedSinceLastRefresh(id, commitBranchPoint);
}
for (CDOID id : getDetachedObjects().keySet())
{
session.setCommittedSinceLastRefresh(id, commitBranchPoint);
}
}
}
else
{
applyNewCommitData(newCommitData, timeStamp);
}
CDOTransactionHandler2[] handlers = getTransactionHandlers2();
for (int i = 0; i < handlers.length; i++)
{
CDOTransactionHandler2 handler = handlers[i];
if (handler instanceof CDOTransactionHandler3)
{
CDOTransactionHandler3 handler3 = (CDOTransactionHandler3)handler;
handler3.committedTransaction(transaction, this, commitInfo);
}
else
{
handler.committedTransaction(transaction, this);
}
}
getChangeSubscriptionManager().committedTransaction(transaction, this);
getAdapterManager().committedTransaction(transaction, this);
if (newCommitData == null)
{
cleanUp(this);
}
IListener[] listeners = getListeners();
if (listeners != null)
{
if (branchChanged)
{
fireViewTargetChangedEvent(oldBranch.getHead(), listeners);
}
Map<CDOID, CDOID> idMappings = result.getIDMappings();
fireEvent(new FinishedEvent(idMappings), listeners);
}
if (lockChangeInfo != null && isActive())
{
fireLocksChangedEvent(CDOTransactionImpl.this, lockChangeInfo);
}
}
catch (RuntimeException ex)
{
throw ex;
}
catch (Exception ex)
{
throw new TransactionException(ex);
}
}
private CDOCommitInfo makeCommitInfo(long timeStamp, long previousTimeStamp)
{
InternalCDOSession session = getSession();
CDOBranch branch = getBranch();
String userID = session.getUserID();
String comment = getCommitComment();
CDOBranchPoint mergeSource = getCommitMergeSource();
InternalCDOCommitInfoManager commitInfoManager = session.getCommitInfoManager();
return commitInfoManager.createCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, mergeSource, commitData);
}
private CDOLockChangeInfo makeUnlockChangeInfo(CommitTransactionResult result)
{
Map<CDOObject, CDOLockState> lockStates = getLockStates();
if (lockStates.isEmpty())
{
return null;
}
List<CDOLockState> objectsToUnlock = new ArrayList<>();
for (Map.Entry<CDOObject, CDOLockState> entry : lockStates.entrySet())
{
CDOObject object = entry.getKey();
if (options().isEffectiveAutoReleaseLock(object))
{
InternalCDOLockState lockState = (InternalCDOLockState)entry.getValue();
lockState.updateFrom(InternalCDOLockState.UNLOCKED);
objectsToUnlock.add(lockState);
}
}
CDOLockState[] newLockStates = objectsToUnlock.toArray(new CDOLockState[objectsToUnlock.size()]);
return makeLockChangeInfo(CDOLockChangeInfo.Operation.UNLOCK, null, result.getTimeStamp(), newLockStates);
}
private void preCommit(Map<CDOID, CDOObject> objects, Map<ByteArrayWrapper, CDOLob<?>> lobs)
{
if (!objects.isEmpty())
{
for (CDOObject object : objects.values())
{
collectLobs((InternalCDORevision)object.cdoRevision(), lobs);
((InternalCDOObject)object).cdoInternalPreCommit();
}
}
}
private void collectLobs(InternalCDORevision revision, Map<ByteArrayWrapper, CDOLob<?>> lobs)
{
EStructuralFeature[] features = revision.getClassInfo().getAllPersistentFeatures();
for (int i = 0; i < features.length; i++)
{
EStructuralFeature feature = features[i];
if (CDOModelUtil.isLob(feature.getEType()))
{
CDOLob<?> lob = (CDOLob<?>)revision.getValue(feature);
if (lob != null)
{
lobs.put(new ByteArrayWrapper(lob.getID()), lob);
}
}
}
}
private void postCommit(Map<CDOID, CDOObject> objects, CommitTransactionResult result)
{
if (!objects.isEmpty())
{
CDOStateMachine.INSTANCE.commit(objects, result);
}
}
private void applyNewCommitData(CommitData newCommitData, long timeStamp)
{
getTransactionStrategy().rollback(CDOTransactionImpl.this, firstSavepoint); // Transitions objects to CLEAN.
waitForBaseline(timeStamp); // Transitions objects to PROXY.
}
}
/**
* @author Eike Stepper
*/
private final class StartedEvent extends Event implements CDOTransactionStartedEvent
{
private static final long serialVersionUID = 1L;
private StartedEvent()
{
}
@Override
public String toString()
{
return MessageFormat.format("CDOTransactionStartedEvent[source={0}]", getSource()); //$NON-NLS-1$
}
}
/**
* @author Eike Stepper
*/
private final class FinishedEvent extends Event implements CDOTransactionFinishedEvent
{
private static final long serialVersionUID = 1L;
private final Cause cause;
private final Map<CDOID, CDOID> idMappings;
private FinishedEvent(Map<CDOID, CDOID> idMappings)
{
cause = Cause.COMMITTED;
this.idMappings = idMappings;
}
private FinishedEvent(boolean rolledBack)
{
cause = rolledBack ? Cause.ROLLED_BACK : Cause.UNDONE;
idMappings = Collections.emptyMap();
}
@Override
@Deprecated
public Type getType()
{
switch (cause)
{
case COMMITTED:
return Type.COMMITTED;
case ROLLED_BACK:
case UNDONE:
return Type.ROLLED_BACK;
default:
throw new IllegalStateException("Illegal cause: " + cause);
}
}
@Override
public Cause getCause()
{
return cause;
}
@Override
public Map<CDOID, CDOID> getIDMappings()
{
return idMappings;
}
@Override
public String toString()
{
return MessageFormat.format("CDOTransactionFinishedEvent[source={0}, cause={1}, idMappings={2}]", getSource(), //$NON-NLS-1$
getCause(), idMappings == null ? 0 : idMappings.size());
}
}
/**
* @author Eike Stepper
*/
private final class ConflictEvent extends Event implements CDOTransactionConflictEvent
{
private static final long serialVersionUID = 1L;
private InternalCDOObject conflictingObject;
private boolean firstConflict;
public ConflictEvent(InternalCDOObject conflictingObject, boolean firstConflict)
{
this.conflictingObject = conflictingObject;
this.firstConflict = firstConflict;
}
@Override
public InternalCDOObject getConflictingObject()
{
return conflictingObject;
}
@Override
public boolean isFirstConflict()
{
return firstConflict;
}
@Override
public String toString()
{
return MessageFormat.format("CDOTransactionConflictEvent[source={0}, conflictingObject={1}, firstConflict={2}]", //$NON-NLS-1$
getSource(), getConflictingObject(), isFirstConflict());
}
}
/**
* @author Eike Stepper
*/
private static final class CountedRetryPredicate implements Predicate<Long>
{
private final int attempts;
private int attempt;
private CountedRetryPredicate(int attempts)
{
this.attempts = attempts;
}
@Override
public boolean test(Long startMillis)
{
return ++attempt < attempts;
}
}
/**
* @author Eike Stepper
* @since 2.0
*/
protected final class OptionsImpl extends CDOViewImpl.OptionsImpl implements CDOTransaction.Options
{
private CDOUndoDetector undoDetector = DEFAULT_UNDO_DETECTOR;
private final List<CDOConflictResolver> conflictResolvers = new ArrayList<>();
private CDOStaleReferenceCleaner staleReferenceCleaner = CDOStaleReferenceCleaner.DEFAULT;
private boolean autoReleaseLocksEnabled = true;
private final Map<EObject, Boolean> autoReleaseLocksExemptions = new WeakHashMap<>();
private long commitInfoTimeout = DEFAULT_COMMIT_INFO_TIMEOUT;
private Map<CDOID, CDORevision> attachedRevisionsMap;
public OptionsImpl()
{
}
@Override
public CDOUndoDetector getUndoDetector()
{
return undoDetector;
}
@Override
public void setUndoDetector(CDOUndoDetector undoDetector)
{
checkActive();
if (undoDetector == null)
{
undoDetector = DEFAULT_UNDO_DETECTOR;
}
IEvent event = null;
synchronized (getViewMonitor())
{
lockView();
try
{
if (this.undoDetector != undoDetector)
{
this.undoDetector = undoDetector;
event = new UndoDetectorEventImpl();
}
}
finally
{
unlockView();
}
}
fireEvent(event);
}
@Override
public CDOTransactionImpl getContainer()
{
return (CDOTransactionImpl)super.getContainer();
}
@Override
public CDOConflictResolver[] getConflictResolvers()
{
synchronized (getViewMonitor())
{
lockView();
try
{
return conflictResolvers.toArray(new CDOConflictResolver[conflictResolvers.size()]);
}
finally
{
unlockView();
}
}
}
@Override
public void setConflictResolvers(CDOConflictResolver[] resolvers)
{
checkActive();
synchronized (getViewMonitor())
{
lockView();
try
{
for (CDOConflictResolver resolver : conflictResolvers)
{
resolver.setTransaction(null);
}
conflictResolvers.clear();
for (CDOConflictResolver resolver : resolvers)
{
validateResolver(resolver);
conflictResolvers.add(resolver);
}
}
finally
{
unlockView();
}
}
fireEvent(new ConflictResolversEventImpl());
}
@Override
public void addConflictResolver(CDOConflictResolver resolver)
{
checkActive();
IEvent event = null;
synchronized (getViewMonitor())
{
lockView();
try
{
validateResolver(resolver);
conflictResolvers.add(resolver);
event = new ConflictResolversEventImpl();
}
finally
{
unlockView();
}
}
fireEvent(event);
}
@Override
public void removeConflictResolver(CDOConflictResolver resolver)
{
checkActive();
IEvent event = null;
synchronized (getViewMonitor())
{
lockView();
try
{
if (conflictResolvers.remove(resolver))
{
resolver.setTransaction(null);
event = new ConflictResolversEventImpl();
}
}
finally
{
unlockView();
}
}
fireEvent(event);
}
@Override
public CDOStaleReferenceCleaner getStaleReferenceCleaner()
{
return staleReferenceCleaner;
}
@Override
public void setStaleReferenceCleaner(CDOStaleReferenceCleaner staleReferenceCleaner)
{
checkActive();
if (staleReferenceCleaner == null)
{
staleReferenceCleaner = CDOStaleReferenceCleaner.DEFAULT;
}
IEvent event = null;
synchronized (getViewMonitor())
{
lockView();
try
{
if (this.staleReferenceCleaner != staleReferenceCleaner)
{
this.staleReferenceCleaner = staleReferenceCleaner;
event = new StaleReferenceCleanerEventImpl();
}
}
finally
{
unlockView();
}
}
fireEvent(event);
}
public void disposeConflictResolvers()
{
try
{
// Do not call getConflictResolvers() because that method may block!
CDOConflictResolver[] array = conflictResolvers.toArray(new CDOConflictResolver[conflictResolvers.size()]);
for (CDOConflictResolver resolver : array)
{
try
{
resolver.setTransaction(null);
}
catch (Exception ignore)
{
}
}
}
catch (Exception ignore)
{
}
}
private void validateResolver(CDOConflictResolver resolver)
{
if (resolver.getTransaction() != null)
{
throw new IllegalArgumentException(Messages.getString("CDOTransactionImpl.17")); //$NON-NLS-1$
}
resolver.setTransaction(CDOTransactionImpl.this);
}
@Override
public boolean isAutoReleaseLocksEnabled()
{
return autoReleaseLocksEnabled;
}
@Override
public void setAutoReleaseLocksEnabled(boolean on)
{
checkActive();
IEvent event = null;
synchronized (getViewMonitor())
{
lockView();
try
{
if (autoReleaseLocksEnabled != on)
{
autoReleaseLocksEnabled = on;
event = new AutoReleaseLocksEnabledEventImpl();
}
}
finally
{
unlockView();
}
}
fireEvent(event);
}
@Override
public Set<? extends EObject> getAutoReleaseLocksExemptions()
{
synchronized (getViewMonitor())
{
lockView();
try
{
return new HashSet<>(autoReleaseLocksExemptions.keySet());
}
finally
{
unlockView();
}
}
}
@Override
public boolean isAutoReleaseLocksExemption(EObject object)
{
synchronized (getViewMonitor())
{
lockView();
try
{
return autoReleaseLocksExemptions.get(CDOUtil.getCDOObject(object)) == Boolean.TRUE;
}
finally
{
unlockView();
}
}
}
@Override
public void clearAutoReleaseLocksExemptions()
{
checkActive();
IEvent event = null;
synchronized (getViewMonitor())
{
lockView();
try
{
if (!autoReleaseLocksExemptions.isEmpty())
{
autoReleaseLocksExemptions.clear();
event = new AutoReleaseLocksExemptionsEventImpl();
}
}
finally
{
unlockView();
}
}
fireEvent(event);
}
@Override
public void addAutoReleaseLocksExemptions(boolean recursive, EObject... objects)
{
checkActive();
IEvent event = null;
synchronized (getViewMonitor())
{
lockView();
try
{
for (EObject object : objects)
{
if (autoReleaseLocksExemptions.put(CDOUtil.getCDOObject(object), Boolean.TRUE) == null)
{
event = new AutoReleaseLocksExemptionsEventImpl();
}
if (recursive)
{
for (TreeIterator<EObject> it = object.eAllContents(); it.hasNext();)
{
EObject child = it.next();
if (autoReleaseLocksExemptions.put(CDOUtil.getCDOObject(child), Boolean.TRUE) == null && event == null)
{
event = new AutoReleaseLocksExemptionsEventImpl();
}
}
}
}
}
finally
{
unlockView();
}
}
fireEvent(event);
}
@Override
public void removeAutoReleaseLocksExemptions(boolean recursive, EObject... objects)
{
checkActive();
IEvent event = null;
synchronized (getViewMonitor())
{
lockView();
try
{
for (EObject object : objects)
{
if (autoReleaseLocksExemptions.remove(CDOUtil.getCDOObject(object)) != null)
{
event = new AutoReleaseLocksExemptionsEventImpl();
}
if (recursive)
{
for (TreeIterator<EObject> it = object.eAllContents(); it.hasNext();)
{
EObject child = it.next();
if (autoReleaseLocksExemptions.remove(CDOUtil.getCDOObject(child)) != null && event == null)
{
event = new AutoReleaseLocksExemptionsEventImpl();
}
}
}
}
}
finally
{
unlockView();
}
}
fireEvent(event);
}
public boolean isEffectiveAutoReleaseLock(CDOObject newObject)
{
boolean effectiveAutoReleaseLock = autoReleaseLocksEnabled;
if (autoReleaseLocksExemptions.containsKey(newObject))
{
effectiveAutoReleaseLock = !effectiveAutoReleaseLock;
}
return effectiveAutoReleaseLock;
}
@Override
public Map<CDOID, CDORevision> getAttachedRevisionsMap()
{
return attachedRevisionsMap;
}
@Override
public void setAttachedRevisionsMap(Map<CDOID, CDORevision> attachedRevisionsMap)
{
checkActive();
IEvent event = null;
synchronized (getViewMonitor())
{
lockView();
try
{
if (this.attachedRevisionsMap != attachedRevisionsMap)
{
this.attachedRevisionsMap = attachedRevisionsMap;
event = new AttachedRevisionsMapImpl();
}
}
finally
{
unlockView();
}
}
fireEvent(event);
}
@Override
public long getCommitInfoTimeout()
{
return commitInfoTimeout;
}
@Override
public void setCommitInfoTimeout(long commitInfoTimeout)
{
checkActive();
IEvent event = null;
synchronized (getViewMonitor())
{
lockView();
try
{
if (this.commitInfoTimeout != commitInfoTimeout)
{
this.commitInfoTimeout = commitInfoTimeout;
event = new CommitInfoTimeoutImpl();
}
}
finally
{
unlockView();
}
}
fireEvent(event);
}
/**
* @author Eike Stepper
*/
private final class UndoDetectorEventImpl extends OptionsEvent implements UndoDetectorEvent
{
private static final long serialVersionUID = 1L;
public UndoDetectorEventImpl()
{
super(OptionsImpl.this);
}
}
/**
* @author Eike Stepper
*/
private final class ConflictResolversEventImpl extends OptionsEvent implements ConflictResolversEvent
{
private static final long serialVersionUID = 1L;
public ConflictResolversEventImpl()
{
super(OptionsImpl.this);
}
}
/**
* @author Eike Stepper
*/
private final class StaleReferenceCleanerEventImpl extends OptionsEvent implements StaleReferenceCleanerEvent
{
private static final long serialVersionUID = 1L;
public StaleReferenceCleanerEventImpl()
{
super(OptionsImpl.this);
}
}
/**
* @author Eike Stepper
*/
private final class AutoReleaseLocksEnabledEventImpl extends OptionsEvent implements AutoReleaseLocksEnabledEvent
{
private static final long serialVersionUID = 1L;
public AutoReleaseLocksEnabledEventImpl()
{
super(OptionsImpl.this);
}
}
/**
* @author Eike Stepper
*/
private final class AutoReleaseLocksExemptionsEventImpl extends OptionsEvent implements AutoReleaseLocksExemptionsEvent
{
private static final long serialVersionUID = 1L;
public AutoReleaseLocksExemptionsEventImpl()
{
super(OptionsImpl.this);
}
}
/**
* @author Eike Stepper
*/
private final class AttachedRevisionsMapImpl extends OptionsEvent implements AttachedRevisionsMap
{
private static final long serialVersionUID = 1L;
public AttachedRevisionsMapImpl()
{
super(OptionsImpl.this);
}
}
/**
* @author Eike Stepper
*/
private final class CommitInfoTimeoutImpl extends OptionsEvent implements CommitInfoTimeout
{
private static final long serialVersionUID = 1L;
public CommitInfoTimeoutImpl()
{
super(OptionsImpl.this);
}
}
}
}