| /* |
| * Copyright (c) 2004 - 2011 Eike Stepper (Berlin, 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.CDOState; |
| import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation; |
| 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.CDOIDTemp; |
| import org.eclipse.emf.cdo.common.id.CDOIDUtil; |
| import org.eclipse.emf.cdo.common.lob.CDOLob; |
| import org.eclipse.emf.cdo.common.lob.CDOLobStore; |
| import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo.Operation; |
| 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.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.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.CDORevisionDelta; |
| import org.eclipse.emf.cdo.common.util.CDOException; |
| 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.EresourceFactory; |
| import org.eclipse.emf.cdo.eresource.impl.CDOResourceImpl; |
| import org.eclipse.emf.cdo.eresource.impl.CDOResourceNodeImpl; |
| import org.eclipse.emf.cdo.internal.common.commit.CDOCommitDataImpl; |
| import org.eclipse.emf.cdo.internal.common.commit.FailureCommitInfo; |
| import org.eclipse.emf.cdo.internal.common.protocol.CDODataInputImpl; |
| import org.eclipse.emf.cdo.internal.common.protocol.CDODataOutputImpl; |
| import org.eclipse.emf.cdo.internal.common.revision.CDOListWithElementProxiesImpl; |
| import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil; |
| import org.eclipse.emf.cdo.spi.common.commit.CDORevisionAvailabilityInfo; |
| 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.InternalCDOPackageUnit; |
| import org.eclipse.emf.cdo.spi.common.revision.CDOIDMapper; |
| import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; |
| import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; |
| 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.CDODefaultTransactionHandler1; |
| import org.eclipse.emf.cdo.transaction.CDOMerger; |
| import org.eclipse.emf.cdo.transaction.CDOSavepoint; |
| import org.eclipse.emf.cdo.transaction.CDOTransaction; |
| 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.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.LegacyModeNotEnabledException; |
| import org.eclipse.emf.cdo.util.ObjectNotFoundException; |
| import org.eclipse.emf.cdo.view.CDOView; |
| |
| 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.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.ByteArrayWrapper; |
| import org.eclipse.net4j.util.collection.ConcurrentArray; |
| import org.eclipse.net4j.util.collection.Pair; |
| import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; |
| 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.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.URI; |
| 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.EStructuralFeature.Setting; |
| import org.eclipse.emf.ecore.InternalEObject; |
| import org.eclipse.emf.ecore.InternalEObject.EStore; |
| import org.eclipse.emf.ecore.impl.EClassImpl.FeatureSubsetSupplier; |
| import org.eclipse.emf.ecore.util.EContentsEList.FeatureIterator; |
| import org.eclipse.emf.ecore.util.ECrossReferenceEList; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| 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.InternalCDOTransaction; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| 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.concurrent.Callable; |
| import java.util.concurrent.ConcurrentMap; |
| import java.util.concurrent.atomic.AtomicInteger; |
| |
| /** |
| * @author Eike Stepper |
| */ |
| public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransaction |
| { |
| private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_TRANSACTION, CDOTransactionImpl.class); |
| |
| 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; |
| |
| // 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 ResolvingRevisionMap(); |
| |
| public CDOTransactionImpl(CDOBranch branch) |
| { |
| super(branch, UNSPECIFIED_DATE); |
| } |
| |
| public CDOTransactionImpl(String durableLockingID) |
| { |
| super(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 synchronized boolean setBranchPoint(CDOBranchPoint branchPoint) |
| { |
| 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); |
| } |
| |
| public void addTransactionHandler(CDOTransactionHandlerBase handler) |
| { |
| synchronized (transactionHandlersLock) |
| { |
| if (handler instanceof CDOTransactionHandler1) |
| { |
| transactionHandlers1.add((CDOTransactionHandler1)handler); |
| } |
| |
| if (handler instanceof CDOTransactionHandler2) |
| { |
| transactionHandlers2.add((CDOTransactionHandler2)handler); |
| } |
| } |
| } |
| |
| public void removeTransactionHandler(CDOTransactionHandlerBase handler) |
| { |
| synchronized (transactionHandlersLock) |
| { |
| if (handler instanceof CDOTransactionHandler1) |
| { |
| transactionHandlers1.remove((CDOTransactionHandler1)handler); |
| } |
| |
| if (handler instanceof CDOTransactionHandler2) |
| { |
| transactionHandlers2.remove((CDOTransactionHandler2)handler); |
| } |
| } |
| } |
| |
| public CDOTransactionHandler[] getTransactionHandlers() |
| { |
| Set<CDOTransactionHandler> result = new HashSet<CDOTransactionHandler>(); |
| 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()]); |
| } |
| |
| public CDOTransactionHandler1[] getTransactionHandlers1() |
| { |
| synchronized (transactionHandlersLock) |
| { |
| return transactionHandlers1.get(); |
| } |
| } |
| |
| public CDOTransactionHandler2[] getTransactionHandlers2() |
| { |
| synchronized (transactionHandlersLock) |
| { |
| return transactionHandlers2.get(); |
| } |
| } |
| |
| @Override |
| public synchronized boolean isDirty() |
| { |
| if (isClosed()) |
| { |
| return false; |
| } |
| |
| return dirty; |
| } |
| |
| @Override |
| public synchronized boolean hasConflict() |
| { |
| checkActive(); |
| return conflict != 0; |
| } |
| |
| public void setConflict(InternalCDOObject object) |
| { |
| IEvent event = null; |
| synchronized (this) |
| { |
| event = new ConflictEvent(object, conflict == 0); |
| ++conflict; |
| } |
| |
| fireEvent(event); |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| public synchronized Set<CDOObject> getConflicts() |
| { |
| Set<CDOObject> conflicts = new HashSet<CDOObject>(); |
| for (CDOObject object : getDirtyObjects().values()) |
| { |
| if (object.cdoConflict()) |
| { |
| conflicts.add(object); |
| } |
| } |
| |
| for (CDOObject object : getDetachedObjects().values()) |
| { |
| if (object.cdoConflict()) |
| { |
| conflicts.add(object); |
| } |
| } |
| |
| return conflicts; |
| } |
| |
| public synchronized CDOChangeSetData getChangeSetData() |
| { |
| checkActive(); |
| return lastSavepoint.getAllChangeSetData(); |
| } |
| |
| public synchronized CDOChangeSetData merge(CDOBranchPoint source, CDOMerger merger) |
| { |
| return merge(source, null, merger); |
| } |
| |
| public synchronized CDOChangeSetData merge(CDOBranchPoint source, CDOBranchPoint sourceBase, CDOMerger merger) |
| { |
| if (isDirty()) |
| { |
| throw new IllegalStateException("Merging into dirty transactions not yet supported"); |
| } |
| |
| long now = getLastUpdateTime(); |
| CDOBranchPoint target = getBranch().getPoint(now); |
| |
| if (source.getTimeStamp() == CDOBranchPoint.UNSPECIFIED_DATE) |
| { |
| source = source.getBranch().getPoint(now); |
| } |
| |
| if (CDOBranchUtil.isContainedBy(source, target)) |
| { |
| throw new IllegalArgumentException("Source is already contained in " + target); |
| } |
| |
| if (sourceBase != null && CDOBranchUtil.isContainedBy(sourceBase, source)) |
| { |
| throw new IllegalArgumentException("Source base is not contained in " + source); |
| } |
| |
| CDOBranchPoint ancestor = CDOBranchUtil.getAncestor(target, source); |
| |
| InternalCDOSession session = getSession(); |
| CDORevisionAvailabilityInfo ancestorInfo = session.createRevisionAvailabilityInfo(ancestor); |
| CDORevisionAvailabilityInfo targetInfo = session.createRevisionAvailabilityInfo(target); |
| CDORevisionAvailabilityInfo sourceInfo = session.createRevisionAvailabilityInfo(source); |
| CDORevisionAvailabilityInfo baseInfo = sourceBase != null ? session.createRevisionAvailabilityInfo(sourceBase) |
| : null; |
| |
| CDOSessionProtocol sessionProtocol = session.getSessionProtocol(); |
| Set<CDOID> ids = sessionProtocol.loadMergeData(targetInfo, sourceInfo, ancestorInfo, baseInfo); |
| |
| session.cacheRevisions(targetInfo); |
| session.cacheRevisions(sourceInfo); |
| session.cacheRevisions(ancestorInfo); |
| |
| if (baseInfo != null) |
| { |
| session.cacheRevisions(baseInfo); |
| } |
| else |
| { |
| baseInfo = ancestorInfo; |
| } |
| |
| CDOChangeSet targetChanges = createChangeSet(ids, ancestorInfo, targetInfo); |
| CDOChangeSet sourceChanges = createChangeSet(ids, baseInfo, sourceInfo); |
| |
| CDOChangeSetData result = merger.merge(targetChanges, sourceChanges); |
| if (result == null) |
| { |
| return null; |
| } |
| |
| return applyChangeSet(result, ancestorInfo, targetInfo, source, false).getChangeSetData(); |
| } |
| |
| private CDOChangeSet createChangeSet(Set<CDOID> ids, CDORevisionAvailabilityInfo startInfo, |
| CDORevisionAvailabilityInfo endInfo) |
| { |
| CDOChangeSetData data = CDORevisionUtil.createChangeSetData(ids, startInfo, endInfo); |
| return CDORevisionUtil.createChangeSet(startInfo.getBranchPoint(), endInfo.getBranchPoint(), data); |
| } |
| |
| @Deprecated |
| public Pair<CDOChangeSetData, Pair<Map<CDOID, CDOID>, List<CDOID>>> applyChangeSetData( |
| CDOChangeSetData changeSetData, CDORevisionProvider ancestorProvider, CDORevisionProvider targetProvider, |
| CDOBranchPoint source) |
| { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public synchronized ApplyChangeSetResult applyChangeSet(CDOChangeSetData changeSetData, |
| CDORevisionProvider ancestorProvider, CDORevisionProvider targetProvider, CDOBranchPoint source, |
| boolean keepVersions) throws ChangeSetOutdatedException |
| { |
| ApplyChangeSetResult result = new ApplyChangeSetResult(); |
| |
| // Merges from local offline branches may require additional ID mappings: localID -> tempID |
| if (source != null && source.getBranch().isLocal()) |
| { |
| applyLocalIDMapping(changeSetData, result); |
| } |
| |
| // New objects |
| applyNewObjects(changeSetData.getNewObjects(), result.getChangeSetData().getNewObjects()); |
| |
| // Detached objects |
| Set<CDOObject> detachedSet = applyDetachedObjects(changeSetData.getDetachedObjects(), result.getChangeSetData() |
| .getDetachedObjects()); |
| |
| // Changed objects |
| Map<CDOID, InternalCDORevision> oldRevisions = applyChangedObjects(changeSetData.getChangedObjects(), |
| ancestorProvider, targetProvider, keepVersions, result.getChangeSetData().getChangedObjects()); |
| |
| // Delta notifications |
| Collection<CDORevisionDelta> notificationDeltas = lastSavepoint.getRevisionDeltas().values(); |
| if (!notificationDeltas.isEmpty() || !detachedSet.isEmpty()) |
| { |
| sendDeltaNotifications(notificationDeltas, detachedSet, oldRevisions); |
| } |
| |
| return result; |
| } |
| |
| 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) |
| { |
| for (CDOIDAndVersion key : newObjects) |
| { |
| InternalCDORevision revision = (InternalCDORevision)key; |
| CDOID id = revision.getID(); |
| if (getObjectIfExists(id) == null) |
| { |
| InternalCDOObject object = newInstance(revision.getEClass()); |
| object.cdoInternalSetView(this); |
| object.cdoInternalSetRevision(revision); |
| object.cdoInternalSetID(id); |
| object.cdoInternalSetState(CDOState.NEW); |
| object.cdoInternalPostLoad(); |
| |
| registerObject(object); |
| registerAttached(object, true); |
| result.add(revision); |
| dirty = true; |
| } |
| } |
| } |
| |
| private Set<CDOObject> applyDetachedObjects(List<CDOIDAndVersion> detachedObjects, List<CDOIDAndVersion> result) |
| { |
| Set<CDOObject> detachedSet = new HashSet<CDOObject>(); |
| 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); |
| dirty = true; |
| } |
| } |
| |
| return detachedSet; |
| } |
| |
| private Map<CDOID, InternalCDORevision> applyChangedObjects(List<CDORevisionKey> changedObjects, |
| CDORevisionProvider ancestorProvider, CDORevisionProvider targetProvider, boolean keepVersions, |
| List<CDORevisionKey> result) throws ChangeSetOutdatedException |
| { |
| Map<CDOID, InternalCDORevision> oldRevisions = new HashMap<CDOID, InternalCDORevision>(); |
| |
| Map<CDOID, CDOObject> dirtyObjects = lastSavepoint.getDirtyObjects(); |
| ConcurrentMap<CDOID, CDORevisionDelta> revisionDeltas = lastSavepoint.getRevisionDeltas(); |
| |
| for (CDORevisionKey key : changedObjects) |
| { |
| InternalCDORevisionDelta ancestorGoalDelta = (InternalCDORevisionDelta)key; |
| ancestorGoalDelta.setTarget(null); |
| CDOID id = ancestorGoalDelta.getID(); |
| InternalCDORevision ancestorRevision = (InternalCDORevision)ancestorProvider.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 = ancestorRevision.copy(); |
| goalRevision.setBranchPoint(this); |
| if (!keepVersions) |
| { |
| goalRevision.setVersion(targetRevision.getVersion()); |
| } |
| |
| goalRevision.setRevised(CDOBranchPoint.UNSPECIFIED_DATE); |
| ancestorGoalDelta.apply(goalRevision); |
| |
| InternalCDORevisionDelta targetGoalDelta = goalRevision.compare(targetRevision); |
| targetGoalDelta.setTarget(null); |
| |
| if (!targetGoalDelta.isEmpty()) |
| { |
| if (keepVersions && targetGoalDelta.getVersion() != ancestorRevision.getVersion()) |
| { |
| throw new ChangeSetOutdatedException(); |
| } |
| |
| revisionDeltas.put(id, targetGoalDelta); |
| result.add(targetGoalDelta); |
| |
| // handle reattached objects. |
| if (lastSavepoint.getDetachedObjects().containsKey(id)) |
| { |
| CDOStateMachine.INSTANCE.attach(object, this); |
| } |
| |
| object.cdoInternalSetState(CDOState.DIRTY); |
| object.cdoInternalSetRevision(goalRevision); |
| revisionChanged = true; |
| |
| dirtyObjects.put(id, object); |
| dirty = 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 synchronized void handleConflicts(Map<CDOObject, Pair<CDORevision, CDORevisionDelta>> conflicts, |
| List<CDORevisionDelta> deltas) |
| { |
| CDOConflictResolver[] resolvers = options().getConflictResolvers(); |
| if (resolvers.length == 0) |
| { |
| return; |
| } |
| |
| // Remember original state to be able to restore it after an exception |
| List<CDOState> states = new ArrayList<CDOState>(conflicts.size()); |
| List<CDORevision> revisions = new ArrayList<CDORevision>(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<CDOObject, Pair<CDORevision, CDORevisionDelta>>( |
| 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).cdoInternalSetState(state.next()); |
| ((InternalCDOObject)object).cdoInternalSetRevision(revision.next()); |
| } |
| |
| throw WrappedException.wrap(ex); |
| } |
| |
| conflict -= resolved; |
| } |
| |
| /** |
| * @deprecated {@link #createIDForNewObject(EObject object)} is called since 4.1. |
| */ |
| @Deprecated |
| public synchronized CDOIDTemp getNextTemporaryID() |
| { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public CDOID createIDForNewObject(EObject object) |
| { |
| return idGenerator.generateCDOID(object); |
| } |
| |
| public synchronized CDOResourceFolder createResourceFolder(String path) |
| { |
| 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 = getResourceNode(path); |
| if (parent instanceof CDOResourceFolder) |
| { |
| ((CDOResourceFolder)parent).getNodes().add(folder); |
| } |
| else |
| { |
| throw new CDOException("Parent is not a folder: " + parent); |
| } |
| } |
| |
| return folder; |
| } |
| |
| public synchronized CDOResource createResource(String path) |
| { |
| checkActive(); |
| URI uri = CDOURIUtil.createResourceURI(this, path); |
| return (CDOResource)getResourceSet().createResource(uri); |
| } |
| |
| public synchronized CDOResource getOrCreateResource(String path) |
| { |
| checkActive(); |
| |
| try |
| { |
| CDOID id = getResourceNodeID(path); |
| if (!CDOIDUtil.isNull(id)) |
| { |
| return (CDOResource)getObject(id); |
| } |
| } |
| catch (Exception ignore) |
| { |
| // Just create the missing resource |
| } |
| |
| return createResource(path); |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| @Override |
| public synchronized void attachResource(CDOResourceImpl resource) |
| { |
| if (resource.isExisting()) |
| { |
| super.attachResource(resource); |
| } |
| else |
| { |
| // ResourceSet.createResource(uri) was called!! |
| attachNewResource(resource); |
| } |
| } |
| |
| 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); |
| } |
| |
| public synchronized CDOResourceFolder getOrCreateResourceFolder(String path) |
| { |
| checkActive(); |
| |
| try |
| { |
| CDOID id = getResourceNodeID(path); |
| if (!CDOIDUtil.isNull(id)) |
| { |
| return (CDOResourceFolder)getObject(id); |
| } |
| } |
| catch (Exception ignore) |
| { |
| // Just create the missing folder |
| } |
| |
| return createResourceFolder(path); |
| } |
| |
| /** |
| * @return never <code>null</code>; |
| * @since 2.0 |
| */ |
| public synchronized CDOResourceFolder getOrCreateResourceFolder(List<String> names) |
| { |
| CDOResourceFolder folder = null; |
| for (String name : names) |
| { |
| CDOResourceNode node; |
| |
| try |
| { |
| CDOID folderID = folder == null ? null : folder.cdoID(); |
| node = getResourceNode(folderID, name); |
| } |
| catch (CDOException 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; |
| } |
| |
| 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 synchronized void detach(CDOResourceImpl cdoResource) |
| { |
| CDOStateMachine.INSTANCE.detach(cdoResource); |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| public synchronized InternalCDOSavepoint getLastSavepoint() |
| { |
| checkActive(); |
| return lastSavepoint; |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| public synchronized CDOTransactionStrategy getTransactionStrategy() |
| { |
| if (transactionStrategy == null) |
| { |
| transactionStrategy = CDOTransactionStrategy.DEFAULT; |
| transactionStrategy.setTarget(this); |
| } |
| |
| return transactionStrategy; |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| public synchronized void setTransactionStrategy(CDOTransactionStrategy transactionStrategy) |
| { |
| if (this.transactionStrategy != null) |
| { |
| this.transactionStrategy.unsetTarget(this); |
| } |
| |
| this.transactionStrategy = transactionStrategy; |
| |
| if (this.transactionStrategy != null) |
| { |
| this.transactionStrategy.setTarget(this); |
| } |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| @Override |
| protected synchronized CDOID getRootOrTopLevelResourceNodeID(String name) |
| { |
| 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 (getLastSavepoint().getAllDetachedObjects().containsKey(id) || getDirtyObjects().containsKey(id)) |
| { |
| throw new CDOException(MessageFormat.format(Messages.getString("CDOTransactionImpl.1"), name)); //$NON-NLS-1$ |
| } |
| |
| return id; |
| } |
| |
| 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; |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| @Override |
| public synchronized InternalCDOObject getObject(CDOID id, boolean loadOnDemand) |
| { |
| checkActive(); |
| if (CDOIDUtil.isNull(id)) |
| { |
| return null; |
| } |
| |
| if (isObjectNew(id) && isObjectDetached(id)) |
| { |
| throw new ObjectNotFoundException(id, this); |
| } |
| |
| return super.getObject(id, loadOnDemand); |
| } |
| |
| @Override |
| public boolean isObjectNew(CDOID id) |
| { |
| return lastSavepoint.isNewObject(id); |
| } |
| |
| private boolean isObjectDetached(CDOID id) |
| { |
| return lastSavepoint.getAllDetachedObjects().containsKey(id); |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| public synchronized InternalCDOCommitContext createCommitContext() |
| { |
| return new CDOCommitContextImpl(this); |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| public synchronized CDOCommitInfo commit(IProgressMonitor progressMonitor) throws CommitException |
| { |
| try |
| { |
| checkActive(); |
| if (hasConflict()) |
| { |
| throw new CommitException(Messages.getString("CDOTransactionImpl.2")); //$NON-NLS-1$ |
| } |
| |
| if (progressMonitor == null) |
| { |
| progressMonitor = new NullProgressMonitor(); |
| } |
| |
| CDOTransactionStrategy transactionStrategy = getTransactionStrategy(); |
| CDOCommitInfo info = transactionStrategy.commit(this, progressMonitor); |
| if (info != null) |
| { |
| lastCommitTime = info.getTimeStamp(); |
| } |
| |
| return info; |
| } |
| catch (CommitException ex) |
| { |
| throw ex; |
| } |
| catch (Throwable t) |
| { |
| throw new CommitException(t); |
| } |
| } |
| |
| public synchronized CDOCommitInfo commit() throws CommitException |
| { |
| return commit(null); |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| public synchronized void rollback() |
| { |
| checkActive(); |
| getTransactionStrategy().rollback(this, firstSavepoint); |
| cleanUp(null); |
| } |
| |
| private void removeObject(CDOID id, final CDOObject object) |
| { |
| ((InternalCDOObject)object).cdoInternalSetState(CDOState.TRANSIENT); |
| removeObject(id); |
| |
| if (object instanceof CDOResource) |
| { |
| getViewSet().executeWithoutNotificationHandling(new Callable<Boolean>() |
| { |
| public Boolean call() throws Exception |
| { |
| getResourceSet().getResources().remove(object); |
| return true; |
| } |
| }); |
| } |
| |
| ((InternalCDOObject)object).cdoInternalSetID(null); |
| ((InternalCDOObject)object).cdoInternalSetRevision(null); |
| ((InternalCDOObject)object).cdoInternalSetView(null); |
| } |
| |
| private Set<CDOID> rollbackCompletely(CDOUserSavepoint savepoint) |
| { |
| Set<CDOID> idsOfNewObjectsWithDeltas = new HashSet<CDOID>(); |
| |
| // Start from the last savepoint and come back up to the active |
| for (InternalCDOSavepoint itrSavepoint = lastSavepoint; itrSavepoint != null; itrSavepoint = itrSavepoint |
| .getPreviousSavepoint()) |
| { |
| // Rollback new objects attached after the save point |
| Map<CDOID, CDOObject> newObjectsMap = itrSavepoint.getNewObjects(); |
| for (CDOID id : newObjectsMap.keySet()) |
| { |
| CDOObject object = newObjectsMap.get(id); |
| removeObject(id, 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)) |
| { |
| removeObject(id, reattachedObject); |
| } |
| } |
| |
| Map<CDOID, CDORevisionDelta> revisionDeltas = itrSavepoint.getRevisionDeltas(); |
| 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); |
| } |
| } |
| } |
| |
| 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); |
| } |
| } |
| } |
| |
| if (savepoint == itrSavepoint) |
| { |
| break; |
| } |
| } |
| |
| 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.cdoInternalSetID(revision.getID()); |
| 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.getRevisionDeltas().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(); |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| public synchronized void detachObject(InternalCDOObject object) |
| { |
| 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 = getLastSavepoint().getNewObjects(); |
| |
| // Determine if we added object |
| if (map.containsKey(id)) |
| { |
| map.remove(id); |
| } |
| else |
| { |
| getLastSavepoint().getDetachedObjects().put(id, object); |
| } |
| |
| // deregister object |
| deregisterObject(object); |
| } |
| else |
| { |
| getLastSavepoint().getDetachedObjects().put(id, object); |
| |
| if (!cleanRevisions.containsKey(object)) |
| { |
| cleanRevisions.put(object, object.cdoRevision()); |
| } |
| |
| // Object may have been reattached previously, in which case it must |
| // here be removed from the collection of reattached objects |
| lastSavepoint.getReattachedObjects().remove(id); |
| } |
| |
| if (!dirty) |
| { |
| dirty = true; |
| IListener[] listeners = getListeners(); |
| if (listeners != null) |
| { |
| fireEvent(new StartedEvent(), listeners); |
| } |
| } |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| public synchronized 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$ |
| } |
| |
| try |
| { |
| // Remember current revisions |
| Map<CDOObject, CDORevision> oldRevisions = new HashMap<CDOObject, CDORevision>(); |
| for (CDOObject object : getDirtyObjects().values()) |
| { |
| CDORevision oldRevision = object.cdoRevision(); |
| if (oldRevision != null) |
| { |
| oldRevisions.put(object, oldRevision); |
| } |
| } |
| |
| // Rollback objects |
| Set<CDOID> idsOfNewObjectWithDeltas = rollbackCompletely(savepoint); |
| |
| lastSavepoint = savepoint; |
| lastSavepoint.setNextSavepoint(null); |
| lastSavepoint.clear(); |
| |
| // Load from first savepoint up to current savepoint |
| loadSavepoint(lastSavepoint, idsOfNewObjectWithDeltas); |
| |
| if (lastSavepoint == firstSavepoint && options().isAutoReleaseLocksEnabled()) |
| { |
| // Unlock all objects |
| unlockObjects(null, null); |
| } |
| |
| // Send notifications |
| for (Entry<CDOObject, CDORevision> entry : oldRevisions.entrySet()) |
| { |
| InternalCDOObject object = (InternalCDOObject)entry.getKey(); |
| if (FSMUtil.isTransient(object)) |
| { |
| continue; |
| } |
| |
| InternalCDORevision oldRevision = (InternalCDORevision)entry.getValue(); |
| InternalCDORevision newRevision = object.cdoRevision(); |
| if (newRevision == null) |
| { |
| newRevision = getRevision(oldRevision.getID(), 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(); |
| } |
| } |
| } |
| } |
| |
| Map<CDOID, CDOID> idMappings = Collections.emptyMap(); |
| IListener[] listeners = getListeners(); |
| if (listeners != null) |
| { |
| fireEvent(new FinishedEvent(CDOTransactionFinishedEvent.Type.ROLLED_BACK, idMappings), 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); |
| } |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| public synchronized InternalCDOSavepoint handleSetSavepoint() |
| { |
| addToBase(lastSavepoint.getNewObjects()); |
| lastSavepoint = createSavepoint(lastSavepoint); |
| return lastSavepoint; |
| } |
| |
| private CDOSavepointImpl createSavepoint(InternalCDOSavepoint lastSavepoint) |
| { |
| return new CDOSavepointImpl(this, lastSavepoint); |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| public synchronized InternalCDOSavepoint setSavepoint() |
| { |
| checkActive(); |
| return (InternalCDOSavepoint)getTransactionStrategy().setSavepoint(this); |
| } |
| |
| 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$ |
| } |
| |
| public synchronized void registerAttached(InternalCDOObject object, boolean isNew) |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Registering new object {0}", object); //$NON-NLS-1$ |
| } |
| |
| 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); |
| } |
| } |
| |
| private void registerNewPackage(EPackage ePackage) |
| { |
| CDOPackageRegistry packageRegistry = getSession().getPackageRegistry(); |
| if (!packageRegistry.containsKey(ePackage.getNsURI())) |
| { |
| packageRegistry.putEPackage(ePackage); |
| } |
| } |
| |
| /** |
| * Receives notification for new and dirty objects |
| */ |
| public synchronized void registerFeatureDelta(InternalCDOObject object, CDOFeatureDelta featureDelta) |
| { |
| 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 (getLastSavepoint().getPreviousSavepoint() == null || featureDelta == null) |
| { |
| needToSaveFeatureDelta = false; |
| } |
| else |
| { |
| Map<CDOID, CDOObject> map = getLastSavepoint().getNewObjects(); |
| needToSaveFeatureDelta = !map.containsKey(id); |
| } |
| } |
| |
| if (needToSaveFeatureDelta) |
| { |
| CDORevisionDelta revisionDelta = lastSavepoint.getRevisionDeltas().get(id); |
| if (revisionDelta == null) |
| { |
| revisionDelta = CDORevisionUtil.createDelta(object.cdoRevision()); |
| lastSavepoint.getRevisionDeltas().put(id, revisionDelta); |
| } |
| |
| ((InternalCDORevisionDelta)revisionDelta).addFeatureDelta(featureDelta); |
| } |
| |
| CDOTransactionHandler1[] handlers = getTransactionHandlers1(); |
| for (int i = 0; i < handlers.length; i++) |
| { |
| CDOTransactionHandler1 handler = handlers[i]; |
| handler.modifyingObject(this, object, featureDelta); |
| } |
| } |
| |
| public synchronized void registerRevisionDelta(CDORevisionDelta revisionDelta) |
| { |
| lastSavepoint.getRevisionDeltas().putIfAbsent(revisionDelta.getID(), revisionDelta); |
| } |
| |
| public synchronized void registerDirty(InternalCDOObject object, CDOFeatureDelta featureDelta) |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Registering dirty object {0}", object); //$NON-NLS-1$ |
| } |
| |
| if (featureDelta != null) |
| { |
| registerFeatureDelta(object, featureDelta); |
| } |
| |
| 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$ |
| } |
| |
| if (!dirty) |
| { |
| dirty = true; |
| IListener[] listeners = getListeners(); |
| if (listeners != null) |
| { |
| fireEvent(new StartedEvent(), listeners); |
| } |
| } |
| } |
| |
| public synchronized List<CDOPackageUnit> analyzeNewPackages() |
| { |
| CDOPackageRegistry packageRegistry = getSession().getPackageRegistry(); |
| Set<EPackage> usedPackages = new HashSet<EPackage>(); |
| Set<EPackage> usedNewPackages = new HashSet<EPackage>(); |
| 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<CDOPackageUnit>(); |
| for (EPackage usedNewPackage : analyzeNewPackages(usedNewPackages, packageRegistry)) |
| { |
| CDOPackageUnit packageUnit = packageRegistry.getPackageUnit(usedNewPackage); |
| result.add(packageUnit); |
| } |
| |
| return new ArrayList<CDOPackageUnit>(result); |
| } |
| |
| return Collections.emptyList(); |
| } |
| |
| private static List<EPackage> analyzeNewPackages(Collection<EPackage> usedTopLevelPackages, |
| CDOPackageRegistry packageRegistry) |
| { |
| // Determine which of the corresdonding EPackages are new |
| List<EPackage> newPackages = new ArrayList<EPackage>(); |
| |
| 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()) |
| { |
| lastSavepoint = firstSavepoint; |
| firstSavepoint.clear(); |
| firstSavepoint.setNextSavepoint(null); |
| |
| cleanRevisions.clear(); |
| dirty = false; |
| conflict = 0; |
| idGenerator.reset(); |
| } |
| else |
| { |
| collapseSavepoints(commitContext); |
| |
| for (CDOObject object : commitContext.getDetachedObjects().values()) |
| { |
| cleanRevisions.remove(object); |
| } |
| |
| for (CDOObject object : commitContext.getDirtyObjects().values()) |
| { |
| cleanRevisions.remove(object); |
| } |
| } |
| |
| // Reset partial-commit filter |
| committables = null; |
| } |
| |
| 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.getRevisionDeltas()); |
| 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()); |
| } |
| } |
| } |
| |
| public synchronized CDOSavepoint[] exportChanges(OutputStream stream) throws IOException |
| { |
| CDODataOutput out = new CDODataOutputImpl(new ExtendedDataOutputStream(stream)) |
| { |
| @Override |
| public CDOIDProvider getIDProvider() |
| { |
| return CDOTransactionImpl.this; |
| } |
| |
| @Override |
| public CDOPackageRegistry getPackageRegistry() |
| { |
| return getSession().getPackageRegistry(); |
| } |
| }; |
| |
| List<CDOSavepoint> savepoints = new ArrayList<CDOSavepoint>(); |
| int totalNewObjects = 0; |
| |
| InternalCDOSavepoint savepoint = firstSavepoint; |
| while (savepoint != null) |
| { |
| Collection<CDOObject> newObjects = savepoint.getNewObjects().values(); |
| totalNewObjects += newObjects.size(); |
| |
| savepoint = savepoint.getNextSavepoint(); |
| } |
| |
| out.writeInt(totalNewObjects); |
| |
| savepoint = firstSavepoint; |
| while (savepoint != null) |
| { |
| Collection<CDOObject> newObjects = savepoint.getNewObjects().values(); |
| Collection<CDORevisionDelta> revisionDeltas = savepoint.getRevisionDeltas().values(); |
| if (newObjects.isEmpty() && revisionDeltas.isEmpty()) |
| { |
| savepoint = savepoint.getNextSavepoint(); |
| continue; |
| } |
| |
| savepoints.add(savepoint); |
| out.writeBoolean(true); |
| |
| out.writeInt(newObjects.size()); |
| for (CDOObject newObject : newObjects) |
| { |
| out.writeCDORevision(newObject.cdoRevision(), CDORevision.UNCHUNKED); |
| } |
| |
| out.writeInt(revisionDeltas.size()); |
| for (CDORevisionDelta revisionDelta : revisionDeltas) |
| { |
| out.writeCDORevisionDelta(revisionDelta); |
| } |
| |
| savepoint = savepoint.getNextSavepoint(); |
| } |
| |
| out.writeBoolean(false); |
| return savepoints.toArray(new CDOSavepoint[savepoints.size()]); |
| } |
| |
| public synchronized CDOSavepoint[] importChanges(InputStream stream, boolean reconstructSavepoints) |
| throws IOException |
| { |
| List<CDOSavepoint> savepoints = new ArrayList<CDOSavepoint>(); |
| if (stream.available() > 0) |
| { |
| CDODataInput in = new CDODataInputImpl(new ExtendedDataInputStream(stream)) |
| { |
| @Override |
| protected 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; |
| } |
| }; |
| |
| // Increase the internal tempID counter to prevent ID collisions during mapping |
| int totalNewObjects = in.readInt(); |
| for (int i = 0; i < totalNewObjects; i++) |
| { |
| createIDForNewObject(null); |
| } |
| |
| Map<CDOID, CDOID> idMappings = new HashMap<CDOID, CDOID>(); |
| while (in.readBoolean()) |
| { |
| if (reconstructSavepoints) |
| { |
| InternalCDOSavepoint savepoint = setSavepoint(); |
| savepoints.add(savepoint); |
| } |
| |
| // Import revisions and deltas |
| List<InternalCDORevision> revisions = new ArrayList<InternalCDORevision>(); |
| importNewRevisions(in, revisions, idMappings); |
| List<InternalCDORevisionDelta> revisionDeltas = importRevisionDeltas(in); |
| |
| // Re-map temp IDs |
| CDOIDMapper idMapper = new CDOIDMapper(idMappings); |
| for (InternalCDORevision revision : revisions) |
| { |
| revision.adjustReferences(idMapper); |
| } |
| |
| for (InternalCDORevisionDelta delta : revisionDeltas) |
| { |
| delta.adjustReferences(idMapper); |
| } |
| |
| // Create new objects |
| List<InternalCDOObject> newObjects = new ArrayList<InternalCDOObject>(); |
| 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()]); |
| } |
| |
| private void importNewRevisions(CDODataInput in, List<InternalCDORevision> revisions, Map<CDOID, CDOID> idMappings) |
| throws IOException |
| { |
| int size = in.readInt(); |
| 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.readInt(); |
| List<InternalCDORevisionDelta> deltas = new ArrayList<InternalCDORevisionDelta>(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.cdoInternalSetID(revision.getID()); |
| object.cdoInternalSetRevision(revision); |
| object.cdoInternalSetState(CDOState.NEW); |
| object.cdoInternalSetView(this); |
| return object; |
| } |
| |
| public synchronized Map<CDOID, CDOObject> getDirtyObjects() |
| { |
| checkActive(); |
| return lastSavepoint.getAllDirtyObjects(); |
| } |
| |
| public synchronized Map<CDOID, CDOObject> getNewObjects() |
| { |
| checkActive(); |
| return lastSavepoint.getAllNewObjects(); |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| public synchronized Map<CDOID, CDORevision> getBaseNewObjects() |
| { |
| checkActive(); |
| return lastSavepoint.getAllBaseNewObjects(); |
| } |
| |
| public synchronized Map<CDOID, CDORevisionDelta> getRevisionDeltas() |
| { |
| checkActive(); |
| return lastSavepoint.getAllRevisionDeltas(); |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| public synchronized Map<CDOID, CDOObject> getDetachedObjects() |
| { |
| checkActive(); |
| return lastSavepoint.getAllDetachedObjects(); |
| } |
| |
| @Override |
| protected synchronized CDOID getXRefTargetID(CDOObject target) |
| { |
| CDORevisionKey key = cleanRevisions.get(target); |
| if (key != null) |
| { |
| return key.getID(); |
| } |
| |
| return super.getXRefTargetID(target); |
| } |
| |
| @Override |
| protected synchronized CDOID getID(InternalCDOObject object, boolean onlyPersistedID) |
| { |
| CDOID id = super.getID(object, onlyPersistedID); |
| |
| // If super returned a good result, return immediately |
| 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()) |
| { |
| return null; |
| } |
| |
| // The super implementation will return null for a transient (unattached) object; |
| // but in a tx, an transient object may previously have been attached. So we consult |
| // the cleanRevisions if that's the case. |
| CDORevisionKey revKey = cleanRevisions.get(object); |
| if (revKey != null && getDetachedObjects().containsValue(object)) |
| { |
| id = revKey.getID(); |
| } |
| |
| return id; |
| } |
| |
| @Override |
| public synchronized CDOID provideCDOID(Object idOrObject) |
| { |
| try |
| { |
| providingCDOID.set(true); |
| return super.provideCDOID(idOrObject); |
| } |
| finally |
| { |
| providingCDOID.set(false); |
| } |
| } |
| |
| @Override |
| public synchronized CDOQueryImpl createQuery(String language, String queryString, Object context) |
| { |
| return createQuery(language, queryString, context, false); |
| } |
| |
| public synchronized CDOQueryImpl createQuery(String language, String queryString, boolean considerDirtyState) |
| { |
| return createQuery(language, queryString, null, considerDirtyState); |
| } |
| |
| public synchronized CDOQueryImpl createQuery(String language, String queryString, Object context, |
| boolean considerDirtyState) |
| { |
| CDOQueryImpl query = super.createQuery(language, queryString, context); |
| if (considerDirtyState && isDirty()) |
| { |
| query.setChangeSetData(getChangeSetData()); |
| } |
| |
| return query; |
| } |
| |
| @Override |
| protected void doActivate() throws Exception |
| { |
| super.doActivate(); |
| |
| InternalCDOSession session = getSession(); |
| if (session.getRepositoryInfo().getIDGenerationLocation() == IDGenerationLocation.STORE) |
| { |
| idGenerator = new TempIDGenerator(); |
| } |
| else |
| { |
| idGenerator = session.getIDGenerator(); |
| if (idGenerator == null) |
| { |
| idGenerator = CDOIDGenerator.UUID; |
| } |
| } |
| } |
| |
| /** |
| * @since 2.0 |
| */ |
| @Override |
| protected void doDeactivate() throws Exception |
| { |
| 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 |
| */ |
| /* |
| * Synchronized through InvlidationRunner.run() |
| */ |
| @Override |
| protected Map<CDOObject, Pair<CDORevision, CDORevisionDelta>> invalidate(long lastUpdateTime, |
| List<CDORevisionKey> allChangedObjects, List<CDOIDAndVersion> allDetachedObjects, List<CDORevisionDelta> deltas, |
| Map<CDOObject, CDORevisionDelta> revisionDeltas, Set<CDOObject> detachedObjects, |
| Map<CDOID, InternalCDORevision> oldRevisions) |
| { |
| if (!allDetachedObjects.isEmpty()) |
| { |
| Set<CDOID> referencedOIDs = new HashSet<CDOID>(); |
| for (CDOIDAndVersion key : allDetachedObjects) |
| { |
| referencedOIDs.add(key.getID()); |
| } |
| |
| Collection<CDOObject> cachedDirtyObjects = getDirtyObjects().values(); |
| removeCrossReferences(cachedDirtyObjects, referencedOIDs); |
| |
| Collection<CDOObject> cachedNewObjects = getNewObjects().values(); |
| removeCrossReferences(cachedNewObjects, referencedOIDs); |
| } |
| |
| // Bug 290032 - Sticky views |
| InternalCDOSession session = getSession(); |
| if (session.isSticky()) |
| { |
| session.clearCommittedSinceLastRefresh(); |
| } |
| |
| return super.invalidate(lastUpdateTime, allChangedObjects, allDetachedObjects, deltas, revisionDeltas, |
| detachedObjects, oldRevisions); |
| } |
| |
| private void removeCrossReferences(Collection<CDOObject> referencers, Set<CDOID> referencedOIDs) |
| { |
| List<Pair<Setting, EObject>> objectsToBeRemoved = new LinkedList<Pair<Setting, EObject>>(); |
| for (CDOObject referencer : referencers) |
| { |
| FeatureIterator<EObject> it = getChangeableCrossReferences(referencer); |
| while (it.hasNext()) |
| { |
| EObject referencedObject = it.next(); |
| CDOID referencedOID = CDOUtil.getCDOObject(referencedObject).cdoID(); |
| |
| if (referencedOIDs.contains(referencedOID)) |
| { |
| 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 && EMFUtil.isPersistent(reference)) |
| { |
| InternalCDORevision cleanRevision = cleanRevisions.get(referencer); |
| |
| Object value = cleanRevision.get(reference, EStore.NO_INDEX); |
| if (value instanceof CDOObject && value == referencedObject || // |
| value instanceof CDOID && value.equals(referencedOID) || // |
| value instanceof CDOList && ((CDOList)value).contains(referencedOID)) |
| { |
| continue; |
| } |
| } |
| |
| Setting setting = ((InternalEObject)referencer).eSetting(reference); |
| objectsToBeRemoved.add(new Pair<Setting, EObject>(setting, referencedObject)); |
| } |
| } |
| } |
| |
| for (Pair<Setting, EObject> pair : objectsToBeRemoved) |
| { |
| EcoreUtil.remove(pair.getElement1(), pair.getElement2()); |
| } |
| } |
| |
| private FeatureIterator<EObject> getChangeableCrossReferences(EObject object) |
| { |
| FeatureSubsetSupplier features = (FeatureSubsetSupplier)object.eClass().getEAllStructuralFeatures(); |
| EStructuralFeature[] crossReferences = features.crossReferences(); |
| if (crossReferences != null) |
| { |
| List<EStructuralFeature> changeableReferences = new ArrayList<EStructuralFeature>(); |
| 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 (FeatureIterator<EObject>)new ECrossReferenceEListDerived(object, collectedStructuralFeatures) |
| .iterator(); |
| } |
| } |
| |
| return (FeatureIterator<EObject>)ECrossReferenceEList.<EObject> emptyContentsEList().iterator(); |
| } |
| |
| public synchronized long getLastCommitTime() |
| { |
| return lastCommitTime; |
| } |
| |
| public synchronized String getCommitComment() |
| { |
| return commitComment; |
| } |
| |
| public synchronized void setCommitComment(String comment) |
| { |
| commitComment = comment; |
| } |
| |
| public synchronized void setCommittables(Set<? extends EObject> committables) |
| { |
| this.committables = committables; |
| } |
| |
| public synchronized Set<? extends EObject> getCommittables() |
| { |
| return committables; |
| } |
| |
| public synchronized Map<InternalCDOObject, InternalCDORevision> getCleanRevisions() |
| { |
| return cleanRevisions; |
| } |
| |
| @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 || object.cdoState() == CDOState.TRANSIENT) |
| { |
| 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, 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<CDOLockState>(); |
| for (CDOObject object : getNewObjects().values()) |
| { |
| Object lockTarget = getLockTarget(object); |
| CDOLockState lockState = CDOLockUtil.createLockState(lockTarget); |
| locksOnNewObjects.add(lockState); |
| } |
| |
| return locksOnNewObjects; |
| } |
| |
| private static Object getLockTarget(CDOObject object) |
| { |
| CDOView view = object.cdoView(); |
| if (view == null) |
| { |
| return null; |
| } |
| |
| CDOID id = object.cdoID(); |
| boolean branching = view.getSession().getRepositoryInfo().isSupportingBranches(); |
| if (branching) |
| { |
| return CDOIDUtil.createIDAndBranch(id, view.getBranch()); |
| } |
| |
| return id; |
| } |
| |
| private final class ResolvingRevisionMap extends HashMap<InternalCDOObject, InternalCDORevision> |
| { |
| private static final long serialVersionUID = 1L; |
| |
| public ResolvingRevisionMap() |
| { |
| } |
| |
| @Override |
| public InternalCDORevision get(Object cdoObject) |
| { |
| InternalCDORevision revision = super.get(cdoObject); |
| if (revision != null) |
| { |
| getSession().resolveAllElementProxies(revision); |
| } |
| |
| return revision; |
| } |
| } |
| |
| /** |
| * Generates {@link CDOIDTemp temporary} ID values. |
| * |
| * @author Eike Stepper |
| */ |
| private static final class TempIDGenerator implements CDOIDGenerator |
| { |
| private AtomicInteger lastTemporaryID = new AtomicInteger(); |
| |
| public TempIDGenerator() |
| { |
| } |
| |
| public CDOID generateCDOID(EObject object) |
| { |
| return CDOIDUtil.createTempObject(lastTemporaryID.incrementAndGet()); |
| } |
| |
| 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 Collection<CDOLockState> locksOnNewObjects; |
| |
| 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<ByteArrayWrapper, CDOLob<?>>(); |
| |
| public CDOCommitContextImpl(InternalCDOTransaction transaction) |
| { |
| this.transaction = transaction; |
| calculateCommitData(); |
| } |
| |
| private void calculateCommitData() |
| { |
| List<CDOPackageUnit> newPackageUnits = analyzeNewPackages(); |
| newObjects = filterCommittables(transaction.getNewObjects()); |
| List<CDOIDAndVersion> revisions = new ArrayList<CDOIDAndVersion>(newObjects.size()); |
| for (CDOObject newObject : newObjects.values()) |
| { |
| revisions.add(newObject.cdoRevision()); |
| } |
| |
| revisionDeltas = filterCommittables(transaction.getRevisionDeltas()); |
| List<CDORevisionKey> deltas = new ArrayList<CDORevisionKey>(revisionDeltas.size()); |
| for (CDORevisionDelta delta : revisionDeltas.values()) |
| { |
| deltas.add(delta); |
| } |
| |
| detachedObjects = filterCommittables(transaction.getDetachedObjects()); |
| boolean supportingBranches = transaction.getSession().getRepositoryInfo().isSupportingBranches(); |
| List<CDOIDAndVersion> detached = new ArrayList<CDOIDAndVersion>(detachedObjects.size()); |
| for (CDOID id : detachedObjects.keySet()) |
| { |
| // Add "version-less" key. |
| // CDOSessionImpl.reviseRevisions() will call reviseLatest() accordingly. |
| InternalCDORevision detachedRevision = getCleanRevisions().get(detachedObjects.get(id)); |
| |
| if (supportingBranches) |
| { |
| if (detachedRevision.getBranch().equals(getBranch())) |
| { |
| detached.add(CDOIDUtil.createIDAndVersion(id, detachedRevision.getVersion())); |
| } |
| else |
| { |
| // Branch for clean revision do not match to branch in which it was deleted. This can only happen |
| // when in current branch there was no version created |
| detached.add(CDOIDUtil.createIDAndVersion(id, CDORevision.UNSPECIFIED_VERSION)); |
| } |
| } |
| else |
| { |
| detached.add(CDOIDUtil.createIDAndVersion(id, detachedRevision.getVersion())); |
| } |
| } |
| |
| dirtyObjects = filterCommittables(transaction.getDirtyObjects()); |
| |
| CDOLockState[] locksOnNewObjectsArray = getLockStates(newObjects.keySet(), false); |
| locksOnNewObjects = Arrays.asList(locksOnNewObjectsArray); |
| |
| commitData = new CDOCommitDataImpl(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 = new HashMap<CDOID, T>(); |
| 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; |
| } |
| |
| public String getUserID() |
| { |
| return transaction.getSession().getUserID(); |
| } |
| |
| public int getViewID() |
| { |
| return transaction.getViewID(); |
| } |
| |
| public CDOBranch getBranch() |
| { |
| return transaction.getBranch(); |
| } |
| |
| public InternalCDOTransaction getTransaction() |
| { |
| return transaction; |
| } |
| |
| public boolean isPartialCommit() |
| { |
| return isPartialCommit; |
| } |
| |
| public boolean isAutoReleaseLocks() |
| { |
| return transaction.options().isAutoReleaseLocksEnabled(); |
| } |
| |
| public String getCommitComment() |
| { |
| return transaction.getCommitComment(); |
| } |
| |
| public CDOCommitData getCommitData() |
| { |
| return commitData; |
| } |
| |
| public Map<CDOID, CDOObject> getDirtyObjects() |
| { |
| return dirtyObjects; |
| } |
| |
| public Map<CDOID, CDOObject> getNewObjects() |
| { |
| return newObjects; |
| } |
| |
| public List<CDOPackageUnit> getNewPackageUnits() |
| { |
| return commitData.getNewPackageUnits(); |
| } |
| |
| public Collection<CDOLockState> getLocksOnNewObjects() |
| { |
| return locksOnNewObjects; |
| } |
| |
| public Map<CDOID, CDOObject> getDetachedObjects() |
| { |
| return detachedObjects; |
| } |
| |
| public Map<CDOID, CDORevisionDelta> getRevisionDeltas() |
| { |
| return revisionDeltas; |
| } |
| |
| public Collection<CDOLob<?>> getLobs() |
| { |
| return lobs.values(); |
| } |
| |
| 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); |
| } |
| } |
| } |
| |
| public void postCommit(CommitTransactionResult result) |
| { |
| try |
| { |
| InternalCDOSession session = getSession(); |
| long timeStamp = result.getTimeStamp(); |
| |
| if (result.getRollbackMessage() != null) |
| { |
| CDOCommitInfo commitInfo = new FailureCommitInfo(timeStamp, result.getPreviousTimeStamp()); |
| session.invalidate(commitInfo, transaction); |
| return; |
| } |
| |
| CDOBranch branch = result.getBranch(); |
| boolean branchChanged = !ObjectUtil.equals(branch, getBranch()); |
| if (branchChanged) |
| { |
| basicSetBranchPoint(branch.getHead()); |
| } |
| |
| for (CDOPackageUnit newPackageUnit : getNewPackageUnits()) |
| { |
| ((InternalCDOPackageUnit)newPackageUnit).setState(CDOPackageUnit.State.LOADED); |
| } |
| |
| postCommit(getNewObjects(), result); |
| postCommit(getDirtyObjects(), result); |
| |
| for (CDORevisionDelta delta : getRevisionDeltas().values()) |
| { |
| ((InternalCDORevisionDelta)delta).adjustReferences(result.getReferenceAdjuster()); |
| } |
| |
| for (CDOID id : getDetachedObjects().keySet()) |
| { |
| removeObject(id); |
| } |
| |
| CDOCommitInfo commitInfo = makeCommitInfo(timeStamp, result.getPreviousTimeStamp()); |
| session.invalidate(commitInfo, transaction); |
| |
| // Bug 290032 - Sticky views |
| if (session.isSticky()) |
| { |
| CDOBranchPoint commitBranchPoint = CDOBranchUtil.copyBranchPoint(result); |
| for (CDOObject object : getNewObjects().values()) // Note: keyset() does not work because ID mappings are |
| // not applied there! |
| { |
| session.setCommittedSinceLastRefresh(object.cdoID(), commitBranchPoint); |
| } |
| |
| for (CDOID id : getDirtyObjects().keySet()) |
| { |
| session.setCommittedSinceLastRefresh(id, commitBranchPoint); |
| } |
| |
| for (CDOID id : getDetachedObjects().keySet()) |
| { |
| session.setCommittedSinceLastRefresh(id, commitBranchPoint); |
| } |
| } |
| |
| 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); |
| |
| cleanUp(this); |
| Map<CDOID, CDOID> idMappings = result.getIDMappings(); |
| IListener[] listeners = getListeners(); |
| if (listeners != null) |
| { |
| if (branchChanged) |
| { |
| fireViewTargetChangedEvent(listeners); |
| } |
| |
| fireEvent(new FinishedEvent(CDOTransactionFinishedEvent.Type.COMMITTED, idMappings), listeners); |
| } |
| |
| CDOLockState[] newLockStates = result.getNewLockStates(); |
| if (newLockStates != null) |
| { |
| updateAndNotifyLockStates(Operation.UNLOCK, null, result.getTimeStamp(), newLockStates); |
| } |
| } |
| 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(); |
| |
| InternalCDOCommitInfoManager commitInfoManager = session.getCommitInfoManager(); |
| return commitInfoManager.createCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, commitData); |
| } |
| |
| private void preCommit(Map<CDOID, CDOObject> objects, Map<ByteArrayWrapper, CDOLob<?>> lobs) |
| { |
| if (!objects.isEmpty()) |
| { |
| boolean noLegacy = !isLegacyModeEnabled(); |
| for (CDOObject object : objects.values()) |
| { |
| if (noLegacy && object instanceof CDOObjectWrapper) |
| { |
| throw new LegacyModeNotEnabledException(); |
| } |
| |
| 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()) |
| { |
| for (CDOObject object : objects.values()) |
| { |
| CDOStateMachine.INSTANCE.commit((InternalCDOObject)object, result); |
| } |
| } |
| } |
| } |
| |
| /** |
| * @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 Type type; |
| |
| private Map<CDOID, CDOID> idMappings; |
| |
| private FinishedEvent(Type type, Map<CDOID, CDOID> idMappings) |
| { |
| this.type = type; |
| this.idMappings = idMappings; |
| } |
| |
| public Type getType() |
| { |
| return type; |
| } |
| |
| public Map<CDOID, CDOID> getIDMappings() |
| { |
| return idMappings; |
| } |
| |
| @Override |
| public String toString() |
| { |
| return MessageFormat.format("CDOTransactionFinishedEvent[source={0}, type={1}, idMappings={2}]", getSource(), //$NON-NLS-1$ |
| getType(), 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; |
| } |
| |
| public InternalCDOObject getConflictingObject() |
| { |
| return conflictingObject; |
| } |
| |
| 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 |
| * @since 2.0 |
| */ |
| protected final class OptionsImpl extends CDOViewImpl.OptionsImpl implements CDOTransaction.Options |
| { |
| private List<CDOConflictResolver> conflictResolvers = new ArrayList<CDOConflictResolver>(); |
| |
| private boolean autoReleaseLocksEnabled = true; |
| |
| public OptionsImpl() |
| { |
| } |
| |
| @Override |
| public CDOTransactionImpl getContainer() |
| { |
| return (CDOTransactionImpl)super.getContainer(); |
| } |
| |
| public CDOConflictResolver[] getConflictResolvers() |
| { |
| synchronized (CDOTransactionImpl.this) |
| { |
| return conflictResolvers.toArray(new CDOConflictResolver[conflictResolvers.size()]); |
| } |
| } |
| |
| public void setConflictResolvers(CDOConflictResolver[] resolvers) |
| { |
| synchronized (CDOTransactionImpl.this) |
| { |
| for (CDOConflictResolver resolver : conflictResolvers) |
| { |
| resolver.setTransaction(null); |
| } |
| |
| conflictResolvers.clear(); |
| |
| for (CDOConflictResolver resolver : resolvers) |
| { |
| validateResolver(resolver); |
| conflictResolvers.add(resolver); |
| } |
| } |
| |
| fireEvent(new ConflictResolversEventImpl()); |
| } |
| |
| public void addConflictResolver(CDOConflictResolver resolver) |
| { |
| IEvent event = null; |
| synchronized (CDOTransactionImpl.this) |
| { |
| validateResolver(resolver); |
| conflictResolvers.add(resolver); |
| event = new ConflictResolversEventImpl(); |
| } |
| |
| fireEvent(event); |
| } |
| |
| public void removeConflictResolver(CDOConflictResolver resolver) |
| { |
| IEvent event = null; |
| synchronized (CDOTransactionImpl.this) |
| { |
| if (conflictResolvers.remove(resolver)) |
| { |
| resolver.setTransaction(null); |
| event = new ConflictResolversEventImpl(); |
| } |
| } |
| |
| 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); |
| } |
| |
| public boolean isAutoReleaseLocksEnabled() |
| { |
| return autoReleaseLocksEnabled; |
| } |
| |
| public void setAutoReleaseLocksEnabled(boolean on) |
| { |
| IEvent event = null; |
| synchronized (CDOTransactionImpl.this) |
| { |
| if (autoReleaseLocksEnabled != on) |
| { |
| autoReleaseLocksEnabled = on; |
| event = new AutoReleaseLocksEventImpl(); |
| } |
| } |
| |
| fireEvent(event); |
| } |
| |
| /** |
| * @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 AutoReleaseLocksEventImpl extends OptionsEvent implements AutoReleaseLocksEvent |
| { |
| private static final long serialVersionUID = 1L; |
| |
| public AutoReleaseLocksEventImpl() |
| { |
| super(OptionsImpl.this); |
| } |
| } |
| } |
| |
| public static class ECrossReferenceEListDerived extends ECrossReferenceEList<EObject> |
| { |
| |
| public ECrossReferenceEListDerived(EObject eObject) |
| { |
| super(eObject); |
| } |
| |
| public ECrossReferenceEListDerived(EObject eObject, EStructuralFeature[] eStructuralFeatures) |
| { |
| super(eObject, eStructuralFeatures); |
| } |
| } |
| |
| } |