/* * Copyright (c) 2011-2013, 2015, 2016 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.CDOBranchVersion; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.common.revision.CDORevisionKey; import org.eclipse.emf.cdo.internal.common.bundle.OM; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionCache; import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; import org.eclipse.net4j.util.event.IListener; import org.eclipse.net4j.util.lifecycle.Lifecycle; import org.eclipse.net4j.util.om.trace.ContextTracer; import org.eclipse.net4j.util.ref.CleanableReferenceQueue; import org.eclipse.net4j.util.ref.ReferenceQueueWorker; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.text.MessageFormat; /** * @author Eike Stepper */ public abstract class AbstractCDORevisionCache extends Lifecycle implements InternalCDORevisionCache { private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_REVISION, AbstractCDORevisionCache.class); private static boolean disableGC; @ExcludeFromDump private final CleanableReferenceQueue<InternalCDORevision> referenceQueue = new CleanableReferenceQueue<InternalCDORevision>() { @Override protected Reference<InternalCDORevision> createReference(InternalCDORevision revision) { return AbstractCDORevisionCache.this.createReference(revision); } @Override protected void cleanReference(Reference<? extends InternalCDORevision> reference) { AbstractCDORevisionCache.this.cleanReference(reference); } }; private CDOBranchManager branchManager; private String name; public AbstractCDORevisionCache() { setPollMillis(ReferenceQueueWorker.DEFAULT_POLL_MILLIS); setMaxWorkPerPoll(ReferenceQueueWorker.DEFAULT_MAX_WORK_PER_POLL); } public CDOBranchManager getBranchManager() { return branchManager; } public void setBranchManager(CDOBranchManager branchManager) { this.branchManager = branchManager; } protected final void checkBranch(CDOBranch branch) { if (branchManager != null) { CDOBranchManager actualBranchManager = branch.getBranchManager(); if (actualBranchManager != branchManager) { throw new IllegalArgumentException("Wrong branch manager: " + actualBranchManager + "; expected: " + branchManager); } } } public String getName() { return name; } public void setName(String name) { this.name = name; } public long getPollMillis() { return referenceQueue.getPollMillis(); } public void setPollMillis(long pollMillis) { referenceQueue.setPollMillis(pollMillis); } public int getMaxWorkPerPoll() { return referenceQueue.getMaxWorkPerPoll(); } public void setMaxWorkPerPoll(int maxWorkPerPoll) { referenceQueue.setMaxWorkPerPoll(maxWorkPerPoll); } public final void addRevision(CDORevision revision) { referenceQueue.register((InternalCDORevision)revision); doAddRevision(revision); } protected abstract void doAddRevision(CDORevision revision); public final CDORevision removeRevision(CDOID id, CDOBranchVersion branchVersion) { referenceQueue.clean(); return doRemoveRevision(id, branchVersion); } protected abstract CDORevision doRemoveRevision(CDOID id, CDOBranchVersion branchVersion); @Override public String toString() { return formatName("CDORevisionCache"); } private String formatName(String prefix) { return prefix + (name == null ? "" : "-" + name); } protected Reference<InternalCDORevision> createReference(CDORevision revision) { if (disableGC) { return createStrongReference(revision); } if (TRACER.isEnabled()) { TRACER.format("Adding revision {0} to {1}", revision, this); //$NON-NLS-1$ } return new CacheSoftReference((InternalCDORevision)revision, referenceQueue); } private Reference<InternalCDORevision> createStrongReference(CDORevision revision) { if (TRACER.isEnabled()) { TRACER.format("Adding revision {0} to {1} (STRONGLY REFERENCED)", revision, this); //$NON-NLS-1$ } return new CacheStrongReference((InternalCDORevision)revision); } protected void cleanReference(Reference<? extends InternalCDORevision> reference) { CDORevisionKey key = (CDORevisionKey)reference; CDOID id = key.getID(); CDOBranch branch = key.getBranch(); int version = key.getVersion(); InternalCDORevision revision = (InternalCDORevision)removeRevision(id, branch.getVersion(version)); if (revision != null) { // Use revision in eviction event key = revision; } IListener[] listeners = getListeners(); if (listeners != null) { fireEvent(new EvictionEventImpl(this, key), listeners); } if (TRACER.isEnabled()) { TRACER.format("Evicted {0} from {1}", key, this); //$NON-NLS-1$ } } /** * @author Eike Stepper */ private static final class CacheSoftReference extends SoftReference<InternalCDORevision> implements CDORevisionKey { private CDOID id; private CDOBranch branch; private int version; public CacheSoftReference(InternalCDORevision revision, ReferenceQueue<InternalCDORevision> queue) { super(revision, queue); id = revision.getID(); branch = revision.getBranch(); version = revision.getVersion(); } public CDOID getID() { return id; } public CDOBranch getBranch() { return branch; } public int getVersion() { return version; } @Override public String toString() { return MessageFormat.format("{0}:{1}v{2}", getID(), getBranch().getID(), getVersion()); } } /** * @author Eike Stepper */ private static final class CacheStrongReference extends SoftReference<InternalCDORevision> implements CDORevisionKey { private CDOID id; private CDOBranch branch; private int version; public CacheStrongReference(InternalCDORevision revision) { super(revision); id = revision.getID(); branch = revision.getBranch(); version = revision.getVersion(); } public CDOID getID() { return id; } public CDOBranch getBranch() { return branch; } public int getVersion() { return version; } @Override public String toString() { return MessageFormat.format("{0}:{1}v{2}", getID(), getBranch().getID(), getVersion()); } } }