blob: ffb25c41265cf8a68015dd0dad59ab4587c45e8d [file] [log] [blame]
/**
* Copyright (c) 2004 - 2010 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
* Caspar De Groot - https://bugs.eclipse.org/333260
*/
package org.eclipse.emf.cdo.spi.server;
import org.eclipse.emf.cdo.common.CDOCommonView;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.id.CDOIDMetaRange;
import org.eclipse.emf.cdo.common.id.CDOIDUtil;
import org.eclipse.emf.cdo.server.IRepository;
import org.eclipse.emf.cdo.server.ISession;
import org.eclipse.emf.cdo.server.ISessionManager;
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.IView;
import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump;
import org.eclipse.net4j.util.StringUtil;
import org.eclipse.net4j.util.container.IContainerDelta;
import org.eclipse.net4j.util.lifecycle.Lifecycle;
import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
import org.eclipse.net4j.util.om.monitor.ProgressDistributor;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* @author Eike Stepper
* @since 2.0
*/
public abstract class Store extends Lifecycle implements IStore
{
protected static final long CRASHED = -1L;
@ExcludeFromDump
private final transient String type;
@ExcludeFromDump
private final transient Set<ChangeFormat> supportedChangeFormats;
@ExcludeFromDump
private final transient Set<RevisionTemporality> supportedRevisionTemporalities;
@ExcludeFromDump
private final transient Set<RevisionParallelism> supportedRevisionParallelisms;
private RevisionTemporality revisionTemporality = RevisionTemporality.NONE;
private RevisionParallelism revisionParallelism = RevisionParallelism.NONE;
private IRepository repository;
@ExcludeFromDump
private transient long lastMetaID;
@ExcludeFromDump
private transient Object lastMetaIDLock = new Object();
@ExcludeFromDump
private transient ProgressDistributor indicatingCommitDistributor = new ProgressDistributor.Geometric()
{
@Override
public String toString()
{
String result = "indicatingCommitDistributor"; //$NON-NLS-1$
if (repository != null)
{
result += ": " + repository.getName(); //$NON-NLS-1$
}
return result;
}
};
public Store(String type, Set<ChangeFormat> supportedChangeFormats,
Set<RevisionTemporality> supportedRevisionTemporalities, Set<RevisionParallelism> supportedRevisionParallelisms)
{
checkArg(!StringUtil.isEmpty(type), "Empty type"); //$NON-NLS-1$
this.type = type;
checkArg(supportedChangeFormats != null && !supportedChangeFormats.isEmpty(), "Empty supportedChangeFormats"); //$NON-NLS-1$
this.supportedChangeFormats = supportedChangeFormats;
checkArg(supportedRevisionTemporalities != null && !supportedRevisionTemporalities.isEmpty(),
"Empty supportedRevisionTemporalities"); //$NON-NLS-1$
this.supportedRevisionTemporalities = supportedRevisionTemporalities;
checkArg(supportedRevisionParallelisms != null && !supportedRevisionParallelisms.isEmpty(),
"Empty supportedRevisionParallelisms"); //$NON-NLS-1$
this.supportedRevisionParallelisms = supportedRevisionParallelisms;
}
public String getType()
{
return type;
}
public IRepository getRepository()
{
return repository;
}
public void setRepository(IRepository repository)
{
this.repository = repository;
}
public Set<ChangeFormat> getSupportedChangeFormats()
{
return supportedChangeFormats;
}
public Set<RevisionTemporality> getSupportedRevisionTemporalities()
{
return supportedRevisionTemporalities;
}
public Set<RevisionParallelism> getSupportedRevisionParallelisms()
{
return supportedRevisionParallelisms;
}
public RevisionTemporality getRevisionTemporality()
{
return revisionTemporality;
}
public void setRevisionTemporality(RevisionTemporality revisionTemporality)
{
checkInactive();
checkState(supportedRevisionTemporalities.contains(revisionTemporality), "Revision temporality not supported: " //$NON-NLS-1$
+ revisionTemporality);
this.revisionTemporality = revisionTemporality;
}
public RevisionParallelism getRevisionParallelism()
{
return revisionParallelism;
}
public void setRevisionParallelism(RevisionParallelism revisionParallelism)
{
checkInactive();
checkState(supportedRevisionParallelisms.contains(revisionParallelism), "Revision parallelism not supported: " //$NON-NLS-1$
+ revisionParallelism);
this.revisionParallelism = revisionParallelism;
}
public long getLastMetaID()
{
synchronized (lastMetaIDLock)
{
return lastMetaID;
}
}
public void setLastMetaID(long lastMetaID)
{
synchronized (lastMetaIDLock)
{
this.lastMetaID = lastMetaID;
}
}
public CDOIDMetaRange getNextMetaIDRange(int count)
{
synchronized (lastMetaIDLock)
{
CDOID lowerBound = CDOIDUtil.createMeta(lastMetaID + 1);
lastMetaID += count;
return CDOIDUtil.createMetaRange(lowerBound, count);
}
}
public IStoreAccessor getReader(ISession session)
{
IStoreAccessor reader = null;
StoreAccessorPool pool = getReaderPool(session, false);
if (pool != null)
{
reader = pool.removeStoreAccessor();
}
if (reader == null && session != null)
{
CDOCommonView[] views = session.getViews();
for (CDOCommonView view : views)
{
pool = getWriterPool((IView)view, false);
if (pool != null)
{
reader = pool.removeStoreAccessor();
if (reader != null)
{
break;
}
}
}
}
if (reader == null)
{
reader = createReader(session);
LifecycleUtil.activate(reader);
}
return reader;
}
public IStoreAccessor getWriter(ITransaction transaction)
{
IStoreAccessor writer = null;
StoreAccessorPool pool = getWriterPool(transaction, false);
if (pool != null)
{
writer = pool.removeStoreAccessor();
}
if (writer == null)
{
writer = createWriter(transaction);
LifecycleUtil.activate(writer);
}
return writer;
}
public ProgressDistributor getIndicatingCommitDistributor()
{
return indicatingCommitDistributor;
}
protected void releaseAccessor(StoreAccessor accessor)
{
StoreAccessorPool pool = null;
if (accessor.isReader())
{
pool = getReaderPool(accessor.getSession(), true);
}
else
{
pool = getWriterPool(accessor.getTransaction(), true);
}
if (pool != null)
{
pool.addStoreAccessor(accessor);
}
else
{
accessor.deactivate();
}
}
/**
* Returns a {@link StoreAccessorPool pool} that may contain {@link IStoreAccessor} instances that are compatible with
* the given session. The implementor may return <code>null</code> to indicate that no pooling occurs. It's also left
* to the implementors choice how to determine the appropriate pool instance to be used for the given session, for
* example it could always return the same pool instance, regardless of the given session.
* <p>
* If the implementor of this method decides to create pools that are only compatible with certain sessions or views,
* then it is his responsibility to listen to {@link IContainerDelta.Kind#REMOVED REMOVED} events sent by either the
* {@link ISessionManager} (indicating that a session is closed) or any of its sessions (indicating that a view is
* closed). <b>Note:</b> Closing a session <em>implies</em> that all contained views are closed sliently without
* firing respective events!
*
* @param session
* The context which the pool must be compatible with. Must not be <code>null</code>.
* @param forReleasing
* Enables lazy pool creation. The implementor is not supposed to create a new pool if <code>false</code> is
* passed. If <code>true</code> is passed it's up to the implementor whether to create a new pool or not.
*/
protected abstract StoreAccessorPool getReaderPool(ISession session, boolean forReleasing);
/**
* Returns a {@link StoreAccessorPool pool} that may contain {@link IStoreAccessor} instances that are compatible with
* the given session. The implementor may return <code>null</code> to indicate that no pooling occurs. It's also left
* to the implementors choice how to determine the appropriate pool instance to be used for the given session, for
* example it could always return the same pool instance, regardless of the given session.
* <p>
* If the implementor of this method decides to create pools that are only compatible with certain sessions or views,
* then it is his responsibility to listen to {@link IContainerDelta.Kind#REMOVED REMOVED} events sent by either the
* {@link ISessionManager} (indicating that a session is closed) or any of its sessions (indicating that a view is
* closed). <b>Note:</b> Closing a session <em>implies</em> that all contained views are closed sliently without
* firing respective events!
*
* @param view
* The context which the pool must be compatible with. Must not be <code>null</code>.
* @param forReleasing
* Enables lazy pool creation. The implementor is not supposed to create a new pool if <code>false</code> is
* passed. If <code>true</code> is passed it's up to the implementor whether to create a new pool or not.
*/
protected abstract StoreAccessorPool getWriterPool(IView view, boolean forReleasing);
/**
* Creates and returns a <b>new</b> {@link IStoreAccessor} instance. The caller of this method is responsible for
* {@link Lifecycle#activate() activating} the new instance.
*/
protected abstract IStoreAccessor createReader(ISession session);
/**
* Creates and returns a <b>new</b> {@link IStoreAccessor} instance. The caller of this method is responsible for
* {@link Lifecycle#activate() activating} the new instance.
*/
protected abstract IStoreAccessor createWriter(ITransaction transaction);
public static IStoreAccessor.QueryResourcesContext.ExactMatch createExactMatchContext(final CDOID folderID,
final String name, final long timeStamp)
{
return new IStoreAccessor.QueryResourcesContext.ExactMatch()
{
private CDOID resourceID;
public CDOID getResourceID()
{
return resourceID;
}
public long getTimeStamp()
{
return timeStamp;
}
public CDOID getFolderID()
{
return folderID;
}
public String getName()
{
return name;
}
public boolean exactMatch()
{
return true;
}
public int getMaxResults()
{
return 1;
}
public boolean addResource(CDOID resourceID)
{
this.resourceID = resourceID;
return false;
}
};
}
protected static <T> Set<T> set(T... elements)
{
return Collections.unmodifiableSet(new HashSet<T>(Arrays.asList(elements)));
}
public CDOID getRootResourceID()
{
throw new RuntimeException("Method getRootResourceID not supported");
}
}