blob: f23979da326a45f10c67fff239e7af354c9ea8bc [file] [log] [blame]
/*
* Copyright (c) 2009-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
* Simon McDuff - bug 201266
* Simon McDuff - bug 230832
*/
package org.eclipse.emf.cdo.internal.common.revision;
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.CDOBranchPointRange;
import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.common.revision.CDORevisionCache;
import org.eclipse.emf.cdo.common.revision.CDORevisionFactory;
import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
import org.eclipse.emf.cdo.common.revision.CDORevisionManager;
import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
import org.eclipse.emf.cdo.common.revision.CDORevisionsLoadedEvent;
import org.eclipse.emf.cdo.internal.common.bundle.OM;
import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil;
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.InternalCDORevisionCache;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
import org.eclipse.emf.cdo.spi.common.revision.PointerCDORevision;
import org.eclipse.emf.cdo.spi.common.revision.RevisionInfo;
import org.eclipse.emf.cdo.spi.common.revision.SyntheticCDORevision;
import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
import org.eclipse.net4j.util.event.Event;
import org.eclipse.net4j.util.lifecycle.Lifecycle;
import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
import org.eclipse.net4j.util.om.trace.ContextTracer;
import org.eclipse.emf.ecore.EClass;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* @author Eike Stepper
*/
public class CDORevisionManagerImpl extends Lifecycle implements InternalCDORevisionManager
{
private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_REVISION, CDORevisionManagerImpl.class);
private boolean supportingAudits;
private boolean supportingBranches;
private RevisionLoader revisionLoader;
private RevisionLocker revisionLocker;
private CDORevisionFactory factory;
private InternalCDORevisionCache cache;
@ExcludeFromDump
private transient Object loadAndAddLock = new Object()
{
@Override
public String toString()
{
return "LoadAndAddLock"; //$NON-NLS-1$
}
};
@ExcludeFromDump
private transient Object reviseLock = new Object()
{
@Override
public String toString()
{
return "ReviseLock"; //$NON-NLS-1$
}
};
public CDORevisionManagerImpl()
{
}
public boolean isSupportingAudits()
{
return supportingAudits;
}
public void setSupportingAudits(boolean on)
{
checkInactive();
supportingAudits = on;
}
public boolean isSupportingBranches()
{
return supportingBranches;
}
public void setSupportingBranches(boolean on)
{
checkInactive();
supportingBranches = on;
}
public RevisionLoader getRevisionLoader()
{
return revisionLoader;
}
public void setRevisionLoader(RevisionLoader revisionLoader)
{
checkInactive();
this.revisionLoader = revisionLoader;
}
public RevisionLocker getRevisionLocker()
{
return revisionLocker;
}
public void setRevisionLocker(RevisionLocker revisionLocker)
{
checkInactive();
this.revisionLocker = revisionLocker;
}
public CDORevisionFactory getFactory()
{
return factory;
}
public void setFactory(CDORevisionFactory factory)
{
checkInactive();
this.factory = factory;
}
public InternalCDORevisionCache getCache()
{
return cache;
}
public void setCache(CDORevisionCache cache)
{
checkInactive();
this.cache = (InternalCDORevisionCache)cache;
}
public EClass getObjectType(CDOID id, CDOBranchManager branchManagerForLoadOnDemand)
{
EClass type = cache.getObjectType(id);
if (type == null && branchManagerForLoadOnDemand != null)
{
CDOBranch mainBranch = branchManagerForLoadOnDemand.getMainBranch();
CDORevision revision = getRevisionByVersion(id, mainBranch.getVersion(CDOBranchVersion.FIRST_VERSION), 0, true);
if (revision != null)
{
type = revision.getEClass();
}
}
return type;
}
public EClass getObjectType(CDOID id)
{
return getObjectType(id, null);
}
public boolean containsRevision(CDOID id, CDOBranchPoint branchPoint)
{
if (supportingBranches)
{
return getRevision(id, branchPoint, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, false, null) != null;
}
return getCachedRevision(id, branchPoint) != null;
}
public boolean containsRevisionByVersion(CDOID id, CDOBranchVersion branchVersion)
{
return cache.getRevisionByVersion(id, branchVersion) != null;
}
public void reviseLatest(CDOID id, CDOBranch branch)
{
acquireAtomicRequestLock(reviseLock);
try
{
InternalCDORevision revision = (InternalCDORevision)cache.getRevision(id, branch.getHead());
if (revision != null)
{
CDOBranchVersion version = branch.getVersion(revision.getVersion());
cache.removeRevision(id, version);
}
}
finally
{
releaseAtomicRequestLock(reviseLock);
}
}
public void reviseVersion(CDOID id, CDOBranchVersion branchVersion, long timeStamp)
{
acquireAtomicRequestLock(reviseLock);
try
{
InternalCDORevision revision = getCachedRevisionByVersion(id, branchVersion);
if (revision != null)
{
if (timeStamp == CDORevision.UNSPECIFIED_DATE || !supportingAudits)
{
cache.removeRevision(id, branchVersion);
}
revision.setRevised(timeStamp - 1);
}
}
finally
{
releaseAtomicRequestLock(reviseLock);
}
}
public InternalCDORevision getRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int referenceChunk,
boolean loadOnDemand)
{
checkArg(branchVersion.getVersion() >= CDOBranchVersion.FIRST_VERSION,
"Invalid version: " + branchVersion.getVersion());
acquireAtomicRequestLock(loadAndAddLock);
try
{
InternalCDORevision revision = getCachedRevisionByVersion(id, branchVersion);
if (revision == null)
{
if (loadOnDemand)
{
if (TRACER.isEnabled())
{
TRACER.format("Loading revision {0} from {1}", id, branchVersion); //$NON-NLS-1$
}
revision = revisionLoader.loadRevisionByVersion(id, branchVersion, referenceChunk);
addRevision(revision);
}
}
return revision;
}
finally
{
releaseAtomicRequestLock(loadAndAddLock);
}
}
public CDOBranchPointRange getObjectLifetime(CDOID id, CDOBranchPoint branchPoint)
{
if (revisionLoader instanceof RevisionLoader2)
{
RevisionLoader2 revisionLoader2 = (RevisionLoader2)revisionLoader;
return revisionLoader2.loadObjectLifetime(id, branchPoint);
}
return null;
}
public InternalCDORevision getRevision(CDOID id, CDOBranchPoint branchPoint, int referenceChunk, int prefetchDepth,
boolean loadOnDemand)
{
return getRevision(id, branchPoint, referenceChunk, prefetchDepth, loadOnDemand, null);
}
public InternalCDORevision getRevision(CDOID id, CDOBranchPoint branchPoint, int referenceChunk, int prefetchDepth,
boolean loadOnDemand, SyntheticCDORevision[] synthetics)
{
List<CDOID> ids = Collections.singletonList(id);
CDORevision result = getRevisions(ids, branchPoint, referenceChunk, prefetchDepth, loadOnDemand, synthetics).get(0);
return (InternalCDORevision)result;
}
public List<CDORevision> getRevisions(List<CDOID> ids, CDOBranchPoint branchPoint, int referenceChunk,
int prefetchDepth, boolean loadOnDemand)
{
return getRevisions(ids, branchPoint, referenceChunk, prefetchDepth, loadOnDemand, null);
}
public List<CDORevision> getRevisions(List<CDOID> ids, CDOBranchPoint branchPoint, int referenceChunk,
int prefetchDepth, boolean loadOnDemand, SyntheticCDORevision[] synthetics)
{
RevisionInfo[] infos = new RevisionInfo[ids.size()];
List<RevisionInfo> infosToLoad = createRevisionInfos(ids, branchPoint, prefetchDepth, loadOnDemand, infos);
if (infosToLoad != null)
{
List<? extends CDORevision> additionalLoadedRevisions //
= loadRevisions(infosToLoad, branchPoint, referenceChunk, prefetchDepth);
List<? extends CDORevision> primaryLoadedRevisions //
= getResultsAndSynthetics(infosToLoad.toArray(new RevisionInfo[0]), null);
if (primaryLoadedRevisions != null && !primaryLoadedRevisions.isEmpty()
|| additionalLoadedRevisions != null && !additionalLoadedRevisions.isEmpty())
{
if (primaryLoadedRevisions == null)
{
primaryLoadedRevisions = Collections.emptyList();
}
if (additionalLoadedRevisions == null)
{
additionalLoadedRevisions = Collections.emptyList();
}
fireEvent(new RevisionsLoadedEvent(this, primaryLoadedRevisions, additionalLoadedRevisions, prefetchDepth));
}
}
return getResultsAndSynthetics(infos, synthetics);
}
public void handleRevisions(EClass eClass, CDOBranch branch, boolean exactBranch, long timeStamp, boolean exactTime,
CDORevisionHandler handler)
{
revisionLoader.handleRevisions(eClass, branch, exactBranch, timeStamp, exactTime, handler);
}
@Override
public String toString()
{
return "RevisionManager";
}
private List<RevisionInfo> createRevisionInfos(List<CDOID> ids, CDOBranchPoint branchPoint, int prefetchDepth,
boolean loadOnDemand, RevisionInfo[] infos)
{
List<RevisionInfo> infosToLoad = null;
Iterator<CDOID> idIterator = ids.iterator();
for (int i = 0; i < infos.length; i++)
{
CDOID id = idIterator.next();
RevisionInfo info = createRevisionInfo(id, branchPoint);
infos[i] = info;
if (loadOnDemand && (prefetchDepth != CDORevision.DEPTH_NONE || info.isLoadNeeded()))
{
if (infosToLoad == null)
{
infosToLoad = new ArrayList<RevisionInfo>(1);
}
infosToLoad.add(info);
}
}
return infosToLoad;
}
private RevisionInfo createRevisionInfo(CDOID id, CDOBranchPoint branchPoint)
{
InternalCDORevision revision = getCachedRevision(id, branchPoint);
if (revision != null)
{
return createRevisionInfoAvailable(revision, branchPoint);
}
if (supportingBranches)
{
revision = getCachedRevisionRecursively(id, branchPoint);
if (revision != null)
{
return createRevisionInfoAvailable(revision, branchPoint);
}
}
return createRevisionInfoMissing(id, branchPoint);
}
private RevisionInfo.Available createRevisionInfoAvailable(InternalCDORevision revision,
CDOBranchPoint requestedBranchPoint)
{
if (revision instanceof PointerCDORevision)
{
PointerCDORevision pointer = (PointerCDORevision)revision;
CDOBranchVersion target = pointer.getTarget();
InternalCDORevision targetRevision = target == null ? null : getCachedRevisionByVersion(pointer.getID(), target);
if (targetRevision != null)
{
target = targetRevision;
}
return new RevisionInfo.Available.Pointer(pointer.getID(), requestedBranchPoint, pointer, target);
}
if (revision instanceof DetachedCDORevision)
{
DetachedCDORevision detached = (DetachedCDORevision)revision;
return new RevisionInfo.Available.Detached(detached.getID(), requestedBranchPoint, detached);
}
return new RevisionInfo.Available.Normal(revision.getID(), requestedBranchPoint, revision);
}
private RevisionInfo.Missing createRevisionInfoMissing(CDOID id, CDOBranchPoint requestedBranchPoint)
{
return new RevisionInfo.Missing(id, requestedBranchPoint);
}
protected List<InternalCDORevision> loadRevisions(List<RevisionInfo> infosToLoad, CDOBranchPoint branchPoint,
int referenceChunk, int prefetchDepth)
{
acquireAtomicRequestLock(loadAndAddLock);
try
{
List<InternalCDORevision> additionalRevisions = null;
List<RevisionInfo> additionalRevisionInfos = //
revisionLoader.loadRevisions(infosToLoad, branchPoint, referenceChunk, prefetchDepth);
if (additionalRevisionInfos != null)
{
additionalRevisions = new ArrayList<InternalCDORevision>(additionalRevisionInfos.size());
for (RevisionInfo info : additionalRevisionInfos)
{
info.processResult(this, new ArrayList<CDORevision>(), null, 0);
additionalRevisions.add(info.getResult());
}
}
return additionalRevisions;
}
finally
{
releaseAtomicRequestLock(loadAndAddLock);
}
}
private List<CDORevision> getResultsAndSynthetics(RevisionInfo[] infos, SyntheticCDORevision[] synthetics)
{
List<CDORevision> results = new ArrayList<CDORevision>(infos.length);
for (int i = 0; i < infos.length; i++)
{
RevisionInfo info = infos[i];
info.processResult(this, results, synthetics, i);
}
return results;
}
public void addRevision(CDORevision revision)
{
if (revision != null)
{
acquireAtomicRequestLock(loadAndAddLock);
try
{
if (revision instanceof PointerCDORevision)
{
PointerCDORevision pointer = (PointerCDORevision)revision;
CDOBranchVersion target = pointer.getTarget();
if (target instanceof InternalCDORevision)
{
revision = new PointerCDORevision(pointer.getEClass(), pointer.getID(), pointer.getBranch(),
pointer.getRevised(), CDOBranchUtil.copyBranchVersion(target));
}
}
int oldVersion = revision.getVersion() - 1;
if (oldVersion >= CDORevision.UNSPECIFIED_VERSION)
{
CDOBranchVersion old = revision.getBranch().getVersion(oldVersion);
InternalCDORevision oldRevision = getCachedRevisionByVersion(revision.getID(), old);
if (!revision.isHistorical())
{
if (oldRevision != null)
{
oldRevision.setRevised(revision.getTimeStamp() - 1);
}
else
{
// Remove last revision from cache, which is not revised
InternalCDORevision cachedLatestRevision = getCachedRevision(revision.getID(), revision);
if (cachedLatestRevision != null && !cachedLatestRevision.isHistorical())
{
// Found revision is stale.
// We cannot revise it now because of lack information, thus remove it from the cache
cache.removeRevision(cachedLatestRevision.getID(), cachedLatestRevision);
}
}
}
}
cache.addRevision(revision);
}
finally
{
releaseAtomicRequestLock(loadAndAddLock);
}
}
}
@Override
protected void doBeforeActivate() throws Exception
{
super.doBeforeActivate();
if (factory == null)
{
factory = CDORevisionFactory.DEFAULT;
}
if (cache == null)
{
cache = (InternalCDORevisionCache)CDORevisionUtil.createRevisionCache(supportingAudits, supportingBranches);
}
if (cache instanceof AbstractCDORevisionCache)
{
String name = revisionLoader.toString();
((AbstractCDORevisionCache)cache).setName(name);
}
}
@Override
protected void doActivate() throws Exception
{
super.doActivate();
LifecycleUtil.activate(cache);
}
@Override
protected void doDeactivate() throws Exception
{
LifecycleUtil.deactivate(cache);
super.doDeactivate();
}
private void acquireAtomicRequestLock(Object key)
{
if (revisionLocker != null)
{
revisionLocker.acquireAtomicRequestLock(key);
}
}
private void releaseAtomicRequestLock(Object key)
{
if (revisionLocker != null)
{
revisionLocker.releaseAtomicRequestLock(key);
}
}
private InternalCDORevision getCachedRevisionByVersion(CDOID id, CDOBranchVersion branchVersion)
{
return (InternalCDORevision)cache.getRevisionByVersion(id, branchVersion);
}
private InternalCDORevision getCachedRevision(CDOID id, CDOBranchPoint branchPoint)
{
return (InternalCDORevision)cache.getRevision(id, branchPoint);
}
private InternalCDORevision getCachedRevisionRecursively(CDOID id, CDOBranchPoint branchPoint)
{
CDOBranch branch = branchPoint.getBranch();
if (!branch.isMainBranch())
{
CDOBranchPoint base = branch.getBase();
InternalCDORevision revision = getCachedRevision(id, base);
if (revision != null)
{
return revision;
}
// Recurse
return getCachedRevisionRecursively(id, base);
}
// Reached main branch
return null;
}
/**
* @author Esteban Dugueperoux
*/
private static class RevisionsLoadedEvent extends Event implements CDORevisionsLoadedEvent
{
private static final long serialVersionUID = 1L;
private List<? extends CDORevision> primaryLoadedRevisions;
private List<? extends CDORevision> additionalLoadedRevisions;
private int prefetchDepth;
public RevisionsLoadedEvent(CDORevisionManager revisionManager, List<? extends CDORevision> primaryLoadedRevisions,
List<? extends CDORevision> additionalLoadedRevisions, int prefetchDepth)
{
super(revisionManager);
this.primaryLoadedRevisions = primaryLoadedRevisions;
this.additionalLoadedRevisions = additionalLoadedRevisions;
this.prefetchDepth = prefetchDepth;
}
@Override
public CDORevisionManager getSource()
{
return (CDORevisionManager)super.getSource();
}
public List<? extends CDORevision> getPrimaryLoadedRevisions()
{
return primaryLoadedRevisions;
}
public List<? extends CDORevision> getAdditionalLoadedRevisions()
{
return additionalLoadedRevisions;
}
public int getPrefetchDepth()
{
return prefetchDepth;
}
}
}