| /* |
| * Copyright (c) 2010-2015 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 |
| */ |
| package org.eclipse.emf.cdo.internal.workspace; |
| |
| 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.CDOBranchPoint; |
| import org.eclipse.emf.cdo.common.branch.CDOBranchPointRange; |
| import org.eclipse.emf.cdo.common.commit.CDOChangeSet; |
| import org.eclipse.emf.cdo.common.commit.CDOChangeSetData; |
| import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; |
| import org.eclipse.emf.cdo.common.id.CDOID; |
| import org.eclipse.emf.cdo.common.id.CDOIDGenerator; |
| import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; |
| import org.eclipse.emf.cdo.common.revision.CDORevision; |
| import org.eclipse.emf.cdo.common.revision.CDORevisionCache; |
| import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; |
| 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.CDORevisionDelta; |
| import org.eclipse.emf.cdo.common.util.CDOException; |
| import org.eclipse.emf.cdo.internal.server.Repository; |
| import org.eclipse.emf.cdo.net4j.CDONet4jSessionConfiguration; |
| import org.eclipse.emf.cdo.net4j.CDONet4jUtil; |
| import org.eclipse.emf.cdo.server.CDOServerUtil; |
| import org.eclipse.emf.cdo.server.IRepository.Props; |
| import org.eclipse.emf.cdo.server.ISession; |
| import org.eclipse.emf.cdo.server.IStore; |
| import org.eclipse.emf.cdo.server.IStoreAccessor; |
| import org.eclipse.emf.cdo.server.ITransaction; |
| import org.eclipse.emf.cdo.server.StoreThreadLocal; |
| import org.eclipse.emf.cdo.server.net4j.CDONet4jServerUtil; |
| import org.eclipse.emf.cdo.session.CDORepositoryInfo; |
| import org.eclipse.emf.cdo.session.CDOSessionConfiguration; |
| import org.eclipse.emf.cdo.session.CDOSessionConfigurationFactory; |
| import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil; |
| import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; |
| import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; |
| import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; |
| 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.DetachedCDORevision; |
| import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; |
| 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.spi.server.InternalRepository; |
| import org.eclipse.emf.cdo.spi.server.InternalStore; |
| import org.eclipse.emf.cdo.spi.workspace.InternalCDOWorkspace; |
| import org.eclipse.emf.cdo.spi.workspace.InternalCDOWorkspaceBase; |
| import org.eclipse.emf.cdo.transaction.CDOCommitContext; |
| import org.eclipse.emf.cdo.transaction.CDODefaultTransactionHandler1; |
| import org.eclipse.emf.cdo.transaction.CDODefaultTransactionHandler2; |
| import org.eclipse.emf.cdo.transaction.CDODefaultTransactionHandler3; |
| import org.eclipse.emf.cdo.transaction.CDOMerger; |
| import org.eclipse.emf.cdo.transaction.CDOTransaction; |
| import org.eclipse.emf.cdo.transaction.CDOTransactionFinishedEvent; |
| import org.eclipse.emf.cdo.util.CommitException; |
| import org.eclipse.emf.cdo.util.ConcurrentAccessException; |
| import org.eclipse.emf.cdo.util.ReadOnlyException; |
| import org.eclipse.emf.cdo.view.CDOView; |
| import org.eclipse.emf.cdo.workspace.CDOWorkspace; |
| |
| import org.eclipse.net4j.Net4jUtil; |
| import org.eclipse.net4j.jvm.IJVMAcceptor; |
| import org.eclipse.net4j.jvm.IJVMConnector; |
| import org.eclipse.net4j.jvm.JVMUtil; |
| import org.eclipse.net4j.signal.ISignalProtocol; |
| import org.eclipse.net4j.util.ObjectUtil; |
| import org.eclipse.net4j.util.StringUtil; |
| import org.eclipse.net4j.util.collection.Closeable; |
| import org.eclipse.net4j.util.container.ContainerUtil; |
| import org.eclipse.net4j.util.container.IManagedContainer; |
| import org.eclipse.net4j.util.event.Event; |
| import org.eclipse.net4j.util.event.IEvent; |
| import org.eclipse.net4j.util.event.IListener; |
| import org.eclipse.net4j.util.event.Notifier; |
| import org.eclipse.net4j.util.lifecycle.ILifecycle; |
| import org.eclipse.net4j.util.lifecycle.LifecycleEventAdapter; |
| import org.eclipse.net4j.util.lifecycle.LifecycleUtil; |
| import org.eclipse.net4j.util.om.monitor.Monitor; |
| import org.eclipse.net4j.util.om.monitor.OMMonitor; |
| import org.eclipse.net4j.util.properties.IPropertiesContainer; |
| import org.eclipse.net4j.util.registry.IRegistry; |
| |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| import org.eclipse.emf.spi.cdo.CDOSessionProtocol.RefreshSessionResult; |
| import org.eclipse.emf.spi.cdo.DefaultCDOMerger; |
| import org.eclipse.emf.spi.cdo.InternalCDOSession; |
| import org.eclipse.emf.spi.cdo.InternalCDOSessionConfiguration; |
| import org.eclipse.emf.spi.cdo.InternalCDOTransaction; |
| import org.eclipse.emf.spi.cdo.InternalCDOTransaction.ApplyChangeSetResult; |
| import org.eclipse.emf.spi.cdo.InternalCDOTransaction.ChangeSetOutdatedException; |
| import org.eclipse.emf.spi.cdo.InternalCDOView; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| /** |
| * @author Eike Stepper |
| */ |
| public class CDOWorkspaceImpl extends Notifier implements InternalCDOWorkspace |
| { |
| private static final String PROP_BRANCH_ID = "org.eclipse.emf.cdo.workspace.branchID"; //$NON-NLS-1$ |
| |
| private static final String PROP_BRANCH_PATH = "org.eclipse.emf.cdo.workspace.branchPath"; //$NON-NLS-1$ |
| |
| private static final String PROP_TIME_STAMP = "org.eclipse.emf.cdo.workspace.timeStamp"; //$NON-NLS-1$ |
| |
| private static final String PROP_FIXED = "org.eclipse.emf.cdo.workspace.fixed"; //$NON-NLS-1$ |
| |
| private IManagedContainer container; |
| |
| private InternalCDOWorkspaceBase base; |
| |
| private IDGenerationLocation idGenerationLocation; |
| |
| private CDOIDGenerator idGenerator; |
| |
| private InternalRepository localRepository; |
| |
| private CDOBranchPoint localRepositoryHead; |
| |
| private InternalCDOSession localSession; |
| |
| private CDOBranchPoint localSessionHead; |
| |
| private int branchID = NO_BRANCH_ID; |
| |
| private String branchPath; |
| |
| private long timeStamp; |
| |
| private boolean fixed; |
| |
| private boolean dirty; |
| |
| private CDOSessionConfigurationFactory remoteSessionConfigurationFactory; |
| |
| private Map<InternalCDOSession, Closeable> closeables = new ConcurrentHashMap<InternalCDOSession, Closeable>(); |
| |
| private Set<InternalCDOView> views = new HashSet<InternalCDOView>(); |
| |
| /** |
| * Checkout. |
| */ |
| public CDOWorkspaceImpl(String localRepositoryName, IStore local, IDGenerationLocation idGenerationLocation, |
| CDOIDGenerator idGenerator, InternalCDOWorkspaceBase base, CDOSessionConfigurationFactory remote, int branchID, |
| String branchPath, long timeStamp) |
| { |
| init(localRepositoryName, local, idGenerationLocation, idGenerator, base, remote); |
| |
| this.branchID = branchID; |
| this.branchPath = branchPath; |
| this.timeStamp = timeStamp; |
| fixed = timeStamp != CDOBranchPoint.UNSPECIFIED_DATE; |
| |
| checkout(); |
| saveProperties(); |
| } |
| |
| /** |
| * Open. |
| */ |
| public CDOWorkspaceImpl(String localRepositoryName, IStore local, IDGenerationLocation idGenerationLocation, |
| CDOIDGenerator idGenerator, InternalCDOWorkspaceBase base, CDOSessionConfigurationFactory remote) |
| { |
| init(localRepositoryName, local, idGenerationLocation, idGenerator, base, remote); |
| loadProperties(); |
| } |
| |
| protected void checkout() |
| { |
| final OMMonitor monitor = new Monitor(); |
| final IStoreAccessor.Raw accessor = getLocalWriter(null); |
| StoreThreadLocal.setAccessor(accessor); |
| |
| try |
| { |
| InternalCDOSession remoteSession = openRemoteSession(); |
| |
| try |
| { |
| CDOID rootResourceID = remoteSession.getRepositoryInfo().getRootResourceID(); |
| localRepository.setRootResourceID(rootResourceID); |
| |
| InternalCDOPackageRegistry localPackageRegistry = localRepository.getPackageRegistry(false); |
| InternalCDOPackageUnit[] remotePackageUnits = remoteSession.getPackageRegistry().getPackageUnits(true); |
| for (InternalCDOPackageUnit remotePackageUnit : remotePackageUnits) |
| { |
| InternalCDOPackageUnit localPackageUnit = remotePackageUnit.copy(); |
| localPackageRegistry.putPackageUnit(localPackageUnit); |
| } |
| |
| accessor.rawStore(remotePackageUnits, monitor); |
| |
| CDORevisionHandler handler = new CDORevisionHandler() |
| { |
| public boolean handleRevision(CDORevision remoteRevision) |
| { |
| InternalCDORevision repositoryRevision = (InternalCDORevision)remoteRevision; |
| |
| long commitTime = repositoryRevision.getTimeStamp(); |
| if (commitTime > timeStamp) |
| { |
| timeStamp = commitTime; |
| } |
| |
| repositoryRevision.setBranchPoint(localRepositoryHead); |
| accessor.rawStore(repositoryRevision, monitor); |
| |
| return true; |
| } |
| }; |
| |
| InternalCDOBranchManager branchManager = remoteSession.getBranchManager(); |
| CDOBranch branch; |
| if (branchID == NO_BRANCH_ID) |
| { |
| if (StringUtil.isEmpty(branchPath)) |
| { |
| branchPath = CDOBranch.MAIN_BRANCH_NAME; |
| } |
| |
| branch = branchManager.getBranch(branchPath); |
| branchID = branch.getID(); |
| } |
| else |
| { |
| branch = branchManager.getBranch(branchID); |
| branchPath = branch.getPathName(); |
| } |
| |
| remoteSession.getSessionProtocol().handleRevisions(null, branch, false, timeStamp, false, handler); |
| } |
| finally |
| { |
| closeRemoteSession(remoteSession); |
| } |
| |
| accessor.rawCommit(1, monitor); |
| } |
| finally |
| { |
| StoreThreadLocal.release(); |
| monitor.done(); |
| } |
| } |
| |
| protected void init(String localRepositoryName, IStore local, IDGenerationLocation idGenerationLocation, |
| CDOIDGenerator idGenerator, InternalCDOWorkspaceBase base, CDOSessionConfigurationFactory remote) |
| { |
| this.idGenerationLocation = idGenerationLocation; |
| this.idGenerator = idGenerator; |
| |
| container = createContainer(local); |
| remoteSessionConfigurationFactory = remote; |
| |
| try |
| { |
| localRepository = createLocalRepository(localRepositoryName, local); |
| localRepositoryHead = localRepository.getBranchManager().getMainBranch().getHead(); |
| |
| this.base = base; |
| this.base.init(this); |
| setDirtyFromBase(); |
| } |
| catch (RuntimeException ex) |
| { |
| close(); |
| throw ex; |
| } |
| catch (Error ex) |
| { |
| close(); |
| throw ex; |
| } |
| } |
| |
| private void setDirtyFromBase() |
| { |
| setDirty(!base.isEmpty()); |
| } |
| |
| public int getBranchID() |
| { |
| return branchID; |
| } |
| |
| public String getBranchPath() |
| { |
| return branchPath; |
| } |
| |
| public long getTimeStamp() |
| { |
| return timeStamp; |
| } |
| |
| public boolean isFixed() |
| { |
| return fixed; |
| } |
| |
| public boolean isDirty() |
| { |
| return dirty; |
| } |
| |
| protected void setDirty(boolean dirty) |
| { |
| if (this.dirty != dirty) |
| { |
| this.dirty = dirty; |
| fireEvent(new DirtyStateChangedEventImpl(this, dirty)); |
| } |
| } |
| |
| protected void clearBase() |
| { |
| base.clear(); |
| setDirty(false); |
| } |
| |
| public CDOState getState(Object object) |
| { |
| if (object instanceof CDOObject) |
| { |
| CDOObject cdoObject = (CDOObject)object; |
| CDOID id = cdoObject.cdoID(); |
| |
| if (base.containsID(id)) |
| { |
| if (base.isAddedObject(id)) |
| { |
| return CDOState.NEW; |
| } |
| |
| return CDOState.DIRTY; |
| } |
| |
| return CDOState.CLEAN; |
| } |
| |
| return null; |
| } |
| |
| public IDGenerationLocation getIDGenerationLocation() |
| { |
| return idGenerationLocation; |
| } |
| |
| public CDOIDGenerator getIDGenerator() |
| { |
| return idGenerator; |
| } |
| |
| public InternalCDOWorkspaceBase getBase() |
| { |
| return base; |
| } |
| |
| public InternalCDOView openView() |
| { |
| CDOView view = getLocalSession().openView(); |
| initView(view); |
| return (InternalCDOView)view; |
| } |
| |
| public InternalCDOView openView(ResourceSet resourceSet) |
| { |
| CDOView view = getLocalSession().openView(resourceSet); |
| initView(view); |
| return (InternalCDOView)view; |
| } |
| |
| public InternalCDOTransaction openTransaction() |
| { |
| CDOTransaction transaction = getLocalSession().openTransaction(); |
| initView(transaction); |
| initTransaction(transaction); |
| return (InternalCDOTransaction)transaction; |
| } |
| |
| public InternalCDOTransaction openTransaction(ResourceSet resourceSet) |
| { |
| CDOTransaction transaction = getLocalSession().openTransaction(resourceSet); |
| initView(transaction); |
| initTransaction(transaction); |
| return (InternalCDOTransaction)transaction; |
| } |
| |
| protected void initView(CDOView view) |
| { |
| setWorkspaceProperty(view); |
| |
| synchronized (views) |
| { |
| views.add((InternalCDOView)view); |
| } |
| |
| view.addListener(new ViewAdapter()); |
| |
| if (view instanceof CDOTransaction) |
| { |
| if (fixed) |
| { |
| throw new ReadOnlyException("Workspace is fixed"); |
| } |
| |
| if (idGenerationLocation != IDGenerationLocation.CLIENT) |
| { |
| CDOTransaction transaction = (CDOTransaction)view; |
| transaction.addTransactionHandler(new CDODefaultTransactionHandler1() |
| { |
| @Override |
| public void attachingObject(CDOTransaction transaction, CDOObject object) |
| { |
| throw new IllegalStateException("Attaching new objects is only supported for IDGenerationLocation.CLIENT"); |
| } |
| }); |
| } |
| } |
| } |
| |
| protected void initTransaction(CDOTransaction transaction) |
| { |
| transaction.addTransactionHandler(new CDODefaultTransactionHandler2() |
| { |
| @Override |
| public void committedTransaction(CDOTransaction transaction, CDOCommitContext commitContext) |
| { |
| Set<CDOID> changedIDs = new HashSet<CDOID>(); |
| |
| InternalCDOTransaction tx = (InternalCDOTransaction)transaction; |
| Set<CDOID> dirtyObjects = tx.getDirtyObjects().keySet(); |
| Set<CDOID> detachedObjects = tx.getDetachedObjects().keySet(); |
| |
| for (InternalCDORevision revision : tx.getCleanRevisions().values()) |
| { |
| CDOID id = revision.getID(); |
| changedIDs.add(id); |
| |
| boolean isDetached = detachedObjects.contains(id); |
| if (isDetached) |
| { |
| if (base.isAddedObject(id)) |
| { |
| base.registerAddedAndDetachedObject(revision); |
| } |
| else |
| { |
| base.registerChangedOrDetachedObject(revision); |
| } |
| } |
| else if (dirtyObjects.contains(id)) |
| { |
| base.registerChangedOrDetachedObject(revision); |
| } |
| } |
| |
| // Don't use keySet() because only the values() are ID-mapped! |
| for (CDOObject object : tx.getNewObjects().values()) |
| { |
| CDOID id = object.cdoID(); |
| base.registerAddedObject(id); |
| changedIDs.add(id); |
| } |
| |
| setDirtyFromBase(); |
| |
| fireEvent(new ObjectStatesChangedEventImpl(CDOWorkspaceImpl.this, changedIDs)); |
| } |
| }); |
| } |
| |
| private void setWorkspaceProperty(IPropertiesContainer container) |
| { |
| IRegistry<String, Object> properties = container.properties(); |
| properties.put("org.eclipse.emf.cdo.workspace.CDOWorkspace", this); |
| } |
| |
| public InternalCDOTransaction update(CDOMerger merger) |
| { |
| return merge(merger, branchPath); |
| } |
| |
| public InternalCDOTransaction merge(CDOMerger merger, String branchPath) |
| { |
| return merge(merger, branchPath, CDOBranchPoint.UNSPECIFIED_DATE); |
| } |
| |
| public InternalCDOTransaction merge(CDOMerger merger, String branchPath, long timeStamp) |
| { |
| return merge(merger, branchPath, CDOBranchPoint.UNSPECIFIED_DATE, false); |
| } |
| |
| private InternalCDOTransaction merge(CDOMerger merger, String branchPath, long timeStamp, boolean saveBranchPoint) |
| { |
| final InternalCDOSession remoteSession = openRemoteSession(); |
| |
| try |
| { |
| if (timeStamp == CDOBranchPoint.UNSPECIFIED_DATE) |
| { |
| timeStamp = remoteSession.getLastUpdateTime(); |
| } |
| |
| final long newTimeStamp = timeStamp; |
| |
| final InternalCDOBranchManager branchManager = remoteSession.getBranchManager(); |
| final CDOBranchPoint basePoint = branchManager.getBranch(this.branchPath).getPoint(this.timeStamp); |
| final CDOBranchPoint remotePoint = branchManager.getBranch(branchPath).getPoint(newTimeStamp); |
| |
| final CDOBranchPointRange range = CDOBranchUtil.createRange(basePoint, remotePoint); |
| |
| final CDOChangeSetData remoteData = remoteSession.getSessionProtocol().loadChangeSets(range)[0]; |
| final CDOChangeSetData localData = getLocalChanges(); |
| final CDOChangeSetData result = getMergeResult(merger, basePoint, remotePoint, localData, remoteData); |
| |
| final InternalCDOTransaction transaction = (InternalCDOTransaction)getLocalSession().openTransaction(); |
| initView(transaction); |
| |
| transaction.applyChangeSet(result, new BaseRevisionProvider(), this, null, false); |
| transaction.addTransactionHandler(new CDODefaultTransactionHandler3() |
| { |
| @Override |
| public void rolledBackTransaction(CDOTransaction transaction) |
| { |
| closeRemoteSession(remoteSession); |
| } |
| |
| @Override |
| public void committedTransaction(CDOTransaction transaction, CDOCommitContext commitContext, |
| CDOCommitInfo result) |
| { |
| try |
| { |
| Set<CDOID> affectedIDs = getAffectedIDs(commitContext, remoteData); |
| |
| CDORevisionProvider local = CDOWorkspaceImpl.this; |
| CDORevisionProvider remote = new ManagedRevisionProvider(remoteSession.getRevisionManager(), remotePoint); |
| |
| updateBase(affectedIDs, local, remote); |
| setTimeStamp(newTimeStamp); |
| |
| fireEvent(new ObjectStatesChangedEventImpl(CDOWorkspaceImpl.this, affectedIDs)); |
| } |
| finally |
| { |
| closeRemoteSession(remoteSession); |
| } |
| } |
| |
| private void updateBase(Set<CDOID> affectedIDs, CDORevisionProvider local, CDORevisionProvider remote) |
| { |
| for (CDOID id : affectedIDs) |
| { |
| CDORevision localRevision = getRevision(id, local); |
| CDORevision remoteRevision = getRevision(id, remote); |
| if (localRevision == null) |
| { |
| if (remoteRevision == null) |
| { |
| // Unchanged |
| base.deregisterObject(id); |
| } |
| else |
| { |
| // Detached |
| InternalCDORevision baseRevision = ((InternalCDORevision)remoteRevision).copy(); |
| baseRevision.setBranchPoint(localSessionHead); |
| |
| base.registerChangedOrDetachedObject(baseRevision); |
| } |
| } |
| else |
| { |
| if (remoteRevision == null) |
| { |
| // Added |
| base.registerAddedObject(id); |
| } |
| else |
| { |
| // Unchanged |
| base.deregisterObject(id); |
| |
| CDORevisionDelta delta = localRevision.compare(remoteRevision); |
| if (!delta.isEmpty()) |
| { |
| // Changed |
| InternalCDORevision baseRevision = ((InternalCDORevision)remoteRevision).copy(); |
| baseRevision.setBranchPoint(localSessionHead); |
| |
| base.registerChangedOrDetachedObject(baseRevision); |
| } |
| } |
| } |
| } |
| } |
| |
| private Set<CDOID> getAffectedIDs(CDOCommitContext commitContext, final CDOChangeSetData remoteData) |
| { |
| Set<CDOID> affectedIDs = new HashSet<CDOID>(); |
| |
| // Base IDs |
| affectedIDs.addAll(base.getIDs()); |
| |
| // Remote IDs |
| affectedIDs.addAll(remoteData.getChangeKinds().keySet()); |
| |
| // Local IDs |
| affectedIDs.addAll(commitContext.getNewObjects().keySet()); |
| affectedIDs.addAll(commitContext.getDirtyObjects().keySet()); |
| affectedIDs.addAll(commitContext.getDetachedObjects().keySet()); |
| |
| return affectedIDs; |
| } |
| |
| private CDORevision getRevision(CDOID id, CDORevisionProvider revisionProvider) |
| { |
| CDORevision revision = revisionProvider.getRevision(id); |
| if (revision instanceof DetachedCDORevision) |
| { |
| revision = null; |
| } |
| |
| return revision; |
| } |
| }); |
| |
| if (saveBranchPoint) |
| { |
| CDOBranch branch = remotePoint.getBranch(); |
| branchID = branch.getID(); |
| this.branchPath = branch.getPathName(); |
| this.timeStamp = timeStamp; |
| saveProperties(); |
| } |
| |
| return transaction; |
| } |
| catch (RuntimeException ex) |
| { |
| closeRemoteSession(remoteSession); |
| throw ex; |
| } |
| catch (Error ex) |
| { |
| closeRemoteSession(remoteSession); |
| throw ex; |
| } |
| } |
| |
| private CDOChangeSetData getMergeResult(CDOMerger merger, CDOBranchPoint basePoint, CDOBranchPoint remotePoint, |
| CDOChangeSetData localData, CDOChangeSetData remoteData) |
| { |
| if (localData.isEmpty()) |
| { |
| return remoteData; |
| } |
| |
| CDOChangeSet localChanges = CDORevisionUtil.createChangeSet(basePoint, null, localData); |
| CDOChangeSet remoteChanges = CDORevisionUtil.createChangeSet(basePoint, remotePoint, remoteData); |
| return merger.merge(localChanges, remoteChanges); |
| } |
| |
| public void revert() |
| { |
| CDOChangeSetData revertData = getLocalChanges(false); |
| revert(revertData); |
| } |
| |
| public void revert(CDOChangeSetData revertData) |
| { |
| final CDOBranch localBranch = localSessionHead.getBranch(); |
| |
| IStoreAccessor.Raw accessor = getLocalWriter(null); |
| StoreThreadLocal.setAccessor(accessor); |
| |
| final List<InternalCDORevision> changedRevisions = new ArrayList<InternalCDORevision>(); |
| for (CDORevisionKey key : revertData.getChangedObjects()) |
| { |
| CDOID id = key.getID(); |
| |
| InternalCDORevision localRevision = (InternalCDORevision)getRevision(id); |
| InternalCDORevision baseRevision = (InternalCDORevision)base.getRevision(id); |
| changedRevisions.add(baseRevision); |
| |
| EClass eClass = baseRevision.getEClass(); |
| int version = Math.abs(localRevision.getVersion()); |
| |
| for (int v = baseRevision.getVersion(); v <= version; v++) |
| { |
| accessor.rawDelete(id, v, localBranch, eClass, new Monitor()); |
| } |
| |
| // Base revisions have the correct branch point and can directly be stored in the local repository. |
| accessor.rawStore(baseRevision, new Monitor()); |
| } |
| |
| final List<CDORevisionKey> detachedRevisions = new ArrayList<CDORevisionKey>(); |
| for (CDOIDAndVersion key : revertData.getDetachedObjects()) |
| { |
| CDOID id = key.getID(); |
| int version = getRevision(id).getVersion(); |
| |
| for (int v = 1; v <= version; v++) |
| { |
| accessor.rawDelete(id, v, localBranch, null, new Monitor()); |
| } |
| |
| detachedRevisions.add(CDORevisionUtil.createRevisionKey(id, localBranch, version)); |
| } |
| |
| for (CDOIDAndVersion key : revertData.getNewObjects()) |
| { |
| CDOID id = key.getID(); |
| |
| SyntheticCDORevision[] synthetics = { null }; |
| InternalCDORevisionManager revisionManager = localSession.getRevisionManager(); |
| revisionManager.getRevision(id, localSessionHead, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true, |
| synthetics); |
| |
| int max = synthetics[0].getVersion(); |
| EClass eClass = synthetics[0].getEClass(); |
| |
| InternalCDORevision baseRevision = (InternalCDORevision)base.getRevision(id); |
| for (int v = baseRevision.getVersion(); v <= max; v++) |
| { |
| int version = v == max ? -max : v; |
| accessor.rawDelete(id, version, localBranch, eClass, new Monitor()); |
| } |
| |
| // Base revisions have the correct branch point and can directly be stored in the local repository. |
| accessor.rawStore(baseRevision, new Monitor()); |
| } |
| |
| base.deleteAddedAndDetachedObjects(accessor, localBranch); |
| finishRawAccess(accessor); |
| base.clear(); |
| |
| localSession.refresh(new RefreshSessionResult.Provider() |
| { |
| public RefreshSessionResult getRefreshSessionResult(Map<CDOBranch, List<InternalCDOView>> views, |
| Map<CDOBranch, Map<CDOID, InternalCDORevision>> viewedRevisions) |
| { |
| RefreshSessionResult result = new RefreshSessionResult(timeStamp); |
| |
| Map<CDOID, InternalCDORevision> revisions = viewedRevisions.get(localBranch); |
| if (revisions != null) |
| { |
| for (InternalCDORevision baseRevision : changedRevisions) |
| { |
| CDOID id = baseRevision.getID(); |
| if (revisions.containsKey(id)) |
| { |
| InternalCDORevision newRevision = baseRevision.copy(); |
| newRevision.setBranchPoint(localSessionHead); |
| |
| result.addChangedObject(newRevision); |
| } |
| } |
| } |
| |
| for (CDORevisionKey key : detachedRevisions) |
| { |
| result.addDetachedObject(key); |
| } |
| |
| return result; |
| } |
| }); |
| |
| setDirty(false); |
| } |
| |
| public void replace(String branchPath, long timeStamp) |
| { |
| CDOChangeSetData revertData = getLocalChanges(false); |
| replace(branchPath, timeStamp, revertData); |
| } |
| |
| public void replace(String branchPath, long timeStamp, CDOChangeSetData revertData) |
| { |
| revert(revertData); |
| CDOTransaction transaction = merge(new DefaultCDOMerger.PerFeature.ManyValued(), branchPath, timeStamp, true); |
| |
| try |
| { |
| transaction.commit(); |
| } |
| catch (ConcurrentAccessException ex) |
| { |
| throw new CDOException(ex); |
| } |
| catch (CommitException ex) |
| { |
| throw new CDOException(ex); |
| } |
| finally |
| { |
| transaction.close(); |
| } |
| } |
| |
| public CDOCommitInfo checkin() throws CommitException |
| { |
| return checkin(null); |
| } |
| |
| public CDOCommitInfo checkin(String comment) throws CommitException |
| { |
| Set<CDOID> ids = null; |
| InternalCDOSession remoteSession = openRemoteSession(); |
| |
| try |
| { |
| InternalCDOBranch remoteBranch = remoteSession.getBranchManager().getBranch(branchPath); |
| InternalCDOTransaction remoteTransaction = (InternalCDOTransaction)remoteSession.openTransaction(remoteBranch); |
| |
| try |
| { |
| CDOChangeSetData changes = getLocalChanges(); |
| |
| try |
| { |
| ApplyChangeSetResult result = remoteTransaction.applyChangeSet(changes, base, this, localSessionHead, true); |
| if (!result.getIDMappings().isEmpty()) |
| { |
| throw new IllegalStateException("Attaching new objects is only supported for IDGenerationLocation.CLIENT"); |
| } |
| } |
| catch (ChangeSetOutdatedException ex) |
| { |
| throw new CommitException(ex); |
| } |
| |
| remoteTransaction.setCommitComment(comment); |
| CDOCommitInfo info = remoteTransaction.commit(); |
| |
| ids = checkinPostProcessing(remoteTransaction, info.getChangedObjects()); |
| |
| clearBase(); |
| setTimeStamp(info.getTimeStamp()); |
| |
| for (CDOIDAndVersion key : changes.getNewObjects()) |
| { |
| ids.add(key.getID()); |
| } |
| |
| return info; |
| } |
| finally |
| { |
| remoteTransaction.close(); |
| } |
| } |
| finally |
| { |
| closeRemoteSession(remoteSession); |
| |
| if (ids != null) |
| { |
| fireEvent(new ObjectStatesChangedEventImpl(this, ids)); |
| } |
| } |
| } |
| |
| protected Set<CDOID> checkinPostProcessing(InternalCDOTransaction remoteTransaction, |
| List<CDORevisionKey> changedObjects) |
| { |
| Set<CDOID> ids = new HashSet<CDOID>(); |
| IStoreAccessor.Raw accessor = null; |
| |
| try |
| { |
| for (CDORevisionKey key : changedObjects) |
| { |
| CDOID id = key.getID(); |
| ids.add(id); |
| |
| InternalCDORevision localSessionRevision = (InternalCDORevision)getRevision(id); |
| CDOBranch localSessionBranch = localSessionHead.getBranch(); |
| |
| CDORevision baseRevision = base.getRevision(id); |
| CDORevision remoteRevision = remoteTransaction.getObject(id).cdoRevision(); |
| |
| EClass eClass = localSessionRevision.getEClass(); |
| |
| for (int v = baseRevision.getVersion(); v < localSessionRevision.getVersion(); v++) |
| { |
| if (accessor == null) |
| { |
| accessor = getLocalWriter(null); |
| StoreThreadLocal.setAccessor(accessor); |
| } |
| |
| accessor.rawDelete(id, v, localSessionBranch, eClass, new Monitor()); |
| } |
| |
| if (localSessionRevision.getVersion() != remoteRevision.getVersion()) |
| { |
| if (accessor == null) |
| { |
| accessor = getLocalWriter(null); |
| StoreThreadLocal.setAccessor(accessor); |
| } |
| |
| accessor.rawDelete(id, localSessionRevision.getVersion(), localSessionBranch, eClass, new Monitor()); |
| |
| // Local session revisions are not cached and can directly be modified and stored in the local repository. |
| localSessionRevision.setBranchPoint(localRepositoryHead); |
| localSessionRevision.setVersion(remoteRevision.getVersion()); |
| |
| accessor.rawStore(localSessionRevision, new Monitor()); |
| } |
| } |
| } |
| finally |
| { |
| finishRawAccess(accessor); |
| } |
| |
| return ids; |
| } |
| |
| private void finishRawAccess(IStoreAccessor.Raw accessor) |
| { |
| if (accessor != null) |
| { |
| accessor.rawCommit(1, new Monitor()); |
| StoreThreadLocal.release(); |
| |
| localRepository.getRevisionManager().getCache().clear(); |
| } |
| } |
| |
| /** |
| * @deprecated Attaching new objects is only supported for IDGenerationLocation.CLIENT |
| */ |
| @Deprecated |
| protected CDOIDMapper getIDMapper(InternalCDOTransaction transaction, final Map<CDOID, CDOID> idMappings) |
| { |
| if (idMappings.isEmpty()) |
| { |
| return null; |
| } |
| |
| transaction.addListener(new IListener() |
| { |
| public void notifyEvent(IEvent event) |
| { |
| if (event instanceof CDOTransactionFinishedEvent) |
| { |
| CDOTransactionFinishedEvent e = (CDOTransactionFinishedEvent)event; |
| Map<CDOID, CDOID> remoteMappings = e.getIDMappings(); |
| for (Entry<CDOID, CDOID> entry : idMappings.entrySet()) |
| { |
| CDOID tempID = entry.getValue(); |
| CDOID newID = remoteMappings.get(tempID); |
| entry.setValue(newID); |
| } |
| } |
| } |
| }); |
| |
| return new CDOIDMapper(idMappings); |
| } |
| |
| /** |
| * @deprecated Attaching new objects is only supported for IDGenerationLocation.CLIENT |
| */ |
| @Deprecated |
| protected void adjustLocalIDs(CDOIDMapper idMapper, List<CDOID> adjustedObjects) |
| { |
| Map<CDOID, CDOID> idMappings = idMapper.getIDMappings(); |
| if (!idMappings.isEmpty()) |
| { |
| CDOTransaction transaction = null; |
| OMMonitor monitor = new Monitor(); |
| |
| try |
| { |
| transaction = localSession.openTransaction(); |
| ISession repoSession = localRepository.getSessionManager().getSession(localSession.getSessionID()); |
| ITransaction repoTransaction = (ITransaction)repoSession.getView(transaction.getViewID()); |
| |
| IStoreAccessor.Raw accessor = getLocalWriter(repoTransaction); |
| StoreThreadLocal.setAccessor(accessor); |
| |
| monitor.begin(idMappings.size() * 2 + adjustedObjects.size() * 2 + 10); |
| |
| for (Entry<CDOID, CDOID> entry : idMappings.entrySet()) |
| { |
| CDOID id = entry.getKey(); |
| |
| InternalCDORevision revision = accessor.readRevision(id, localSessionHead, CDORevision.UNCHUNKED, null); |
| int version = revision.getVersion(); |
| CDOBranch branch = revision.getBranch(); |
| EClass eClass = revision.getEClass(); |
| |
| CDOID newID = entry.getValue(); |
| revision.setID(newID); |
| revision.setVersion(CDORevision.FIRST_VERSION); |
| |
| accessor.rawDelete(id, version, branch, eClass, monitor.fork()); |
| revision.adjustReferences(idMapper); |
| accessor.rawStore(revision, monitor.fork()); |
| } |
| |
| for (CDOID id : adjustedObjects) |
| { |
| InternalCDORevision revision = accessor.readRevision(id, localSessionHead, CDORevision.UNCHUNKED, null); |
| int version = revision.getVersion(); |
| CDOBranch branch = revision.getBranch(); |
| EClass eClass = revision.getEClass(); |
| |
| // TODO DBStoreAccessor.rawDelete() creates a new row with -(version+1)!!! |
| accessor.rawDelete(id, version, branch, eClass, monitor.fork()); |
| revision.adjustReferences(idMapper); |
| accessor.rawStore(revision, monitor.fork()); |
| } |
| |
| accessor.rawCommit(1, monitor.fork(10)); |
| } |
| finally |
| { |
| monitor.done(); |
| StoreThreadLocal.release(); |
| |
| if (transaction != null) |
| { |
| transaction.close(); |
| } |
| } |
| } |
| } |
| |
| public CDOChangeSetData compare(String branchPath) |
| { |
| return compare(branchPath, CDOBranchPoint.UNSPECIFIED_DATE); |
| } |
| |
| public CDOChangeSetData compare(String branchPath, long timeStamp) |
| { |
| // TODO: implement CDOWorkspaceImpl.compare(branchPath, timeStamp) |
| throw new UnsupportedOperationException(); |
| } |
| |
| public synchronized void close() |
| { |
| LifecycleUtil.deactivate(localSession); |
| localSession = null; |
| localSessionHead = null; |
| |
| LifecycleUtil.deactivate(localRepository); |
| localRepository = null; |
| localRepositoryHead = null; |
| |
| LifecycleUtil.deactivate(container); |
| container = null; |
| } |
| |
| public synchronized boolean isClosed() |
| { |
| return container == null; |
| } |
| |
| public CDORevision getRevision(CDOID id) |
| { |
| InternalCDOSession session = getLocalSession(); |
| CDORevisionManager revisionManager = session.getRevisionManager(); |
| return revisionManager.getRevision(id, localSessionHead, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true); |
| } |
| |
| public InternalRepository getLocalRepository() |
| { |
| return localRepository; |
| } |
| |
| public synchronized InternalCDOSession getLocalSession() |
| { |
| if (localSession == null) |
| { |
| localSession = openLocalSession(); |
| } |
| |
| return localSession; |
| } |
| |
| public CDOChangeSetData getLocalChanges() |
| { |
| return getLocalChanges(true); |
| } |
| |
| public CDOChangeSetData getLocalChanges(boolean forward) |
| { |
| Set<CDOID> ids = base.getIDs(); |
| |
| if (forward) |
| { |
| return CDORevisionUtil.createChangeSetData(ids, base, this, true); |
| } |
| |
| return CDORevisionUtil.createChangeSetData(ids, this, base, true); |
| } |
| |
| public CDOSessionConfigurationFactory getRemoteSessionConfigurationFactory() |
| { |
| return remoteSessionConfigurationFactory; |
| } |
| |
| public IManagedContainer getContainer() |
| { |
| return container; |
| } |
| |
| protected IManagedContainer createContainer(IStore local) |
| { |
| IManagedContainer container = ContainerUtil.createContainer(); |
| Net4jUtil.prepareContainer(container); |
| JVMUtil.prepareContainer(container); |
| CDONet4jServerUtil.prepareContainer(container); |
| container.activate(); |
| return container; |
| } |
| |
| protected String getLocalAcceptorName() |
| { |
| return "acceptor-for-" + localRepository.getUUID(); |
| } |
| |
| protected IJVMAcceptor getLocalAcceptor() |
| { |
| String localAcceptorName = getLocalAcceptorName(); |
| return JVMUtil.getAcceptor(container, localAcceptorName); |
| } |
| |
| protected IJVMConnector getLocalConnector() |
| { |
| String localAcceptorName = getLocalAcceptorName(); |
| return JVMUtil.getConnector(container, localAcceptorName); |
| } |
| |
| protected IStoreAccessor.Raw getLocalWriter(ITransaction transaction) |
| { |
| return (IStoreAccessor.Raw)localRepository.getStore().getWriter(transaction); |
| } |
| |
| protected InternalRepository createLocalRepository(String localRepositoryName, IStore store) |
| { |
| Map<String, String> props = new HashMap<String, String>(); |
| props.put(Props.OVERRIDE_UUID, ""); // UUID := name !!! |
| props.put(Props.SUPPORTING_AUDITS, "false"); |
| props.put(Props.SUPPORTING_BRANCHES, "false"); |
| props.put(Props.ID_GENERATION_LOCATION, idGenerationLocation.toString()); |
| |
| Repository repository = new Repository.Default() |
| { |
| @Override |
| public void initMainBranch(InternalCDOBranchManager branchManager, long timeStamp) |
| { |
| if (idGenerationLocation == IDGenerationLocation.STORE) |
| { |
| // Mark the main branch local so that new objects get local IDs |
| branchManager.initMainBranch(true, timeStamp); |
| } |
| else |
| { |
| super.initMainBranch(branchManager, timeStamp); |
| } |
| } |
| |
| @Override |
| protected void initRootResource() |
| { |
| // Don't create the root resource as it will be checked out |
| setState(State.INITIAL); |
| } |
| }; |
| |
| repository.setName(localRepositoryName); |
| repository.setStore((InternalStore)store); |
| repository.setProperties(props); |
| |
| CDOServerUtil.addRepository(container, repository); |
| return repository; |
| } |
| |
| protected InternalCDOSession openLocalSession() |
| { |
| getLocalAcceptor(); |
| |
| IJVMConnector connector = getLocalConnector(); |
| String repositoryName = localRepository.getName(); |
| |
| CDONet4jSessionConfiguration configuration = CDONet4jUtil.createNet4jSessionConfiguration(); |
| configuration.setConnector(connector); |
| configuration.setRepositoryName(repositoryName); |
| configuration.setIDGenerator(idGenerator); |
| |
| // Use no revision cache. |
| // This is important because session revisions are modified and copied into the repository. |
| configuration.setRevisionManager(CDORevisionUtil.createRevisionManager(CDORevisionCache.NOOP)); |
| |
| if (idGenerationLocation == IDGenerationLocation.STORE) |
| { |
| ((InternalCDOSessionConfiguration)configuration).setMainBranchLocal(true); |
| } |
| |
| InternalCDOSession session = (InternalCDOSession)configuration.openNet4jSession(); |
| session.setPackageRegistry(localRepository.getPackageRegistry(false)); // Use repo's registry |
| setWorkspaceProperty(session); |
| |
| ISignalProtocol<?> protocol = (ISignalProtocol<?>)session.getSessionProtocol(); |
| protocol.setTimeout(ISignalProtocol.NO_TIMEOUT); |
| |
| localSessionHead = session.getBranchManager().getMainBranch().getHead(); |
| return session; |
| } |
| |
| public InternalCDOView[] getViews() |
| { |
| synchronized (views) |
| { |
| return views.toArray(new InternalCDOView[views.size()]); |
| } |
| } |
| |
| protected InternalCDOSession openRemoteSession() |
| { |
| CDOSessionConfiguration configuration = remoteSessionConfigurationFactory.createSessionConfiguration(); |
| InternalCDOSession remoteSession = (InternalCDOSession)configuration.openSession(); |
| |
| if (configuration instanceof Closeable) |
| { |
| Closeable closeable = (Closeable)configuration; |
| closeables.put(remoteSession, closeable); |
| } |
| |
| CDORepositoryInfo repositoryInfo = remoteSession.getRepositoryInfo(); |
| if (!repositoryInfo.isSupportingAudits()) |
| { |
| remoteSession.close(); |
| throw new IllegalStateException("Remote repository does not support auditing"); |
| } |
| |
| IDGenerationLocation remoteLocation = repositoryInfo.getIDGenerationLocation(); |
| if (!remoteLocation.equals(idGenerationLocation)) |
| { |
| remoteSession.close(); |
| throw new IllegalStateException("Remote repository uses different ID generation location: " + remoteLocation); |
| } |
| |
| InternalCDOBranchManager branchManager = remoteSession.getBranchManager(); |
| InternalCDOBranch branch; |
| boolean changed = false; |
| |
| if (branchID == NO_BRANCH_ID) |
| { |
| branch = branchManager.getBranch(branchPath); |
| branchID = branch.getID(); |
| changed = true; |
| } |
| else |
| { |
| branch = branchManager.getBranch(branchID); |
| } |
| |
| String pathName = branch.getPathName(); |
| if (!ObjectUtil.equals(pathName, branchPath)) |
| { |
| branchPath = pathName; |
| changed = true; |
| } |
| |
| if (changed) |
| { |
| saveProperties(); |
| } |
| |
| return remoteSession; |
| } |
| |
| protected void closeRemoteSession(InternalCDOSession remoteSession) |
| { |
| Closeable closeable = closeables.remove(remoteSession); |
| if (closeable != null) |
| { |
| closeable.close(); |
| } |
| else |
| { |
| LifecycleUtil.deactivate(remoteSession); |
| } |
| } |
| |
| protected void setTimeStamp(long timeStamp) |
| { |
| Map<String, String> props = new HashMap<String, String>(); |
| props.put(PROP_TIME_STAMP, String.valueOf(timeStamp)); |
| localRepository.getStore().setPersistentProperties(props); |
| |
| this.timeStamp = timeStamp; |
| } |
| |
| protected void saveProperties() |
| { |
| Map<String, String> props = new HashMap<String, String>(); |
| props.put(PROP_BRANCH_ID, String.valueOf(branchID)); |
| props.put(PROP_BRANCH_PATH, branchPath); |
| props.put(PROP_TIME_STAMP, String.valueOf(timeStamp)); |
| props.put(PROP_FIXED, String.valueOf(fixed)); |
| localRepository.getStore().setPersistentProperties(props); |
| } |
| |
| protected void loadProperties() |
| { |
| Set<String> names = new HashSet<String>( |
| Arrays.asList(PROP_BRANCH_ID, PROP_BRANCH_PATH, PROP_TIME_STAMP, PROP_FIXED)); |
| Map<String, String> props = localRepository.getStore().getPersistentProperties(names); |
| String prop = props.get(PROP_BRANCH_ID); |
| branchID = prop == null ? InternalCDOWorkspace.NO_BRANCH_ID : Integer.parseInt(prop); |
| branchPath = props.get(PROP_BRANCH_PATH); |
| timeStamp = Long.parseLong(props.get(PROP_TIME_STAMP)); |
| fixed = Boolean.parseBoolean(props.get(PROP_FIXED)); |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| private class BaseRevisionProvider implements CDORevisionProvider |
| { |
| public CDORevision getRevision(CDOID id) |
| { |
| CDORevision revision = base.getRevision(id); |
| if (revision == null) |
| { |
| revision = CDOWorkspaceImpl.this.getRevision(id); |
| } |
| |
| return revision; |
| } |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| public final class ViewAdapter extends LifecycleEventAdapter |
| { |
| public ViewAdapter() |
| { |
| } |
| |
| public CDOWorkspace getWorkspace() |
| { |
| return CDOWorkspaceImpl.this; |
| } |
| |
| @Override |
| protected void onDeactivated(ILifecycle view) |
| { |
| synchronized (views) |
| { |
| views.remove(view); |
| } |
| } |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| private static final class DirtyStateChangedEventImpl extends Event implements DirtyStateChangedEvent |
| { |
| private static final long serialVersionUID = 1L; |
| |
| private final boolean dirty; |
| |
| public DirtyStateChangedEventImpl(CDOWorkspace workspace, boolean dirty) |
| { |
| super(workspace); |
| this.dirty = dirty; |
| } |
| |
| @Override |
| public CDOWorkspace getSource() |
| { |
| return (CDOWorkspace)super.getSource(); |
| } |
| |
| public boolean isDirty() |
| { |
| return dirty; |
| } |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| private static final class ObjectStatesChangedEventImpl extends Event implements ObjectStatesChangedEvent |
| { |
| private static final long serialVersionUID = 1L; |
| |
| private final Set<CDOID> changedIDs; |
| |
| public ObjectStatesChangedEventImpl(CDOWorkspace workspace, Set<CDOID> changedIDs) |
| { |
| super(workspace); |
| this.changedIDs = changedIDs; |
| } |
| |
| @Override |
| public CDOWorkspace getSource() |
| { |
| return (CDOWorkspace)super.getSource(); |
| } |
| |
| public Set<CDOID> getChangedIDs() |
| { |
| return changedIDs; |
| } |
| } |
| } |