blob: cac8de1432629bce39fe1d262b5b418e8578a2e8 [file] [log] [blame]
/***************************************************************************
* Copyright (c) 2004 - 2008 Eike Stepper, Germany.
* 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 - http://bugs.eclipse.org/201266
* Simon McDuff - http://bugs.eclipse.org/230832
**************************************************************************/
package org.eclipse.emf.cdo.internal.common.revision.cache.lru;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.id.CDOIDUtil;
import org.eclipse.emf.cdo.common.model.CDOClass;
import org.eclipse.emf.cdo.common.model.CDOPackageManager;
import org.eclipse.emf.cdo.common.model.resource.CDONameFeature;
import org.eclipse.emf.cdo.common.model.resource.CDOResourceNodeClass;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.common.revision.cache.CDORevisionCache;
import org.eclipse.emf.cdo.internal.common.bundle.OM;
import org.eclipse.emf.cdo.internal.common.revision.cache.EvictionEventImpl;
import org.eclipse.emf.cdo.spi.common.InternalCDORevision;
import org.eclipse.net4j.util.ObjectUtil;
import org.eclipse.net4j.util.lifecycle.Lifecycle;
import org.eclipse.net4j.util.om.trace.ContextTracer;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Eike Stepper
*/
public class LRURevisionCache extends Lifecycle implements CDORevisionCache
{
private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_REVISION, LRURevisionCache.class);
private Map<CDOID, RevisionHolder> revisions = new HashMap<CDOID, RevisionHolder>();
private CDOPackageManager packageManager;
private int capacityCurrent;
private int capacityRevised;
private LRU currentLRU;
private LRU revisedLRU;
private transient CDONameFeature cdoNameFeature;
public LRURevisionCache()
{
}
public CDOPackageManager getPackageManager()
{
return packageManager;
}
public void setPackageManager(CDOPackageManager packageManager)
{
this.packageManager = packageManager;
if (packageManager != null)
{
CDOResourceNodeClass resourceNodeClass = packageManager.getCDOResourcePackage().getCDOResourceNodeClass();
cdoNameFeature = resourceNodeClass.getCDONameFeature();
}
}
public int getCapacityCurrent()
{
return capacityCurrent;
}
/**
* Sets the capacity of LRU cache for <em>current</em> revisions. A value of zero disables eviction completely such
* that the cache will grow indefinetely.
*/
public void setCapacityCurrent(int capacity)
{
capacityCurrent = capacity;
if (currentLRU != null)
{
currentLRU.capacity(capacity);
}
}
public int getCapacityRevised()
{
return capacityRevised;
}
/**
* Sets the capacity of LRU cache for old (<em>revised</em>) revisions. A value of zero disables eviction completely
* such that the cache will grow indefinetely.
*/
public void setCapacityRevised(int capacity)
{
capacityRevised = capacity;
if (revisedLRU != null)
{
revisedLRU.capacity(capacity);
}
}
public synchronized List<CDORevision> getRevisions()
{
List<CDORevision> currentRevisions = new ArrayList<CDORevision>();
for (RevisionHolder holder : revisions.values())
{
InternalCDORevision revision = holder.getRevision();
if (revision != null && revision.isCurrent())
{
currentRevisions.add(revision);
}
}
return currentRevisions;
}
public synchronized CDOClass getObjectType(CDOID id)
{
RevisionHolder holder = getHolder(id);
if (holder == null)
{
return null;
}
InternalCDORevision revision = holder.getRevision();
return revision.getCDOClass();
}
public synchronized InternalCDORevision getRevision(CDOID id)
{
RevisionHolder holder = getHolder(id);
InternalCDORevision revision = holder == null ? null : holder.getRevision();
if (revision == null || !revision.isCurrent())
{
return null;
}
return revision;
}
public synchronized InternalCDORevision getRevisionByTime(CDOID id, long timeStamp)
{
RevisionHolder holder = getHolder(id);
return getRevisionByTime(holder, timeStamp);
}
public synchronized InternalCDORevision getRevisionByVersion(CDOID id, int version)
{
RevisionHolder holder = getHolder(id);
while (holder != null)
{
int holderVersion = holder.getVersion();
if (holderVersion > version)
{
holder = holder.getNext();
}
else if (holderVersion == version)
{
return holder.getRevision();
}
else
{
break;
}
}
return null;
}
public synchronized boolean addRevision(InternalCDORevision revision)
{
if (TRACER.isEnabled())
{
TRACER.format("Adding revision: {0}, created={1,date} {1,time}, revised={2,date} {2,time}, current={3}",
revision, revision.getCreated(), revision.getRevised(), revision.isCurrent());
}
RevisionHolder newHolder = createHolder(revision);
LRU list = revision.isCurrent() ? currentLRU : revisedLRU;
list.add((DLRevisionHolder)newHolder);
int version = revision.getVersion();
RevisionHolder lastHolder = null;
RevisionHolder holder = getHolder(revision.getID());
while (holder != null)
{
int holderVersion = holder.getVersion();
if (holderVersion > version)
{
lastHolder = holder;
holder = holder.getNext();
}
else if (holderVersion == version)
{
return false;
}
else
{
break;
}
}
adjustHolder(revision, newHolder, lastHolder, holder);
return true;
}
public synchronized InternalCDORevision removeRevision(CDOID id, int version)
{
InternalCDORevision revision = null;
RevisionHolder holder = getHolder(id);
while (holder != null)
{
int holderVersion = holder.getVersion();
if (holderVersion > version)
{
holder = holder.getNext();
}
else
{
if (holderVersion == version)
{
revision = holder.getRevision();
removeHolder(holder);
}
holder = null;
}
}
return revision;
}
public synchronized boolean removeRevisions(CDOID id)
{
RevisionHolder lookupHolder = getHolder(id);
RevisionHolder holder = lookupHolder;
while (holder != null)
{
RevisionHolder nextHolder = holder.getNext();
removeHolder(holder);
holder = nextHolder;
}
return lookupHolder != null;
}
public synchronized CDOID getResourceID(CDOID folderID, String name, long timeStamp)
{
CDOID[] ids = getRevisionIDs();
for (CDOID id : ids)
{
RevisionHolder holder = getHolder(id);
if (holder != null)
{
InternalCDORevision revision = holder.getRevision();
if (revision.isResourceNode())
{
revision = getRevisionByTime(holder, timeStamp);
if (revision != null)
{
CDOID revisionFolderID = (CDOID)revision.getContainerID();
if (CDOIDUtil.equals(revisionFolderID, folderID))
{
String revisionName = (String)revision.getValue(cdoNameFeature);
if (ObjectUtil.equals(revisionName, name))
{
return revision.getID();
}
}
}
}
}
}
return null;
}
public synchronized void clear()
{
revisions.clear();
currentLRU = new LRU(capacityCurrent);
revisedLRU = new LRU(capacityRevised);
}
private synchronized CDOID[] getRevisionIDs()
{
return revisions.keySet().toArray(new CDOID[revisions.size()]);
}
private InternalCDORevision getRevisionByTime(RevisionHolder holder, long timeStamp)
{
while (holder != null)
{
int indicator = holder.compareTo(timeStamp);
if (indicator == 1)
{
// timeStamp is after holder timeSpan
holder = holder.getNext();
}
else if (indicator == 0)
{
// timeStamp is within holder timeSpan
return holder.getRevision();
}
else
{
// timeStamp is before holder timeSpan
break;
}
}
return null;
}
@Override
protected void doActivate() throws Exception
{
super.doActivate();
currentLRU = new LRU(capacityCurrent);
revisedLRU = new LRU(capacityRevised);
}
@Override
protected void doDeactivate() throws Exception
{
currentLRU = null;
revisedLRU = null;
super.doDeactivate();
}
public final synchronized RevisionHolder getHolder(CDOID id)
{
return revisions.get(id);
}
protected RevisionHolder createHolder(InternalCDORevision revision)
{
return new LRURevisionHolder(revision);
}
private void adjustHolder(InternalCDORevision revision, RevisionHolder holder, RevisionHolder prevHolder,
RevisionHolder nextHolder)
{
if (prevHolder != null)
{
if (nextHolder == null)
{
nextHolder = prevHolder.getNext();
}
holder.setPrev(prevHolder);
holder.setNext(nextHolder);
prevHolder.setNext(holder);
}
else
{
holder.setNext(nextHolder);
revisions.put(revision.getID(), holder);
}
reviseHolder(holder, nextHolder);
}
private void reviseHolder(RevisionHolder holder, RevisionHolder nextHolder)
{
if (nextHolder != null)
{
nextHolder.setPrev(holder);
if (holder.isCurrent() && nextHolder.isCurrent())
{
currentLRU.remove((DLRevisionHolder)nextHolder);
revisedLRU.add((DLRevisionHolder)nextHolder);
InternalCDORevision oldRevision = nextHolder.getRevision();
if (oldRevision != null)
{
oldRevision.setRevised(holder.getCreated() - 1);
}
}
}
}
private synchronized void removeHolder(RevisionHolder holder)
{
CDOID id = holder.getID();
RevisionHolder prev = holder.getPrev();
RevisionHolder next = holder.getNext();
if (next != null)
{
next.setPrev(prev);
}
if (prev != null)
{
prev.setNext(next);
}
else
{
if (next != null)
{
revisions.put(id, next);
}
else
{
revisions.remove(id);
}
}
holder.setPrev(null);
holder.setNext(null);
}
/**
* @author Eike Stepper
*/
private final class LRU extends LRURevisionList
{
public LRU(int capacity)
{
super(capacity);
}
@Override
public String toString()
{
return MessageFormat.format("LRU[size={0}, capacity={1}]", size(), capacity());
}
@Override
protected void evict(LRURevisionHolder holder)
{
if (TRACER.isEnabled())
{
TRACER.format("Evicting revision {0}v{1}", holder.getID(), holder.getVersion());
}
// Remember some values before the holder may be changed
InternalCDORevision revision = holder.getRevision();
boolean revised = !holder.isCurrent();
super.evict(holder);
removeHolder(holder);
if (revision != null)
{
if (this == currentLRU && revised)
{
addRevision(revision);
}
else
{
fireEvent(new EvictionEventImpl(LRURevisionCache.this, revision));
}
}
}
}
}