diff options
26 files changed, 14677 insertions, 14205 deletions
diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java index aecd6a32e5..4132088e45 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java @@ -1,1404 +1,1410 @@ -/*
- * Copyright (c) 2004 - 2012 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
- * Stefan Winkler - bug 259402
- * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
- * Andre Dietisheim - bug 256649
- * Caspar De Groot - maintenance
- */
-package org.eclipse.emf.cdo.server.internal.db;
-
-import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation;
-import org.eclipse.emf.cdo.common.branch.CDOBranch;
-import org.eclipse.emf.cdo.common.branch.CDOBranchHandler;
-import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
-import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
-import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
-import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler;
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.lob.CDOLobHandler;
-import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea.Handler;
-import org.eclipse.emf.cdo.common.model.CDOClassifierRef;
-import org.eclipse.emf.cdo.common.model.CDOPackageRegistry;
-import org.eclipse.emf.cdo.common.protocol.CDODataInput;
-import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.common.revision.CDORevisionCacheAdder;
-import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
-import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
-import org.eclipse.emf.cdo.eresource.EresourcePackage;
-import org.eclipse.emf.cdo.server.IQueryHandler;
-import org.eclipse.emf.cdo.server.IRepository;
-import org.eclipse.emf.cdo.server.ISession;
-import org.eclipse.emf.cdo.server.IStoreAccessor;
-import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking2;
-import org.eclipse.emf.cdo.server.ITransaction;
-import org.eclipse.emf.cdo.server.db.CDODBUtil;
-import org.eclipse.emf.cdo.server.db.IDBStore;
-import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
-import org.eclipse.emf.cdo.server.db.IIDHandler;
-import org.eclipse.emf.cdo.server.db.IMetaDataManager;
-import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
-import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
-import org.eclipse.emf.cdo.server.db.mapping.IClassMapping;
-import org.eclipse.emf.cdo.server.db.mapping.IClassMappingAuditSupport;
-import org.eclipse.emf.cdo.server.db.mapping.IClassMappingDeltaSupport;
-import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
-import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
-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.commit.CDOChangeSetSegment;
-import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager;
-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.DetachedCDORevision;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
-import org.eclipse.emf.cdo.spi.server.InternalCommitContext;
-import org.eclipse.emf.cdo.spi.server.InternalRepository;
-import org.eclipse.emf.cdo.spi.server.StoreAccessor;
-
-import org.eclipse.net4j.db.DBException;
-import org.eclipse.net4j.db.DBUtil;
-import org.eclipse.net4j.util.HexUtil;
-import org.eclipse.net4j.util.ObjectUtil;
-import org.eclipse.net4j.util.StringUtil;
-import org.eclipse.net4j.util.collection.CloseableIterator;
-import org.eclipse.net4j.util.collection.Pair;
-import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
-import org.eclipse.net4j.util.io.IOUtil;
-import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
-import org.eclipse.net4j.util.om.monitor.OMMonitor;
-import org.eclipse.net4j.util.om.monitor.OMMonitor.Async;
-import org.eclipse.net4j.util.om.trace.ContextTracer;
-
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EPackage;
-import org.eclipse.emf.ecore.EStructuralFeature;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.Writer;
-import java.sql.Blob;
-import java.sql.Clob;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TimerTask;
-
-/**
- * @author Eike Stepper
- */
-public class DBStoreAccessor extends StoreAccessor implements IDBStoreAccessor, DurableLocking2
-{
- private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, DBStoreAccessor.class);
-
- private Connection connection;
-
- private ConnectionKeepAliveTask connectionKeepAliveTask;
-
- private IPreparedStatementCache statementCache;
-
- private Set<CDOID> newObjects = new HashSet<CDOID>();
-
- private CDOID maxID = CDOID.NULL;
-
- public DBStoreAccessor(DBStore store, ISession session) throws DBException
- {
- super(store, session);
- }
-
- public DBStoreAccessor(DBStore store, ITransaction transaction) throws DBException
- {
- super(store, transaction);
- }
-
- @Override
- public DBStore getStore()
- {
- return (DBStore)super.getStore();
- }
-
- public IPreparedStatementCache getStatementCache()
- {
- return statementCache;
- }
-
- public DBStoreChunkReader createChunkReader(InternalCDORevision revision, EStructuralFeature feature)
- {
- return new DBStoreChunkReader(this, revision, feature);
- }
-
- /**
- * Returns an iterator that iterates over all objects in the store and makes their CDOIDs available for processing.
- * This method is supposed to be called very infrequently, for example during the recovery from a crash.
- *
- * @since 2.0
- * @deprecated Not used by the framework anymore.
- */
- @Deprecated
- public CloseableIterator<CDOID> readObjectIDs()
- {
- if (TRACER.isEnabled())
- {
- TRACER.trace("Selecting object IDs"); //$NON-NLS-1$
- }
-
- return getStore().getMappingStrategy().readObjectIDs(this);
- }
-
- public CDOClassifierRef readObjectType(CDOID id)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Selecting object type: {0}", id); //$NON-NLS-1$
- }
-
- IMappingStrategy mappingStrategy = getStore().getMappingStrategy();
- return mappingStrategy.readObjectType(this, id);
- }
-
- protected EClass getObjectType(CDOID id)
- {
- IRepository repository = getStore().getRepository();
- if (repository.getRootResourceID().equals(id))
- {
- return EresourcePackage.Literals.CDO_RESOURCE;
- }
-
- EClass result = repository.getRevisionManager().getObjectType(id);
- if (result != null)
- {
- return result;
- }
-
- CDOClassifierRef type = readObjectType(id);
- if (type != null)
- {
- CDOPackageRegistry packageRegistry = repository.getPackageRegistry();
- return (EClass)type.resolve(packageRegistry);
- }
-
- return null;
- }
-
- public InternalCDORevision readRevision(CDOID id, CDOBranchPoint branchPoint, int listChunk,
- CDORevisionCacheAdder cache)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Selecting revision {0} from {1}", id, branchPoint); //$NON-NLS-1$
- }
-
- IMappingStrategy mappingStrategy = getStore().getMappingStrategy();
-
- EClass eClass = getObjectType(id);
- if (eClass != null)
- {
- InternalCDORevision revision = getStore().createRevision(eClass, id);
- revision.setBranchPoint(branchPoint);
-
- IClassMapping mapping = mappingStrategy.getClassMapping(eClass);
- if (mapping.readRevision(this, revision, listChunk))
- {
- int version = revision.getVersion();
- if (version < CDOBranchVersion.FIRST_VERSION - 1)
- {
- return new DetachedCDORevision(eClass, id, revision.getBranch(), -version, revision.getTimeStamp(),
- revision.getRevised());
- }
-
- return revision;
- }
- }
-
- // Reading failed - revision does not exist.
- return null;
- }
-
- public InternalCDORevision readRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int listChunk,
- CDORevisionCacheAdder cache)
- {
- DBStore store = getStore();
- EClass eClass = getObjectType(id);
-
- IMappingStrategy mappingStrategy = store.getMappingStrategy();
- IClassMapping mapping = mappingStrategy.getClassMapping(eClass);
-
- InternalCDORevision revision = store.createRevision(eClass, id);
- revision.setVersion(branchVersion.getVersion());
- revision.setBranchPoint(branchVersion.getBranch().getHead());
-
- boolean success = false;
-
- if (mappingStrategy.hasAuditSupport())
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Selecting revision {0} from {1}", id, branchVersion); //$NON-NLS-1$
- }
-
- // if audit support is present, just use the audit method
- success = ((IClassMappingAuditSupport)mapping).readRevisionByVersion(this, revision, listChunk);
- if (success && revision.getVersion() < CDOBranchVersion.FIRST_VERSION - 1)
- {
- // it is detached revision
- revision = new DetachedCDORevision(eClass, id, revision.getBranch(), -revision.getVersion(),
- revision.getTimeStamp(), revision.getRevised());
-
- }
- }
- else
- {
- // if audit support is not present, we still have to provide a method
- // to readRevisionByVersion because TransactionCommitContext.computeDirtyObject
- // needs to lookup the base revision for a change. Hence we emulate this
- // behavior by getting the current revision and asserting that the version
- // has not changed. This is valid because if the version has changed,
- // we are in trouble because of a conflict anyways.
- if (TRACER.isEnabled())
- {
- TRACER.format("Selecting current base revision: {0}", id); //$NON-NLS-1$
- }
-
- success = mapping.readRevision(this, revision, listChunk);
-
- if (success && revision.getVersion() != branchVersion.getVersion())
- {
- throw new IllegalStateException("Can only retrieve current version " + revision.getVersion() + " for " + id //$NON-NLS-1$ //$NON-NLS-2$
- + " - version requested was " + branchVersion); //$NON-NLS-1$
- }
- }
-
- return success ? revision : null;
- }
-
- /**
- * @since 2.0
- */
- public void queryResources(QueryResourcesContext context)
- {
- IMappingStrategy mappingStrategy = getStore().getMappingStrategy();
- mappingStrategy.queryResources(this, context);
- }
-
- public void queryXRefs(QueryXRefsContext context)
- {
- IMappingStrategy mappingStrategy = getStore().getMappingStrategy();
- mappingStrategy.queryXRefs(this, context);
- }
-
- public IQueryHandler getQueryHandler(CDOQueryInfo info)
- {
- String queryLanguage = info.getQueryLanguage();
- if (StringUtil.equalsUpperOrLowerCase(queryLanguage, SQLQueryHandler.QUERY_LANGUAGE))
- {
- return new SQLQueryHandler(this);
- }
-
- return null;
- }
-
- public void queryLobs(List<byte[]> ids)
- {
- PreparedStatement stmt = null;
- ResultSet resultSet = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_QUERY_LOBS, ReuseProbability.MEDIUM);
-
- for (Iterator<byte[]> it = ids.iterator(); it.hasNext();)
- {
- byte[] id = it.next();
- stmt.setString(1, HexUtil.bytesToHex(id));
-
- try
- {
- resultSet = stmt.executeQuery();
- if (!resultSet.next())
- {
- it.remove();
- }
- }
- finally
- {
- DBUtil.close(resultSet);
- }
- }
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public void loadLob(byte[] id, OutputStream out) throws IOException
- {
- PreparedStatement stmt = null;
- ResultSet resultSet = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_LOAD_LOB, ReuseProbability.MEDIUM);
- stmt.setString(1, HexUtil.bytesToHex(id));
-
- try
- {
- resultSet = stmt.executeQuery();
- resultSet.next();
-
- long size = resultSet.getLong(1);
- Blob blob = resultSet.getBlob(2);
- if (resultSet.wasNull())
- {
- Clob clob = resultSet.getClob(3);
- Reader in = clob.getCharacterStream();
- IOUtil.copyCharacter(in, new OutputStreamWriter(out), size);
- }
- else
- {
- InputStream in = blob.getBinaryStream();
- IOUtil.copyBinary(in, out, size);
- }
- }
- finally
- {
- DBUtil.close(resultSet);
- }
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException
- {
- PreparedStatement stmt = null;
- ResultSet resultSet = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_HANDLE_LOBS, ReuseProbability.LOW);
-
- try
- {
- resultSet = stmt.executeQuery();
- while (resultSet.next())
- {
- byte[] id = HexUtil.hexToBytes(resultSet.getString(1));
- long size = resultSet.getLong(2);
- Blob blob = resultSet.getBlob(3);
- if (resultSet.wasNull())
- {
- Clob clob = resultSet.getClob(4);
- Reader in = clob.getCharacterStream();
- Writer out = handler.handleClob(id, size);
- if (out != null)
- {
- try
- {
- IOUtil.copyCharacter(in, out, size);
- }
- finally
- {
- IOUtil.close(out);
- }
- }
- }
- else
- {
- InputStream in = blob.getBinaryStream();
- OutputStream out = handler.handleBlob(id, size);
- if (out != null)
- {
- try
- {
- IOUtil.copyBinary(in, out, size);
- }
- finally
- {
- IOUtil.close(out);
- }
- }
- }
- }
- }
- finally
- {
- DBUtil.close(resultSet);
- }
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- @Override
- protected void applyIDMappings(InternalCommitContext context, OMMonitor monitor)
- {
- super.applyIDMappings(context, monitor);
-
- // Remember maxID because it may have to be adjusted if the repository is BACKUP or CLONE. See bug 325097.
- boolean adjustMaxID = !context.getBranchPoint().getBranch().isLocal()
- && getStore().getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE;
-
- IIDHandler idHandler = getStore().getIDHandler();
-
- // Remember CDOIDs of new objects. They are cleared after writeRevisions()
- for (InternalCDORevision revision : context.getNewObjects())
- {
- CDOID id = revision.getID();
- newObjects.add(id);
-
- if (adjustMaxID && idHandler.compare(id, maxID) > 0)
- {
- maxID = id;
- }
- }
- }
-
- @Override
- protected void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID,
- String comment, OMMonitor monitor)
- {
- PreparedStatement stmt = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_CREATE_COMMIT_INFO, ReuseProbability.HIGH);
- stmt.setLong(1, timeStamp);
- stmt.setLong(2, previousTimeStamp);
- stmt.setInt(3, branch.getID());
- stmt.setString(4, userID);
- stmt.setString(5, comment);
-
- DBUtil.update(stmt, true);
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- @Override
- protected void writeRevisionDeltas(InternalCDORevisionDelta[] revisionDeltas, CDOBranch branch, long created,
- OMMonitor monitor)
- {
- IMappingStrategy mappingStrategy = getStore().getMappingStrategy();
-
- if (!mappingStrategy.hasDeltaSupport())
- {
- throw new UnsupportedOperationException("Mapping strategy does not support revision deltas"); //$NON-NLS-1$
- }
-
- monitor.begin(revisionDeltas.length);
- try
- {
- for (InternalCDORevisionDelta delta : revisionDeltas)
- {
- writeRevisionDelta(delta, created, monitor.fork());
- }
- }
- finally
- {
- monitor.done();
- }
- }
-
- protected void writeRevisionDelta(InternalCDORevisionDelta delta, long created, OMMonitor monitor)
- {
- CDOID id = delta.getID();
- EClass eClass = getObjectType(id);
- IClassMappingDeltaSupport mapping = (IClassMappingDeltaSupport)getStore().getMappingStrategy().getClassMapping(
- eClass);
- mapping.writeRevisionDelta(this, delta, created, monitor);
- }
-
- @Override
- protected void writeRevisions(InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor)
- {
- try
- {
- monitor.begin(revisions.length);
- for (InternalCDORevision revision : revisions)
- {
- writeRevision(revision, newObjects.contains(revision.getID()), true, monitor.fork());
- }
- }
- finally
- {
- newObjects.clear();
- monitor.done();
- }
- }
-
- protected void writeRevision(InternalCDORevision revision, boolean mapType, boolean revise, OMMonitor monitor)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Writing revision: {0}", revision); //$NON-NLS-1$
- }
-
- EClass eClass = revision.getEClass();
-
- IClassMapping mapping = getStore().getMappingStrategy().getClassMapping(eClass);
- mapping.writeRevision(this, revision, mapType, revise, monitor);
- }
-
- /*
- * XXX Eike: change API from CDOID[] to CDOIDAndVersion[]
- */
- @Override
- protected void detachObjects(CDOID[] detachedObjects, CDOBranch branch, long timeStamp, OMMonitor monitor)
- {
- IMappingStrategy mappingStrategy = getStore().getMappingStrategy();
- monitor.begin(detachedObjects.length);
-
- try
- {
- InternalCDORevisionManager revisionManager = getStore().getRepository().getRevisionManager();
- for (CDOID id : detachedObjects)
- {
- // TODO when CDOIDAndVersion is available:
- // CDOID id = idAndVersion.getID(); //
- // int version = idAndVersion.getVersion(); //
-
- // but for now:
-
- InternalCDORevision revision = revisionManager.getRevision(id, branch.getHead(), CDORevision.UNCHUNKED,
- CDORevision.DEPTH_NONE, true);
- int version = ObjectUtil.equals(branch, revision.getBranch()) ? revision.getVersion()
- : CDOBranchVersion.FIRST_VERSION;
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Detaching object: {0}", id); //$NON-NLS-1$
- }
-
- EClass eClass = getObjectType(id);
- IClassMapping mapping = mappingStrategy.getClassMapping(eClass);
- mapping.detachObject(this, id, version, branch, timeStamp, monitor.fork());
- }
- }
- finally
- {
- monitor.done();
- }
- }
-
- public Connection getConnection()
- {
- return connection;
- }
-
- @Override
- protected CDOID getNextCDOID(CDORevision revision)
- {
- return getStore().getIDHandler().getNextCDOID(revision);
- }
-
- @Override
- protected void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException
- {
- PreparedStatement stmt = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_WRITE_BLOB, ReuseProbability.MEDIUM);
- stmt.setString(1, HexUtil.bytesToHex(id));
- stmt.setLong(2, size);
- stmt.setBinaryStream(3, inputStream, (int)size);
-
- DBUtil.update(stmt, true);
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- @Override
- protected void writeClob(byte[] id, long size, Reader reader) throws IOException
- {
- PreparedStatement stmt = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_WRITE_CLOB, ReuseProbability.MEDIUM);
- stmt.setString(1, HexUtil.bytesToHex(id));
- stmt.setLong(2, size);
- stmt.setCharacterStream(3, reader, (int)size);
-
- DBUtil.update(stmt, true);
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- @Override
- protected final void doCommit(OMMonitor monitor)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("--- DB COMMIT ---"); //$NON-NLS-1$
- }
-
- Async async = null;
- monitor.begin();
-
- try
- {
- try
- {
- async = monitor.forkAsync();
- getConnection().commit();
-
- if (maxID != CDOID.NULL)
- {
- // See bug 325097
- getStore().getIDHandler().adjustLastObjectID(maxID);
- maxID = CDOID.NULL;
- }
- }
- finally
- {
- if (async != null)
- {
- async.stop();
- }
- }
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- monitor.done();
- }
- }
-
- @Override
- protected final void doRollback(IStoreAccessor.CommitContext commitContext)
- {
- getStore().getMetaDataManager().clearMetaIDMappings();
-
- if (TRACER.isEnabled())
- {
- TRACER.format("--- DB ROLLBACK ---"); //$NON-NLS-1$
- }
-
- try
- {
- getConnection().rollback();
-
- // Bugzilla 298632: Must rollback DBSchema to its prior state and drop the tables
- getStore().getMappingStrategy().removeMapping(getConnection(), commitContext.getNewPackageUnits());
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- }
-
- @Override
- protected void doActivate() throws Exception
- {
- DBStore store = getStore();
- connection = store.getConnection();
- connectionKeepAliveTask = new ConnectionKeepAliveTask();
-
- long keepAlivePeriod = ConnectionKeepAliveTask.EXECUTION_PERIOD;
- Map<String, String> storeProps = store.getProperties();
- if (storeProps != null)
- {
- String value = storeProps.get(IDBStore.Props.CONNECTION_KEEPALIVE_PERIOD);
- if (value != null)
- {
- keepAlivePeriod = Long.parseLong(value) * 60L * 1000L;
- }
- }
-
- store.getConnectionKeepAliveTimer().schedule(connectionKeepAliveTask, keepAlivePeriod, keepAlivePeriod);
-
- // TODO - make this configurable?
- statementCache = CDODBUtil.createStatementCache();
- statementCache.setConnection(connection);
- LifecycleUtil.activate(statementCache);
- }
-
- @Override
- protected void doDeactivate() throws Exception
- {
- LifecycleUtil.deactivate(statementCache);
- connectionKeepAliveTask.cancel();
- DBUtil.close(connection);
- connection = null;
- }
-
- @Override
- protected void doPassivate() throws Exception
- {
- // this is called when the accessor is put back into the pool
- // we want to make sure that no DB lock is held (see Bug 276926)
- connection.rollback();
- }
-
- @Override
- protected void doUnpassivate() throws Exception
- {
- // do nothing
- }
-
- public EPackage[] loadPackageUnit(InternalCDOPackageUnit packageUnit)
- {
- return getStore().getMetaDataManager().loadPackageUnit(getConnection(), packageUnit);
- }
-
- public Collection<InternalCDOPackageUnit> readPackageUnits()
- {
- return getStore().getMetaDataManager().readPackageUnits(getConnection());
- }
-
- public void writePackageUnits(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor)
- {
- monitor.begin(2);
-
- try
- {
- DBStore store = getStore();
- Connection connection = getConnection();
-
- IMetaDataManager metaDataManager = store.getMetaDataManager();
- metaDataManager.writePackageUnits(connection, packageUnits, monitor.fork());
-
- IMappingStrategy mappingStrategy = store.getMappingStrategy();
- mappingStrategy.createMapping(connection, packageUnits, monitor.fork());
- }
- finally
- {
- monitor.done();
- }
- }
-
- public Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo)
- {
- checkBranchingSupport();
- if (branchID == NEW_BRANCH)
- {
- branchID = getStore().getNextBranchID();
- }
- else if (branchID == NEW_LOCAL_BRANCH)
- {
- branchID = getStore().getNextLocalBranchID();
- }
-
- PreparedStatement stmt = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_CREATE_BRANCH, ReuseProbability.LOW);
- stmt.setInt(1, branchID);
- stmt.setString(2, branchInfo.getName());
- stmt.setInt(3, branchInfo.getBaseBranchID());
- stmt.setLong(4, branchInfo.getBaseTimeStamp());
-
- DBUtil.update(stmt, true);
- getConnection().commit();
- return new Pair<Integer, Long>(branchID, branchInfo.getBaseTimeStamp());
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public BranchInfo loadBranch(int branchID)
- {
- checkBranchingSupport();
- PreparedStatement stmt = null;
- ResultSet resultSet = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_LOAD_BRANCH, ReuseProbability.HIGH);
- stmt.setInt(1, branchID);
-
- resultSet = stmt.executeQuery();
- if (!resultSet.next())
- {
- throw new DBException("Branch with ID " + branchID + " does not exist");
- }
-
- String name = resultSet.getString(1);
- int baseBranchID = resultSet.getInt(2);
- long baseTimeStamp = resultSet.getLong(3);
- return new BranchInfo(name, baseBranchID, baseTimeStamp);
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- DBUtil.close(resultSet);
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public SubBranchInfo[] loadSubBranches(int baseID)
- {
- checkBranchingSupport();
- PreparedStatement stmt = null;
- ResultSet resultSet = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_LOAD_SUB_BRANCHES, ReuseProbability.HIGH);
- stmt.setInt(1, baseID);
-
- resultSet = stmt.executeQuery();
- List<SubBranchInfo> result = new ArrayList<SubBranchInfo>();
- while (resultSet.next())
- {
- int id = resultSet.getInt(1);
- String name = resultSet.getString(2);
- long baseTimeStamp = resultSet.getLong(3);
- result.add(new SubBranchInfo(id, name, baseTimeStamp));
- }
-
- return result.toArray(new SubBranchInfo[result.size()]);
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- DBUtil.close(resultSet);
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- private void checkBranchingSupport()
- {
- if (!getStore().getMappingStrategy().hasBranchingSupport())
- {
- throw new UnsupportedOperationException("Mapping strategy does not support branching"); //$NON-NLS-1$
- }
- }
-
- public int loadBranches(int startID, int endID, CDOBranchHandler handler)
- {
- int count = 0;
- PreparedStatement stmt = null;
- ResultSet resultSet = null;
-
- InternalRepository repository = getSession().getManager().getRepository();
- InternalCDOBranchManager branchManager = repository.getBranchManager();
-
- try
- {
- stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_LOAD_BRANCHES, ReuseProbability.HIGH);
- stmt.setInt(1, startID);
- stmt.setInt(2, endID > 0 ? endID : Integer.MAX_VALUE);
-
- resultSet = stmt.executeQuery();
- while (resultSet.next())
- {
- int branchID = resultSet.getInt(1);
- String name = resultSet.getString(2);
- int baseBranchID = resultSet.getInt(3);
- long baseTimeStamp = resultSet.getLong(4);
-
- InternalCDOBranch branch = branchManager.getBranch(branchID, new BranchInfo(name, baseBranchID, baseTimeStamp));
- handler.handleBranch(branch);
- ++count;
- }
-
- return count;
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- DBUtil.close(resultSet);
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler)
- {
- StringBuilder builder = new StringBuilder();
- builder.append("SELECT "); //$NON-NLS-1$
- builder.append(CDODBSchema.COMMIT_INFOS_TIMESTAMP);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.COMMIT_INFOS_PREVIOUS_TIMESTAMP);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.COMMIT_INFOS_USER);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.COMMIT_INFOS_COMMENT);
- if (branch == null)
- {
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.COMMIT_INFOS_BRANCH);
- }
-
- builder.append(" FROM "); //$NON-NLS-1$
- builder.append(CDODBSchema.COMMIT_INFOS);
- boolean where = false;
-
- if (branch != null)
- {
- builder.append(where ? " AND " : " WHERE "); //$NON-NLS-1$ //$NON-NLS-2$
- builder.append(CDODBSchema.COMMIT_INFOS_BRANCH);
- builder.append("="); //$NON-NLS-1$
- builder.append(branch.getID());
- where = true;
- }
-
- if (startTime != CDOBranchPoint.UNSPECIFIED_DATE)
- {
- builder.append(where ? " AND " : " WHERE "); //$NON-NLS-1$ //$NON-NLS-2$
- builder.append(CDODBSchema.COMMIT_INFOS_TIMESTAMP);
- builder.append(">="); //$NON-NLS-1$
- builder.append(startTime);
- where = true;
- }
-
- if (endTime != CDOBranchPoint.UNSPECIFIED_DATE)
- {
- builder.append(where ? " AND " : " WHERE "); //$NON-NLS-1$ //$NON-NLS-2$
- builder.append(CDODBSchema.COMMIT_INFOS_TIMESTAMP);
- builder.append("<="); //$NON-NLS-1$
- builder.append(endTime);
- where = true;
- }
-
- builder.append(" ORDER BY "); //$NON-NLS-1$
- builder.append(CDODBSchema.COMMIT_INFOS_TIMESTAMP);
- String sql = builder.toString();
-
- PreparedStatement stmt = null;
- ResultSet resultSet = null;
-
- InternalRepository repository = getStore().getRepository();
- InternalCDOBranchManager branchManager = repository.getBranchManager();
- InternalCDOCommitInfoManager commitInfoManager = repository.getCommitInfoManager();
-
- try
- {
- stmt = statementCache.getPreparedStatement(sql, ReuseProbability.MEDIUM);
-
- resultSet = stmt.executeQuery();
- while (resultSet.next())
- {
- long timeStamp = resultSet.getLong(1);
- long previousTimeStamp = resultSet.getLong(2);
- String userID = resultSet.getString(3);
- String comment = resultSet.getString(4);
- CDOBranch infoBranch = branch;
- if (infoBranch == null)
- {
- int id = resultSet.getInt(5);
- infoBranch = branchManager.getBranch(id);
- }
-
- CDOCommitInfo commitInfo = commitInfoManager.createCommitInfo(infoBranch, timeStamp, previousTimeStamp, userID,
- comment, null);
- handler.handleCommitInfo(commitInfo);
- }
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- DBUtil.close(resultSet);
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public Set<CDOID> readChangeSet(OMMonitor monitor, CDOChangeSetSegment... segments)
- {
- IMappingStrategy mappingStrategy = getStore().getMappingStrategy();
- return mappingStrategy.readChangeSet(this, monitor, segments);
- }
-
- public void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime,
- CDORevisionHandler handler)
- {
- IMappingStrategy mappingStrategy = getStore().getMappingStrategy();
- mappingStrategy.handleRevisions(this, eClass, branch, timeStamp, exactTime, new DBRevisionHandler(handler));
- }
-
- public void rawExport(CDODataOutput out, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime)
- throws IOException
- {
- DBStore store = getStore();
- if (store.getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE)
- {
- out.writeCDOID(store.getIDHandler().getLastObjectID()); // See bug 325097
- }
-
- String where = " WHERE " + CDODBSchema.BRANCHES_ID + " BETWEEN " + fromBranchID + " AND " + toBranchID;
- DBUtil.serializeTable(out, connection, CDODBSchema.BRANCHES, null, where);
-
- where = " WHERE " + CDODBSchema.COMMIT_INFOS_TIMESTAMP + " BETWEEN " + fromCommitTime + " AND " + toCommitTime;
- DBUtil.serializeTable(out, connection, CDODBSchema.COMMIT_INFOS, null, where);
-
- DurableLockingManager durableLockingManager = store.getDurableLockingManager();
- durableLockingManager.rawExport(connection, out, fromCommitTime, toCommitTime);
-
- IIDHandler idHandler = store.getIDHandler();
- idHandler.rawExport(connection, out, fromCommitTime, toCommitTime);
-
- IMetaDataManager metaDataManager = store.getMetaDataManager();
- metaDataManager.rawExport(connection, out, fromCommitTime, toCommitTime);
-
- IMappingStrategy mappingStrategy = store.getMappingStrategy();
- mappingStrategy.rawExport(this, out, fromBranchID, toBranchID, fromCommitTime, toCommitTime);
- }
-
- public void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime,
- OMMonitor monitor) throws IOException
- {
- DBStore store = getStore();
- IIDHandler idHandler = store.getIDHandler();
- if (store.getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE)
- {
- idHandler.setLastObjectID(in.readCDOID()); // See bug 325097
- }
-
- IMappingStrategy mappingStrategy = store.getMappingStrategy();
- int size = mappingStrategy.getClassMappings().size();
- int commitWork = 5;
- monitor.begin(commitWork + size + commitWork);
-
- Collection<InternalCDOPackageUnit> packageUnits = new HashSet<InternalCDOPackageUnit>();
-
- try
- {
- DBUtil.deserializeTable(in, connection, CDODBSchema.BRANCHES, monitor.fork());
- DBUtil.deserializeTable(in, connection, CDODBSchema.COMMIT_INFOS, monitor.fork());
-
- DurableLockingManager durableLockingManager = store.getDurableLockingManager();
- durableLockingManager.rawImport(connection, in, fromCommitTime, toCommitTime, monitor.fork());
-
- idHandler.rawImport(connection, in, fromCommitTime, toCommitTime, monitor.fork());
-
- rawImportPackageUnits(in, fromCommitTime, toCommitTime, packageUnits, monitor.fork());
-
- mappingStrategy.rawImport(this, in, fromCommitTime, toCommitTime, monitor.fork(size));
-
- rawCommit(commitWork, monitor);
- }
- catch (RuntimeException ex)
- {
- rawRollback(packageUnits);
- throw ex;
- }
- catch (IOException ex)
- {
- rawRollback(packageUnits);
- throw ex;
- }
- finally
- {
- monitor.done();
- }
- }
-
- private void rawRollback(Collection<InternalCDOPackageUnit> packageUnits)
- {
- try
- {
- connection.rollback();
- }
- catch (SQLException ex)
- {
- OM.LOG.error(ex);
- }
-
- getStore().getMappingStrategy().removeMapping(getConnection(),
- packageUnits.toArray(new InternalCDOPackageUnit[packageUnits.size()]));
- }
-
- protected void rawImportPackageUnits(CDODataInput in, long fromCommitTime, long toCommitTime,
- Collection<InternalCDOPackageUnit> packageUnits, OMMonitor monitor) throws IOException
- {
- monitor.begin(2);
-
- try
- {
- DBStore store = getStore();
- IMetaDataManager metaDataManager = store.getMetaDataManager();
-
- packageUnits.addAll(metaDataManager.rawImport(connection, in, fromCommitTime, toCommitTime, monitor.fork()));
-
- InternalRepository repository = store.getRepository();
- InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false);
-
- for (InternalCDOPackageUnit packageUnit : packageUnits)
- {
- packageRegistry.putPackageUnit(packageUnit);
- }
-
- IMappingStrategy mappingStrategy = store.getMappingStrategy();
-
- // Using another connection because CREATE TABLE (which is called in createMapping) on H2 databases does a commit.
- Connection connection2 = null;
- try
- {
- connection2 = store.getConnection();
-
- mappingStrategy.createMapping(connection2,
- packageUnits.toArray(new InternalCDOPackageUnit[packageUnits.size()]), monitor.fork());
- }
- finally
- {
- DBUtil.close(connection2);
- }
- }
- finally
- {
- monitor.done();
- }
- }
-
- public void rawStore(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor)
- {
- writePackageUnits(packageUnits, monitor);
- }
-
- public void rawStore(InternalCDORevision revision, OMMonitor monitor)
- {
- CDOID id = revision.getID();
-
- CDOClassifierRef classifierRef = getStore().getMappingStrategy().readObjectType(this, id);
- boolean isFirstRevision = classifierRef == null;
-
- if (!isFirstRevision)
- {
- EClass eClass = revision.getEClass();
- boolean namesMatch = classifierRef.getClassifierName().equals(eClass.getName());
- boolean packagesMatch = classifierRef.getPackageURI().equals(eClass.getEPackage().getNsURI());
- if (!namesMatch || !packagesMatch)
- {
- throw new IllegalStateException();
- }
- }
-
- writeRevision(revision, isFirstRevision, false, monitor);
- getStore().getIDHandler().adjustLastObjectID(id);
- }
-
- public void rawStore(byte[] id, long size, InputStream inputStream) throws IOException
- {
- writeBlob(id, size, inputStream);
- }
-
- public void rawStore(byte[] id, long size, Reader reader) throws IOException
- {
- writeClob(id, size, reader);
- }
-
- public void rawStore(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment,
- OMMonitor monitor)
- {
- writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, monitor);
- }
-
- @Deprecated
- public void rawDelete(CDOID id, int version, CDOBranch branch, EClass eClass, OMMonitor monitor)
- {
- throw new UnsupportedOperationException();
-
- // IMappingStrategy mappingStrategy = getStore().getMappingStrategy();
- // if (eClass == null)
- // {
- // eClass = getObjectType(id);
- // }
- //
- // IClassMapping mapping = mappingStrategy.getClassMapping(eClass);
- // mapping.detachObject(this, id, version, branch, CDOBranchPoint.UNSPECIFIED_DATE, monitor);
- }
-
- public void rawCommit(double commitWork, OMMonitor monitor)
- {
- monitor.begin();
- Async async = monitor.forkAsync();
-
- try
- {
- connection.commit();
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- async.stop();
- monitor.done();
- }
- }
-
- public LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly,
- Map<CDOID, LockGrade> locks)
- {
- return createLockArea(null, userID, branchPoint, readOnly, locks);
- }
-
- public LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly,
- Map<CDOID, LockGrade> locks)
- {
- DurableLockingManager manager = getStore().getDurableLockingManager();
- return manager.createLockArea(this, durableLockingID, userID, branchPoint, readOnly, locks);
- }
-
- public void updateLockArea(LockArea area)
- {
- DurableLockingManager manager = getStore().getDurableLockingManager();
- manager.updateLockArea(this, area);
- }
-
- public LockArea getLockArea(String durableLockingID) throws LockAreaNotFoundException
- {
- DurableLockingManager manager = getStore().getDurableLockingManager();
- return manager.getLockArea(this, durableLockingID);
- }
-
- public void getLockAreas(String userIDPrefix, Handler handler)
- {
- DurableLockingManager manager = getStore().getDurableLockingManager();
- manager.getLockAreas(this, userIDPrefix, handler);
- }
-
- public void deleteLockArea(String durableLockingID)
- {
- DurableLockingManager manager = getStore().getDurableLockingManager();
- manager.deleteLockArea(this, durableLockingID);
- }
-
- public void lock(String durableLockingID, LockType type, Collection<? extends Object> objectsToLock)
- {
- DurableLockingManager manager = getStore().getDurableLockingManager();
- manager.lock(this, durableLockingID, type, objectsToLock);
- }
-
- public void unlock(String durableLockingID, LockType type, Collection<? extends Object> objectsToUnlock)
- {
- DurableLockingManager manager = getStore().getDurableLockingManager();
- manager.unlock(this, durableLockingID, type, objectsToUnlock);
- }
-
- public void unlock(String durableLockingID)
- {
- DurableLockingManager manager = getStore().getDurableLockingManager();
- manager.unlock(this, durableLockingID);
- }
-
- /**
- * @author Stefan Winkler
- */
- private class ConnectionKeepAliveTask extends TimerTask
- {
- public static final long EXECUTION_PERIOD = 1000 * 60 * 60 * 4; // 4 hours
-
- @Override
- public void run()
- {
- Statement stmt = null;
-
- try
- {
- if (TRACER.isEnabled())
- {
- TRACER.trace("DB connection keep-alive task activated"); //$NON-NLS-1$
- }
-
- stmt = connection.createStatement();
- stmt.executeQuery("SELECT 1 FROM " + CDODBSchema.PROPERTIES); //$NON-NLS-1$
- }
- catch (java.sql.SQLException ex)
- {
- OM.LOG.error("DB connection keep-alive failed", ex); //$NON-NLS-1$
-
- // Assume the connection has failed.
- try
- {
- LifecycleUtil.deactivate(DBStoreAccessor.this);
- LifecycleUtil.activate(DBStoreAccessor.this);
- }
- catch (Exception ex2)
- {
- OM.LOG.error("DB connection reconnect failed", ex2); //$NON-NLS-1$
- }
- }
- catch (Exception ex) // Important: Do not throw any unchecked exceptions to the TimerThread!!!
- {
- OM.LOG.error("DB connection keep-alive failed", ex); //$NON-NLS-1$
- }
- finally
- {
- DBUtil.close(stmt);
- }
- }
- }
-}
+/* + * Copyright (c) 2004 - 2012 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 + * Stefan Winkler - bug 259402 + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + * Andre Dietisheim - bug 256649 + * Caspar De Groot - maintenance + */ +package org.eclipse.emf.cdo.server.internal.db; + +import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation; +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchHandler; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lob.CDOLobHandler; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea.Handler; +import org.eclipse.emf.cdo.common.model.CDOClassifierRef; +import org.eclipse.emf.cdo.common.model.CDOPackageRegistry; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionCacheAdder; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +import org.eclipse.emf.cdo.eresource.EresourcePackage; +import org.eclipse.emf.cdo.server.IQueryHandler; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking2; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.server.db.CDODBUtil; +import org.eclipse.emf.cdo.server.db.IDBStore; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IMetaDataManager; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IClassMapping; +import org.eclipse.emf.cdo.server.db.mapping.IClassMappingAuditSupport; +import org.eclipse.emf.cdo.server.db.mapping.IClassMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.AbstractHorizontalClassMapping; +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.commit.CDOChangeSetSegment; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; +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.DetachedCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; +import org.eclipse.emf.cdo.spi.server.InternalCommitContext; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.StoreAccessor; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.util.HexUtil; +import org.eclipse.net4j.util.ObjectUtil; +import org.eclipse.net4j.util.StringUtil; +import org.eclipse.net4j.util.collection.CloseableIterator; +import org.eclipse.net4j.util.collection.Pair; +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.io.IOUtil; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TimerTask; + +/** + * @author Eike Stepper + */ +public class DBStoreAccessor extends StoreAccessor implements IDBStoreAccessor, DurableLocking2 +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, DBStoreAccessor.class); + + private Connection connection; + + private ConnectionKeepAliveTask connectionKeepAliveTask; + + private IPreparedStatementCache statementCache; + + private Set<CDOID> newObjects = new HashSet<CDOID>(); + + private CDOID maxID = CDOID.NULL; + + public DBStoreAccessor(DBStore store, ISession session) throws DBException + { + super(store, session); + } + + public DBStoreAccessor(DBStore store, ITransaction transaction) throws DBException + { + super(store, transaction); + } + + @Override + public DBStore getStore() + { + return (DBStore)super.getStore(); + } + + public IPreparedStatementCache getStatementCache() + { + return statementCache; + } + + public DBStoreChunkReader createChunkReader(InternalCDORevision revision, EStructuralFeature feature) + { + return new DBStoreChunkReader(this, revision, feature); + } + + /** + * Returns an iterator that iterates over all objects in the store and makes their CDOIDs available for processing. + * This method is supposed to be called very infrequently, for example during the recovery from a crash. + * + * @since 2.0 + * @deprecated Not used by the framework anymore. + */ + @Deprecated + public CloseableIterator<CDOID> readObjectIDs() + { + if (TRACER.isEnabled()) + { + TRACER.trace("Selecting object IDs"); //$NON-NLS-1$ + } + + return getStore().getMappingStrategy().readObjectIDs(this); + } + + public CDOClassifierRef readObjectType(CDOID id) + { + if (TRACER.isEnabled()) + { + TRACER.format("Selecting object type: {0}", id); //$NON-NLS-1$ + } + + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + return mappingStrategy.readObjectType(this, id); + } + + protected EClass getObjectType(CDOID id) + { + IRepository repository = getStore().getRepository(); + if (repository.getRootResourceID().equals(id)) + { + return EresourcePackage.Literals.CDO_RESOURCE; + } + + EClass result = repository.getRevisionManager().getObjectType(id); + if (result != null) + { + return result; + } + + CDOClassifierRef type = readObjectType(id); + if (type != null) + { + CDOPackageRegistry packageRegistry = repository.getPackageRegistry(); + return (EClass)type.resolve(packageRegistry); + } + + return null; + } + + public InternalCDORevision readRevision(CDOID id, CDOBranchPoint branchPoint, int listChunk, + CDORevisionCacheAdder cache) + { + if (TRACER.isEnabled()) + { + TRACER.format("Selecting revision {0} from {1}", id, branchPoint); //$NON-NLS-1$ + } + + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + + EClass eClass = getObjectType(id); + if (eClass != null) + { + InternalCDORevision revision = getStore().createRevision(eClass, id); + revision.setBranchPoint(branchPoint); + + IClassMapping mapping = mappingStrategy.getClassMapping(eClass); + if (mapping.readRevision(this, revision, listChunk)) + { + int version = revision.getVersion(); + if (version < CDOBranchVersion.FIRST_VERSION - 1) + { + return new DetachedCDORevision(eClass, id, revision.getBranch(), -version, revision.getTimeStamp(), + revision.getRevised()); + } + + return revision; + } + } + + // Reading failed - revision does not exist. + return null; + } + + public InternalCDORevision readRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int listChunk, + CDORevisionCacheAdder cache) + { + DBStore store = getStore(); + EClass eClass = getObjectType(id); + + IMappingStrategy mappingStrategy = store.getMappingStrategy(); + IClassMapping mapping = mappingStrategy.getClassMapping(eClass); + + InternalCDORevision revision = store.createRevision(eClass, id); + revision.setVersion(branchVersion.getVersion()); + revision.setBranchPoint(branchVersion.getBranch().getHead()); + + boolean success = false; + + if (mappingStrategy.hasAuditSupport()) + { + if (TRACER.isEnabled()) + { + TRACER.format("Selecting revision {0} from {1}", id, branchVersion); //$NON-NLS-1$ + } + + // if audit support is present, just use the audit method + success = ((IClassMappingAuditSupport)mapping).readRevisionByVersion(this, revision, listChunk); + if (success && revision.getVersion() < CDOBranchVersion.FIRST_VERSION - 1) + { + // it is detached revision + revision = new DetachedCDORevision(eClass, id, revision.getBranch(), -revision.getVersion(), + revision.getTimeStamp(), revision.getRevised()); + + } + } + else + { + // if audit support is not present, we still have to provide a method + // to readRevisionByVersion because TransactionCommitContext.computeDirtyObject + // needs to lookup the base revision for a change. Hence we emulate this + // behavior by getting the current revision and asserting that the version + // has not changed. This is valid because if the version has changed, + // we are in trouble because of a conflict anyways. + if (TRACER.isEnabled()) + { + TRACER.format("Selecting current base revision: {0}", id); //$NON-NLS-1$ + } + + success = mapping.readRevision(this, revision, listChunk); + + if (success && revision.getVersion() != branchVersion.getVersion()) + { + throw new IllegalStateException("Can only retrieve current version " + revision.getVersion() + " for " + id //$NON-NLS-1$ //$NON-NLS-2$ + + " - version requested was " + branchVersion); //$NON-NLS-1$ + } + } + + return success ? revision : null; + } + + /** + * @since 2.0 + */ + public void queryResources(QueryResourcesContext context) + { + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + mappingStrategy.queryResources(this, context); + } + + public void queryXRefs(QueryXRefsContext context) + { + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + mappingStrategy.queryXRefs(this, context); + } + + public IQueryHandler getQueryHandler(CDOQueryInfo info) + { + String queryLanguage = info.getQueryLanguage(); + if (StringUtil.equalsUpperOrLowerCase(queryLanguage, SQLQueryHandler.QUERY_LANGUAGE)) + { + return new SQLQueryHandler(this); + } + + return null; + } + + public void queryLobs(List<byte[]> ids) + { + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_QUERY_LOBS, ReuseProbability.MEDIUM); + + for (Iterator<byte[]> it = ids.iterator(); it.hasNext();) + { + byte[] id = it.next(); + stmt.setString(1, HexUtil.bytesToHex(id)); + + try + { + resultSet = stmt.executeQuery(); + if (!resultSet.next()) + { + it.remove(); + } + } + finally + { + DBUtil.close(resultSet); + } + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public void loadLob(byte[] id, OutputStream out) throws IOException + { + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_LOAD_LOB, ReuseProbability.MEDIUM); + stmt.setString(1, HexUtil.bytesToHex(id)); + + try + { + resultSet = stmt.executeQuery(); + resultSet.next(); + + long size = resultSet.getLong(1); + Blob blob = resultSet.getBlob(2); + if (resultSet.wasNull()) + { + Clob clob = resultSet.getClob(3); + Reader in = clob.getCharacterStream(); + IOUtil.copyCharacter(in, new OutputStreamWriter(out), size); + } + else + { + InputStream in = blob.getBinaryStream(); + IOUtil.copyBinary(in, out, size); + } + } + finally + { + DBUtil.close(resultSet); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException + { + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_HANDLE_LOBS, ReuseProbability.LOW); + + try + { + resultSet = stmt.executeQuery(); + while (resultSet.next()) + { + byte[] id = HexUtil.hexToBytes(resultSet.getString(1)); + long size = resultSet.getLong(2); + Blob blob = resultSet.getBlob(3); + if (resultSet.wasNull()) + { + Clob clob = resultSet.getClob(4); + Reader in = clob.getCharacterStream(); + Writer out = handler.handleClob(id, size); + if (out != null) + { + try + { + IOUtil.copyCharacter(in, out, size); + } + finally + { + IOUtil.close(out); + } + } + } + else + { + InputStream in = blob.getBinaryStream(); + OutputStream out = handler.handleBlob(id, size); + if (out != null) + { + try + { + IOUtil.copyBinary(in, out, size); + } + finally + { + IOUtil.close(out); + } + } + } + } + } + finally + { + DBUtil.close(resultSet); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + protected void applyIDMappings(InternalCommitContext context, OMMonitor monitor) + { + super.applyIDMappings(context, monitor); + + // Remember maxID because it may have to be adjusted if the repository is BACKUP or CLONE. See bug 325097. + boolean adjustMaxID = !context.getBranchPoint().getBranch().isLocal() + && getStore().getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE; + + IIDHandler idHandler = getStore().getIDHandler(); + + // Remember CDOIDs of new objects. They are cleared after writeRevisions() + for (InternalCDORevision revision : context.getNewObjects()) + { + CDOID id = revision.getID(); + newObjects.add(id); + + if (adjustMaxID && idHandler.compare(id, maxID) > 0) + { + maxID = id; + } + } + } + + @Override + protected void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, + String comment, OMMonitor monitor) + { + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_CREATE_COMMIT_INFO, ReuseProbability.HIGH); + stmt.setLong(1, timeStamp); + stmt.setLong(2, previousTimeStamp); + stmt.setInt(3, branch.getID()); + stmt.setString(4, userID); + stmt.setString(5, comment); + + DBUtil.update(stmt, true); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + protected void writeRevisionDeltas(InternalCDORevisionDelta[] revisionDeltas, CDOBranch branch, long created, + OMMonitor monitor) + { + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + + if (!mappingStrategy.hasDeltaSupport()) + { + throw new UnsupportedOperationException("Mapping strategy does not support revision deltas"); //$NON-NLS-1$ + } + + monitor.begin(revisionDeltas.length); + try + { + for (InternalCDORevisionDelta delta : revisionDeltas) + { + writeRevisionDelta(delta, created, monitor.fork()); + } + } + finally + { + monitor.done(); + } + } + + protected void writeRevisionDelta(InternalCDORevisionDelta delta, long created, OMMonitor monitor) + { + CDOID id = delta.getID(); + EClass eClass = getObjectType(id); + IClassMappingDeltaSupport mapping = (IClassMappingDeltaSupport)getStore().getMappingStrategy().getClassMapping( + eClass); + mapping.writeRevisionDelta(this, delta, created, monitor); + } + + @Override + protected void writeRevisions(InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor) + { + try + { + monitor.begin(revisions.length); + for (InternalCDORevision revision : revisions) + { + writeRevision(revision, newObjects.contains(revision.getID()), true, monitor.fork()); + } + } + finally + { + newObjects.clear(); + monitor.done(); + } + } + + protected void writeRevision(InternalCDORevision revision, boolean mapType, boolean revise, OMMonitor monitor) + { + if (TRACER.isEnabled()) + { + TRACER.format("Writing revision: {0}", revision); //$NON-NLS-1$ + } + + EClass eClass = revision.getEClass(); + + IClassMapping mapping = getStore().getMappingStrategy().getClassMapping(eClass); + mapping.writeRevision(this, revision, mapType, revise, monitor); + } + + /* + * XXX Eike: change API from CDOID[] to CDOIDAndVersion[] + */ + @Override + protected void detachObjects(CDOID[] detachedObjects, CDOBranch branch, long timeStamp, OMMonitor monitor) + { + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + monitor.begin(detachedObjects.length); + + try + { + InternalCDORevisionManager revisionManager = getStore().getRepository().getRevisionManager(); + for (CDOID id : detachedObjects) + { + // TODO when CDOIDAndVersion is available: + // CDOID id = idAndVersion.getID(); // + // int version = idAndVersion.getVersion(); // + + // but for now: + + InternalCDORevision revision = revisionManager.getRevision(id, branch.getHead(), CDORevision.UNCHUNKED, + CDORevision.DEPTH_NONE, true); + int version = ObjectUtil.equals(branch, revision.getBranch()) ? revision.getVersion() + : CDOBranchVersion.FIRST_VERSION; + + if (TRACER.isEnabled()) + { + TRACER.format("Detaching object: {0}", id); //$NON-NLS-1$ + } + + EClass eClass = getObjectType(id); + IClassMapping mapping = mappingStrategy.getClassMapping(eClass); + mapping.detachObject(this, id, version, branch, timeStamp, monitor.fork()); + } + } + finally + { + monitor.done(); + } + } + + public Connection getConnection() + { + return connection; + } + + @Override + protected CDOID getNextCDOID(CDORevision revision) + { + return getStore().getIDHandler().getNextCDOID(revision); + } + + @Override + protected void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException + { + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_WRITE_BLOB, ReuseProbability.MEDIUM); + stmt.setString(1, HexUtil.bytesToHex(id)); + stmt.setLong(2, size); + stmt.setBinaryStream(3, inputStream, (int)size); + + DBUtil.update(stmt, true); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + protected void writeClob(byte[] id, long size, Reader reader) throws IOException + { + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_WRITE_CLOB, ReuseProbability.MEDIUM); + stmt.setString(1, HexUtil.bytesToHex(id)); + stmt.setLong(2, size); + stmt.setCharacterStream(3, reader, (int)size); + + DBUtil.update(stmt, true); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + protected final void doCommit(OMMonitor monitor) + { + if (TRACER.isEnabled()) + { + TRACER.format("--- DB COMMIT ---"); //$NON-NLS-1$ + } + + Async async = null; + monitor.begin(); + + try + { + try + { + async = monitor.forkAsync(); + getConnection().commit(); + + if (maxID != CDOID.NULL) + { + // See bug 325097 + getStore().getIDHandler().adjustLastObjectID(maxID); + maxID = CDOID.NULL; + } + } + finally + { + if (async != null) + { + async.stop(); + } + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + monitor.done(); + } + } + + @Override + protected final void doRollback(IStoreAccessor.CommitContext commitContext) + { + getStore().getMetaDataManager().clearMetaIDMappings(); + + if (TRACER.isEnabled()) + { + TRACER.format("--- DB ROLLBACK ---"); //$NON-NLS-1$ + } + + try + { + getConnection().rollback(); + + // Bugzilla 298632: Must rollback DBSchema to its prior state and drop the tables + getStore().getMappingStrategy().removeMapping(getConnection(), commitContext.getNewPackageUnits()); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } + + @Override + protected void doActivate() throws Exception + { + DBStore store = getStore(); + connection = store.getConnection(); + connectionKeepAliveTask = new ConnectionKeepAliveTask(); + + long keepAlivePeriod = ConnectionKeepAliveTask.EXECUTION_PERIOD; + Map<String, String> storeProps = store.getProperties(); + if (storeProps != null) + { + String value = storeProps.get(IDBStore.Props.CONNECTION_KEEPALIVE_PERIOD); + if (value != null) + { + keepAlivePeriod = Long.parseLong(value) * 60L * 1000L; + } + } + + store.getConnectionKeepAliveTimer().schedule(connectionKeepAliveTask, keepAlivePeriod, keepAlivePeriod); + + // TODO - make this configurable? + statementCache = CDODBUtil.createStatementCache(); + statementCache.setConnection(connection); + LifecycleUtil.activate(statementCache); + } + + @Override + protected void doDeactivate() throws Exception + { + LifecycleUtil.deactivate(statementCache); + connectionKeepAliveTask.cancel(); + DBUtil.close(connection); + connection = null; + } + + @Override + protected void doPassivate() throws Exception + { + // this is called when the accessor is put back into the pool + // we want to make sure that no DB lock is held (see Bug 276926) + connection.rollback(); + } + + @Override + protected void doUnpassivate() throws Exception + { + // do nothing + } + + public EPackage[] loadPackageUnit(InternalCDOPackageUnit packageUnit) + { + return getStore().getMetaDataManager().loadPackageUnit(getConnection(), packageUnit); + } + + public Collection<InternalCDOPackageUnit> readPackageUnits() + { + return getStore().getMetaDataManager().readPackageUnits(getConnection()); + } + + public void writePackageUnits(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor) + { + monitor.begin(2); + + try + { + DBStore store = getStore(); + Connection connection = getConnection(); + + IMetaDataManager metaDataManager = store.getMetaDataManager(); + metaDataManager.writePackageUnits(connection, packageUnits, monitor.fork()); + + IMappingStrategy mappingStrategy = store.getMappingStrategy(); + mappingStrategy.createMapping(connection, packageUnits, monitor.fork()); + } + finally + { + monitor.done(); + } + } + + public Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo) + { + checkBranchingSupport(); + if (branchID == NEW_BRANCH) + { + branchID = getStore().getNextBranchID(); + } + else if (branchID == NEW_LOCAL_BRANCH) + { + branchID = getStore().getNextLocalBranchID(); + } + + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_CREATE_BRANCH, ReuseProbability.LOW); + stmt.setInt(1, branchID); + stmt.setString(2, branchInfo.getName()); + stmt.setInt(3, branchInfo.getBaseBranchID()); + stmt.setLong(4, branchInfo.getBaseTimeStamp()); + + DBUtil.update(stmt, true); + getConnection().commit(); + return new Pair<Integer, Long>(branchID, branchInfo.getBaseTimeStamp()); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public BranchInfo loadBranch(int branchID) + { + checkBranchingSupport(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_LOAD_BRANCH, ReuseProbability.HIGH); + stmt.setInt(1, branchID); + + resultSet = stmt.executeQuery(); + if (!resultSet.next()) + { + throw new DBException("Branch with ID " + branchID + " does not exist"); + } + + String name = resultSet.getString(1); + int baseBranchID = resultSet.getInt(2); + long baseTimeStamp = resultSet.getLong(3); + return new BranchInfo(name, baseBranchID, baseTimeStamp); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + public SubBranchInfo[] loadSubBranches(int baseID) + { + checkBranchingSupport(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_LOAD_SUB_BRANCHES, ReuseProbability.HIGH); + stmt.setInt(1, baseID); + + resultSet = stmt.executeQuery(); + List<SubBranchInfo> result = new ArrayList<SubBranchInfo>(); + while (resultSet.next()) + { + int id = resultSet.getInt(1); + String name = resultSet.getString(2); + long baseTimeStamp = resultSet.getLong(3); + result.add(new SubBranchInfo(id, name, baseTimeStamp)); + } + + return result.toArray(new SubBranchInfo[result.size()]); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + private void checkBranchingSupport() + { + if (!getStore().getMappingStrategy().hasBranchingSupport()) + { + throw new UnsupportedOperationException("Mapping strategy does not support branching"); //$NON-NLS-1$ + } + } + + public int loadBranches(int startID, int endID, CDOBranchHandler handler) + { + int count = 0; + PreparedStatement stmt = null; + ResultSet resultSet = null; + + InternalRepository repository = getSession().getManager().getRepository(); + InternalCDOBranchManager branchManager = repository.getBranchManager(); + + try + { + stmt = statementCache.getPreparedStatement(CDODBSchema.SQL_LOAD_BRANCHES, ReuseProbability.HIGH); + stmt.setInt(1, startID); + stmt.setInt(2, endID > 0 ? endID : Integer.MAX_VALUE); + + resultSet = stmt.executeQuery(); + while (resultSet.next()) + { + int branchID = resultSet.getInt(1); + String name = resultSet.getString(2); + int baseBranchID = resultSet.getInt(3); + long baseTimeStamp = resultSet.getLong(4); + + InternalCDOBranch branch = branchManager.getBranch(branchID, new BranchInfo(name, baseBranchID, baseTimeStamp)); + handler.handleBranch(branch); + ++count; + } + + return count; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + public void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler) + { + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.COMMIT_INFOS_TIMESTAMP); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.COMMIT_INFOS_PREVIOUS_TIMESTAMP); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.COMMIT_INFOS_USER); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.COMMIT_INFOS_COMMENT); + if (branch == null) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.COMMIT_INFOS_BRANCH); + } + + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(CDODBSchema.COMMIT_INFOS); + boolean where = false; + + if (branch != null) + { + builder.append(where ? " AND " : " WHERE "); //$NON-NLS-1$ //$NON-NLS-2$ + builder.append(CDODBSchema.COMMIT_INFOS_BRANCH); + builder.append("="); //$NON-NLS-1$ + builder.append(branch.getID()); + where = true; + } + + if (startTime != CDOBranchPoint.UNSPECIFIED_DATE) + { + builder.append(where ? " AND " : " WHERE "); //$NON-NLS-1$ //$NON-NLS-2$ + builder.append(CDODBSchema.COMMIT_INFOS_TIMESTAMP); + builder.append(">="); //$NON-NLS-1$ + builder.append(startTime); + where = true; + } + + if (endTime != CDOBranchPoint.UNSPECIFIED_DATE) + { + builder.append(where ? " AND " : " WHERE "); //$NON-NLS-1$ //$NON-NLS-2$ + builder.append(CDODBSchema.COMMIT_INFOS_TIMESTAMP); + builder.append("<="); //$NON-NLS-1$ + builder.append(endTime); + where = true; + } + + builder.append(" ORDER BY "); //$NON-NLS-1$ + builder.append(CDODBSchema.COMMIT_INFOS_TIMESTAMP); + String sql = builder.toString(); + + PreparedStatement stmt = null; + ResultSet resultSet = null; + + InternalRepository repository = getStore().getRepository(); + InternalCDOBranchManager branchManager = repository.getBranchManager(); + InternalCDOCommitInfoManager commitInfoManager = repository.getCommitInfoManager(); + + try + { + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.MEDIUM); + + resultSet = stmt.executeQuery(); + while (resultSet.next()) + { + long timeStamp = resultSet.getLong(1); + long previousTimeStamp = resultSet.getLong(2); + String userID = resultSet.getString(3); + String comment = resultSet.getString(4); + CDOBranch infoBranch = branch; + if (infoBranch == null) + { + int id = resultSet.getInt(5); + infoBranch = branchManager.getBranch(id); + } + + CDOCommitInfo commitInfo = commitInfoManager.createCommitInfo(infoBranch, timeStamp, previousTimeStamp, userID, + comment, null); + handler.handleCommitInfo(commitInfo); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + public Set<CDOID> readChangeSet(OMMonitor monitor, CDOChangeSetSegment... segments) + { + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + return mappingStrategy.readChangeSet(this, monitor, segments); + } + + public void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime, + CDORevisionHandler handler) + { + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + mappingStrategy.handleRevisions(this, eClass, branch, timeStamp, exactTime, new DBRevisionHandler(handler)); + } + + public void rawExport(CDODataOutput out, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime) + throws IOException + { + DBStore store = getStore(); + if (store.getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE) + { + out.writeCDOID(store.getIDHandler().getLastObjectID()); // See bug 325097 + } + + String where = " WHERE " + CDODBSchema.BRANCHES_ID + " BETWEEN " + fromBranchID + " AND " + toBranchID; + DBUtil.serializeTable(out, connection, CDODBSchema.BRANCHES, null, where); + + where = " WHERE " + CDODBSchema.COMMIT_INFOS_TIMESTAMP + " BETWEEN " + fromCommitTime + " AND " + toCommitTime; + DBUtil.serializeTable(out, connection, CDODBSchema.COMMIT_INFOS, null, where); + + DurableLockingManager durableLockingManager = store.getDurableLockingManager(); + durableLockingManager.rawExport(connection, out, fromCommitTime, toCommitTime); + + IIDHandler idHandler = store.getIDHandler(); + idHandler.rawExport(connection, out, fromCommitTime, toCommitTime); + + IMetaDataManager metaDataManager = store.getMetaDataManager(); + metaDataManager.rawExport(connection, out, fromCommitTime, toCommitTime); + + IMappingStrategy mappingStrategy = store.getMappingStrategy(); + mappingStrategy.rawExport(this, out, fromBranchID, toBranchID, fromCommitTime, toCommitTime); + } + + public void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime, + OMMonitor monitor) throws IOException + { + DBStore store = getStore(); + IIDHandler idHandler = store.getIDHandler(); + if (store.getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE) + { + idHandler.setLastObjectID(in.readCDOID()); // See bug 325097 + } + + IMappingStrategy mappingStrategy = store.getMappingStrategy(); + int size = mappingStrategy.getClassMappings().size(); + int commitWork = 5; + monitor.begin(commitWork + size + commitWork); + + Collection<InternalCDOPackageUnit> packageUnits = new HashSet<InternalCDOPackageUnit>(); + + try + { + DBUtil.deserializeTable(in, connection, CDODBSchema.BRANCHES, monitor.fork()); + DBUtil.deserializeTable(in, connection, CDODBSchema.COMMIT_INFOS, monitor.fork()); + + DurableLockingManager durableLockingManager = store.getDurableLockingManager(); + durableLockingManager.rawImport(connection, in, fromCommitTime, toCommitTime, monitor.fork()); + + idHandler.rawImport(connection, in, fromCommitTime, toCommitTime, monitor.fork()); + + rawImportPackageUnits(in, fromCommitTime, toCommitTime, packageUnits, monitor.fork()); + + mappingStrategy.rawImport(this, in, fromCommitTime, toCommitTime, monitor.fork(size)); + + rawCommit(commitWork, monitor); + } + catch (RuntimeException ex) + { + rawRollback(packageUnits); + throw ex; + } + catch (IOException ex) + { + rawRollback(packageUnits); + throw ex; + } + finally + { + monitor.done(); + } + } + + private void rawRollback(Collection<InternalCDOPackageUnit> packageUnits) + { + try + { + connection.rollback(); + } + catch (SQLException ex) + { + OM.LOG.error(ex); + } + + getStore().getMappingStrategy().removeMapping(getConnection(), + packageUnits.toArray(new InternalCDOPackageUnit[packageUnits.size()])); + } + + protected void rawImportPackageUnits(CDODataInput in, long fromCommitTime, long toCommitTime, + Collection<InternalCDOPackageUnit> packageUnits, OMMonitor monitor) throws IOException + { + monitor.begin(2); + + try + { + DBStore store = getStore(); + IMetaDataManager metaDataManager = store.getMetaDataManager(); + + packageUnits.addAll(metaDataManager.rawImport(connection, in, fromCommitTime, toCommitTime, monitor.fork())); + + InternalRepository repository = store.getRepository(); + InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false); + + for (InternalCDOPackageUnit packageUnit : packageUnits) + { + packageRegistry.putPackageUnit(packageUnit); + } + + IMappingStrategy mappingStrategy = store.getMappingStrategy(); + + // Using another connection because CREATE TABLE (which is called in createMapping) on H2 databases does a commit. + Connection connection2 = null; + try + { + connection2 = store.getConnection(); + + mappingStrategy.createMapping(connection2, + packageUnits.toArray(new InternalCDOPackageUnit[packageUnits.size()]), monitor.fork()); + } + finally + { + DBUtil.close(connection2); + } + } + finally + { + monitor.done(); + } + } + + public void rawStore(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor) + { + writePackageUnits(packageUnits, monitor); + } + + public void rawStore(InternalCDORevision revision, OMMonitor monitor) + { + CDOID id = revision.getID(); + + CDOClassifierRef classifierRef = getStore().getMappingStrategy().readObjectType(this, id); + boolean isFirstRevision = classifierRef == null; + + if (!isFirstRevision) + { + EClass eClass = revision.getEClass(); + boolean namesMatch = classifierRef.getClassifierName().equals(eClass.getName()); + boolean packagesMatch = classifierRef.getPackageURI().equals(eClass.getEPackage().getNsURI()); + if (!namesMatch || !packagesMatch) + { + throw new IllegalStateException(); + } + } + + writeRevision(revision, isFirstRevision, false, monitor); + getStore().getIDHandler().adjustLastObjectID(id); + } + + public void rawStore(byte[] id, long size, InputStream inputStream) throws IOException + { + writeBlob(id, size, inputStream); + } + + public void rawStore(byte[] id, long size, Reader reader) throws IOException + { + writeClob(id, size, reader); + } + + public void rawStore(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, + OMMonitor monitor) + { + writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, monitor); + } + + public void rawDelete(CDOID id, int version, CDOBranch branch, EClass eClass, OMMonitor monitor) + { + if (eClass == null) + { + eClass = getObjectType(id); + } + + IMappingStrategy mappingStrategy = getStore().getMappingStrategy(); + IClassMapping mapping = mappingStrategy.getClassMapping(eClass); + if (mapping instanceof AbstractHorizontalClassMapping) + { + AbstractHorizontalClassMapping m = (AbstractHorizontalClassMapping)mapping; + m.rawDelete(this, id, version, branch, monitor); + } + else + { + throw new UnsupportedOperationException("rawDelete() is not supported by " + mapping.getClass().getName()); + } + } + + public void rawCommit(double commitWork, OMMonitor monitor) + { + monitor.begin(); + Async async = monitor.forkAsync(); + + try + { + connection.commit(); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + async.stop(); + monitor.done(); + } + } + + public LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, + Map<CDOID, LockGrade> locks) + { + return createLockArea(null, userID, branchPoint, readOnly, locks); + } + + public LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly, + Map<CDOID, LockGrade> locks) + { + DurableLockingManager manager = getStore().getDurableLockingManager(); + return manager.createLockArea(this, durableLockingID, userID, branchPoint, readOnly, locks); + } + + public void updateLockArea(LockArea area) + { + DurableLockingManager manager = getStore().getDurableLockingManager(); + manager.updateLockArea(this, area); + } + + public LockArea getLockArea(String durableLockingID) throws LockAreaNotFoundException + { + DurableLockingManager manager = getStore().getDurableLockingManager(); + return manager.getLockArea(this, durableLockingID); + } + + public void getLockAreas(String userIDPrefix, Handler handler) + { + DurableLockingManager manager = getStore().getDurableLockingManager(); + manager.getLockAreas(this, userIDPrefix, handler); + } + + public void deleteLockArea(String durableLockingID) + { + DurableLockingManager manager = getStore().getDurableLockingManager(); + manager.deleteLockArea(this, durableLockingID); + } + + public void lock(String durableLockingID, LockType type, Collection<? extends Object> objectsToLock) + { + DurableLockingManager manager = getStore().getDurableLockingManager(); + manager.lock(this, durableLockingID, type, objectsToLock); + } + + public void unlock(String durableLockingID, LockType type, Collection<? extends Object> objectsToUnlock) + { + DurableLockingManager manager = getStore().getDurableLockingManager(); + manager.unlock(this, durableLockingID, type, objectsToUnlock); + } + + public void unlock(String durableLockingID) + { + DurableLockingManager manager = getStore().getDurableLockingManager(); + manager.unlock(this, durableLockingID); + } + + /** + * @author Stefan Winkler + */ + private class ConnectionKeepAliveTask extends TimerTask + { + public static final long EXECUTION_PERIOD = 1000 * 60 * 60 * 4; // 4 hours + + @Override + public void run() + { + Statement stmt = null; + + try + { + if (TRACER.isEnabled()) + { + TRACER.trace("DB connection keep-alive task activated"); //$NON-NLS-1$ + } + + stmt = connection.createStatement(); + stmt.executeQuery("SELECT 1 FROM " + CDODBSchema.PROPERTIES); //$NON-NLS-1$ + } + catch (java.sql.SQLException ex) + { + OM.LOG.error("DB connection keep-alive failed", ex); //$NON-NLS-1$ + + // Assume the connection has failed. + try + { + LifecycleUtil.deactivate(DBStoreAccessor.this); + LifecycleUtil.activate(DBStoreAccessor.this); + } + catch (Exception ex2) + { + OM.LOG.error("DB connection reconnect failed", ex2); //$NON-NLS-1$ + } + } + catch (Exception ex) // Important: Do not throw any unchecked exceptions to the TimerThread!!! + { + OM.LOG.error("DB connection keep-alive failed", ex); //$NON-NLS-1$ + } + finally + { + DBUtil.close(stmt); + } + } + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java index 09a24caaf2..fb3663cd5a 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java @@ -1,870 +1,914 @@ -/*
- * Copyright (c) 2004 - 2012 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
- * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
- * Stefan Winkler - 249610: [DB] Support external references (Implementation)
- * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy
- */
-package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
-
-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.CDOBranchVersion;
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.model.CDOModelUtil;
-import org.eclipse.emf.cdo.common.revision.CDOList;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
-import org.eclipse.emf.cdo.common.revision.CDORevisionManager;
-import org.eclipse.emf.cdo.eresource.EresourcePackage;
-import org.eclipse.emf.cdo.server.IRepository;
-import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
-import org.eclipse.emf.cdo.server.db.IDBStore;
-import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
-import org.eclipse.emf.cdo.server.db.IIDHandler;
-import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
-import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
-import org.eclipse.emf.cdo.server.db.mapping.IClassMapping;
-import org.eclipse.emf.cdo.server.db.mapping.IListMapping;
-import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
-import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
-import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
-import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
-import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDOList;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
-
-import org.eclipse.net4j.db.DBException;
-import org.eclipse.net4j.db.DBType;
-import org.eclipse.net4j.db.DBUtil;
-import org.eclipse.net4j.db.ddl.IDBField;
-import org.eclipse.net4j.db.ddl.IDBIndex;
-import org.eclipse.net4j.db.ddl.IDBTable;
-import org.eclipse.net4j.util.om.monitor.OMMonitor;
-import org.eclipse.net4j.util.om.monitor.OMMonitor.Async;
-import org.eclipse.net4j.util.om.trace.ContextTracer;
-
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EReference;
-import org.eclipse.emf.ecore.EStructuralFeature;
-import org.eclipse.emf.ecore.util.FeatureMapUtil;
-
-import org.eclipse.core.runtime.Assert;
-
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * @author Eike Stepper
- * @since 2.0
- */
-public abstract class AbstractHorizontalClassMapping implements IClassMapping
-{
- private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractHorizontalClassMapping.class);
-
- private EClass eClass;
-
- private IDBTable table;
-
- private AbstractHorizontalMappingStrategy mappingStrategy;
-
- private List<ITypeMapping> valueMappings;
-
- private List<IListMapping> listMappings;
-
- private Map<EStructuralFeature, String> listSizeFields;
-
- private Map<EStructuralFeature, String> unsettableFields;
-
- private String sqlSelectForHandle;
-
- private String sqlSelectForChangeSet;
-
- public AbstractHorizontalClassMapping(AbstractHorizontalMappingStrategy mappingStrategy, EClass eClass)
- {
- this.mappingStrategy = mappingStrategy;
- this.eClass = eClass;
-
- initTable();
- initFeatures();
- initSQLStrings();
- }
-
- private void initTable()
- {
- IDBStore store = getMappingStrategy().getStore();
- DBType idType = store.getIDHandler().getDBType();
-
- String name = getMappingStrategy().getTableName(eClass);
- table = store.getDBSchema().addTable(name);
-
- IDBField idField = table.addField(CDODBSchema.ATTRIBUTES_ID, idType, true);
- IDBField versionField = table.addField(CDODBSchema.ATTRIBUTES_VERSION, DBType.INTEGER, true);
-
- IDBField branchField = addBranchingField(table);
-
- table.addField(CDODBSchema.ATTRIBUTES_CREATED, DBType.BIGINT, true);
- IDBField revisedField = table.addField(CDODBSchema.ATTRIBUTES_REVISED, DBType.BIGINT, true);
- table.addField(CDODBSchema.ATTRIBUTES_RESOURCE, idType, true);
- table.addField(CDODBSchema.ATTRIBUTES_CONTAINER, idType, true);
- table.addField(CDODBSchema.ATTRIBUTES_FEATURE, DBType.INTEGER, true);
-
- if (branchField != null)
- {
- table.addIndex(IDBIndex.Type.UNIQUE, idField, versionField, branchField);
- }
- else
- {
- table.addIndex(IDBIndex.Type.UNIQUE, idField, versionField);
- }
-
- table.addIndex(IDBIndex.Type.NON_UNIQUE, idField, revisedField);
- }
-
- protected IDBField addBranchingField(IDBTable table)
- {
- return null;
- }
-
- private void initFeatures()
- {
- EStructuralFeature[] features = CDOModelUtil.getAllPersistentFeatures(eClass);
-
- if (features == null)
- {
- valueMappings = Collections.emptyList();
- listMappings = Collections.emptyList();
- }
- else
- {
- valueMappings = createValueMappings(features);
- listMappings = createListMappings(features);
- }
- }
-
- private void initSQLStrings()
- {
- // ----------- Select all revisions (for handleRevisions) ---
- StringBuilder builder = new StringBuilder("SELECT "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_ID);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_VERSION);
- builder.append(" FROM "); //$NON-NLS-1$
- builder.append(getTable());
- sqlSelectForHandle = builder.toString();
-
- // ----------- Select all revisions (for readChangeSet) ---
- builder = new StringBuilder("SELECT DISTINCT "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_ID);
- builder.append(" FROM "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" WHERE "); //$NON-NLS-1$
- sqlSelectForChangeSet = builder.toString();
- }
-
- private List<ITypeMapping> createValueMappings(EStructuralFeature[] features)
- {
- List<ITypeMapping> mappings = new ArrayList<ITypeMapping>();
- for (EStructuralFeature feature : features)
- {
- if (!feature.isMany())
- {
- ITypeMapping mapping = mappingStrategy.createValueMapping(feature);
- mapping.createDBField(getTable());
- mappings.add(mapping);
-
- if (feature.isUnsettable())
- {
- String fieldName = mappingStrategy.getUnsettableFieldName(feature);
- if (unsettableFields == null)
- {
- unsettableFields = new LinkedHashMap<EStructuralFeature, String>();
- }
-
- unsettableFields.put(feature, fieldName);
- }
- }
- }
-
- // add unsettable fields to end of table
- if (unsettableFields != null)
- {
- for (String fieldName : unsettableFields.values())
- {
- table.addField(fieldName, DBType.BOOLEAN, 1);
- }
- }
-
- return mappings;
- }
-
- private List<IListMapping> createListMappings(EStructuralFeature[] features)
- {
- List<IListMapping> listMappings = new ArrayList<IListMapping>();
- for (EStructuralFeature feature : features)
- {
- if (feature.isMany())
- {
- IListMapping mapping = null;
- if (FeatureMapUtil.isFeatureMap(feature))
- {
- mapping = mappingStrategy.createFeatureMapMapping(eClass, feature);
- }
- else
- {
- mapping = mappingStrategy.createListMapping(eClass, feature);
- }
-
- listMappings.add(mapping);
-
- // add field for list sizes
- createListSizeField(feature);
- }
- }
-
- return listMappings;
- }
-
- /**
- * Create an integer field in the attribute tabel for the list size of the associated list mapping.
- */
- private void createListSizeField(EStructuralFeature feature)
- {
- if (listSizeFields == null)
- {
- listSizeFields = new LinkedHashMap<EStructuralFeature, String>();
- }
-
- String fieldName = mappingStrategy.getFieldName(feature);
- table.addField(fieldName, DBType.INTEGER);
-
- listSizeFields.put(feature, fieldName);
- }
-
- /**
- * Read the revision's values from the DB.
- *
- * @return <code>true</code> if the revision has been read successfully.<br>
- * <code>false</code> if the revision does not exist in the DB.
- */
- protected final boolean readValuesFromStatement(PreparedStatement stmt, InternalCDORevision revision,
- IDBStoreAccessor accessor)
- {
- ResultSet resultSet = null;
-
- try
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Executing Query: {0}", stmt.toString()); //$NON-NLS-1$
- }
-
- stmt.setMaxRows(1); // Optimization: only 1 row
-
- resultSet = stmt.executeQuery();
- if (!resultSet.next())
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Resultset was empty"); //$NON-NLS-1$
- }
-
- return false;
- }
-
- revision.setVersion(resultSet.getInt(CDODBSchema.ATTRIBUTES_VERSION));
-
- long timeStamp = resultSet.getLong(CDODBSchema.ATTRIBUTES_CREATED);
-
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- CDOBranchPoint branchPoint = revision.getBranch().getPoint(timeStamp);
-
- revision.setBranchPoint(branchPoint);
- revision.setRevised(resultSet.getLong(CDODBSchema.ATTRIBUTES_REVISED));
- revision.setResourceID(idHandler.getCDOID(resultSet, CDODBSchema.ATTRIBUTES_RESOURCE));
- revision.setContainerID(idHandler.getCDOID(resultSet, CDODBSchema.ATTRIBUTES_CONTAINER));
- revision.setContainingFeatureID(resultSet.getInt(CDODBSchema.ATTRIBUTES_FEATURE));
-
- for (ITypeMapping mapping : valueMappings)
- {
- EStructuralFeature feature = mapping.getFeature();
- if (feature.isUnsettable())
- {
- if (!resultSet.getBoolean(unsettableFields.get(feature)))
- {
- // isSet==false -- setValue: null
- revision.setValue(feature, null);
- continue;
- }
- }
-
- mapping.readValueToRevision(resultSet, revision);
- }
-
- if (listSizeFields != null)
- {
- for (Map.Entry<EStructuralFeature, String> listSizeEntry : listSizeFields.entrySet())
- {
- EStructuralFeature feature = listSizeEntry.getKey();
- String fieldName = listSizeEntry.getValue();
- int size = resultSet.getInt(fieldName);
-
- // ensure the listSize (TODO: remove assertion)
- CDOList list = revision.getList(feature, size);
-
- for (int i = 0; i < size; i++)
- {
- list.add(InternalCDOList.UNINITIALIZED);
- }
-
- if (list.size() != size)
- {
- Assert.isTrue(false);
- }
- }
- }
-
- return true;
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- DBUtil.close(resultSet);
- }
- }
-
- protected final void readLists(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk)
- {
- for (IListMapping listMapping : listMappings)
- {
- listMapping.readValues(accessor, revision, listChunk);
- }
- }
-
- protected final IMappingStrategy getMappingStrategy()
- {
- return mappingStrategy;
- }
-
- public final EClass getEClass()
- {
- return eClass;
- }
-
- protected final Map<EStructuralFeature, String> getUnsettableFields()
- {
- return unsettableFields;
- }
-
- protected final Map<EStructuralFeature, String> getListSizeFields()
- {
- return listSizeFields;
- }
-
- public final List<ITypeMapping> getValueMappings()
- {
- return valueMappings;
- }
-
- public final ITypeMapping getValueMapping(EStructuralFeature feature)
- {
- for (ITypeMapping mapping : valueMappings)
- {
- if (mapping.getFeature() == feature)
- {
- return mapping;
- }
- }
-
- return null;
- }
-
- public final List<IListMapping> getListMappings()
- {
- return listMappings;
- }
-
- public final IListMapping getListMapping(EStructuralFeature feature)
- {
- for (IListMapping mapping : listMappings)
- {
- if (mapping.getFeature() == feature)
- {
- return mapping;
- }
- }
-
- throw new IllegalArgumentException("List mapping for feature " + feature + " does not exist"); //$NON-NLS-1$ //$NON-NLS-2$
- }
-
- protected final IDBTable getTable()
- {
- return table;
- }
-
- public List<IDBTable> getDBTables()
- {
- List<IDBTable> tables = new ArrayList<IDBTable>();
- tables.add(table);
-
- for (IListMapping listMapping : listMappings)
- {
- tables.addAll(listMapping.getDBTables());
- }
-
- return tables;
- }
-
- protected void checkDuplicateResources(IDBStoreAccessor accessor, CDORevision revision) throws IllegalStateException
- {
- CDOID folderID = (CDOID)revision.data().getContainerID();
- String name = (String)revision.data().get(EresourcePackage.eINSTANCE.getCDOResourceNode_Name(), 0);
- CDOID existingID = accessor.readResourceID(folderID, name, revision.getBranch().getHead());
- if (existingID != null && !existingID.equals(revision.getID()))
- {
- throw new IllegalStateException("Duplicate resource or folder: " + name + " in folder " + folderID); //$NON-NLS-1$ //$NON-NLS-2$
- }
- }
-
- protected void writeLists(IDBStoreAccessor accessor, InternalCDORevision revision)
- {
- for (IListMapping listMapping : listMappings)
- {
- listMapping.writeValues(accessor, revision);
- }
- }
-
- public void writeRevision(IDBStoreAccessor accessor, InternalCDORevision revision, boolean mapType, boolean revise,
- OMMonitor monitor)
- {
- Async async = null;
- monitor.begin(10);
-
- try
- {
- try
- {
- async = monitor.forkAsync();
- CDOID id = revision.getID();
- if (mapType)
- {
- long timeStamp = revision.getTimeStamp();
- mappingStrategy.putObjectType(accessor, timeStamp, id, eClass);
- }
- else if (revise)
- {
- long revised = revision.getTimeStamp() - 1;
- reviseOldRevision(accessor, id, revision.getBranch(), revised);
- for (IListMapping mapping : getListMappings())
- {
- mapping.objectDetached(accessor, id, revised);
- }
- }
- }
- finally
- {
- if (async != null)
- {
- async.stop();
- }
- }
-
- try
- {
- async = monitor.forkAsync();
- if (revision.isResourceFolder() || revision.isResource())
- {
- checkDuplicateResources(accessor, revision);
- }
- }
- finally
- {
- if (async != null)
- {
- async.stop();
- }
- }
-
- try
- {
- // Write attribute table always (even without modeled attributes!)
- async = monitor.forkAsync();
- writeValues(accessor, revision);
- }
- finally
- {
- if (async != null)
- {
- async.stop();
- }
- }
-
- try
- {
- // Write list tables only if they exist
- if (listMappings != null)
- {
- async = monitor.forkAsync(7);
- writeLists(accessor, revision);
- }
- else
- {
- monitor.worked(7);
- }
- }
- finally
- {
- if (async != null)
- {
- async.stop();
- }
- }
- }
- finally
- {
- monitor.done();
- }
- }
-
- public void handleRevisions(IDBStoreAccessor accessor, CDOBranch branch, long timeStamp, boolean exactTime,
- CDORevisionHandler handler)
- {
- // branch parameter is ignored, because either it is null or main branch.
- // this does not make any difference for non-branching store.
- // see #handleRevisions() implementation in HorizontalBranchingClassMapping
- // for branch handling.
-
- IRepository repository = accessor.getStore().getRepository();
- CDORevisionManager revisionManager = repository.getRevisionManager();
- CDOBranchManager branchManager = repository.getBranchManager();
-
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
- ResultSet resultSet = null;
-
- // TODO: test for timeStamp == INVALID_TIME and encode revision.isValid() as WHERE instead of fetching all revisions
- // in order to increase performance
-
- StringBuilder builder = new StringBuilder(sqlSelectForHandle);
-
- int timeParameters = 0;
- if (timeStamp != CDOBranchPoint.INVALID_DATE)
- {
- if (exactTime)
- {
- if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE)
- {
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_CREATED);
- builder.append("=?"); //$NON-NLS-1$
- timeParameters = 1;
- }
- }
- else
- {
- builder.append(" WHERE "); //$NON-NLS-1$
- if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE)
- {
- builder.append(CDODBSchema.ATTRIBUTES_CREATED);
- builder.append(">=?"); //$NON-NLS-1$
- builder.append(" AND ("); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append("<=? OR "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append("="); //$NON-NLS-1$
- builder.append(CDOBranchPoint.UNSPECIFIED_DATE);
- builder.append(")"); //$NON-NLS-1$
- timeParameters = 2;
- }
- else
- {
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append("="); //$NON-NLS-1$
- builder.append(CDOBranchPoint.UNSPECIFIED_DATE);
- }
- }
- }
-
- try
- {
- stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.LOW);
- for (int i = 0; i < timeParameters; i++)
- {
- stmt.setLong(i + 1, timeStamp);
- }
-
- resultSet = stmt.executeQuery();
- while (resultSet.next())
- {
- CDOID id = idHandler.getCDOID(resultSet, 1);
- int version = resultSet.getInt(2);
-
- if (version >= CDOBranchVersion.FIRST_VERSION)
- {
- InternalCDORevision revision = (InternalCDORevision)revisionManager.getRevisionByVersion(id, branchManager
- .getMainBranch().getVersion(version), CDORevision.UNCHUNKED, true);
-
- if (!handler.handleRevision(revision))
- {
- break;
- }
- }
- }
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- DBUtil.close(resultSet);
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public Set<CDOID> readChangeSet(IDBStoreAccessor accessor, CDOChangeSetSegment[] segments)
- {
- StringBuilder builder = new StringBuilder(sqlSelectForChangeSet);
- boolean isFirst = true;
-
- for (int i = 0; i < segments.length; i++)
- {
- if (isFirst)
- {
- isFirst = false;
- }
- else
- {
- builder.append(" OR "); //$NON-NLS-1$
- }
-
- builder.append(CDODBSchema.ATTRIBUTES_CREATED);
- builder.append(">=?"); //$NON-NLS-1$
- builder.append(" AND ("); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append("<=? OR "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append("="); //$NON-NLS-1$
- builder.append(CDOBranchPoint.UNSPECIFIED_DATE);
- builder.append(")"); //$NON-NLS-1$
- }
-
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
- ResultSet resultSet = null;
-
- Set<CDOID> result = new HashSet<CDOID>();
-
- try
- {
- stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.LOW);
- int column = 1;
- for (CDOChangeSetSegment segment : segments)
- {
- stmt.setLong(column++, segment.getTimeStamp());
- stmt.setLong(column++, segment.getEndTime());
- }
-
- resultSet = stmt.executeQuery();
- while (resultSet.next())
- {
- CDOID id = idHandler.getCDOID(resultSet, 1);
- result.add(id);
- }
-
- return result;
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- DBUtil.close(resultSet);
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public void detachObject(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp,
- OMMonitor monitor)
- {
- Async async = null;
- monitor.begin(1 + listMappings.size());
-
- try
- {
- if (version >= CDOBranchVersion.FIRST_VERSION)
- {
- reviseOldRevision(accessor, id, branch, timeStamp - 1);
- }
-
- detachAttributes(accessor, id, version + 1, branch, timeStamp, monitor.fork());
-
- // notify list mappings so they can clean up
- for (IListMapping mapping : getListMappings())
- {
- try
- {
- async = monitor.forkAsync();
- mapping.objectDetached(accessor, id, timeStamp);
- }
- finally
- {
- if (async != null)
- {
- async.stop();
- }
- }
- }
- }
- finally
- {
- monitor.done();
- }
- }
-
- public final boolean queryXRefs(IDBStoreAccessor accessor, QueryXRefsContext context, String idString)
- {
- String tableName = getTable().getName();
- EClass eClass = getEClass();
- List<EReference> refs = context.getSourceCandidates().get(eClass);
- List<EReference> scalarRefs = new ArrayList<EReference>();
-
- for (EReference ref : refs)
- {
- if (ref.isMany())
- {
- IListMapping listMapping = getListMapping(ref);
- String where = getListXRefsWhere(context);
-
- boolean more = listMapping.queryXRefs(accessor, tableName, where, context, idString);
- if (!more)
- {
- return false;
- }
- }
- else
- {
- scalarRefs.add(ref);
- }
- }
-
- if (!scalarRefs.isEmpty())
- {
- boolean more = queryScalarXRefs(accessor, scalarRefs, context, idString);
- if (!more)
- {
- return false;
- }
- }
-
- return true;
- }
-
- protected final boolean queryScalarXRefs(IDBStoreAccessor accessor, List<EReference> scalarRefs,
- QueryXRefsContext context, String idString)
- {
- String tableName = getTable().getName();
- String where = getListXRefsWhere(context);
-
- for (EReference ref : scalarRefs)
- {
- ITypeMapping valueMapping = getValueMapping(ref);
- String valueField = valueMapping.getField().getName();
-
- StringBuilder builder = new StringBuilder();
- builder.append("SELECT ");
- builder.append(CDODBSchema.ATTRIBUTES_ID);
- builder.append(", ");
- builder.append(valueField);
- builder.append(" FROM ");
- builder.append(tableName);
- builder.append(" WHERE ");
- builder.append(CDODBSchema.ATTRIBUTES_VERSION);
- builder.append(">0 AND ");
- builder.append(where);
- builder.append(" AND ");
- builder.append(valueField);
- builder.append(" IN ");
- builder.append(idString);
- String sql = builder.toString();
-
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- ResultSet resultSet = null;
- Statement stmt = null;
-
- try
- {
- stmt = accessor.getConnection().createStatement();
- if (TRACER.isEnabled())
- {
- TRACER.format("Query XRefs (attributes): {0}", sql);
- }
-
- resultSet = stmt.executeQuery(sql);
- while (resultSet.next())
- {
- CDOID sourceID = idHandler.getCDOID(resultSet, 1);
- CDOID targetID = idHandler.getCDOID(resultSet, 2);
-
- boolean more = context.addXRef(targetID, sourceID, ref, 0);
- if (TRACER.isEnabled())
- {
- TRACER.format(" add XRef to context: src={0}, tgt={1}, idx=0", sourceID, targetID);
- }
-
- if (!more)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format(" result limit reached. Ignoring further results.");
- }
-
- return false;
- }
- }
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- DBUtil.close(resultSet);
- DBUtil.close(stmt);
- }
- }
-
- return true;
- }
-
- protected abstract String getListXRefsWhere(QueryXRefsContext context);
-
- protected abstract void detachAttributes(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch,
- long timeStamp, OMMonitor fork);
-
- protected abstract void reviseOldRevision(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, long timeStamp);
-
- protected abstract void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision);
-}
+/* + * Copyright (c) 2004 - 2012 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 + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + * Stefan Winkler - 249610: [DB] Support external references (Implementation) + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +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.CDOBranchVersion; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.model.CDOModelUtil; +import org.eclipse.emf.cdo.common.revision.CDOList; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionManager; +import org.eclipse.emf.cdo.eresource.EresourcePackage; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.db.IDBStore; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IClassMapping; +import org.eclipse.emf.cdo.server.db.mapping.IListMapping; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDOList; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.FeatureMapUtil; + +import org.eclipse.core.runtime.Assert; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public abstract class AbstractHorizontalClassMapping implements IClassMapping +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractHorizontalClassMapping.class); + + private EClass eClass; + + private IDBTable table; + + private AbstractHorizontalMappingStrategy mappingStrategy; + + private List<ITypeMapping> valueMappings; + + private List<IListMapping> listMappings; + + private Map<EStructuralFeature, String> listSizeFields; + + private Map<EStructuralFeature, String> unsettableFields; + + private String sqlSelectForHandle; + + private String sqlSelectForChangeSet; + + public AbstractHorizontalClassMapping(AbstractHorizontalMappingStrategy mappingStrategy, EClass eClass) + { + this.mappingStrategy = mappingStrategy; + this.eClass = eClass; + + initTable(); + initFeatures(); + initSQLStrings(); + } + + private void initTable() + { + IDBStore store = getMappingStrategy().getStore(); + DBType idType = store.getIDHandler().getDBType(); + + String name = getMappingStrategy().getTableName(eClass); + table = store.getDBSchema().addTable(name); + + IDBField idField = table.addField(CDODBSchema.ATTRIBUTES_ID, idType, true); + IDBField versionField = table.addField(CDODBSchema.ATTRIBUTES_VERSION, DBType.INTEGER, true); + + IDBField branchField = addBranchingField(table); + + table.addField(CDODBSchema.ATTRIBUTES_CREATED, DBType.BIGINT, true); + IDBField revisedField = table.addField(CDODBSchema.ATTRIBUTES_REVISED, DBType.BIGINT, true); + table.addField(CDODBSchema.ATTRIBUTES_RESOURCE, idType, true); + table.addField(CDODBSchema.ATTRIBUTES_CONTAINER, idType, true); + table.addField(CDODBSchema.ATTRIBUTES_FEATURE, DBType.INTEGER, true); + + if (branchField != null) + { + table.addIndex(IDBIndex.Type.UNIQUE, idField, versionField, branchField); + } + else + { + table.addIndex(IDBIndex.Type.UNIQUE, idField, versionField); + } + + table.addIndex(IDBIndex.Type.NON_UNIQUE, idField, revisedField); + } + + protected IDBField addBranchingField(IDBTable table) + { + return null; + } + + private void initFeatures() + { + EStructuralFeature[] features = CDOModelUtil.getAllPersistentFeatures(eClass); + + if (features == null) + { + valueMappings = Collections.emptyList(); + listMappings = Collections.emptyList(); + } + else + { + valueMappings = createValueMappings(features); + listMappings = createListMappings(features); + } + } + + private void initSQLStrings() + { + // ----------- Select all revisions (for handleRevisions) --- + StringBuilder builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + sqlSelectForHandle = builder.toString(); + + // ----------- Select all revisions (for readChangeSet) --- + builder = new StringBuilder("SELECT DISTINCT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + sqlSelectForChangeSet = builder.toString(); + } + + private List<ITypeMapping> createValueMappings(EStructuralFeature[] features) + { + List<ITypeMapping> mappings = new ArrayList<ITypeMapping>(); + for (EStructuralFeature feature : features) + { + if (!feature.isMany()) + { + ITypeMapping mapping = mappingStrategy.createValueMapping(feature); + mapping.createDBField(getTable()); + mappings.add(mapping); + + if (feature.isUnsettable()) + { + String fieldName = mappingStrategy.getUnsettableFieldName(feature); + if (unsettableFields == null) + { + unsettableFields = new LinkedHashMap<EStructuralFeature, String>(); + } + + unsettableFields.put(feature, fieldName); + } + } + } + + // add unsettable fields to end of table + if (unsettableFields != null) + { + for (String fieldName : unsettableFields.values()) + { + table.addField(fieldName, DBType.BOOLEAN, 1); + } + } + + return mappings; + } + + private List<IListMapping> createListMappings(EStructuralFeature[] features) + { + List<IListMapping> listMappings = new ArrayList<IListMapping>(); + for (EStructuralFeature feature : features) + { + if (feature.isMany()) + { + IListMapping mapping = null; + if (FeatureMapUtil.isFeatureMap(feature)) + { + mapping = mappingStrategy.createFeatureMapMapping(eClass, feature); + } + else + { + mapping = mappingStrategy.createListMapping(eClass, feature); + } + + listMappings.add(mapping); + + // add field for list sizes + createListSizeField(feature); + } + } + + return listMappings; + } + + /** + * Create an integer field in the attribute tabel for the list size of the associated list mapping. + */ + private void createListSizeField(EStructuralFeature feature) + { + if (listSizeFields == null) + { + listSizeFields = new LinkedHashMap<EStructuralFeature, String>(); + } + + String fieldName = mappingStrategy.getFieldName(feature); + table.addField(fieldName, DBType.INTEGER); + + listSizeFields.put(feature, fieldName); + } + + /** + * Read the revision's values from the DB. + * + * @return <code>true</code> if the revision has been read successfully.<br> + * <code>false</code> if the revision does not exist in the DB. + */ + protected final boolean readValuesFromStatement(PreparedStatement stmt, InternalCDORevision revision, + IDBStoreAccessor accessor) + { + ResultSet resultSet = null; + + try + { + if (TRACER.isEnabled()) + { + TRACER.format("Executing Query: {0}", stmt.toString()); //$NON-NLS-1$ + } + + stmt.setMaxRows(1); // Optimization: only 1 row + + resultSet = stmt.executeQuery(); + if (!resultSet.next()) + { + if (TRACER.isEnabled()) + { + TRACER.format("Resultset was empty"); //$NON-NLS-1$ + } + + return false; + } + + revision.setVersion(resultSet.getInt(CDODBSchema.ATTRIBUTES_VERSION)); + + long timeStamp = resultSet.getLong(CDODBSchema.ATTRIBUTES_CREATED); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + CDOBranchPoint branchPoint = revision.getBranch().getPoint(timeStamp); + + revision.setBranchPoint(branchPoint); + revision.setRevised(resultSet.getLong(CDODBSchema.ATTRIBUTES_REVISED)); + revision.setResourceID(idHandler.getCDOID(resultSet, CDODBSchema.ATTRIBUTES_RESOURCE)); + revision.setContainerID(idHandler.getCDOID(resultSet, CDODBSchema.ATTRIBUTES_CONTAINER)); + revision.setContainingFeatureID(resultSet.getInt(CDODBSchema.ATTRIBUTES_FEATURE)); + + for (ITypeMapping mapping : valueMappings) + { + EStructuralFeature feature = mapping.getFeature(); + if (feature.isUnsettable()) + { + if (!resultSet.getBoolean(unsettableFields.get(feature))) + { + // isSet==false -- setValue: null + revision.setValue(feature, null); + continue; + } + } + + mapping.readValueToRevision(resultSet, revision); + } + + if (listSizeFields != null) + { + for (Map.Entry<EStructuralFeature, String> listSizeEntry : listSizeFields.entrySet()) + { + EStructuralFeature feature = listSizeEntry.getKey(); + String fieldName = listSizeEntry.getValue(); + int size = resultSet.getInt(fieldName); + + // ensure the listSize (TODO: remove assertion) + CDOList list = revision.getList(feature, size); + + for (int i = 0; i < size; i++) + { + list.add(InternalCDOList.UNINITIALIZED); + } + + if (list.size() != size) + { + Assert.isTrue(false); + } + } + } + + return true; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + } + } + + protected final void readLists(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + for (IListMapping listMapping : listMappings) + { + listMapping.readValues(accessor, revision, listChunk); + } + } + + protected final IMappingStrategy getMappingStrategy() + { + return mappingStrategy; + } + + public final EClass getEClass() + { + return eClass; + } + + protected final Map<EStructuralFeature, String> getUnsettableFields() + { + return unsettableFields; + } + + protected final Map<EStructuralFeature, String> getListSizeFields() + { + return listSizeFields; + } + + public final List<ITypeMapping> getValueMappings() + { + return valueMappings; + } + + public final ITypeMapping getValueMapping(EStructuralFeature feature) + { + for (ITypeMapping mapping : valueMappings) + { + if (mapping.getFeature() == feature) + { + return mapping; + } + } + + return null; + } + + public final List<IListMapping> getListMappings() + { + return listMappings; + } + + public final IListMapping getListMapping(EStructuralFeature feature) + { + for (IListMapping mapping : listMappings) + { + if (mapping.getFeature() == feature) + { + return mapping; + } + } + + throw new IllegalArgumentException("List mapping for feature " + feature + " does not exist"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + protected final IDBTable getTable() + { + return table; + } + + public List<IDBTable> getDBTables() + { + List<IDBTable> tables = new ArrayList<IDBTable>(); + tables.add(table); + + for (IListMapping listMapping : listMappings) + { + tables.addAll(listMapping.getDBTables()); + } + + return tables; + } + + protected void checkDuplicateResources(IDBStoreAccessor accessor, CDORevision revision) throws IllegalStateException + { + CDOID folderID = (CDOID)revision.data().getContainerID(); + String name = (String)revision.data().get(EresourcePackage.eINSTANCE.getCDOResourceNode_Name(), 0); + CDOID existingID = accessor.readResourceID(folderID, name, revision.getBranch().getHead()); + if (existingID != null && !existingID.equals(revision.getID())) + { + throw new IllegalStateException("Duplicate resource or folder: " + name + " in folder " + folderID); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + protected void writeLists(IDBStoreAccessor accessor, InternalCDORevision revision) + { + for (IListMapping listMapping : listMappings) + { + listMapping.writeValues(accessor, revision); + } + } + + public void writeRevision(IDBStoreAccessor accessor, InternalCDORevision revision, boolean mapType, boolean revise, + OMMonitor monitor) + { + Async async = null; + monitor.begin(10); + + try + { + try + { + async = monitor.forkAsync(); + CDOID id = revision.getID(); + if (mapType) + { + long timeStamp = revision.getTimeStamp(); + mappingStrategy.putObjectType(accessor, timeStamp, id, eClass); + } + else if (revise) + { + long revised = revision.getTimeStamp() - 1; + reviseOldRevision(accessor, id, revision.getBranch(), revised); + for (IListMapping mapping : getListMappings()) + { + mapping.objectDetached(accessor, id, revised); + } + } + } + finally + { + if (async != null) + { + async.stop(); + } + } + + try + { + async = monitor.forkAsync(); + if (revision.isResourceFolder() || revision.isResource()) + { + checkDuplicateResources(accessor, revision); + } + } + finally + { + if (async != null) + { + async.stop(); + } + } + + try + { + // Write attribute table always (even without modeled attributes!) + async = monitor.forkAsync(); + writeValues(accessor, revision); + } + finally + { + if (async != null) + { + async.stop(); + } + } + + try + { + // Write list tables only if they exist + if (listMappings != null) + { + async = monitor.forkAsync(7); + writeLists(accessor, revision); + } + else + { + monitor.worked(7); + } + } + finally + { + if (async != null) + { + async.stop(); + } + } + } + finally + { + monitor.done(); + } + } + + public void handleRevisions(IDBStoreAccessor accessor, CDOBranch branch, long timeStamp, boolean exactTime, + CDORevisionHandler handler) + { + // branch parameter is ignored, because either it is null or main branch. + // this does not make any difference for non-branching store. + // see #handleRevisions() implementation in HorizontalBranchingClassMapping + // for branch handling. + + IRepository repository = accessor.getStore().getRepository(); + CDORevisionManager revisionManager = repository.getRevisionManager(); + CDOBranchManager branchManager = repository.getBranchManager(); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + // TODO: test for timeStamp == INVALID_TIME and encode revision.isValid() as WHERE instead of fetching all revisions + // in order to increase performance + + StringBuilder builder = new StringBuilder(sqlSelectForHandle); + + int timeParameters = 0; + if (timeStamp != CDOBranchPoint.INVALID_DATE) + { + if (exactTime) + { + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append("=?"); //$NON-NLS-1$ + timeParameters = 1; + } + } + else + { + builder.append(" WHERE "); //$NON-NLS-1$ + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append(">=?"); //$NON-NLS-1$ + builder.append(" AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("<=? OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("="); //$NON-NLS-1$ + builder.append(CDOBranchPoint.UNSPECIFIED_DATE); + builder.append(")"); //$NON-NLS-1$ + timeParameters = 2; + } + else + { + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("="); //$NON-NLS-1$ + builder.append(CDOBranchPoint.UNSPECIFIED_DATE); + } + } + } + + try + { + stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.LOW); + for (int i = 0; i < timeParameters; i++) + { + stmt.setLong(i + 1, timeStamp); + } + + resultSet = stmt.executeQuery(); + while (resultSet.next()) + { + CDOID id = idHandler.getCDOID(resultSet, 1); + int version = resultSet.getInt(2); + + if (version >= CDOBranchVersion.FIRST_VERSION) + { + InternalCDORevision revision = (InternalCDORevision)revisionManager.getRevisionByVersion(id, branchManager + .getMainBranch().getVersion(version), CDORevision.UNCHUNKED, true); + + if (!handler.handleRevision(revision)) + { + break; + } + } + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + public Set<CDOID> readChangeSet(IDBStoreAccessor accessor, CDOChangeSetSegment[] segments) + { + StringBuilder builder = new StringBuilder(sqlSelectForChangeSet); + boolean isFirst = true; + + for (int i = 0; i < segments.length; i++) + { + if (isFirst) + { + isFirst = false; + } + else + { + builder.append(" OR "); //$NON-NLS-1$ + } + + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append(">=?"); //$NON-NLS-1$ + builder.append(" AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("<=? OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("="); //$NON-NLS-1$ + builder.append(CDOBranchPoint.UNSPECIFIED_DATE); + builder.append(")"); //$NON-NLS-1$ + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + Set<CDOID> result = new HashSet<CDOID>(); + + try + { + stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.LOW); + int column = 1; + for (CDOChangeSetSegment segment : segments) + { + stmt.setLong(column++, segment.getTimeStamp()); + stmt.setLong(column++, segment.getEndTime()); + } + + resultSet = stmt.executeQuery(); + while (resultSet.next()) + { + CDOID id = idHandler.getCDOID(resultSet, 1); + result.add(id); + } + + return result; + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + public void detachObject(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp, + OMMonitor monitor) + { + Async async = null; + monitor.begin(1 + listMappings.size()); + + try + { + if (version >= CDOBranchVersion.FIRST_VERSION) + { + reviseOldRevision(accessor, id, branch, timeStamp - 1); + } + + detachAttributes(accessor, id, version + 1, branch, timeStamp, monitor.fork()); + + // notify list mappings so they can clean up + for (IListMapping mapping : getListMappings()) + { + try + { + async = monitor.forkAsync(); + mapping.objectDetached(accessor, id, timeStamp); + } + finally + { + if (async != null) + { + async.stop(); + } + } + } + } + finally + { + monitor.done(); + } + } + + public void rawDelete(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, OMMonitor monitor) + { + Async async = null; + monitor.begin(1 + listMappings.size()); + + try + { + rawDeleteAttributes(accessor, id, branch, version, monitor.fork()); + + // notify list mappings so they can clean up + for (IListMapping mapping : getListMappings()) + { + if (mapping instanceof BasicAbstractListTableMapping) + { + try + { + async = monitor.forkAsync(); + + BasicAbstractListTableMapping m = (BasicAbstractListTableMapping)mapping; + m.rawDeleted(accessor, id, branch, version); + } + finally + { + if (async != null) + { + async.stop(); + } + } + } + else + { + throw new UnsupportedOperationException("rawDeleted() is not supported by " + mapping.getClass().getName()); + } + } + } + finally + { + monitor.done(); + } + } + + protected abstract void rawDeleteAttributes(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version, + OMMonitor fork); + + public final boolean queryXRefs(IDBStoreAccessor accessor, QueryXRefsContext context, String idString) + { + String tableName = getTable().getName(); + EClass eClass = getEClass(); + List<EReference> refs = context.getSourceCandidates().get(eClass); + List<EReference> scalarRefs = new ArrayList<EReference>(); + + for (EReference ref : refs) + { + if (ref.isMany()) + { + IListMapping listMapping = getListMapping(ref); + String where = getListXRefsWhere(context); + + boolean more = listMapping.queryXRefs(accessor, tableName, where, context, idString); + if (!more) + { + return false; + } + } + else + { + scalarRefs.add(ref); + } + } + + if (!scalarRefs.isEmpty()) + { + boolean more = queryScalarXRefs(accessor, scalarRefs, context, idString); + if (!more) + { + return false; + } + } + + return true; + } + + protected final boolean queryScalarXRefs(IDBStoreAccessor accessor, List<EReference> scalarRefs, + QueryXRefsContext context, String idString) + { + String tableName = getTable().getName(); + String where = getListXRefsWhere(context); + + for (EReference ref : scalarRefs) + { + ITypeMapping valueMapping = getValueMapping(ref); + String valueField = valueMapping.getField().getName(); + + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(", "); + builder.append(valueField); + builder.append(" FROM "); + builder.append(tableName); + builder.append(" WHERE "); + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(">0 AND "); + builder.append(where); + builder.append(" AND "); + builder.append(valueField); + builder.append(" IN "); + builder.append(idString); + String sql = builder.toString(); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + ResultSet resultSet = null; + Statement stmt = null; + + try + { + stmt = accessor.getConnection().createStatement(); + if (TRACER.isEnabled()) + { + TRACER.format("Query XRefs (attributes): {0}", sql); + } + + resultSet = stmt.executeQuery(sql); + while (resultSet.next()) + { + CDOID sourceID = idHandler.getCDOID(resultSet, 1); + CDOID targetID = idHandler.getCDOID(resultSet, 2); + + boolean more = context.addXRef(targetID, sourceID, ref, 0); + if (TRACER.isEnabled()) + { + TRACER.format(" add XRef to context: src={0}, tgt={1}, idx=0", sourceID, targetID); + } + + if (!more) + { + if (TRACER.isEnabled()) + { + TRACER.format(" result limit reached. Ignoring further results."); + } + + return false; + } + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + } + + return true; + } + + protected abstract String getListXRefsWhere(QueryXRefsContext context); + + protected abstract void detachAttributes(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, + long timeStamp, OMMonitor fork); + + protected abstract void reviseOldRevision(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, long timeStamp); + + protected abstract void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision); +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractListTableMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractListTableMapping.java index 62ac092a73..a979c05a9f 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractListTableMapping.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractListTableMapping.java @@ -1,485 +1,485 @@ -/*
- * Copyright (c) 2004 - 2012 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
- * Stefan Winkler - Bug 271444: [DB] Multiple refactorings
- * Stefan Winkler - Bug 283998: [DB] Chunk reading for multiple chunks fails
- * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy
- */
-package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
-
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.revision.CDOList;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
-import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk;
-import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
-import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader;
-import org.eclipse.emf.cdo.server.db.IIDHandler;
-import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
-import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
-import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
-import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
-import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
-import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
-
-import org.eclipse.net4j.db.DBException;
-import org.eclipse.net4j.db.DBType;
-import org.eclipse.net4j.db.DBUtil;
-import org.eclipse.net4j.db.ddl.IDBField;
-import org.eclipse.net4j.db.ddl.IDBIndex.Type;
-import org.eclipse.net4j.db.ddl.IDBTable;
-import org.eclipse.net4j.util.collection.MoveableList;
-import org.eclipse.net4j.util.om.trace.ContextTracer;
-
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EReference;
-import org.eclipse.emf.ecore.EStructuralFeature;
-
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * This abstract base class provides basic behavior needed for mapping many-valued attributes to tables.
- *
- * @author Eike Stepper
- * @since 2.0
- */
-public abstract class AbstractListTableMapping extends BasicAbstractListTableMapping
-{
- private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractListTableMapping.class);
-
- /**
- * The table of this mapping.
- */
- private IDBTable table;
-
- /**
- * The type mapping for the value field.
- */
- private ITypeMapping typeMapping;
-
- // --------- SQL strings - see initSQLStrings() -----------------
- private String sqlSelectChunksPrefix;
-
- private String sqlOrderByIndex;
-
- private String sqlInsertEntry;
-
- public AbstractListTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature)
- {
- super(mappingStrategy, eClass, feature);
- initTable();
- initSQLStrings();
- }
-
- private void initTable()
- {
- IMappingStrategy mappingStrategy = getMappingStrategy();
- String tableName = mappingStrategy.getTableName(getContainingClass(), getFeature());
- table = mappingStrategy.getStore().getDBSchema().addTable(tableName);
-
- // add fields for keys (cdo_id, version, feature_id)
- FieldInfo[] fields = getKeyFields();
- IDBField[] dbFields = new IDBField[fields.length + 1];
-
- for (int i = 0; i < fields.length; i++)
- {
- dbFields[i] = table.addField(fields[i].getName(), fields[i].getDbType());
- }
-
- // add field for list index
- dbFields[dbFields.length - 1] = table.addField(CDODBSchema.LIST_IDX, DBType.INTEGER);
-
- // add field for value
- typeMapping = mappingStrategy.createValueMapping(getFeature());
- typeMapping.createDBField(table, CDODBSchema.LIST_VALUE);
-
- // add table indexes
- table.addIndex(Type.UNIQUE, dbFields);
- }
-
- protected abstract FieldInfo[] getKeyFields();
-
- protected abstract void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException;
-
- public Collection<IDBTable> getDBTables()
- {
- return Arrays.asList(table);
- }
-
- private void initSQLStrings()
- {
- String tableName = getTable().getName();
- FieldInfo[] fields = getKeyFields();
-
- // ---------------- SELECT to read chunks ----------------------------
- StringBuilder builder = new StringBuilder();
- builder.append("SELECT "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_VALUE);
- builder.append(" FROM "); //$NON-NLS-1$
- builder.append(tableName);
- builder.append(" WHERE "); //$NON-NLS-1$
-
- for (int i = 0; i < fields.length; i++)
- {
- builder.append(fields[i].getName());
- if (i + 1 < fields.length)
- {
- // more to come
- builder.append("=? AND "); //$NON-NLS-1$
- }
- else
- {
- // last one
- builder.append("=? "); //$NON-NLS-1$
- }
- }
-
- sqlSelectChunksPrefix = builder.toString();
-
- sqlOrderByIndex = " ORDER BY " + CDODBSchema.LIST_IDX; //$NON-NLS-1$
-
- // ----------------- INSERT - reference entry -----------------
- builder = new StringBuilder("INSERT INTO "); //$NON-NLS-1$
- builder.append(tableName);
- builder.append("("); //$NON-NLS-1$
-
- for (int i = 0; i < fields.length; i++)
- {
- builder.append(fields[i].getName());
- builder.append(", "); //$NON-NLS-1$
- }
-
- builder.append(CDODBSchema.LIST_IDX);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_VALUE);
- builder.append(") VALUES ("); //$NON-NLS-1$
- for (int i = 0; i < fields.length; i++)
- {
- builder.append("?, "); //$NON-NLS-1$
- }
-
- builder.append(" ?, ?)"); //$NON-NLS-1$
- sqlInsertEntry = builder.toString();
- }
-
- protected final IDBTable getTable()
- {
- return table;
- }
-
- protected final ITypeMapping getTypeMapping()
- {
- return typeMapping;
- }
-
- public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk)
- {
- MoveableList<Object> list = revision.getList(getFeature());
-
- if (listChunk == 0 || list.size() == 0)
- {
- // nothing to read take shortcut
- return;
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading list values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$
- getFeature().getName(), revision.getID(), revision.getVersion());
- }
-
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
- ResultSet resultSet = null;
-
- try
- {
- String sql = sqlSelectChunksPrefix + sqlOrderByIndex;
- stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH);
- setKeyFields(stmt, revision);
-
- if (TRACER.isEnabled())
- {
- TRACER.trace(stmt.toString());
- }
-
- if (listChunk != CDORevision.UNCHUNKED)
- {
- stmt.setMaxRows(listChunk); // optimization - don't read unneeded rows.
- }
-
- resultSet = stmt.executeQuery();
-
- int currentIndex = 0;
- while ((listChunk == CDORevision.UNCHUNKED || --listChunk >= 0) && resultSet.next())
- {
- Object value = typeMapping.readValue(resultSet);
- if (TRACER.isEnabled())
- {
- TRACER.format("Read value for index {0} from result set: {1}", list.size(), value); //$NON-NLS-1$
- }
-
- list.set(currentIndex++, value);
- }
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- DBUtil.close(resultSet);
- statementCache.releasePreparedStatement(stmt);
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading list values done for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$
- getFeature().getName(), revision.getID(), revision.getVersion());
- }
- }
-
- public final void readChunks(IDBStoreChunkReader chunkReader, List<Chunk> chunks, String where)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading list chunk values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$
- getFeature().getName(), chunkReader.getRevision().getID(), chunkReader.getRevision().getVersion());
- }
-
- IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache();
- PreparedStatement stmt = null;
- ResultSet resultSet = null;
-
- try
- {
- StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix);
- if (where != null)
- {
- builder.append(" AND "); //$NON-NLS-1$
- builder.append(where);
- }
-
- builder.append(sqlOrderByIndex);
-
- String sql = builder.toString();
- stmt = statementCache.getPreparedStatement(sql, ReuseProbability.LOW);
- setKeyFields(stmt, chunkReader.getRevision());
-
- resultSet = stmt.executeQuery();
-
- Chunk chunk = null;
- int chunkSize = 0;
- int chunkIndex = 0;
- int indexInChunk = 0;
-
- while (resultSet.next())
- {
- Object value = typeMapping.readValue(resultSet);
-
- if (chunk == null)
- {
- chunk = chunks.get(chunkIndex++);
- chunkSize = chunk.size();
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Current chunk no. {0} is [start = {1}, size = {2}]", chunkIndex - 1, chunk.getStartIndex(), //$NON-NLS-1$
- chunkSize);
- }
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Read value for chunk index {0} from result set: {1}", indexInChunk, value); //$NON-NLS-1$
- }
-
- chunk.add(indexInChunk++, value);
- if (indexInChunk == chunkSize)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Chunk finished"); //$NON-NLS-1$
- }
-
- chunk = null;
- indexInChunk = 0;
- }
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading list chunk values done for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$
- getFeature().getName(), chunkReader.getRevision().getID(), chunkReader.getRevision().getVersion());
- }
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- DBUtil.close(resultSet);
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision)
- {
- CDOList values = revision.getList(getFeature());
-
- int idx = 0;
- for (Object element : values)
- {
- writeValue(accessor, revision, idx++, element);
- }
- }
-
- protected final void writeValue(IDBStoreAccessor accessor, CDORevision revision, int idx, Object value)
- {
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Writing value for feature {0}.{1} index {2} of {3}v{4} : {5}", getContainingClass().getName(),
- getFeature().getName(), idx, revision.getID(), revision.getVersion(), value);
- }
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlInsertEntry, ReuseProbability.HIGH);
-
- setKeyFields(stmt, revision);
- int column = getKeyFields().length + 1;
- stmt.setInt(column++, idx);
- typeMapping.setValue(stmt, column++, value);
-
- DBUtil.update(stmt, true);
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere,
- QueryXRefsContext context, String idString)
- {
- String tableName = getTable().getName();
- String listJoin = getMappingStrategy().getListJoin("a_t", "l_t");
-
- StringBuilder builder = new StringBuilder();
- builder.append("SELECT l_t."); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_ID);
- builder.append(", l_t."); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_VALUE);
- builder.append(", l_t."); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_IDX);
- builder.append(" FROM "); //$NON-NLS-1$
- builder.append(tableName);
- builder.append(" AS l_t, ");//$NON-NLS-1$
- builder.append(mainTableName);
- builder.append(" AS a_t WHERE ");//$NON-NLS-1$
- builder.append("a_t." + mainTableWhere);//$NON-NLS-1$
- builder.append(listJoin);
- builder.append(" AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_VALUE);
- builder.append(" IN "); //$NON-NLS-1$
- builder.append(idString);
- String sql = builder.toString();
-
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- ResultSet resultSet = null;
- Statement stmt = null;
-
- try
- {
- stmt = accessor.getConnection().createStatement();
- if (TRACER.isEnabled())
- {
- TRACER.format("Query XRefs (list): {0}", sql);
- }
-
- resultSet = stmt.executeQuery(sql);
- while (resultSet.next())
- {
- CDOID srcId = idHandler.getCDOID(resultSet, 1);
- CDOID targetId = idHandler.getCDOID(resultSet, 2);
- int idx = resultSet.getInt(3);
-
- boolean more = context.addXRef(targetId, srcId, (EReference)getFeature(), idx);
- if (TRACER.isEnabled())
- {
- TRACER.format(" add XRef to context: src={0}, tgt={1}, idx={2}", srcId, targetId, idx);
- }
-
- if (!more)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format(" result limit reached. Ignoring further results.");
- }
-
- return false;
- }
- }
-
- return true;
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- DBUtil.close(resultSet);
- DBUtil.close(stmt);
- }
- }
-
- /**
- * Used by subclasses to indicate which fields should be in the table. I.e. just a pair of name and DBType ...
- *
- * @author Stefan Winkler
- */
- protected static class FieldInfo
- {
- private String name;
-
- private DBType dbType;
-
- public FieldInfo(String name, DBType dbType)
- {
- this.name = name;
- this.dbType = dbType;
- }
-
- public String getName()
- {
- return name;
- }
-
- public DBType getDbType()
- {
- return dbType;
- }
- }
-}
+/* + * Copyright (c) 2004 - 2012 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 + * Stefan Winkler - Bug 271444: [DB] Multiple refactorings + * Stefan Winkler - Bug 283998: [DB] Chunk reading for multiple chunks fails + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDOList; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex.Type; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.collection.MoveableList; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * This abstract base class provides basic behavior needed for mapping many-valued attributes to tables. + * + * @author Eike Stepper + * @since 2.0 + */ +public abstract class AbstractListTableMapping extends BasicAbstractListTableMapping +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractListTableMapping.class); + + /** + * The table of this mapping. + */ + private IDBTable table; + + /** + * The type mapping for the value field. + */ + private ITypeMapping typeMapping; + + // --------- SQL strings - see initSQLStrings() ----------------- + private String sqlSelectChunksPrefix; + + private String sqlOrderByIndex; + + private String sqlInsertEntry; + + public AbstractListTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + initTable(); + initSQLStrings(); + } + + private void initTable() + { + IMappingStrategy mappingStrategy = getMappingStrategy(); + String tableName = mappingStrategy.getTableName(getContainingClass(), getFeature()); + table = mappingStrategy.getStore().getDBSchema().addTable(tableName); + + // add fields for keys (cdo_id, version, feature_id) + FieldInfo[] fields = getKeyFields(); + IDBField[] dbFields = new IDBField[fields.length + 1]; + + for (int i = 0; i < fields.length; i++) + { + dbFields[i] = table.addField(fields[i].getName(), fields[i].getDbType()); + } + + // add field for list index + dbFields[dbFields.length - 1] = table.addField(CDODBSchema.LIST_IDX, DBType.INTEGER); + + // add field for value + typeMapping = mappingStrategy.createValueMapping(getFeature()); + typeMapping.createDBField(table, CDODBSchema.LIST_VALUE); + + // add table indexes + table.addIndex(Type.UNIQUE, dbFields); + } + + protected abstract FieldInfo[] getKeyFields(); + + protected abstract void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException; + + public Collection<IDBTable> getDBTables() + { + return Arrays.asList(table); + } + + private void initSQLStrings() + { + String tableName = getTable().getName(); + FieldInfo[] fields = getKeyFields(); + + // ---------------- SELECT to read chunks ---------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" WHERE "); //$NON-NLS-1$ + + for (int i = 0; i < fields.length; i++) + { + builder.append(fields[i].getName()); + if (i + 1 < fields.length) + { + // more to come + builder.append("=? AND "); //$NON-NLS-1$ + } + else + { + // last one + builder.append("=? "); //$NON-NLS-1$ + } + } + + sqlSelectChunksPrefix = builder.toString(); + + sqlOrderByIndex = " ORDER BY " + CDODBSchema.LIST_IDX; //$NON-NLS-1$ + + // ----------------- INSERT - reference entry ----------------- + builder = new StringBuilder("INSERT INTO "); //$NON-NLS-1$ + builder.append(tableName); + builder.append("("); //$NON-NLS-1$ + + for (int i = 0; i < fields.length; i++) + { + builder.append(fields[i].getName()); + builder.append(", "); //$NON-NLS-1$ + } + + builder.append(CDODBSchema.LIST_IDX); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(") VALUES ("); //$NON-NLS-1$ + for (int i = 0; i < fields.length; i++) + { + builder.append("?, "); //$NON-NLS-1$ + } + + builder.append(" ?, ?)"); //$NON-NLS-1$ + sqlInsertEntry = builder.toString(); + } + + protected final IDBTable getTable() + { + return table; + } + + protected final ITypeMapping getTypeMapping() + { + return typeMapping; + } + + public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + MoveableList<Object> list = revision.getList(getFeature()); + + if (listChunk == 0 || list.size() == 0) + { + // nothing to read take shortcut + return; + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), revision.getID(), revision.getVersion()); + } + + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + String sql = sqlSelectChunksPrefix + sqlOrderByIndex; + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH); + setKeyFields(stmt, revision); + + if (TRACER.isEnabled()) + { + TRACER.trace(stmt.toString()); + } + + if (listChunk != CDORevision.UNCHUNKED) + { + stmt.setMaxRows(listChunk); // optimization - don't read unneeded rows. + } + + resultSet = stmt.executeQuery(); + + int currentIndex = 0; + while ((listChunk == CDORevision.UNCHUNKED || --listChunk >= 0) && resultSet.next()) + { + Object value = typeMapping.readValue(resultSet); + if (TRACER.isEnabled()) + { + TRACER.format("Read value for index {0} from result set: {1}", list.size(), value); //$NON-NLS-1$ + } + + list.set(currentIndex++, value); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list values done for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), revision.getID(), revision.getVersion()); + } + } + + public final void readChunks(IDBStoreChunkReader chunkReader, List<Chunk> chunks, String where) + { + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), chunkReader.getRevision().getID(), chunkReader.getRevision().getVersion()); + } + + IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix); + if (where != null) + { + builder.append(" AND "); //$NON-NLS-1$ + builder.append(where); + } + + builder.append(sqlOrderByIndex); + + String sql = builder.toString(); + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.LOW); + setKeyFields(stmt, chunkReader.getRevision()); + + resultSet = stmt.executeQuery(); + + Chunk chunk = null; + int chunkSize = 0; + int chunkIndex = 0; + int indexInChunk = 0; + + while (resultSet.next()) + { + Object value = typeMapping.readValue(resultSet); + + if (chunk == null) + { + chunk = chunks.get(chunkIndex++); + chunkSize = chunk.size(); + + if (TRACER.isEnabled()) + { + TRACER.format("Current chunk no. {0} is [start = {1}, size = {2}]", chunkIndex - 1, chunk.getStartIndex(), //$NON-NLS-1$ + chunkSize); + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Read value for chunk index {0} from result set: {1}", indexInChunk, value); //$NON-NLS-1$ + } + + chunk.add(indexInChunk++, value); + if (indexInChunk == chunkSize) + { + if (TRACER.isEnabled()) + { + TRACER.format("Chunk finished"); //$NON-NLS-1$ + } + + chunk = null; + indexInChunk = 0; + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values done for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), chunkReader.getRevision().getID(), chunkReader.getRevision().getVersion()); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + CDOList values = revision.getList(getFeature()); + + int idx = 0; + for (Object element : values) + { + writeValue(accessor, revision, idx++, element); + } + } + + protected final void writeValue(IDBStoreAccessor accessor, CDORevision revision, int idx, Object value) + { + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + if (TRACER.isEnabled()) + { + TRACER.format("Writing value for feature {0}.{1} index {2} of {3}v{4} : {5}", getContainingClass().getName(), + getFeature().getName(), idx, revision.getID(), revision.getVersion(), value); + } + + try + { + stmt = statementCache.getPreparedStatement(sqlInsertEntry, ReuseProbability.HIGH); + + setKeyFields(stmt, revision); + int column = getKeyFields().length + 1; + stmt.setInt(column++, idx); + typeMapping.setValue(stmt, column++, value); + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere, + QueryXRefsContext context, String idString) + { + String tableName = getTable().getName(); + String listJoin = getMappingStrategy().getListJoin("a_t", "l_t"); + + StringBuilder builder = new StringBuilder(); + builder.append("SELECT l_t."); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append(", l_t."); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(", l_t."); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" AS l_t, ");//$NON-NLS-1$ + builder.append(mainTableName); + builder.append(" AS a_t WHERE ");//$NON-NLS-1$ + builder.append("a_t." + mainTableWhere);//$NON-NLS-1$ + builder.append(listJoin); + builder.append(" AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(" IN "); //$NON-NLS-1$ + builder.append(idString); + String sql = builder.toString(); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + ResultSet resultSet = null; + Statement stmt = null; + + try + { + stmt = accessor.getConnection().createStatement(); + if (TRACER.isEnabled()) + { + TRACER.format("Query XRefs (list): {0}", sql); + } + + resultSet = stmt.executeQuery(sql); + while (resultSet.next()) + { + CDOID srcId = idHandler.getCDOID(resultSet, 1); + CDOID targetId = idHandler.getCDOID(resultSet, 2); + int idx = resultSet.getInt(3); + + boolean more = context.addXRef(targetId, srcId, (EReference)getFeature(), idx); + if (TRACER.isEnabled()) + { + TRACER.format(" add XRef to context: src={0}, tgt={1}, idx={2}", srcId, targetId, idx); + } + + if (!more) + { + if (TRACER.isEnabled()) + { + TRACER.format(" result limit reached. Ignoring further results."); + } + + return false; + } + } + + return true; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + } + + /** + * Used by subclasses to indicate which fields should be in the table. I.e. just a pair of name and DBType ... + * + * @author Stefan Winkler + */ + protected static class FieldInfo + { + private String name; + + private DBType dbType; + + public FieldInfo(String name, DBType dbType) + { + this.name = name; + this.dbType = dbType; + } + + public String getName() + { + return name; + } + + public DBType getDbType() + { + return dbType; + } + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMapping.java index 9dffc2b3d4..05af4cb6dd 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMapping.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMapping.java @@ -1,68 +1,113 @@ -/*
- * Copyright (c) 2004 - 2012 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
- * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
- * Christopher Albert - 254455: [DB] Support FeatureMaps bug 254455
- */
-package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
-
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
-import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
-import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
-
-import org.eclipse.net4j.db.DBType;
-
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EStructuralFeature;
-
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-
-/**
- * This is a featuremap-table mapping for audit mode. It has ID and version columns and no delta support.
- *
- * @author Eike Stepper
- * @since 3.0
- */
-public class AuditFeatureMapTableMapping extends AbstractFeatureMapTableMapping
-{
- private FieldInfo[] keyFields;
-
- public AuditFeatureMapTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature)
- {
- super(mappingStrategy, eClass, feature);
- }
-
- @Override
- protected FieldInfo[] getKeyFields()
- {
- if (keyFields == null)
- {
- keyFields = new FieldInfo[] {
- new FieldInfo(CDODBSchema.FEATUREMAP_REVISION_ID, getMappingStrategy().getStore().getIDHandler().getDBType()),
- new FieldInfo(CDODBSchema.FEATUREMAP_VERSION, DBType.INTEGER) };
- }
-
- return keyFields;
- }
-
- @Override
- protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException
- {
- getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID());
- stmt.setInt(2, revision.getVersion());
- }
-
- public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised)
- {
- // the audit list mapping does not care about revised references -> NOP
- }
-}
+/* + * Copyright (c) 2004 - 2012 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 + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + * Christopher Albert - 254455: [DB] Support FeatureMaps bug 254455 + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * This is a featuremap-table mapping for audit mode. It has ID and version columns and no delta support. + * + * @author Eike Stepper + * @since 3.0 + */ +public class AuditFeatureMapTableMapping extends AbstractFeatureMapTableMapping +{ + private FieldInfo[] keyFields; + + private String sqlClear; + + public AuditFeatureMapTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + initSQLStrings(); + } + + private void initSQLStrings() + { + // ----------- clear list ------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("DELETE FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION); + builder.append("=? "); //$NON-NLS-1$ + sqlClear = builder.toString(); + } + + @Override + protected FieldInfo[] getKeyFields() + { + if (keyFields == null) + { + keyFields = new FieldInfo[] { + new FieldInfo(CDODBSchema.FEATUREMAP_REVISION_ID, getMappingStrategy().getStore().getIDHandler().getDBType()), + new FieldInfo(CDODBSchema.FEATUREMAP_VERSION, DBType.INTEGER) }; + } + + return keyFields; + } + + @Override + protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException + { + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, revision.getVersion()); + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + // the audit list mapping does not care about revised references -> NOP + } + + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlClear, ReuseProbability.HIGH); + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id); + stmt.setInt(2, version); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMappingWithRanges.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMappingWithRanges.java index 196c67273a..863b08035a 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMappingWithRanges.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMappingWithRanges.java @@ -1,1212 +1,1218 @@ -/*
- * Copyright (c) 2004 - 2012 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
- * Stefan Winkler - Bug 271444: [DB] Multiple refactorings bug 271444
- * Christopher Albert - Bug 254455: [DB] Support FeatureMaps bug 254455
- * Victor Roldan Betancort - Bug 283998: [DB] Chunk reading for multiple chunks fails
- * Lothar Werzinger - Bug 296440: [DB] Change RDB schema to improve scalability of to-many references in audit mode
- * Stefan Winkler - cleanup, merge and maintenance
- * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support
- * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy
- */
-package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
-
-import org.eclipse.emf.cdo.common.branch.CDOBranch;
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.revision.CDOList;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
-import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor;
-import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta;
-import org.eclipse.emf.cdo.server.IRepository;
-import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
-import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk;
-import org.eclipse.emf.cdo.server.db.IDBStore;
-import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
-import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader;
-import org.eclipse.emf.cdo.server.db.IIDHandler;
-import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
-import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
-import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport;
-import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
-import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
-import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
-import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
-
-import org.eclipse.net4j.db.DBException;
-import org.eclipse.net4j.db.DBType;
-import org.eclipse.net4j.db.DBUtil;
-import org.eclipse.net4j.db.ddl.IDBField;
-import org.eclipse.net4j.db.ddl.IDBIndex.Type;
-import org.eclipse.net4j.db.ddl.IDBTable;
-import org.eclipse.net4j.util.ImplementationError;
-import org.eclipse.net4j.util.collection.MoveableList;
-import org.eclipse.net4j.util.om.trace.ContextTracer;
-
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EStructuralFeature;
-import org.eclipse.emf.ecore.util.FeatureMap;
-
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-/**
- * This is a featuremap-table mapping for audit mode. It is optimized for frequent insert operations at the list's end,
- * which causes just 1 DB row to be changed. This is achieved by introducing a version range (columns
- * {@link CDODBSchema#LIST_REVISION_VERSION_ADDED cdo_version_added} and
- * {@link CDODBSchema#LIST_REVISION_VERSION_REMOVED cdo_version_removed}) which records for which revisions a particular
- * entry existed. Also, this mapping is mainly optimized for potentially very large lists: the need for having the
- * complete list stored in memory to do in-the-middle-moved and inserts is traded in for a few more DB access
- * operations.
- *
- * @author Eike Stepper
- * @author Stefan Winkler
- * @author Lothar Werzinger
- * @since 3.0
- */
-public class AuditFeatureMapTableMappingWithRanges extends BasicAbstractListTableMapping implements
- IListMappingDeltaSupport
-{
- private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AuditFeatureMapTableMappingWithRanges.class);
-
- /**
- * Used to clean up lists for detached objects.
- */
- private static final int FINAL_VERSION = Integer.MAX_VALUE;
-
- /**
- * The table of this mapping.
- */
- private IDBTable table;
-
- /**
- * The tags mapped to column names
- */
- private HashMap<CDOID, String> tagMap;
-
- /**
- * Column name Set
- */
- private List<String> columnNames;
-
- /**
- * The type mappings for the value fields.
- */
- private Map<CDOID, ITypeMapping> typeMappings;
-
- private List<DBType> dbTypes;
-
- // --------- SQL strings - see initSQLStrings() -----------------
-
- private String sqlSelectChunksPrefix;
-
- private String sqlOrderByIndex;
-
- private String sqlInsert;
-
- private String sqlRemoveEntry;
-
- private String sqlDeleteEntry;
-
- private String sqlUpdateIndex;
-
- private String sqlGetValue;
-
- private String sqlClearList;
-
- private String sqlDeleteList;
-
- public AuditFeatureMapTableMappingWithRanges(IMappingStrategy mappingStrategy, EClass eClass,
- EStructuralFeature feature)
- {
- super(mappingStrategy, eClass, feature);
- initDBTypes();
- initTable();
- initSQLStrings();
- }
-
- private void initDBTypes()
- {
- // TODO add annotation processing here ...
- ITypeMapping.Registry registry = ITypeMapping.Registry.INSTANCE;
- dbTypes = new ArrayList<DBType>(registry.getDefaultFeatureMapDBTypes());
- }
-
- private void initTable()
- {
- IDBStore store = getMappingStrategy().getStore();
- String tableName = getMappingStrategy().getTableName(getContainingClass(), getFeature());
- table = store.getDBSchema().addTable(tableName);
-
- // add fields for CDOID
- IDBField idField = table.addField(CDODBSchema.FEATUREMAP_REVISION_ID, store.getIDHandler().getDBType());
-
- // add fields for version range
- IDBField versionAddedField = table.addField(CDODBSchema.FEATUREMAP_VERSION_ADDED, DBType.INTEGER);
- IDBField versionRemovedField = table.addField(CDODBSchema.FEATUREMAP_VERSION_REMOVED, DBType.INTEGER);
-
- // add field for list index
- IDBField idxField = table.addField(CDODBSchema.FEATUREMAP_IDX, DBType.INTEGER);
-
- // add field for FeatureMap tag (MetaID for Feature in CDO registry)
- IDBField tagField = table.addField(CDODBSchema.FEATUREMAP_TAG, store.getIDHandler().getDBType());
-
- tagMap = new HashMap<CDOID, String>();
- typeMappings = new HashMap<CDOID, ITypeMapping>();
- columnNames = new ArrayList<String>();
-
- // create columns for all DBTypes
- for (DBType type : getDBTypes())
- {
- String column = CDODBSchema.FEATUREMAP_VALUE + "_" + type.name();
- table.addField(column, type);
- columnNames.add(column);
- }
-
- // TODO think about indices
- table.addIndex(Type.NON_UNIQUE, idField);
- table.addIndex(Type.NON_UNIQUE, versionAddedField);
- table.addIndex(Type.NON_UNIQUE, versionRemovedField);
- table.addIndex(Type.NON_UNIQUE, idxField);
- table.addIndex(Type.NON_UNIQUE, tagField);
- }
-
- private void initSQLStrings()
- {
- String tableName = getTable().getName();
-
- // ---------------- SELECT to read chunks ----------------------------
- StringBuilder builder = new StringBuilder();
- builder.append("SELECT "); //$NON-NLS-1$
-
- builder.append(CDODBSchema.FEATUREMAP_TAG);
- builder.append(", "); //$NON-NLS-1$
-
- Iterator<String> iter = columnNames.iterator();
- while (iter.hasNext())
- {
- builder.append(iter.next());
- if (iter.hasNext())
- {
- builder.append(", "); //$NON-NLS-1$
- }
- }
-
- builder.append(" FROM "); //$NON-NLS-1$
- builder.append(tableName);
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED);
- builder.append("<=? AND ("); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
- builder.append(" IS NULL OR "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
- builder.append(">?)"); //$NON-NLS-1$
- sqlSelectChunksPrefix = builder.toString();
-
- sqlOrderByIndex = " ORDER BY " + CDODBSchema.FEATUREMAP_IDX; //$NON-NLS-1$
-
- // ----------------- INSERT - prefix -----------------
- builder = new StringBuilder("INSERT INTO "); //$NON-NLS-1$
- builder.append(tableName);
- builder.append("("); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_IDX);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_TAG);
-
- for (int i = 0; i < columnNames.size(); i++)
- {
- builder.append(", "); //$NON-NLS-1$
- builder.append(columnNames.get(i));
- }
-
- builder.append(") VALUES (?, ?, ?, ?, ?"); //$NON-NLS-1$
- for (int i = 0; i < columnNames.size(); i++)
- {
- builder.append(", ?"); //$NON-NLS-1$
- }
-
- builder.append(")"); //$NON-NLS-1$
- sqlInsert = builder.toString();
-
- // ----------------- remove current entry -----------------
- builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
- builder.append(tableName);
- builder.append(" SET "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
- builder.append("=? "); //$NON-NLS-1$
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_IDX);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
- builder.append(" IS NULL"); //$NON-NLS-1$
- sqlRemoveEntry = builder.toString();
-
- // ----------------- delete temporary entry -----------------
- builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$
- builder.append(tableName);
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_IDX);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED);
- builder.append("=?"); //$NON-NLS-1$
- sqlDeleteEntry = builder.toString();
-
- // ----------------- update index -----------------
- builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
- builder.append(tableName);
- builder.append(" SET "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_IDX);
- builder.append("=? WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_IDX);
- builder.append("=?"); //$NON-NLS-1$
- sqlUpdateIndex = builder.toString();
-
- // ----------------- get current value -----------------
- builder = new StringBuilder("SELECT "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_TAG);
- builder.append(", "); //$NON-NLS-1$
-
- iter = columnNames.iterator();
- while (iter.hasNext())
- {
- builder.append(iter.next());
- if (iter.hasNext())
- {
- builder.append(", "); //$NON-NLS-1$
- }
- }
-
- builder.append(" FROM "); //$NON-NLS-1$
- builder.append(tableName);
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_IDX);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
- builder.append(" IS NULL"); //$NON-NLS-1$
- sqlGetValue = builder.toString();
-
- // ----------- clear list items -------------------------
- builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
- builder.append(tableName);
- builder.append(" SET "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
- builder.append("=? "); //$NON-NLS-1$
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
- builder.append(" IS NULL"); //$NON-NLS-1$
- sqlClearList = builder.toString();
-
- // ----------- delete temporary list items -------------------------
- builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$
- builder.append(tableName);
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
- builder.append(" IS NULL"); //$NON-NLS-1$
- sqlDeleteList = builder.toString();
- }
-
- protected List<DBType> getDBTypes()
- {
- return dbTypes;
- }
-
- public Collection<IDBTable> getDBTables()
- {
- return Arrays.asList(table);
- }
-
- protected final IDBTable getTable()
- {
- return table;
- }
-
- protected final List<String> getColumnNames()
- {
- return columnNames;
- }
-
- protected final Map<CDOID, ITypeMapping> getTypeMappings()
- {
- return typeMappings;
- }
-
- protected final Map<CDOID, String> getTagMap()
- {
- return tagMap;
- }
-
- public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk)
- {
- MoveableList<Object> list = revision.getList(getFeature());
-
- if (listChunk == 0 || list.size() == 0)
- {
- // nothing to read take shortcut
- return;
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading list values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), getFeature() //$NON-NLS-1$
- .getName(), revision.getID(), revision.getVersion());
- }
-
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
- ResultSet resultSet = null;
-
- try
- {
- String sql = sqlSelectChunksPrefix + sqlOrderByIndex;
-
- stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH);
-
- idHandler.setCDOID(stmt, 1, revision.getID());
- stmt.setInt(2, revision.getVersion());
- stmt.setInt(3, revision.getVersion());
-
- if (listChunk != CDORevision.UNCHUNKED)
- {
- stmt.setMaxRows(listChunk); // optimization - don't read unneeded rows.
- }
-
- resultSet = stmt.executeQuery();
-
- int currentIndex = 0;
- while ((listChunk == CDORevision.UNCHUNKED || --listChunk >= 0) && resultSet.next())
- {
- CDOID tag = idHandler.getCDOID(resultSet, 1);
- Object value = getTypeMapping(tag).readValue(resultSet);
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Read value for index {0} from result set: {1}", list.size(), value); //$NON-NLS-1$
- }
-
- list.set(currentIndex++, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value));
- }
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- DBUtil.close(resultSet);
- statementCache.releasePreparedStatement(stmt);
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading list values done for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$
- getFeature().getName(), revision.getID(), revision.getVersion());
- }
- }
-
- private void addFeature(CDOID tag)
- {
- EStructuralFeature modelFeature = getFeatureByTag(tag);
-
- ITypeMapping typeMapping = getMappingStrategy().createValueMapping(modelFeature);
- String column = CDODBSchema.FEATUREMAP_VALUE + "_" + typeMapping.getDBType(); //$NON-NLS-1$
-
- tagMap.put(tag, column);
- typeMapping.setDBField(table, column);
- typeMappings.put(tag, typeMapping);
- }
-
- public final void readChunks(IDBStoreChunkReader chunkReader, List<Chunk> chunks, String where)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading list chunk values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$
- getFeature().getName(), chunkReader.getRevision().getID(), chunkReader.getRevision().getVersion());
- }
-
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache();
- PreparedStatement stmt = null;
- ResultSet resultSet = null;
-
- try
- {
- StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix);
- if (where != null)
- {
- builder.append(" AND "); //$NON-NLS-1$
- builder.append(where);
- }
-
- builder.append(sqlOrderByIndex);
-
- String sql = builder.toString();
- stmt = statementCache.getPreparedStatement(sql, ReuseProbability.LOW);
- idHandler.setCDOID(stmt, 1, chunkReader.getRevision().getID());
- stmt.setInt(2, chunkReader.getRevision().getVersion());
- stmt.setInt(3, chunkReader.getRevision().getVersion());
-
- resultSet = stmt.executeQuery();
-
- Chunk chunk = null;
- int chunkSize = 0;
- int chunkIndex = 0;
- int indexInChunk = 0;
-
- while (resultSet.next())
- {
- CDOID tag = idHandler.getCDOID(resultSet, 1);
- Object value = getTypeMapping(tag).readValue(resultSet);
-
- if (chunk == null)
- {
- chunk = chunks.get(chunkIndex++);
- chunkSize = chunk.size();
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Current chunk no. {0} is [start = {1}, size = {2}]", chunkIndex - 1, chunk.getStartIndex(), //$NON-NLS-1$
- chunkSize);
- }
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Read value for chunk index {0} from result set: {1}", indexInChunk, value); //$NON-NLS-1$
- }
-
- chunk.add(indexInChunk++, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value));
- if (indexInChunk == chunkSize)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Chunk finished"); //$NON-NLS-1$
- }
-
- chunk = null;
- indexInChunk = 0;
- }
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading list chunk values done for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$
- getFeature(), chunkReader.getRevision());
- }
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- DBUtil.close(resultSet);
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision)
- {
- CDOList values = revision.getList(getFeature());
-
- int idx = 0;
- for (Object element : values)
- {
- writeValue(accessor, revision, idx++, element);
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Writing done"); //$NON-NLS-1$
- }
- }
-
- protected final void writeValue(IDBStoreAccessor accessor, CDORevision revision, int idx, Object value)
- {
- if (TRACER.isEnabled())
- {
- TRACER
- .format(
- "Writing value for feature {0}.{1} index {2} of {3} : {4}", getContainingClass().getName(), getFeature(), idx, revision, value); //$NON-NLS-1$
- }
-
- addEntry(accessor, revision.getID(), revision.getVersion(), idx, value, revision.getTimeStamp());
- }
-
- /**
- * Get column name (lazy).
- *
- * @param tag
- * The feature's MetaID in CDO
- * @return the column name where the values are stored
- */
- protected String getColumnName(CDOID tag)
- {
- String column = tagMap.get(tag);
- if (column == null)
- {
- addFeature(tag);
- column = tagMap.get(tag);
- }
-
- return column;
- }
-
- /**
- * Get type mapping (lazy).
- *
- * @param tag
- * The feature's MetaID in CDO
- * @return the corresponding type mapping
- */
- protected ITypeMapping getTypeMapping(CDOID tag)
- {
- ITypeMapping typeMapping = typeMappings.get(tag);
- if (typeMapping == null)
- {
- addFeature(tag);
- typeMapping = typeMappings.get(tag);
- }
-
- return typeMapping;
- }
-
- /**
- * @param metaID
- * @return the column name where the values are stored
- */
- private EStructuralFeature getFeatureByTag(CDOID tag)
- {
- return (EStructuralFeature)getMappingStrategy().getStore().getMetaDataManager().getMetaInstance(tag);
- }
-
- /**
- * @param feature
- * The EStructuralFeature
- * @return The feature's MetaID in CDO
- */
- protected CDOID getTagByFeature(EStructuralFeature feature, long timestamp)
- {
- return getMappingStrategy().getStore().getMetaDataManager().getMetaID(feature, timestamp);
- }
-
- /**
- * Clear a list of a given revision.
- *
- * @param accessor
- * the accessor to use
- * @param id
- * the id of the revision from which to remove all items
- */
- public void clearList(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmtDeleteTemp = null;
- PreparedStatement stmtClear = null;
-
- try
- {
- // delete temporary entries
- stmtDeleteTemp = statementCache.getPreparedStatement(sqlDeleteList, ReuseProbability.HIGH);
- idHandler.setCDOID(stmtDeleteTemp, 1, id);
- stmtDeleteTemp.setInt(2, newVersion);
-
- int result = DBUtil.update(stmtDeleteTemp, false);
- if (TRACER.isEnabled())
- {
- TRACER.format("DeleteList result: {0}", result); //$NON-NLS-1$
- }
-
- // clear rest of the list
- stmtClear = statementCache.getPreparedStatement(sqlClearList, ReuseProbability.HIGH);
- stmtClear.setInt(1, newVersion);
- idHandler.setCDOID(stmtClear, 2, id);
-
- result = DBUtil.update(stmtClear, false);
- if (TRACER.isEnabled())
- {
- TRACER.format("ClearList result: {0}", result); //$NON-NLS-1$
- }
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmtDeleteTemp);
- statementCache.releasePreparedStatement(stmtClear);
- }
- }
-
- public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("objectRevised {0}: {1}", id, revised); //$NON-NLS-1$
- }
-
- CDOBranch main = getMappingStrategy().getStore().getRepository().getBranchManager().getMainBranch();
-
- // get revision from cache to find out version number
- CDORevision revision = getMappingStrategy().getStore().getRepository().getRevisionManager()
- .getRevision(id, main.getHead(), /* chunksize = */0, CDORevision.DEPTH_NONE, true);
-
- // set cdo_revision_removed for all list items (so we have no NULL values)
- clearList(accessor, id, revision.getVersion(), FINAL_VERSION);
- }
-
- public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion,
- final int newVersion, long created, CDOListFeatureDelta delta)
- {
- IRepository repo = accessor.getStore().getRepository();
- InternalCDORevision originalRevision = (InternalCDORevision)repo.getRevisionManager().getRevision(id,
- repo.getBranchManager().getMainBranch().getHead(), /* chunksize = */0, CDORevision.DEPTH_NONE, true);
-
- int oldListSize = originalRevision.getList(getFeature()).size();
-
- if (TRACER.isEnabled())
- {
- TRACER.format("ListTableMapping.processDelta for revision {0} - previous list size: {1}", originalRevision, //$NON-NLS-1$
- oldListSize);
- }
-
- // let the visitor collect the changes
- ListDeltaVisitor visitor = new ListDeltaVisitor(accessor, originalRevision, oldVersion, newVersion, created);
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Processing deltas..."); //$NON-NLS-1$
- }
-
- for (CDOFeatureDelta listDelta : delta.getListChanges())
- {
- listDelta.accept(visitor);
- }
- }
-
- private class ListDeltaVisitor implements CDOFeatureDeltaVisitor
- {
- private IDBStoreAccessor accessor;
-
- private InternalCDORevision originalRevision;
-
- private CDOID id;
-
- private int oldVersion;
-
- private int newVersion;
-
- private int lastIndex;
-
- private long timestamp;
-
- public ListDeltaVisitor(IDBStoreAccessor accessor, InternalCDORevision originalRevision, int oldVersion,
- int newVersion, long timestamp)
- {
- this.accessor = accessor;
- this.originalRevision = originalRevision;
- id = this.originalRevision.getID();
- this.oldVersion = oldVersion;
- this.newVersion = newVersion;
- lastIndex = originalRevision.getList(getFeature()).size() - 1;
- this.timestamp = timestamp;
- }
-
- public void visit(CDOMoveFeatureDelta delta)
- {
- int fromIdx = delta.getOldPosition();
- int toIdx = delta.getNewPosition();
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Delta Moving: {0} to {1}", fromIdx, toIdx); //$NON-NLS-1$
- }
-
- Object value = getValue(accessor, id, fromIdx);
-
- // remove the item
- removeEntry(accessor, id, oldVersion, newVersion, fromIdx);
-
- // adjust indexes and shift either up or down
- if (fromIdx < toIdx)
- {
- moveOneUp(accessor, id, oldVersion, newVersion, fromIdx + 1, toIdx);
- }
- else
- { // fromIdx > toIdx here
- moveOneDown(accessor, id, oldVersion, newVersion, toIdx, fromIdx - 1);
- }
-
- // create the item
- addEntry(accessor, id, newVersion, toIdx, value, timestamp);
- }
-
- public void visit(CDOAddFeatureDelta delta)
- {
- int startIndex = delta.getIndex();
- int endIndex = lastIndex;
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Delta Adding at: {0}", startIndex); //$NON-NLS-1$
- }
-
- if (startIndex <= endIndex)
- {
- // make room for the new item
- moveOneDown(accessor, id, oldVersion, newVersion, startIndex, endIndex);
- }
-
- // create the item
- addEntry(accessor, id, newVersion, startIndex, delta.getValue(), timestamp);
-
- ++lastIndex;
- }
-
- public void visit(CDORemoveFeatureDelta delta)
- {
- int startIndex = delta.getIndex();
- int endIndex = lastIndex;
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Delta Removing at: {0}", startIndex); //$NON-NLS-1$
- }
-
- // remove the item
- removeEntry(accessor, id, oldVersion, newVersion, startIndex);
-
- // make room for the new item
- moveOneUp(accessor, id, oldVersion, newVersion, startIndex + 1, endIndex);
-
- --lastIndex;
- }
-
- public void visit(CDOSetFeatureDelta delta)
- {
- int index = delta.getIndex();
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Delta Setting at: {0}", index); //$NON-NLS-1$
- }
-
- // remove the item
- removeEntry(accessor, id, oldVersion, newVersion, index);
-
- // create the item
- addEntry(accessor, id, newVersion, index, delta.getValue(), timestamp);
- }
-
- public void visit(CDOUnsetFeatureDelta delta)
- {
- if (delta.getFeature().isUnsettable())
- {
- throw new ImplementationError("Should not be called"); //$NON-NLS-1$
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Delta Unsetting"); //$NON-NLS-1$
- }
-
- clearList(accessor, id, oldVersion, newVersion);
- lastIndex = -1;
- }
-
- public void visit(CDOListFeatureDelta delta)
- {
- throw new ImplementationError("Should not be called"); //$NON-NLS-1$
- }
-
- public void visit(CDOClearFeatureDelta delta)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Delta Clearing"); //$NON-NLS-1$
- }
-
- clearList(accessor, id, oldVersion, newVersion);
- lastIndex = -1;
- }
-
- public void visit(CDOContainerFeatureDelta delta)
- {
- throw new ImplementationError("Should not be called"); //$NON-NLS-1$
- }
-
- private void moveOneUp(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int startIndex,
- int endIndex)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH);
-
- for (int index = startIndex; index <= endIndex; ++index)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneUp moving: {0} -> {1}", index, index - 1); //$NON-NLS-1$
- }
-
- int column = 1;
- stmt.setInt(column++, index - 1);
- idHandler.setCDOID(stmt, column++, id);
- stmt.setInt(column++, newVersion);
- stmt.setInt(column++, index);
-
- int result = DBUtil.update(stmt, false);
- switch (result)
- {
- case 0:
- Object value = getValue(accessor, id, index);
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneUp remove: {0}", index); //$NON-NLS-1$
- }
-
- removeEntry(accessor, id, oldVersion, newVersion, index);
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneUp add: {0}", index - 1); //$NON-NLS-1$
- }
-
- addEntry(accessor, id, newVersion, index - 1, value, timestamp);
- break;
-
- case 1:
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneUp updated: {0} -> {1}", index, index - 1); //$NON-NLS-1$
- }
-
- break;
-
- default:
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneUp Too many results: {0} -> {1}: {2}", index, index + 1, result); //$NON-NLS-1$
- }
-
- throw new DBException("Too many results"); //$NON-NLS-1$
- }
- }
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- private void moveOneDown(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int startIndex,
- int endIndex)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH);
- for (int index = endIndex; index >= startIndex; --index)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneDown moving: {0} -> {1}", index, index + 1); //$NON-NLS-1$
- }
-
- int column = 1;
- stmt.setInt(column++, index + 1);
- idHandler.setCDOID(stmt, column++, id);
- stmt.setInt(column++, newVersion);
- stmt.setInt(column++, index);
-
- int result = DBUtil.update(stmt, false);
- switch (result)
- {
- case 0:
- Object value = getValue(accessor, id, index);
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneDown remove: {0}", index); //$NON-NLS-1$
- }
-
- removeEntry(accessor, id, oldVersion, newVersion, index);
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneDown add: {0}", index + 1); //$NON-NLS-1$
- }
-
- addEntry(accessor, id, newVersion, index + 1, value, timestamp);
- break;
-
- case 1:
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneDown updated: {0} -> {1}", index, index + 1); //$NON-NLS-1$
- }
-
- break;
-
- default:
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneDown Too many results: {0} -> {1}: {2}", index, index + 1, result); //$NON-NLS-1$
- }
-
- throw new DBException("Too many results"); //$NON-NLS-1$
- }
- }
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
- }
-
- private void addEntry(IDBStoreAccessor accessor, CDOID id, int version, int index, Object value, long timestamp)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Adding value for feature() {0}.{1} index {2} of {3}v{4} : {5}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), index, id, version, value);
- }
-
- try
- {
- FeatureMap.Entry entry = (FeatureMap.Entry)value;
- EStructuralFeature entryFeature = entry.getEStructuralFeature();
- CDOID tag = getTagByFeature(entryFeature, timestamp);
- String columnName = getColumnName(tag);
-
- stmt = statementCache.getPreparedStatement(sqlInsert, ReuseProbability.HIGH);
-
- int column = 1;
- idHandler.setCDOID(stmt, column++, id);
- stmt.setInt(column++, version);
- stmt.setNull(column++, DBType.INTEGER.getCode()); // versionRemoved
- stmt.setInt(column++, index);
- idHandler.setCDOID(stmt, column++, tag);
-
- for (int i = 0; i < columnNames.size(); i++)
- {
- if (columnNames.get(i).equals(columnName))
- {
- getTypeMapping(tag).setValue(stmt, column++, entry.getValue());
- }
- else
- {
- stmt.setNull(column++, getDBTypes().get(i).getCode());
- }
- }
-
- DBUtil.update(stmt, true);
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- catch (IllegalStateException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- private void removeEntry(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int index)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), index, id, newVersion);
- }
-
- try
- {
- // try to delete a temporary entry first
- stmt = statementCache.getPreparedStatement(sqlDeleteEntry, ReuseProbability.HIGH);
-
- int column = 1;
- idHandler.setCDOID(stmt, column++, id);
- stmt.setInt(column++, index);
- stmt.setInt(column++, newVersion);
-
- int result = DBUtil.update(stmt, false);
- if (result == 1)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("removeEntry deleted: {0}", index); //$NON-NLS-1$
- }
- }
- else if (result > 1)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("removeEntry Too many results: {0}: {1}", index, result); //$NON-NLS-1$
- }
-
- throw new DBException("Too many results"); //$NON-NLS-1$
- }
- else
- {
- // no temporary entry found, so mark the entry as removed
- statementCache.releasePreparedStatement(stmt);
- stmt = statementCache.getPreparedStatement(sqlRemoveEntry, ReuseProbability.HIGH);
-
- column = 1;
- stmt.setInt(column++, newVersion);
- idHandler.setCDOID(stmt, column++, id);
- stmt.setInt(column++, index);
- DBUtil.update(stmt, true);
- }
- }
- catch (SQLException e)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage());
- }
-
- throw new DBException(e);
- }
- catch (IllegalStateException e)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage());
- }
-
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- private FeatureMap.Entry getValue(IDBStoreAccessor accessor, CDOID id, int index)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
- FeatureMap.Entry result = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlGetValue, ReuseProbability.HIGH);
-
- int column = 1;
- idHandler.setCDOID(stmt, column++, id);
- stmt.setInt(column++, index);
-
- ResultSet resultSet = stmt.executeQuery();
- if (!resultSet.next())
- {
- throw new DBException("getValue expects exactly one result");
- }
-
- CDOID tag = idHandler.getCDOID(resultSet, 1);
- Object value = getTypeMapping(tag).readValue(resultSet);
- result = CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value);
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Read value (index {0}) from result set: {1}", index, result); //$NON-NLS-1$
- }
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
-
- return result;
- }
-
- public final boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere,
- QueryXRefsContext context, String idString)
- {
- // must never be called (a feature map is not associated with an EReference feature, so XRefs are nor supported
- // here)
- throw new ImplementationError("Should never be called!");
- }
-}
+/* + * Copyright (c) 2004 - 2012 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 + * Stefan Winkler - Bug 271444: [DB] Multiple refactorings bug 271444 + * Christopher Albert - Bug 254455: [DB] Support FeatureMaps bug 254455 + * Victor Roldan Betancort - Bug 283998: [DB] Chunk reading for multiple chunks fails + * Lothar Werzinger - Bug 296440: [DB] Change RDB schema to improve scalability of to-many references in audit mode + * Stefan Winkler - cleanup, merge and maintenance + * Stefan Winkler - Bug 285426: [DB] Implement user-defined typeMapping support + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDOList; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk; +import org.eclipse.emf.cdo.server.db.IDBStore; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex.Type; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.ImplementationError; +import org.eclipse.net4j.util.collection.MoveableList; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.FeatureMap; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * This is a featuremap-table mapping for audit mode. It is optimized for frequent insert operations at the list's end, + * which causes just 1 DB row to be changed. This is achieved by introducing a version range (columns + * {@link CDODBSchema#LIST_REVISION_VERSION_ADDED cdo_version_added} and + * {@link CDODBSchema#LIST_REVISION_VERSION_REMOVED cdo_version_removed}) which records for which revisions a particular + * entry existed. Also, this mapping is mainly optimized for potentially very large lists: the need for having the + * complete list stored in memory to do in-the-middle-moved and inserts is traded in for a few more DB access + * operations. + * + * @author Eike Stepper + * @author Stefan Winkler + * @author Lothar Werzinger + * @since 3.0 + */ +public class AuditFeatureMapTableMappingWithRanges extends BasicAbstractListTableMapping implements + IListMappingDeltaSupport +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AuditFeatureMapTableMappingWithRanges.class); + + /** + * Used to clean up lists for detached objects. + */ + private static final int FINAL_VERSION = Integer.MAX_VALUE; + + /** + * The table of this mapping. + */ + private IDBTable table; + + /** + * The tags mapped to column names + */ + private HashMap<CDOID, String> tagMap; + + /** + * Column name Set + */ + private List<String> columnNames; + + /** + * The type mappings for the value fields. + */ + private Map<CDOID, ITypeMapping> typeMappings; + + private List<DBType> dbTypes; + + // --------- SQL strings - see initSQLStrings() ----------------- + + private String sqlSelectChunksPrefix; + + private String sqlOrderByIndex; + + private String sqlInsert; + + private String sqlRemoveEntry; + + private String sqlDeleteEntry; + + private String sqlUpdateIndex; + + private String sqlGetValue; + + private String sqlClearList; + + private String sqlDeleteList; + + public AuditFeatureMapTableMappingWithRanges(IMappingStrategy mappingStrategy, EClass eClass, + EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + initDBTypes(); + initTable(); + initSQLStrings(); + } + + private void initDBTypes() + { + // TODO add annotation processing here ... + ITypeMapping.Registry registry = ITypeMapping.Registry.INSTANCE; + dbTypes = new ArrayList<DBType>(registry.getDefaultFeatureMapDBTypes()); + } + + private void initTable() + { + IDBStore store = getMappingStrategy().getStore(); + String tableName = getMappingStrategy().getTableName(getContainingClass(), getFeature()); + table = store.getDBSchema().addTable(tableName); + + // add fields for CDOID + IDBField idField = table.addField(CDODBSchema.FEATUREMAP_REVISION_ID, store.getIDHandler().getDBType()); + + // add fields for version range + IDBField versionAddedField = table.addField(CDODBSchema.FEATUREMAP_VERSION_ADDED, DBType.INTEGER); + IDBField versionRemovedField = table.addField(CDODBSchema.FEATUREMAP_VERSION_REMOVED, DBType.INTEGER); + + // add field for list index + IDBField idxField = table.addField(CDODBSchema.FEATUREMAP_IDX, DBType.INTEGER); + + // add field for FeatureMap tag (MetaID for Feature in CDO registry) + IDBField tagField = table.addField(CDODBSchema.FEATUREMAP_TAG, store.getIDHandler().getDBType()); + + tagMap = new HashMap<CDOID, String>(); + typeMappings = new HashMap<CDOID, ITypeMapping>(); + columnNames = new ArrayList<String>(); + + // create columns for all DBTypes + for (DBType type : getDBTypes()) + { + String column = CDODBSchema.FEATUREMAP_VALUE + "_" + type.name(); + table.addField(column, type); + columnNames.add(column); + } + + // TODO think about indices + table.addIndex(Type.NON_UNIQUE, idField); + table.addIndex(Type.NON_UNIQUE, versionAddedField); + table.addIndex(Type.NON_UNIQUE, versionRemovedField); + table.addIndex(Type.NON_UNIQUE, idxField); + table.addIndex(Type.NON_UNIQUE, tagField); + } + + private void initSQLStrings() + { + String tableName = getTable().getName(); + + // ---------------- SELECT to read chunks ---------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + + builder.append(CDODBSchema.FEATUREMAP_TAG); + builder.append(", "); //$NON-NLS-1$ + + Iterator<String> iter = columnNames.iterator(); + while (iter.hasNext()) + { + builder.append(iter.next()); + if (iter.hasNext()) + { + builder.append(", "); //$NON-NLS-1$ + } + } + + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(" IS NULL OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(">?)"); //$NON-NLS-1$ + sqlSelectChunksPrefix = builder.toString(); + + sqlOrderByIndex = " ORDER BY " + CDODBSchema.FEATUREMAP_IDX; //$NON-NLS-1$ + + // ----------------- INSERT - prefix ----------------- + builder = new StringBuilder("INSERT INTO "); //$NON-NLS-1$ + builder.append(tableName); + builder.append("("); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_TAG); + + for (int i = 0; i < columnNames.size(); i++) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(columnNames.get(i)); + } + + builder.append(") VALUES (?, ?, ?, ?, ?"); //$NON-NLS-1$ + for (int i = 0; i < columnNames.size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + + builder.append(")"); //$NON-NLS-1$ + sqlInsert = builder.toString(); + + // ----------------- remove current entry ----------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlRemoveEntry = builder.toString(); + + // ----------------- delete temporary entry ----------------- + builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED); + builder.append("=?"); //$NON-NLS-1$ + sqlDeleteEntry = builder.toString(); + + // ----------------- update index ----------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=?"); //$NON-NLS-1$ + sqlUpdateIndex = builder.toString(); + + // ----------------- get current value ----------------- + builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_TAG); + builder.append(", "); //$NON-NLS-1$ + + iter = columnNames.iterator(); + while (iter.hasNext()) + { + builder.append(iter.next()); + if (iter.hasNext()) + { + builder.append(", "); //$NON-NLS-1$ + } + } + + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlGetValue = builder.toString(); + + // ----------- clear list items ------------------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlClearList = builder.toString(); + + // ----------- delete temporary list items ------------------------- + builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_ADDED); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlDeleteList = builder.toString(); + } + + protected List<DBType> getDBTypes() + { + return dbTypes; + } + + public Collection<IDBTable> getDBTables() + { + return Arrays.asList(table); + } + + protected final IDBTable getTable() + { + return table; + } + + protected final List<String> getColumnNames() + { + return columnNames; + } + + protected final Map<CDOID, ITypeMapping> getTypeMappings() + { + return typeMappings; + } + + protected final Map<CDOID, String> getTagMap() + { + return tagMap; + } + + public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + MoveableList<Object> list = revision.getList(getFeature()); + + if (listChunk == 0 || list.size() == 0) + { + // nothing to read take shortcut + return; + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), getFeature() //$NON-NLS-1$ + .getName(), revision.getID(), revision.getVersion()); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + String sql = sqlSelectChunksPrefix + sqlOrderByIndex; + + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH); + + idHandler.setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, revision.getVersion()); + stmt.setInt(3, revision.getVersion()); + + if (listChunk != CDORevision.UNCHUNKED) + { + stmt.setMaxRows(listChunk); // optimization - don't read unneeded rows. + } + + resultSet = stmt.executeQuery(); + + int currentIndex = 0; + while ((listChunk == CDORevision.UNCHUNKED || --listChunk >= 0) && resultSet.next()) + { + CDOID tag = idHandler.getCDOID(resultSet, 1); + Object value = getTypeMapping(tag).readValue(resultSet); + + if (TRACER.isEnabled()) + { + TRACER.format("Read value for index {0} from result set: {1}", list.size(), value); //$NON-NLS-1$ + } + + list.set(currentIndex++, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value)); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list values done for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), revision.getID(), revision.getVersion()); + } + } + + private void addFeature(CDOID tag) + { + EStructuralFeature modelFeature = getFeatureByTag(tag); + + ITypeMapping typeMapping = getMappingStrategy().createValueMapping(modelFeature); + String column = CDODBSchema.FEATUREMAP_VALUE + "_" + typeMapping.getDBType(); //$NON-NLS-1$ + + tagMap.put(tag, column); + typeMapping.setDBField(table, column); + typeMappings.put(tag, typeMapping); + } + + public final void readChunks(IDBStoreChunkReader chunkReader, List<Chunk> chunks, String where) + { + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), chunkReader.getRevision().getID(), chunkReader.getRevision().getVersion()); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix); + if (where != null) + { + builder.append(" AND "); //$NON-NLS-1$ + builder.append(where); + } + + builder.append(sqlOrderByIndex); + + String sql = builder.toString(); + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.LOW); + idHandler.setCDOID(stmt, 1, chunkReader.getRevision().getID()); + stmt.setInt(2, chunkReader.getRevision().getVersion()); + stmt.setInt(3, chunkReader.getRevision().getVersion()); + + resultSet = stmt.executeQuery(); + + Chunk chunk = null; + int chunkSize = 0; + int chunkIndex = 0; + int indexInChunk = 0; + + while (resultSet.next()) + { + CDOID tag = idHandler.getCDOID(resultSet, 1); + Object value = getTypeMapping(tag).readValue(resultSet); + + if (chunk == null) + { + chunk = chunks.get(chunkIndex++); + chunkSize = chunk.size(); + + if (TRACER.isEnabled()) + { + TRACER.format("Current chunk no. {0} is [start = {1}, size = {2}]", chunkIndex - 1, chunk.getStartIndex(), //$NON-NLS-1$ + chunkSize); + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Read value for chunk index {0} from result set: {1}", indexInChunk, value); //$NON-NLS-1$ + } + + chunk.add(indexInChunk++, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value)); + if (indexInChunk == chunkSize) + { + if (TRACER.isEnabled()) + { + TRACER.format("Chunk finished"); //$NON-NLS-1$ + } + + chunk = null; + indexInChunk = 0; + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values done for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature(), chunkReader.getRevision()); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + CDOList values = revision.getList(getFeature()); + + int idx = 0; + for (Object element : values) + { + writeValue(accessor, revision, idx++, element); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Writing done"); //$NON-NLS-1$ + } + } + + protected final void writeValue(IDBStoreAccessor accessor, CDORevision revision, int idx, Object value) + { + if (TRACER.isEnabled()) + { + TRACER + .format( + "Writing value for feature {0}.{1} index {2} of {3} : {4}", getContainingClass().getName(), getFeature(), idx, revision, value); //$NON-NLS-1$ + } + + addEntry(accessor, revision.getID(), revision.getVersion(), idx, value, revision.getTimeStamp()); + } + + /** + * Get column name (lazy). + * + * @param tag + * The feature's MetaID in CDO + * @return the column name where the values are stored + */ + protected String getColumnName(CDOID tag) + { + String column = tagMap.get(tag); + if (column == null) + { + addFeature(tag); + column = tagMap.get(tag); + } + + return column; + } + + /** + * Get type mapping (lazy). + * + * @param tag + * The feature's MetaID in CDO + * @return the corresponding type mapping + */ + protected ITypeMapping getTypeMapping(CDOID tag) + { + ITypeMapping typeMapping = typeMappings.get(tag); + if (typeMapping == null) + { + addFeature(tag); + typeMapping = typeMappings.get(tag); + } + + return typeMapping; + } + + /** + * @param metaID + * @return the column name where the values are stored + */ + private EStructuralFeature getFeatureByTag(CDOID tag) + { + return (EStructuralFeature)getMappingStrategy().getStore().getMetaDataManager().getMetaInstance(tag); + } + + /** + * @param feature + * The EStructuralFeature + * @return The feature's MetaID in CDO + */ + protected CDOID getTagByFeature(EStructuralFeature feature, long timestamp) + { + return getMappingStrategy().getStore().getMetaDataManager().getMetaID(feature, timestamp); + } + + /** + * Clear a list of a given revision. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision from which to remove all items + */ + public void clearList(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmtDeleteTemp = null; + PreparedStatement stmtClear = null; + + try + { + // delete temporary entries + stmtDeleteTemp = statementCache.getPreparedStatement(sqlDeleteList, ReuseProbability.HIGH); + idHandler.setCDOID(stmtDeleteTemp, 1, id); + stmtDeleteTemp.setInt(2, newVersion); + + int result = DBUtil.update(stmtDeleteTemp, false); + if (TRACER.isEnabled()) + { + TRACER.format("DeleteList result: {0}", result); //$NON-NLS-1$ + } + + // clear rest of the list + stmtClear = statementCache.getPreparedStatement(sqlClearList, ReuseProbability.HIGH); + stmtClear.setInt(1, newVersion); + idHandler.setCDOID(stmtClear, 2, id); + + result = DBUtil.update(stmtClear, false); + if (TRACER.isEnabled()) + { + TRACER.format("ClearList result: {0}", result); //$NON-NLS-1$ + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmtDeleteTemp); + statementCache.releasePreparedStatement(stmtClear); + } + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + if (TRACER.isEnabled()) + { + TRACER.format("objectRevised {0}: {1}", id, revised); //$NON-NLS-1$ + } + + CDOBranch main = getMappingStrategy().getStore().getRepository().getBranchManager().getMainBranch(); + + // get revision from cache to find out version number + CDORevision revision = getMappingStrategy().getStore().getRepository().getRevisionManager() + .getRevision(id, main.getHead(), /* chunksize = */0, CDORevision.DEPTH_NONE, true); + + // set cdo_revision_removed for all list items (so we have no NULL values) + clearList(accessor, id, revision.getVersion(), FINAL_VERSION); + } + + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + throw new UnsupportedOperationException("Raw deletion does not work in range-based mappings"); + } + + public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion, + final int newVersion, long created, CDOListFeatureDelta delta) + { + IRepository repo = accessor.getStore().getRepository(); + InternalCDORevision originalRevision = (InternalCDORevision)repo.getRevisionManager().getRevision(id, + repo.getBranchManager().getMainBranch().getHead(), /* chunksize = */0, CDORevision.DEPTH_NONE, true); + + int oldListSize = originalRevision.getList(getFeature()).size(); + + if (TRACER.isEnabled()) + { + TRACER.format("ListTableMapping.processDelta for revision {0} - previous list size: {1}", originalRevision, //$NON-NLS-1$ + oldListSize); + } + + // let the visitor collect the changes + ListDeltaVisitor visitor = new ListDeltaVisitor(accessor, originalRevision, oldVersion, newVersion, created); + + if (TRACER.isEnabled()) + { + TRACER.format("Processing deltas..."); //$NON-NLS-1$ + } + + for (CDOFeatureDelta listDelta : delta.getListChanges()) + { + listDelta.accept(visitor); + } + } + + private class ListDeltaVisitor implements CDOFeatureDeltaVisitor + { + private IDBStoreAccessor accessor; + + private InternalCDORevision originalRevision; + + private CDOID id; + + private int oldVersion; + + private int newVersion; + + private int lastIndex; + + private long timestamp; + + public ListDeltaVisitor(IDBStoreAccessor accessor, InternalCDORevision originalRevision, int oldVersion, + int newVersion, long timestamp) + { + this.accessor = accessor; + this.originalRevision = originalRevision; + id = this.originalRevision.getID(); + this.oldVersion = oldVersion; + this.newVersion = newVersion; + lastIndex = originalRevision.getList(getFeature()).size() - 1; + this.timestamp = timestamp; + } + + public void visit(CDOMoveFeatureDelta delta) + { + int fromIdx = delta.getOldPosition(); + int toIdx = delta.getNewPosition(); + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Moving: {0} to {1}", fromIdx, toIdx); //$NON-NLS-1$ + } + + Object value = getValue(accessor, id, fromIdx); + + // remove the item + removeEntry(accessor, id, oldVersion, newVersion, fromIdx); + + // adjust indexes and shift either up or down + if (fromIdx < toIdx) + { + moveOneUp(accessor, id, oldVersion, newVersion, fromIdx + 1, toIdx); + } + else + { // fromIdx > toIdx here + moveOneDown(accessor, id, oldVersion, newVersion, toIdx, fromIdx - 1); + } + + // create the item + addEntry(accessor, id, newVersion, toIdx, value, timestamp); + } + + public void visit(CDOAddFeatureDelta delta) + { + int startIndex = delta.getIndex(); + int endIndex = lastIndex; + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Adding at: {0}", startIndex); //$NON-NLS-1$ + } + + if (startIndex <= endIndex) + { + // make room for the new item + moveOneDown(accessor, id, oldVersion, newVersion, startIndex, endIndex); + } + + // create the item + addEntry(accessor, id, newVersion, startIndex, delta.getValue(), timestamp); + + ++lastIndex; + } + + public void visit(CDORemoveFeatureDelta delta) + { + int startIndex = delta.getIndex(); + int endIndex = lastIndex; + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Removing at: {0}", startIndex); //$NON-NLS-1$ + } + + // remove the item + removeEntry(accessor, id, oldVersion, newVersion, startIndex); + + // make room for the new item + moveOneUp(accessor, id, oldVersion, newVersion, startIndex + 1, endIndex); + + --lastIndex; + } + + public void visit(CDOSetFeatureDelta delta) + { + int index = delta.getIndex(); + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Setting at: {0}", index); //$NON-NLS-1$ + } + + // remove the item + removeEntry(accessor, id, oldVersion, newVersion, index); + + // create the item + addEntry(accessor, id, newVersion, index, delta.getValue(), timestamp); + } + + public void visit(CDOUnsetFeatureDelta delta) + { + if (delta.getFeature().isUnsettable()) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Unsetting"); //$NON-NLS-1$ + } + + clearList(accessor, id, oldVersion, newVersion); + lastIndex = -1; + } + + public void visit(CDOListFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOClearFeatureDelta delta) + { + if (TRACER.isEnabled()) + { + TRACER.format("Delta Clearing"); //$NON-NLS-1$ + } + + clearList(accessor, id, oldVersion, newVersion); + lastIndex = -1; + } + + public void visit(CDOContainerFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + private void moveOneUp(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int startIndex, + int endIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH); + + for (int index = startIndex; index <= endIndex; ++index) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp moving: {0} -> {1}", index, index - 1); //$NON-NLS-1$ + } + + int column = 1; + stmt.setInt(column++, index - 1); + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, newVersion); + stmt.setInt(column++, index); + + int result = DBUtil.update(stmt, false); + switch (result) + { + case 0: + Object value = getValue(accessor, id, index); + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp remove: {0}", index); //$NON-NLS-1$ + } + + removeEntry(accessor, id, oldVersion, newVersion, index); + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp add: {0}", index - 1); //$NON-NLS-1$ + } + + addEntry(accessor, id, newVersion, index - 1, value, timestamp); + break; + + case 1: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp updated: {0} -> {1}", index, index - 1); //$NON-NLS-1$ + } + + break; + + default: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp Too many results: {0} -> {1}: {2}", index, index + 1, result); //$NON-NLS-1$ + } + + throw new DBException("Too many results"); //$NON-NLS-1$ + } + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private void moveOneDown(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int startIndex, + int endIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH); + for (int index = endIndex; index >= startIndex; --index) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown moving: {0} -> {1}", index, index + 1); //$NON-NLS-1$ + } + + int column = 1; + stmt.setInt(column++, index + 1); + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, newVersion); + stmt.setInt(column++, index); + + int result = DBUtil.update(stmt, false); + switch (result) + { + case 0: + Object value = getValue(accessor, id, index); + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown remove: {0}", index); //$NON-NLS-1$ + } + + removeEntry(accessor, id, oldVersion, newVersion, index); + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown add: {0}", index + 1); //$NON-NLS-1$ + } + + addEntry(accessor, id, newVersion, index + 1, value, timestamp); + break; + + case 1: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown updated: {0} -> {1}", index, index + 1); //$NON-NLS-1$ + } + + break; + + default: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown Too many results: {0} -> {1}: {2}", index, index + 1, result); //$NON-NLS-1$ + } + + throw new DBException("Too many results"); //$NON-NLS-1$ + } + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + } + + private void addEntry(IDBStoreAccessor accessor, CDOID id, int version, int index, Object value, long timestamp) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + if (TRACER.isEnabled()) + { + TRACER.format("Adding value for feature() {0}.{1} index {2} of {3}v{4} : {5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, version, value); + } + + try + { + FeatureMap.Entry entry = (FeatureMap.Entry)value; + EStructuralFeature entryFeature = entry.getEStructuralFeature(); + CDOID tag = getTagByFeature(entryFeature, timestamp); + String columnName = getColumnName(tag); + + stmt = statementCache.getPreparedStatement(sqlInsert, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, version); + stmt.setNull(column++, DBType.INTEGER.getCode()); // versionRemoved + stmt.setInt(column++, index); + idHandler.setCDOID(stmt, column++, tag); + + for (int i = 0; i < columnNames.size(); i++) + { + if (columnNames.get(i).equals(columnName)) + { + getTypeMapping(tag).setValue(stmt, column++, entry.getValue()); + } + else + { + stmt.setNull(column++, getDBTypes().get(i).getCode()); + } + } + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + catch (IllegalStateException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private void removeEntry(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int index) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, newVersion); + } + + try + { + // try to delete a temporary entry first + stmt = statementCache.getPreparedStatement(sqlDeleteEntry, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, index); + stmt.setInt(column++, newVersion); + + int result = DBUtil.update(stmt, false); + if (result == 1) + { + if (TRACER.isEnabled()) + { + TRACER.format("removeEntry deleted: {0}", index); //$NON-NLS-1$ + } + } + else if (result > 1) + { + if (TRACER.isEnabled()) + { + TRACER.format("removeEntry Too many results: {0}: {1}", index, result); //$NON-NLS-1$ + } + + throw new DBException("Too many results"); //$NON-NLS-1$ + } + else + { + // no temporary entry found, so mark the entry as removed + statementCache.releasePreparedStatement(stmt); + stmt = statementCache.getPreparedStatement(sqlRemoveEntry, ReuseProbability.HIGH); + + column = 1; + stmt.setInt(column++, newVersion); + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, index); + DBUtil.update(stmt, true); + } + } + catch (SQLException e) + { + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage()); + } + + throw new DBException(e); + } + catch (IllegalStateException e) + { + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage()); + } + + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private FeatureMap.Entry getValue(IDBStoreAccessor accessor, CDOID id, int index) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + FeatureMap.Entry result = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlGetValue, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, index); + + ResultSet resultSet = stmt.executeQuery(); + if (!resultSet.next()) + { + throw new DBException("getValue expects exactly one result"); + } + + CDOID tag = idHandler.getCDOID(resultSet, 1); + Object value = getTypeMapping(tag).readValue(resultSet); + result = CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value); + + if (TRACER.isEnabled()) + { + TRACER.format("Read value (index {0}) from result set: {1}", index, result); //$NON-NLS-1$ + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + + return result; + } + + public final boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere, + QueryXRefsContext context, String idString) + { + // must never be called (a feature map is not associated with an EReference feature, so XRefs are nor supported + // here) + throw new ImplementationError("Should never be called!"); + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMapping.java index d1df0c024e..84f4c20891 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMapping.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMapping.java @@ -1,67 +1,112 @@ -/*
- * Copyright (c) 2004 - 2012 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
- * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
- */
-package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
-
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
-import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
-import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
-
-import org.eclipse.net4j.db.DBType;
-
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EStructuralFeature;
-
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-
-/**
- * This is a list-table mapping for audit mode. It has ID and version columns and no delta support.
- *
- * @author Eike Stepper
- * @since 2.0
- */
-public class AuditListTableMapping extends AbstractListTableMapping
-{
- private FieldInfo[] keyFields;
-
- public AuditListTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature)
- {
- super(mappingStrategy, eClass, feature);
- }
-
- @Override
- protected FieldInfo[] getKeyFields()
- {
- if (keyFields == null)
- {
- keyFields = new FieldInfo[] {
- new FieldInfo(CDODBSchema.LIST_REVISION_ID, getMappingStrategy().getStore().getIDHandler().getDBType()),
- new FieldInfo(CDODBSchema.LIST_REVISION_VERSION, DBType.INTEGER) };
- }
-
- return keyFields;
- }
-
- @Override
- protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException
- {
- getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID());
- stmt.setInt(2, revision.getVersion());
- }
-
- public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised)
- {
- // The audit list mapping does not care about revised references -> NOOP
- }
-}
+/* + * Copyright (c) 2004 - 2012 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 + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * This is a list-table mapping for audit mode. It has ID and version columns and no delta support. + * + * @author Eike Stepper + * @since 2.0 + */ +public class AuditListTableMapping extends AbstractListTableMapping +{ + private FieldInfo[] keyFields; + + private String sqlClear; + + public AuditListTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + initSQLStrings(); + } + + private void initSQLStrings() + { + // ----------- clear list ------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("DELETE FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION); + builder.append("=? "); //$NON-NLS-1$ + sqlClear = builder.toString(); + } + + @Override + protected FieldInfo[] getKeyFields() + { + if (keyFields == null) + { + keyFields = new FieldInfo[] { + new FieldInfo(CDODBSchema.LIST_REVISION_ID, getMappingStrategy().getStore().getIDHandler().getDBType()), + new FieldInfo(CDODBSchema.LIST_REVISION_VERSION, DBType.INTEGER) }; + } + + return keyFields; + } + + @Override + protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException + { + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, revision.getVersion()); + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + // The audit list mapping does not care about revised references -> NOOP + } + + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlClear, ReuseProbability.HIGH); + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id); + stmt.setInt(2, version); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMappingWithRanges.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMappingWithRanges.java index 6c6af3b6c7..a1470fb530 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMappingWithRanges.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMappingWithRanges.java @@ -1,1119 +1,1125 @@ -/*
- * Copyright (c) 2004 - 2012 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
- *
- * This class has been derived from AbstractListTableMapping
- *
- * Contributors:
- * Eike Stepper - initial API and implementation
- * Lothar Werzinger - Bug 296440: [DB] Change RDB schema to improve scalability of to-many references in audit mode
- * Stefan Winkler - cleanup, merge and maintenance
- * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy
- */
-package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
-
-import org.eclipse.emf.cdo.common.branch.CDOBranch;
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.revision.CDOList;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor;
-import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta;
-import org.eclipse.emf.cdo.server.IRepository;
-import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
-import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk;
-import org.eclipse.emf.cdo.server.db.IDBStore;
-import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
-import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader;
-import org.eclipse.emf.cdo.server.db.IIDHandler;
-import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
-import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
-import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport;
-import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
-import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
-import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
-import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
-
-import org.eclipse.net4j.db.DBException;
-import org.eclipse.net4j.db.DBType;
-import org.eclipse.net4j.db.DBUtil;
-import org.eclipse.net4j.db.ddl.IDBField;
-import org.eclipse.net4j.db.ddl.IDBIndex.Type;
-import org.eclipse.net4j.db.ddl.IDBTable;
-import org.eclipse.net4j.util.ImplementationError;
-import org.eclipse.net4j.util.collection.MoveableList;
-import org.eclipse.net4j.util.om.trace.ContextTracer;
-
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EReference;
-import org.eclipse.emf.ecore.EStructuralFeature;
-
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * This is a list-table mapping for audit mode. It is optimized for frequent insert operations at the list's end, which
- * causes just 1 DB row to be changed. This is achieved by introducing a version range (columns cdo_version_added and
- * cdo_version_removed) which records for which revisions a particular entry existed. Also, this mapping is mainly
- * optimized for potentially very large lists: the need for having the complete list stored in memopy to do
- * in-the-middle-moved and inserts is traded in for a few more DB access operations.
- *
- * @author Eike Stepper
- * @author Stefan Winkler
- * @author Lothar Werzinger
- */
-public class AuditListTableMappingWithRanges extends BasicAbstractListTableMapping implements IListMappingDeltaSupport
-{
- private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AuditListTableMappingWithRanges.class);
-
- /**
- * Used to clean up lists for detached objects.
- */
- private static final int FINAL_VERSION = Integer.MAX_VALUE;
-
- /**
- * The table of this mapping.
- */
- private IDBTable table;
-
- /**
- * The type mapping for the value field.
- */
- private ITypeMapping typeMapping;
-
- // --------- SQL strings - see initSQLStrings() -----------------
- private String sqlSelectChunksPrefix;
-
- private String sqlOrderByIndex;
-
- private String sqlInsertEntry;
-
- private String sqlDeleteEntry;
-
- private String sqlRemoveEntry;
-
- private String sqlUpdateIndex;
-
- private String sqlGetValue;
-
- private String sqlClearList;
-
- private String sqlDeleteList;
-
- public AuditListTableMappingWithRanges(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature)
- {
- super(mappingStrategy, eClass, feature);
- initTable();
- initSQLStrings();
- }
-
- private void initTable()
- {
- IDBStore store = getMappingStrategy().getStore();
- String tableName = getMappingStrategy().getTableName(getContainingClass(), getFeature());
- table = store.getDBSchema().addTable(tableName);
-
- IDBField[] dbFields = new IDBField[4];
-
- dbFields[0] = table.addField(CDODBSchema.LIST_REVISION_ID, store.getIDHandler().getDBType());
- dbFields[1] = table.addField(CDODBSchema.LIST_REVISION_VERSION_ADDED, DBType.INTEGER);
- dbFields[2] = table.addField(CDODBSchema.LIST_REVISION_VERSION_REMOVED, DBType.INTEGER);
- dbFields[3] = table.addField(CDODBSchema.LIST_IDX, DBType.INTEGER);
-
- // add field for value
- typeMapping = getMappingStrategy().createValueMapping(getFeature());
- typeMapping.createDBField(table, CDODBSchema.LIST_VALUE);
-
- // TODO think about indexes
- // add table indexes
- table.addIndex(Type.UNIQUE, dbFields);
- }
-
- public Collection<IDBTable> getDBTables()
- {
- return Arrays.asList(table);
- }
-
- private void initSQLStrings()
- {
- String tableName = getTable().getName();
-
- // ---------------- read chunks ----------------------------
- StringBuilder builder = new StringBuilder();
- builder.append("SELECT "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_VALUE);
- builder.append(" FROM "); //$NON-NLS-1$
- builder.append(tableName);
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED);
- builder.append("<=? AND ("); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
- builder.append(" IS NULL OR "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
- builder.append(">?)"); //$NON-NLS-1$
- sqlSelectChunksPrefix = builder.toString();
-
- sqlOrderByIndex = " ORDER BY " + CDODBSchema.LIST_IDX; //$NON-NLS-1$
-
- // ----------------- insert entry -----------------
- builder = new StringBuilder("INSERT INTO "); //$NON-NLS-1$
- builder.append(tableName);
- builder.append("("); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_ID);
- builder.append(","); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED);
- builder.append(","); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
- builder.append(","); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_IDX);
- builder.append(","); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_VALUE);
- builder.append(") VALUES (?, ?, NULL, ?, ?)"); //$NON-NLS-1$
- sqlInsertEntry = builder.toString();
-
- // ----------------- remove current entry -----------------
- builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" SET "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
- builder.append("=? "); //$NON-NLS-1$
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_IDX);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
- builder.append(" IS NULL"); //$NON-NLS-1$
- sqlRemoveEntry = builder.toString();
-
- // ----------------- delete temporary entry -----------------
- builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_IDX);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED);
- builder.append("=?"); //$NON-NLS-1$
- sqlDeleteEntry = builder.toString();
-
- // ----------------- update index -----------------
- builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" SET "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_IDX);
- builder.append("=? WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_IDX);
- builder.append("=?"); //$NON-NLS-1$
- sqlUpdateIndex = builder.toString();
-
- // ----------------- get current value -----------------
- builder = new StringBuilder("SELECT "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_VALUE);
- builder.append(" FROM "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_IDX);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
- builder.append(" IS NULL"); //$NON-NLS-1$
- sqlGetValue = builder.toString();
-
- // ----------- clear list items -------------------------
- builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" SET "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
- builder.append("=? "); //$NON-NLS-1$
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
- builder.append(" IS NULL"); //$NON-NLS-1$
- sqlClearList = builder.toString();
-
- // ----------- delete temporary list items -------------------------
- builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
- builder.append(" IS NULL"); //$NON-NLS-1$
- sqlDeleteList = builder.toString();
- }
-
- protected final IDBTable getTable()
- {
- return table;
- }
-
- protected final ITypeMapping getTypeMapping()
- {
- return typeMapping;
- }
-
- public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk)
- {
- MoveableList<Object> list = revision.getList(getFeature());
- if (listChunk == 0 || list.size() == 0)
- {
- // nothing to read take shortcut
- return;
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading list values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$
- getFeature().getName(), revision.getID(), revision.getVersion());
- }
-
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
- ResultSet resultSet = null;
-
- try
- {
- String sql = sqlSelectChunksPrefix + sqlOrderByIndex;
- stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH);
- idHandler.setCDOID(stmt, 1, revision.getID());
- stmt.setInt(2, revision.getVersion());
- stmt.setInt(3, revision.getVersion());
-
- if (listChunk != CDORevision.UNCHUNKED)
- {
- stmt.setMaxRows(listChunk); // optimization - don't read unneeded rows.
- }
-
- resultSet = stmt.executeQuery();
-
- int currentIndex = 0;
- while ((listChunk == CDORevision.UNCHUNKED || --listChunk >= 0) && resultSet.next())
- {
- Object value = typeMapping.readValue(resultSet);
- if (TRACER.isEnabled())
- {
- TRACER.format("Read value for index {0} from result set: {1}", list.size(), value); //$NON-NLS-1$
- }
-
- list.set(currentIndex++, value);
- }
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- DBUtil.close(resultSet);
- statementCache.releasePreparedStatement(stmt);
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading {4} list values done for feature {0}.{1} of {2}v{3}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), revision.getID(), revision.getVersion(), list.size());
- }
- }
-
- public final void readChunks(IDBStoreChunkReader chunkReader, List<Chunk> chunks, String where)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading list chunk values for feature() {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$
- getFeature().getName(), chunkReader.getRevision().getID(), chunkReader.getRevision().getVersion());
- }
-
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache();
- PreparedStatement stmt = null;
- ResultSet resultSet = null;
-
- try
- {
- StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix);
- if (where != null)
- {
- builder.append(" AND "); //$NON-NLS-1$
- builder.append(where);
- }
-
- builder.append(sqlOrderByIndex);
-
- String sql = builder.toString();
- stmt = statementCache.getPreparedStatement(sql, ReuseProbability.LOW);
- idHandler.setCDOID(stmt, 1, chunkReader.getRevision().getID());
- stmt.setInt(2, chunkReader.getRevision().getVersion());
- stmt.setInt(3, chunkReader.getRevision().getVersion());
-
- resultSet = stmt.executeQuery();
-
- Chunk chunk = null;
- int chunkSize = 0;
- int chunkIndex = 0;
- int indexInChunk = 0;
-
- while (resultSet.next())
- {
- Object value = typeMapping.readValue(resultSet);
-
- if (chunk == null)
- {
- chunk = chunks.get(chunkIndex++);
- chunkSize = chunk.size();
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Current chunk no. {0} is [start = {1}, size = {2}]", chunkIndex - 1, chunk.getStartIndex(), //$NON-NLS-1$
- chunkSize);
- }
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Read value for chunk index {0} from result set: {1}", indexInChunk, value); //$NON-NLS-1$
- }
-
- chunk.add(indexInChunk++, value);
- if (indexInChunk == chunkSize)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Chunk finished"); //$NON-NLS-1$
- }
-
- chunk = null;
- indexInChunk = 0;
- }
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading list chunk values done for feature() {0}.{1} of {2}v{3}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), chunkReader.getRevision().getID(), chunkReader
- .getRevision().getVersion());
- }
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- DBUtil.close(resultSet);
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision)
- {
- CDOList values = revision.getList(getFeature());
-
- int idx = 0;
- for (Object element : values)
- {
- writeValue(accessor, revision, idx++, element);
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Writing done"); //$NON-NLS-1$
- }
- }
-
- protected final void writeValue(IDBStoreAccessor accessor, CDORevision revision, int index, Object value)
- {
- if (TRACER.isEnabled())
- {
- TRACER
- .format(
- "Writing value for feature {0}.{1} index {2} of {3}v{4} : {5}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), index, revision.getID(), revision.getVersion(),
- value);
- }
-
- addEntry(accessor, revision.getID(), revision.getVersion(), index, value);
- }
-
- /**
- * Clear a list of a given revision.
- *
- * @param accessor
- * the accessor to use
- * @param id
- * the id of the revision from which to remove all items
- */
- public void clearList(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmtDeleteTemp = null;
- PreparedStatement stmtClear = null;
-
- try
- {
- // delete temporary entries
- stmtDeleteTemp = statementCache.getPreparedStatement(sqlDeleteList, ReuseProbability.HIGH);
- idHandler.setCDOID(stmtDeleteTemp, 1, id);
- stmtDeleteTemp.setInt(2, newVersion);
-
- int result = DBUtil.update(stmtDeleteTemp, false);
- if (TRACER.isEnabled())
- {
- TRACER.format("DeleteList result: {0}", result); //$NON-NLS-1$
- }
-
- // clear rest of the list
- stmtClear = statementCache.getPreparedStatement(sqlClearList, ReuseProbability.HIGH);
- stmtClear.setInt(1, newVersion);
- idHandler.setCDOID(stmtClear, 2, id);
-
- result = DBUtil.update(stmtClear, false);
- if (TRACER.isEnabled())
- {
- TRACER.format("ClearList result: {0}", result); //$NON-NLS-1$
- }
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmtDeleteTemp);
- statementCache.releasePreparedStatement(stmtClear);
- }
- }
-
- public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("objectRevised {0}: {1}", id, revised); //$NON-NLS-1$
- }
-
- CDOBranch main = getMappingStrategy().getStore().getRepository().getBranchManager().getMainBranch();
-
- // get revision from cache to find out version number
- CDORevision revision = getMappingStrategy().getStore().getRepository().getRevisionManager()
- .getRevision(id, main.getHead(), /* chunksize = */0, CDORevision.DEPTH_NONE, true);
-
- // set cdo_revision_removed for all list items (so we have no NULL values)
- clearList(accessor, id, revision.getVersion(), FINAL_VERSION);
- }
-
- public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion,
- final int newVersion, long created, CDOListFeatureDelta delta)
- {
- IRepository repo = accessor.getStore().getRepository();
- InternalCDORevision originalRevision = (InternalCDORevision)repo.getRevisionManager().getRevision(id,
- repo.getBranchManager().getMainBranch().getHead(), /* chunksize = */0, CDORevision.DEPTH_NONE, true);
-
- int oldListSize = originalRevision.getList(getFeature()).size();
-
- if (TRACER.isEnabled())
- {
- TRACER.format("ListTableMapping.processDelta for revision {0} - previous list size: {1}", originalRevision, //$NON-NLS-1$
- oldListSize);
- }
-
- // let the visitor collect the changes
- ListDeltaVisitor visitor = new ListDeltaVisitor(accessor, originalRevision, oldVersion, newVersion);
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Processing deltas..."); //$NON-NLS-1$
- }
-
- for (CDOFeatureDelta listDelta : delta.getListChanges())
- {
- listDelta.accept(visitor);
- }
-
- visitor.finishPendingRemove();
- }
-
- /**
- * @author Stefan Winkler
- */
- private class ListDeltaVisitor implements CDOFeatureDeltaVisitor
- {
- private IDBStoreAccessor accessor;
-
- private CDOID id;
-
- private int oldVersion;
-
- private int newVersion;
-
- private int lastIndex;
-
- private int lastRemovedIndex;
-
- public ListDeltaVisitor(IDBStoreAccessor accessor, InternalCDORevision originalRevision, int oldVersion,
- int newVersion)
- {
- this.accessor = accessor;
- id = originalRevision.getID();
- this.oldVersion = oldVersion;
- this.newVersion = newVersion;
- lastIndex = originalRevision.getList(getFeature()).size() - 1;
- lastRemovedIndex = -1;
- }
-
- public void visit(CDOMoveFeatureDelta delta)
- {
- int fromIdx = delta.getOldPosition();
- int toIdx = delta.getNewPosition();
-
- // optimization: a move from the end of the list to an index that was just removed requires no shifting
- boolean optimizeMove = lastRemovedIndex != -1 && fromIdx == lastIndex - 1 && toIdx == lastRemovedIndex;
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Delta Moving: {0} to {1}", fromIdx, toIdx); //$NON-NLS-1$
- }
-
- // items after a pending remove have an index offset by one
- if (optimizeMove)
- {
- fromIdx++;
- }
- else
- {
- finishPendingRemove();
- }
-
- Object value = getValue(accessor, id, fromIdx);
-
- // remove the item
- removeEntry(accessor, id, oldVersion, newVersion, fromIdx);
-
- // adjust indexes and shift either up or down
- if (!optimizeMove)
- {
- if (fromIdx < toIdx)
- {
- moveOneUp(accessor, id, oldVersion, newVersion, fromIdx + 1, toIdx);
- }
- else
- { // fromIdx > toIdx here
- moveOneDown(accessor, id, oldVersion, newVersion, toIdx, fromIdx - 1);
- }
- }
-
- // create the item
- addEntry(accessor, id, newVersion, toIdx, value);
- }
-
- public void visit(CDOAddFeatureDelta delta)
- {
- finishPendingRemove();
- int startIndex = delta.getIndex();
- int endIndex = lastIndex;
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Delta Adding at: {0}", startIndex); //$NON-NLS-1$
- }
-
- if (startIndex <= endIndex)
- {
- // make room for the new item
- moveOneDown(accessor, id, oldVersion, newVersion, startIndex, endIndex);
- }
-
- // create the item
- addEntry(accessor, id, newVersion, startIndex, delta.getValue());
-
- ++lastIndex;
- }
-
- public void visit(CDORemoveFeatureDelta delta)
- {
- finishPendingRemove();
- lastRemovedIndex = delta.getIndex();
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Delta Removing at: {0}", lastRemovedIndex); //$NON-NLS-1$
- }
-
- // remove the item
- removeEntry(accessor, id, oldVersion, newVersion, lastRemovedIndex);
- }
-
- public void visit(CDOSetFeatureDelta delta)
- {
- finishPendingRemove();
- int index = delta.getIndex();
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Delta Setting at: {0}", index); //$NON-NLS-1$
- }
-
- // remove the item
- removeEntry(accessor, id, oldVersion, newVersion, index);
-
- // create the item
- addEntry(accessor, id, newVersion, index, delta.getValue());
- }
-
- public void visit(CDOUnsetFeatureDelta delta)
- {
- if (delta.getFeature().isUnsettable())
- {
- throw new ImplementationError("Should not be called"); //$NON-NLS-1$
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Delta Unsetting"); //$NON-NLS-1$
- }
-
- clearList(accessor, id, oldVersion, newVersion);
- lastIndex = -1;
- lastRemovedIndex = -1;
- }
-
- public void visit(CDOListFeatureDelta delta)
- {
- throw new ImplementationError("Should not be called"); //$NON-NLS-1$
- }
-
- public void visit(CDOClearFeatureDelta delta)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Delta Clearing"); //$NON-NLS-1$
- }
-
- clearList(accessor, id, oldVersion, newVersion);
- lastIndex = -1;
- lastRemovedIndex = -1;
- }
-
- public void visit(CDOContainerFeatureDelta delta)
- {
- throw new ImplementationError("Should not be called"); //$NON-NLS-1$
- }
-
- public void finishPendingRemove()
- {
- if (lastRemovedIndex != -1)
- {
- int startIndex = lastRemovedIndex;
- int endIndex = lastIndex;
-
- // make room for the new item
- moveOneUp(accessor, id, oldVersion, newVersion, startIndex + 1, endIndex);
-
- --lastIndex;
- lastRemovedIndex = -1;
- }
- }
-
- private void moveOneUp(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int startIndex,
- int endIndex)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH);
-
- for (int index = startIndex; index <= endIndex; ++index)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneUp moving: {0} -> {1}", index, index - 1); //$NON-NLS-1$
- }
-
- int column = 1;
- stmt.setInt(column++, index - 1);
- idHandler.setCDOID(stmt, column++, id);
- stmt.setInt(column++, newVersion);
- stmt.setInt(column++, index);
-
- int result = DBUtil.update(stmt, false);
- switch (result)
- {
- case 0:
- Object value = getValue(accessor, id, index);
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneUp remove: {0}", index); //$NON-NLS-1$
- }
-
- removeEntry(accessor, id, oldVersion, newVersion, index);
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneUp add: {0}", index - 1); //$NON-NLS-1$
- }
-
- addEntry(accessor, id, newVersion, index - 1, value);
- break;
-
- case 1:
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneUp updated: {0} -> {1}", index, index - 1); //$NON-NLS-1$
- }
-
- break;
-
- default:
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneUp Too many results: {0} -> {1}: {2}", index, index + 1, result); //$NON-NLS-1$
- }
-
- throw new DBException("Too many results"); //$NON-NLS-1$
- }
- }
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- private void moveOneDown(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int startIndex,
- int endIndex)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH);
-
- for (int index = endIndex; index >= startIndex; --index)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneDown moving: {0} -> {1}", index, index + 1); //$NON-NLS-1$
- }
-
- int column = 1;
- stmt.setInt(column++, index + 1);
- idHandler.setCDOID(stmt, column++, id);
- stmt.setInt(column++, newVersion);
- stmt.setInt(column++, index);
-
- int result = DBUtil.update(stmt, false);
- switch (result)
- {
- case 0:
- Object value = getValue(accessor, id, index);
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneDown remove: {0}", index); //$NON-NLS-1$
- }
-
- removeEntry(accessor, id, oldVersion, newVersion, index);
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneDown add: {0}", index + 1); //$NON-NLS-1$
- }
-
- addEntry(accessor, id, newVersion, index + 1, value);
- break;
-
- case 1:
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneDown updated: {0} -> {1}", index, index + 1); //$NON-NLS-1$
- }
-
- break;
-
- default:
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneDown Too many results: {0} -> {1}: {2}", index, index + 1, result); //$NON-NLS-1$
- }
-
- throw new DBException("Too many results"); //$NON-NLS-1$
- }
- }
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
- }
-
- private void addEntry(IDBStoreAccessor accessor, CDOID id, int version, int index, Object value)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Adding value for feature() {0}.{1} index {2} of {3}v{4} : {5}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), index, id, version, value);
- }
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlInsertEntry, ReuseProbability.HIGH);
-
- int column = 1;
- idHandler.setCDOID(stmt, column++, id);
- stmt.setInt(column++, version);
- stmt.setInt(column++, index);
- typeMapping.setValue(stmt, column++, value);
-
- DBUtil.update(stmt, true);
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- catch (IllegalStateException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- private void removeEntry(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int index)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), index, id, newVersion);
- }
-
- try
- {
- // try to delete a temporary entry first
- stmt = statementCache.getPreparedStatement(sqlDeleteEntry, ReuseProbability.HIGH);
-
- int column = 1;
- idHandler.setCDOID(stmt, column++, id);
- stmt.setInt(column++, index);
- stmt.setInt(column++, newVersion);
-
- int result = DBUtil.update(stmt, false);
- if (result == 1)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("removeEntry deleted: {0}", index); //$NON-NLS-1$
- }
- }
- else if (result > 1)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("removeEntry Too many results: {0}: {1}", index, result); //$NON-NLS-1$
- }
-
- throw new DBException("Too many results"); //$NON-NLS-1$
- }
- else
- {
- // no temporary entry found, so mark the entry as removed
- statementCache.releasePreparedStatement(stmt);
- stmt = statementCache.getPreparedStatement(sqlRemoveEntry, ReuseProbability.HIGH);
-
- column = 1;
- stmt.setInt(column++, newVersion);
- idHandler.setCDOID(stmt, column++, id);
- stmt.setInt(column++, index);
-
- DBUtil.update(stmt, true);
- }
- }
- catch (SQLException e)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage());
- }
-
- throw new DBException(e);
- }
- catch (IllegalStateException e)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage());
- }
-
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- private Object getValue(IDBStoreAccessor accessor, CDOID id, int index)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
- Object result = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlGetValue, ReuseProbability.HIGH);
-
- int column = 1;
- idHandler.setCDOID(stmt, column++, id);
- stmt.setInt(column++, index);
-
- ResultSet resultSet = stmt.executeQuery();
- if (!resultSet.next())
- {
- throw new DBException("getValue() expects exactly one result");
- }
-
- result = typeMapping.readValue(resultSet);
- if (TRACER.isEnabled())
- {
- TRACER.format("Read value (index {0}) from result set: {1}", index, result); //$NON-NLS-1$
- }
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
-
- return result;
- }
-
- public final boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere,
- QueryXRefsContext context, String idString)
- {
-
- String tableName = getTable().getName();
- String listJoin = getMappingStrategy().getListJoin("a_t", "l_t");
-
- StringBuilder builder = new StringBuilder();
- builder.append("SELECT l_t."); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_ID);
- builder.append(", l_t."); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_VALUE);
- builder.append(", l_t."); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_IDX);
- builder.append(" FROM "); //$NON-NLS-1$
- builder.append(tableName);
- builder.append(" AS l_t, ");//$NON-NLS-1$
- builder.append(mainTableName);
- builder.append(" AS a_t WHERE ");//$NON-NLS-1$
- builder.append("a_t." + mainTableWhere);//$NON-NLS-1$
- builder.append(listJoin);
- builder.append(" AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_VALUE);
- builder.append(" IN "); //$NON-NLS-1$
- builder.append(idString);
- String sql = builder.toString();
-
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- ResultSet resultSet = null;
- Statement stmt = null;
-
- try
- {
- stmt = accessor.getConnection().createStatement();
- if (TRACER.isEnabled())
- {
- TRACER.format("Query XRefs (list): {0}", sql);
- }
-
- resultSet = stmt.executeQuery(sql);
- while (resultSet.next())
- {
- CDOID sourceID = idHandler.getCDOID(resultSet, 1);
- CDOID targetID = idHandler.getCDOID(resultSet, 2);
- int idx = resultSet.getInt(3);
-
- boolean more = context.addXRef(targetID, sourceID, (EReference)getFeature(), idx);
- if (TRACER.isEnabled())
- {
- TRACER.format(" add XRef to context: src={0}, tgt={1}, idx={2}", sourceID, targetID, idx);
- }
-
- if (!more)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format(" result limit reached. Ignoring further results.");
- }
-
- return false;
- }
- }
-
- return true;
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- DBUtil.close(resultSet);
- DBUtil.close(stmt);
- }
- }
-}
+/* + * Copyright (c) 2004 - 2012 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 + * + * This class has been derived from AbstractListTableMapping + * + * Contributors: + * Eike Stepper - initial API and implementation + * Lothar Werzinger - Bug 296440: [DB] Change RDB schema to improve scalability of to-many references in audit mode + * Stefan Winkler - cleanup, merge and maintenance + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDOList; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk; +import org.eclipse.emf.cdo.server.db.IDBStore; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IDBStoreChunkReader; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.ddl.IDBField; +import org.eclipse.net4j.db.ddl.IDBIndex.Type; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.ImplementationError; +import org.eclipse.net4j.util.collection.MoveableList; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * This is a list-table mapping for audit mode. It is optimized for frequent insert operations at the list's end, which + * causes just 1 DB row to be changed. This is achieved by introducing a version range (columns cdo_version_added and + * cdo_version_removed) which records for which revisions a particular entry existed. Also, this mapping is mainly + * optimized for potentially very large lists: the need for having the complete list stored in memopy to do + * in-the-middle-moved and inserts is traded in for a few more DB access operations. + * + * @author Eike Stepper + * @author Stefan Winkler + * @author Lothar Werzinger + */ +public class AuditListTableMappingWithRanges extends BasicAbstractListTableMapping implements IListMappingDeltaSupport +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AuditListTableMappingWithRanges.class); + + /** + * Used to clean up lists for detached objects. + */ + private static final int FINAL_VERSION = Integer.MAX_VALUE; + + /** + * The table of this mapping. + */ + private IDBTable table; + + /** + * The type mapping for the value field. + */ + private ITypeMapping typeMapping; + + // --------- SQL strings - see initSQLStrings() ----------------- + private String sqlSelectChunksPrefix; + + private String sqlOrderByIndex; + + private String sqlInsertEntry; + + private String sqlDeleteEntry; + + private String sqlRemoveEntry; + + private String sqlUpdateIndex; + + private String sqlGetValue; + + private String sqlClearList; + + private String sqlDeleteList; + + public AuditListTableMappingWithRanges(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + initTable(); + initSQLStrings(); + } + + private void initTable() + { + IDBStore store = getMappingStrategy().getStore(); + String tableName = getMappingStrategy().getTableName(getContainingClass(), getFeature()); + table = store.getDBSchema().addTable(tableName); + + IDBField[] dbFields = new IDBField[4]; + + dbFields[0] = table.addField(CDODBSchema.LIST_REVISION_ID, store.getIDHandler().getDBType()); + dbFields[1] = table.addField(CDODBSchema.LIST_REVISION_VERSION_ADDED, DBType.INTEGER); + dbFields[2] = table.addField(CDODBSchema.LIST_REVISION_VERSION_REMOVED, DBType.INTEGER); + dbFields[3] = table.addField(CDODBSchema.LIST_IDX, DBType.INTEGER); + + // add field for value + typeMapping = getMappingStrategy().createValueMapping(getFeature()); + typeMapping.createDBField(table, CDODBSchema.LIST_VALUE); + + // TODO think about indexes + // add table indexes + table.addIndex(Type.UNIQUE, dbFields); + } + + public Collection<IDBTable> getDBTables() + { + return Arrays.asList(table); + } + + private void initSQLStrings() + { + String tableName = getTable().getName(); + + // ---------------- read chunks ---------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NULL OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(">?)"); //$NON-NLS-1$ + sqlSelectChunksPrefix = builder.toString(); + + sqlOrderByIndex = " ORDER BY " + CDODBSchema.LIST_IDX; //$NON-NLS-1$ + + // ----------------- insert entry ----------------- + builder = new StringBuilder("INSERT INTO "); //$NON-NLS-1$ + builder.append(tableName); + builder.append("("); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append(","); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED); + builder.append(","); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(","); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append(","); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(") VALUES (?, ?, NULL, ?, ?)"); //$NON-NLS-1$ + sqlInsertEntry = builder.toString(); + + // ----------------- remove current entry ----------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlRemoveEntry = builder.toString(); + + // ----------------- delete temporary entry ----------------- + builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED); + builder.append("=?"); //$NON-NLS-1$ + sqlDeleteEntry = builder.toString(); + + // ----------------- update index ----------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=?"); //$NON-NLS-1$ + sqlUpdateIndex = builder.toString(); + + // ----------------- get current value ----------------- + builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlGetValue = builder.toString(); + + // ----------- clear list items ------------------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlClearList = builder.toString(); + + // ----------- delete temporary list items ------------------------- + builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_ADDED); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlDeleteList = builder.toString(); + } + + protected final IDBTable getTable() + { + return table; + } + + protected final ITypeMapping getTypeMapping() + { + return typeMapping; + } + + public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + MoveableList<Object> list = revision.getList(getFeature()); + if (listChunk == 0 || list.size() == 0) + { + // nothing to read take shortcut + return; + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list values for feature {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), revision.getID(), revision.getVersion()); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + String sql = sqlSelectChunksPrefix + sqlOrderByIndex; + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, revision.getVersion()); + stmt.setInt(3, revision.getVersion()); + + if (listChunk != CDORevision.UNCHUNKED) + { + stmt.setMaxRows(listChunk); // optimization - don't read unneeded rows. + } + + resultSet = stmt.executeQuery(); + + int currentIndex = 0; + while ((listChunk == CDORevision.UNCHUNKED || --listChunk >= 0) && resultSet.next()) + { + Object value = typeMapping.readValue(resultSet); + if (TRACER.isEnabled()) + { + TRACER.format("Read value for index {0} from result set: {1}", list.size(), value); //$NON-NLS-1$ + } + + list.set(currentIndex++, value); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading {4} list values done for feature {0}.{1} of {2}v{3}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), revision.getID(), revision.getVersion(), list.size()); + } + } + + public final void readChunks(IDBStoreChunkReader chunkReader, List<Chunk> chunks, String where) + { + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values for feature() {0}.{1} of {2}v{3}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), chunkReader.getRevision().getID(), chunkReader.getRevision().getVersion()); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + try + { + StringBuilder builder = new StringBuilder(sqlSelectChunksPrefix); + if (where != null) + { + builder.append(" AND "); //$NON-NLS-1$ + builder.append(where); + } + + builder.append(sqlOrderByIndex); + + String sql = builder.toString(); + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.LOW); + idHandler.setCDOID(stmt, 1, chunkReader.getRevision().getID()); + stmt.setInt(2, chunkReader.getRevision().getVersion()); + stmt.setInt(3, chunkReader.getRevision().getVersion()); + + resultSet = stmt.executeQuery(); + + Chunk chunk = null; + int chunkSize = 0; + int chunkIndex = 0; + int indexInChunk = 0; + + while (resultSet.next()) + { + Object value = typeMapping.readValue(resultSet); + + if (chunk == null) + { + chunk = chunks.get(chunkIndex++); + chunkSize = chunk.size(); + + if (TRACER.isEnabled()) + { + TRACER.format("Current chunk no. {0} is [start = {1}, size = {2}]", chunkIndex - 1, chunk.getStartIndex(), //$NON-NLS-1$ + chunkSize); + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Read value for chunk index {0} from result set: {1}", indexInChunk, value); //$NON-NLS-1$ + } + + chunk.add(indexInChunk++, value); + if (indexInChunk == chunkSize) + { + if (TRACER.isEnabled()) + { + TRACER.format("Chunk finished"); //$NON-NLS-1$ + } + + chunk = null; + indexInChunk = 0; + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values done for feature() {0}.{1} of {2}v{3}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), chunkReader.getRevision().getID(), chunkReader + .getRevision().getVersion()); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + } + + public void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + CDOList values = revision.getList(getFeature()); + + int idx = 0; + for (Object element : values) + { + writeValue(accessor, revision, idx++, element); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Writing done"); //$NON-NLS-1$ + } + } + + protected final void writeValue(IDBStoreAccessor accessor, CDORevision revision, int index, Object value) + { + if (TRACER.isEnabled()) + { + TRACER + .format( + "Writing value for feature {0}.{1} index {2} of {3}v{4} : {5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, revision.getID(), revision.getVersion(), + value); + } + + addEntry(accessor, revision.getID(), revision.getVersion(), index, value); + } + + /** + * Clear a list of a given revision. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision from which to remove all items + */ + public void clearList(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmtDeleteTemp = null; + PreparedStatement stmtClear = null; + + try + { + // delete temporary entries + stmtDeleteTemp = statementCache.getPreparedStatement(sqlDeleteList, ReuseProbability.HIGH); + idHandler.setCDOID(stmtDeleteTemp, 1, id); + stmtDeleteTemp.setInt(2, newVersion); + + int result = DBUtil.update(stmtDeleteTemp, false); + if (TRACER.isEnabled()) + { + TRACER.format("DeleteList result: {0}", result); //$NON-NLS-1$ + } + + // clear rest of the list + stmtClear = statementCache.getPreparedStatement(sqlClearList, ReuseProbability.HIGH); + stmtClear.setInt(1, newVersion); + idHandler.setCDOID(stmtClear, 2, id); + + result = DBUtil.update(stmtClear, false); + if (TRACER.isEnabled()) + { + TRACER.format("ClearList result: {0}", result); //$NON-NLS-1$ + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmtDeleteTemp); + statementCache.releasePreparedStatement(stmtClear); + } + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + if (TRACER.isEnabled()) + { + TRACER.format("objectRevised {0}: {1}", id, revised); //$NON-NLS-1$ + } + + CDOBranch main = getMappingStrategy().getStore().getRepository().getBranchManager().getMainBranch(); + + // get revision from cache to find out version number + CDORevision revision = getMappingStrategy().getStore().getRepository().getRevisionManager() + .getRevision(id, main.getHead(), /* chunksize = */0, CDORevision.DEPTH_NONE, true); + + // set cdo_revision_removed for all list items (so we have no NULL values) + clearList(accessor, id, revision.getVersion(), FINAL_VERSION); + } + + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + throw new UnsupportedOperationException("Raw deletion does not work in range-based mappings"); + } + + public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion, + final int newVersion, long created, CDOListFeatureDelta delta) + { + IRepository repo = accessor.getStore().getRepository(); + InternalCDORevision originalRevision = (InternalCDORevision)repo.getRevisionManager().getRevision(id, + repo.getBranchManager().getMainBranch().getHead(), /* chunksize = */0, CDORevision.DEPTH_NONE, true); + + int oldListSize = originalRevision.getList(getFeature()).size(); + + if (TRACER.isEnabled()) + { + TRACER.format("ListTableMapping.processDelta for revision {0} - previous list size: {1}", originalRevision, //$NON-NLS-1$ + oldListSize); + } + + // let the visitor collect the changes + ListDeltaVisitor visitor = new ListDeltaVisitor(accessor, originalRevision, oldVersion, newVersion); + + if (TRACER.isEnabled()) + { + TRACER.format("Processing deltas..."); //$NON-NLS-1$ + } + + for (CDOFeatureDelta listDelta : delta.getListChanges()) + { + listDelta.accept(visitor); + } + + visitor.finishPendingRemove(); + } + + /** + * @author Stefan Winkler + */ + private class ListDeltaVisitor implements CDOFeatureDeltaVisitor + { + private IDBStoreAccessor accessor; + + private CDOID id; + + private int oldVersion; + + private int newVersion; + + private int lastIndex; + + private int lastRemovedIndex; + + public ListDeltaVisitor(IDBStoreAccessor accessor, InternalCDORevision originalRevision, int oldVersion, + int newVersion) + { + this.accessor = accessor; + id = originalRevision.getID(); + this.oldVersion = oldVersion; + this.newVersion = newVersion; + lastIndex = originalRevision.getList(getFeature()).size() - 1; + lastRemovedIndex = -1; + } + + public void visit(CDOMoveFeatureDelta delta) + { + int fromIdx = delta.getOldPosition(); + int toIdx = delta.getNewPosition(); + + // optimization: a move from the end of the list to an index that was just removed requires no shifting + boolean optimizeMove = lastRemovedIndex != -1 && fromIdx == lastIndex - 1 && toIdx == lastRemovedIndex; + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Moving: {0} to {1}", fromIdx, toIdx); //$NON-NLS-1$ + } + + // items after a pending remove have an index offset by one + if (optimizeMove) + { + fromIdx++; + } + else + { + finishPendingRemove(); + } + + Object value = getValue(accessor, id, fromIdx); + + // remove the item + removeEntry(accessor, id, oldVersion, newVersion, fromIdx); + + // adjust indexes and shift either up or down + if (!optimizeMove) + { + if (fromIdx < toIdx) + { + moveOneUp(accessor, id, oldVersion, newVersion, fromIdx + 1, toIdx); + } + else + { // fromIdx > toIdx here + moveOneDown(accessor, id, oldVersion, newVersion, toIdx, fromIdx - 1); + } + } + + // create the item + addEntry(accessor, id, newVersion, toIdx, value); + } + + public void visit(CDOAddFeatureDelta delta) + { + finishPendingRemove(); + int startIndex = delta.getIndex(); + int endIndex = lastIndex; + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Adding at: {0}", startIndex); //$NON-NLS-1$ + } + + if (startIndex <= endIndex) + { + // make room for the new item + moveOneDown(accessor, id, oldVersion, newVersion, startIndex, endIndex); + } + + // create the item + addEntry(accessor, id, newVersion, startIndex, delta.getValue()); + + ++lastIndex; + } + + public void visit(CDORemoveFeatureDelta delta) + { + finishPendingRemove(); + lastRemovedIndex = delta.getIndex(); + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Removing at: {0}", lastRemovedIndex); //$NON-NLS-1$ + } + + // remove the item + removeEntry(accessor, id, oldVersion, newVersion, lastRemovedIndex); + } + + public void visit(CDOSetFeatureDelta delta) + { + finishPendingRemove(); + int index = delta.getIndex(); + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Setting at: {0}", index); //$NON-NLS-1$ + } + + // remove the item + removeEntry(accessor, id, oldVersion, newVersion, index); + + // create the item + addEntry(accessor, id, newVersion, index, delta.getValue()); + } + + public void visit(CDOUnsetFeatureDelta delta) + { + if (delta.getFeature().isUnsettable()) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + if (TRACER.isEnabled()) + { + TRACER.format("Delta Unsetting"); //$NON-NLS-1$ + } + + clearList(accessor, id, oldVersion, newVersion); + lastIndex = -1; + lastRemovedIndex = -1; + } + + public void visit(CDOListFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOClearFeatureDelta delta) + { + if (TRACER.isEnabled()) + { + TRACER.format("Delta Clearing"); //$NON-NLS-1$ + } + + clearList(accessor, id, oldVersion, newVersion); + lastIndex = -1; + lastRemovedIndex = -1; + } + + public void visit(CDOContainerFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void finishPendingRemove() + { + if (lastRemovedIndex != -1) + { + int startIndex = lastRemovedIndex; + int endIndex = lastIndex; + + // make room for the new item + moveOneUp(accessor, id, oldVersion, newVersion, startIndex + 1, endIndex); + + --lastIndex; + lastRemovedIndex = -1; + } + } + + private void moveOneUp(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int startIndex, + int endIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH); + + for (int index = startIndex; index <= endIndex; ++index) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp moving: {0} -> {1}", index, index - 1); //$NON-NLS-1$ + } + + int column = 1; + stmt.setInt(column++, index - 1); + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, newVersion); + stmt.setInt(column++, index); + + int result = DBUtil.update(stmt, false); + switch (result) + { + case 0: + Object value = getValue(accessor, id, index); + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp remove: {0}", index); //$NON-NLS-1$ + } + + removeEntry(accessor, id, oldVersion, newVersion, index); + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp add: {0}", index - 1); //$NON-NLS-1$ + } + + addEntry(accessor, id, newVersion, index - 1, value); + break; + + case 1: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp updated: {0} -> {1}", index, index - 1); //$NON-NLS-1$ + } + + break; + + default: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp Too many results: {0} -> {1}: {2}", index, index + 1, result); //$NON-NLS-1$ + } + + throw new DBException("Too many results"); //$NON-NLS-1$ + } + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private void moveOneDown(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int startIndex, + int endIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH); + + for (int index = endIndex; index >= startIndex; --index) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown moving: {0} -> {1}", index, index + 1); //$NON-NLS-1$ + } + + int column = 1; + stmt.setInt(column++, index + 1); + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, newVersion); + stmt.setInt(column++, index); + + int result = DBUtil.update(stmt, false); + switch (result) + { + case 0: + Object value = getValue(accessor, id, index); + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown remove: {0}", index); //$NON-NLS-1$ + } + + removeEntry(accessor, id, oldVersion, newVersion, index); + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown add: {0}", index + 1); //$NON-NLS-1$ + } + + addEntry(accessor, id, newVersion, index + 1, value); + break; + + case 1: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown updated: {0} -> {1}", index, index + 1); //$NON-NLS-1$ + } + + break; + + default: + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown Too many results: {0} -> {1}: {2}", index, index + 1, result); //$NON-NLS-1$ + } + + throw new DBException("Too many results"); //$NON-NLS-1$ + } + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + } + + private void addEntry(IDBStoreAccessor accessor, CDOID id, int version, int index, Object value) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + if (TRACER.isEnabled()) + { + TRACER.format("Adding value for feature() {0}.{1} index {2} of {3}v{4} : {5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, version, value); + } + + try + { + stmt = statementCache.getPreparedStatement(sqlInsertEntry, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, version); + stmt.setInt(column++, index); + typeMapping.setValue(stmt, column++, value); + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + catch (IllegalStateException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private void removeEntry(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int index) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, newVersion); + } + + try + { + // try to delete a temporary entry first + stmt = statementCache.getPreparedStatement(sqlDeleteEntry, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, index); + stmt.setInt(column++, newVersion); + + int result = DBUtil.update(stmt, false); + if (result == 1) + { + if (TRACER.isEnabled()) + { + TRACER.format("removeEntry deleted: {0}", index); //$NON-NLS-1$ + } + } + else if (result > 1) + { + if (TRACER.isEnabled()) + { + TRACER.format("removeEntry Too many results: {0}: {1}", index, result); //$NON-NLS-1$ + } + + throw new DBException("Too many results"); //$NON-NLS-1$ + } + else + { + // no temporary entry found, so mark the entry as removed + statementCache.releasePreparedStatement(stmt); + stmt = statementCache.getPreparedStatement(sqlRemoveEntry, ReuseProbability.HIGH); + + column = 1; + stmt.setInt(column++, newVersion); + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, index); + + DBUtil.update(stmt, true); + } + } + catch (SQLException e) + { + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage()); + } + + throw new DBException(e); + } + catch (IllegalStateException e) + { + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage()); + } + + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private Object getValue(IDBStoreAccessor accessor, CDOID id, int index) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + Object result = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlGetValue, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, index); + + ResultSet resultSet = stmt.executeQuery(); + if (!resultSet.next()) + { + throw new DBException("getValue() expects exactly one result"); + } + + result = typeMapping.readValue(resultSet); + if (TRACER.isEnabled()) + { + TRACER.format("Read value (index {0}) from result set: {1}", index, result); //$NON-NLS-1$ + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + + return result; + } + + public final boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere, + QueryXRefsContext context, String idString) + { + + String tableName = getTable().getName(); + String listJoin = getMappingStrategy().getListJoin("a_t", "l_t"); + + StringBuilder builder = new StringBuilder(); + builder.append("SELECT l_t."); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append(", l_t."); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(", l_t."); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" AS l_t, ");//$NON-NLS-1$ + builder.append(mainTableName); + builder.append(" AS a_t WHERE ");//$NON-NLS-1$ + builder.append("a_t." + mainTableWhere);//$NON-NLS-1$ + builder.append(listJoin); + builder.append(" AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(" IN "); //$NON-NLS-1$ + builder.append(idString); + String sql = builder.toString(); + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + ResultSet resultSet = null; + Statement stmt = null; + + try + { + stmt = accessor.getConnection().createStatement(); + if (TRACER.isEnabled()) + { + TRACER.format("Query XRefs (list): {0}", sql); + } + + resultSet = stmt.executeQuery(sql); + while (resultSet.next()) + { + CDOID sourceID = idHandler.getCDOID(resultSet, 1); + CDOID targetID = idHandler.getCDOID(resultSet, 2); + int idx = resultSet.getInt(3); + + boolean more = context.addXRef(targetID, sourceID, (EReference)getFeature(), idx); + if (TRACER.isEnabled()) + { + TRACER.format(" add XRef to context: src={0}, tgt={1}, idx={2}", sourceID, targetID, idx); + } + + if (!more) + { + if (TRACER.isEnabled()) + { + TRACER.format(" result limit reached. Ignoring further results."); + } + + return false; + } + } + + return true; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + DBUtil.close(stmt); + } + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BasicAbstractListTableMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BasicAbstractListTableMapping.java index 9e5b422b68..f0e8b34e0f 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BasicAbstractListTableMapping.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BasicAbstractListTableMapping.java @@ -1,73 +1,75 @@ -/*
- * Copyright (c) 2004 - 2012 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:
- * Stefan Winkler - initial API and implementation
- */
-package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
-
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
-import org.eclipse.emf.cdo.server.db.mapping.IListMapping2;
-import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
-import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
-
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EStructuralFeature;
-
-/**
- * @author Stefan Winkler
- */
-public abstract class BasicAbstractListTableMapping implements IListMapping2
-{
- private IMappingStrategy mappingStrategy;
-
- private EClass containingClass;
-
- private EStructuralFeature feature;
-
- public BasicAbstractListTableMapping(IMappingStrategy mappingStrategy, EClass containingClass,
- EStructuralFeature feature)
- {
- this.mappingStrategy = mappingStrategy;
- this.containingClass = containingClass;
- this.feature = feature;
- }
-
- public final IMappingStrategy getMappingStrategy()
- {
- return mappingStrategy;
- }
-
- public final EClass getContainingClass()
- {
- return containingClass;
- }
-
- public final EStructuralFeature getFeature()
- {
- return feature;
- }
-
- public void addSimpleChunkWhere(IDBStoreAccessor accessor, CDOID cdoid, StringBuilder builder, int index)
- {
- builder.append(CDODBSchema.LIST_IDX);
- builder.append('=');
- builder.append(index);
- }
-
- public void addRangedChunkWhere(IDBStoreAccessor accessor, CDOID cdoid, StringBuilder builder, int fromIndex,
- int toIndex)
- {
- builder.append(CDODBSchema.LIST_IDX);
- builder.append(" BETWEEN "); //$NON-NLS-1$
- builder.append(fromIndex);
- builder.append(" AND "); //$NON-NLS-1$
- builder.append(toIndex - 1);
- }
-
-}
+/* + * Copyright (c) 2004 - 2012 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: + * Stefan Winkler - initial API and implementation + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.mapping.IListMapping2; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +/** + * @author Stefan Winkler + */ +public abstract class BasicAbstractListTableMapping implements IListMapping2 +{ + private IMappingStrategy mappingStrategy; + + private EClass containingClass; + + private EStructuralFeature feature; + + public BasicAbstractListTableMapping(IMappingStrategy mappingStrategy, EClass containingClass, + EStructuralFeature feature) + { + this.mappingStrategy = mappingStrategy; + this.containingClass = containingClass; + this.feature = feature; + } + + public final IMappingStrategy getMappingStrategy() + { + return mappingStrategy; + } + + public final EClass getContainingClass() + { + return containingClass; + } + + public final EStructuralFeature getFeature() + { + return feature; + } + + public void addSimpleChunkWhere(IDBStoreAccessor accessor, CDOID cdoid, StringBuilder builder, int index) + { + builder.append(CDODBSchema.LIST_IDX); + builder.append('='); + builder.append(index); + } + + public void addRangedChunkWhere(IDBStoreAccessor accessor, CDOID cdoid, StringBuilder builder, int fromIndex, + int toIndex) + { + builder.append(CDODBSchema.LIST_IDX); + builder.append(" BETWEEN "); //$NON-NLS-1$ + builder.append(fromIndex); + builder.append(" AND "); //$NON-NLS-1$ + builder.append(toIndex - 1); + } + + public abstract void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version); +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMapping.java index 74e0578bcb..9d4be3a3a4 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMapping.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMapping.java @@ -1,72 +1,120 @@ -/*
- * Copyright (c) 2004 - 2012 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
- * Stefan Winkler - Bug 271444: [DB] Multiple refactorings bug 271444
- * Christopher Albert - Bug 254455: [DB] Support FeatureMaps bug 254455
- * Stefan Winkler - derived branch mapping from audit mapping
- */
-package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
-
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
-import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
-import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
-
-import org.eclipse.net4j.db.DBType;
-
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EStructuralFeature;
-
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-
-/**
- * This is a featuremap-table mapping for audit mode. It has ID and version columns and no delta support.
- *
- * @author Eike Stepper
- * @author Stefan Winkler
- * @since 3.0
- */
-public class BranchingFeatureMapTableMapping extends AbstractFeatureMapTableMapping
-{
- private FieldInfo[] keyFields;
-
- public BranchingFeatureMapTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature)
- {
- super(mappingStrategy, eClass, feature);
- }
-
- @Override
- protected FieldInfo[] getKeyFields()
- {
- if (keyFields == null)
- {
- keyFields = new FieldInfo[] {
- new FieldInfo(CDODBSchema.FEATUREMAP_REVISION_ID, getMappingStrategy().getStore().getIDHandler().getDBType()),
- new FieldInfo(CDODBSchema.FEATUREMAP_BRANCH, DBType.INTEGER),
- new FieldInfo(CDODBSchema.FEATUREMAP_VERSION, DBType.INTEGER) };
- }
-
- return keyFields;
- }
-
- @Override
- protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException
- {
- getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID());
- stmt.setInt(2, revision.getBranch().getID());
- stmt.setInt(3, revision.getVersion());
- }
-
- public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised)
- {
- // the audit list mapping does not care about revised references -> NOP
- }
-}
+/* + * Copyright (c) 2004 - 2012 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 + * Stefan Winkler - Bug 271444: [DB] Multiple refactorings bug 271444 + * Christopher Albert - Bug 254455: [DB] Support FeatureMaps bug 254455 + * Stefan Winkler - derived branch mapping from audit mapping + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * This is a featuremap-table mapping for audit mode. It has ID and version columns and no delta support. + * + * @author Eike Stepper + * @author Stefan Winkler + * @since 3.0 + */ +public class BranchingFeatureMapTableMapping extends AbstractFeatureMapTableMapping +{ + private FieldInfo[] keyFields; + + private String sqlClear; + + public BranchingFeatureMapTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + initSQLStrings(); + } + + private void initSQLStrings() + { + // ----------- clear list ------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("DELETE FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION); + builder.append("=?"); //$NON-NLS-1$ + sqlClear = builder.toString(); + } + + @Override + protected FieldInfo[] getKeyFields() + { + if (keyFields == null) + { + keyFields = new FieldInfo[] { + new FieldInfo(CDODBSchema.FEATUREMAP_REVISION_ID, getMappingStrategy().getStore().getIDHandler().getDBType()), + new FieldInfo(CDODBSchema.FEATUREMAP_BRANCH, DBType.INTEGER), + new FieldInfo(CDODBSchema.FEATUREMAP_VERSION, DBType.INTEGER) }; + } + + return keyFields; + } + + @Override + protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException + { + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, revision.getBranch().getID()); + stmt.setInt(3, revision.getVersion()); + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + // the audit list mapping does not care about revised references -> NOP + } + + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlClear, ReuseProbability.HIGH); + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id); + stmt.setInt(2, branch.getID()); + stmt.setInt(3, version); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMappingWithRanges.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMappingWithRanges.java index 607f60eb69..0a70310859 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMappingWithRanges.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMappingWithRanges.java @@ -841,6 +841,12 @@ public class BranchingFeatureMapTableMappingWithRanges extends BasicAbstractList revised); } + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + throw new UnsupportedOperationException("Raw deletion does not work in range-based mappings"); + } + public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion, final int newVersion, long created, CDOListFeatureDelta delta) { diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMapping.java index 47c6bf7976..570665d4d3 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMapping.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMapping.java @@ -1,71 +1,119 @@ -/*
- * Copyright (c) 2004 - 2012 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
- * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
- * Stefan Winkler - derived branch mapping from audit mapping
- */
-package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
-
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
-import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
-import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
-
-import org.eclipse.net4j.db.DBType;
-
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EStructuralFeature;
-
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-
-/**
- * This is a list-table mapping for audit mode. It has ID and version columns and no delta support.
- *
- * @author Eike Stepper
- * @author Stefan Winkler
- * @since 3.0
- */
-public class BranchingListTableMapping extends AbstractListTableMapping
-{
- private FieldInfo[] keyFields;
-
- public BranchingListTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature)
- {
- super(mappingStrategy, eClass, feature);
- }
-
- @Override
- protected FieldInfo[] getKeyFields()
- {
- if (keyFields == null)
- {
- keyFields = new FieldInfo[] {
- new FieldInfo(CDODBSchema.LIST_REVISION_ID, getMappingStrategy().getStore().getIDHandler().getDBType()),
- new FieldInfo(CDODBSchema.LIST_REVISION_BRANCH, DBType.INTEGER),
- new FieldInfo(CDODBSchema.LIST_REVISION_VERSION, DBType.INTEGER) };
- }
-
- return keyFields;
- }
-
- @Override
- protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException
- {
- getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID());
- stmt.setInt(2, revision.getBranch().getID());
- stmt.setInt(3, revision.getVersion());
- }
-
- public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised)
- {
- // the audit list mapping does not care about revised references -> NOP
- }
-}
+/* + * Copyright (c) 2004 - 2012 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 + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + * Stefan Winkler - derived branch mapping from audit mapping + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +/** + * This is a list-table mapping for audit mode. It has ID and version columns and no delta support. + * + * @author Eike Stepper + * @author Stefan Winkler + * @since 3.0 + */ +public class BranchingListTableMapping extends AbstractListTableMapping +{ + private FieldInfo[] keyFields; + + private String sqlClear; + + public BranchingListTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + initSQLStrings(); + } + + private void initSQLStrings() + { + // ----------- clear list ------------------------- + StringBuilder builder = new StringBuilder(); + builder.append("DELETE FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION); + builder.append("=?"); //$NON-NLS-1$ + sqlClear = builder.toString(); + } + + @Override + protected FieldInfo[] getKeyFields() + { + if (keyFields == null) + { + keyFields = new FieldInfo[] { + new FieldInfo(CDODBSchema.LIST_REVISION_ID, getMappingStrategy().getStore().getIDHandler().getDBType()), + new FieldInfo(CDODBSchema.LIST_REVISION_BRANCH, DBType.INTEGER), + new FieldInfo(CDODBSchema.LIST_REVISION_VERSION, DBType.INTEGER) }; + } + + return keyFields; + } + + @Override + protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException + { + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, revision.getBranch().getID()); + stmt.setInt(3, revision.getVersion()); + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + // the audit list mapping does not care about revised references -> NOP + } + + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlClear, ReuseProbability.HIGH); + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id); + stmt.setInt(2, branch.getID()); + stmt.setInt(3, version); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMappingWithRanges.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMappingWithRanges.java index f9a584affd..80a3e3a1ad 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMappingWithRanges.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMappingWithRanges.java @@ -696,6 +696,12 @@ public class BranchingListTableMappingWithRanges extends BasicAbstractListTableM clearList(accessor, id, branchID, revision.getVersion(), FINAL_VERSION, revision.getList(getFeature()).size() - 1); } + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + throw new UnsupportedOperationException("Raw deletion does not work in range-based mappings"); + } + public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, final int oldVersion, final int newVersion, long created, CDOListFeatureDelta delta) { diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditClassMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditClassMapping.java index 980ca614b3..b2b9ecafd9 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditClassMapping.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditClassMapping.java @@ -1,742 +1,777 @@ -/*
- * Copyright (c) 2004 - 2012 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
- * Stefan Winkler - major refactoring
- * Stefan Winkler - Bug 249610: [DB] Support external references (Implementation)
- * Lothar Werzinger - Bug 296440: [DB] Change RDB schema to improve scalability of to-many references in audit mode
- * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy
- */
-package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
-
-import org.eclipse.emf.cdo.common.branch.CDOBranch;
-import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
-import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.revision.CDOList;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor;
-import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta;
-import org.eclipse.emf.cdo.eresource.EresourcePackage;
-import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
-import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
-import org.eclipse.emf.cdo.server.db.IIDHandler;
-import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
-import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
-import org.eclipse.emf.cdo.server.db.mapping.IClassMappingAuditSupport;
-import org.eclipse.emf.cdo.server.db.mapping.IClassMappingDeltaSupport;
-import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport;
-import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
-import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
-import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
-
-import org.eclipse.net4j.db.DBException;
-import org.eclipse.net4j.db.DBUtil;
-import org.eclipse.net4j.util.ImplementationError;
-import org.eclipse.net4j.util.om.monitor.OMMonitor;
-import org.eclipse.net4j.util.om.monitor.OMMonitor.Async;
-import org.eclipse.net4j.util.om.trace.ContextTracer;
-
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EStructuralFeature;
-
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.Map;
-
-/**
- * @author Eike Stepper
- * @since 2.0
- */
-public class HorizontalAuditClassMapping extends AbstractHorizontalClassMapping implements IClassMappingAuditSupport,
- IClassMappingDeltaSupport
-{
- private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, HorizontalAuditClassMapping.class);
-
- private String sqlInsertAttributes;
-
- private String sqlSelectCurrentAttributes;
-
- private String sqlSelectAllObjectIDs;
-
- private String sqlSelectAttributesByTime;
-
- private String sqlSelectAttributesByVersion;
-
- private String sqlReviseAttributes;
-
- private ThreadLocal<FeatureDeltaWriter> deltaWriter = new ThreadLocal<FeatureDeltaWriter>()
- {
- @Override
- protected FeatureDeltaWriter initialValue()
- {
- return new FeatureDeltaWriter();
- }
- };
-
- public HorizontalAuditClassMapping(AbstractHorizontalMappingStrategy mappingStrategy, EClass eClass)
- {
- super(mappingStrategy, eClass);
-
- initSQLStrings();
- }
-
- private void initSQLStrings()
- {
- Map<EStructuralFeature, String> unsettableFields = getUnsettableFields();
- Map<EStructuralFeature, String> listSizeFields = getListSizeFields();
-
- // ----------- Select Revision ---------------------------
- StringBuilder builder = new StringBuilder();
-
- builder.append("SELECT "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_VERSION);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_CREATED);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_RESOURCE);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_CONTAINER);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_FEATURE);
-
- for (ITypeMapping singleMapping : getValueMappings())
- {
- builder.append(", "); //$NON-NLS-1$
- builder.append(singleMapping.getField());
- }
-
- if (unsettableFields != null)
- {
- for (String fieldName : unsettableFields.values())
- {
- builder.append(", "); //$NON-NLS-1$
- builder.append(fieldName);
- }
- }
-
- if (listSizeFields != null)
- {
- for (String fieldName : listSizeFields.values())
- {
- builder.append(", "); //$NON-NLS-1$
- builder.append(fieldName);
- }
- }
-
- builder.append(" FROM "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_ID);
- builder.append("=? AND ("); //$NON-NLS-1$
-
- String sqlSelectAttributesPrefix = builder.toString();
-
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append("=0)"); //$NON-NLS-1$
-
- sqlSelectCurrentAttributes = builder.toString();
-
- builder = new StringBuilder(sqlSelectAttributesPrefix);
-
- builder.append(CDODBSchema.ATTRIBUTES_CREATED);
- builder.append("<=? AND ("); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append("=0 OR "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append(">=?))"); //$NON-NLS-1$
-
- sqlSelectAttributesByTime = builder.toString();
-
- builder = new StringBuilder(sqlSelectAttributesPrefix);
-
- builder.append("ABS(");
- builder.append(CDODBSchema.ATTRIBUTES_VERSION);
- builder.append(")=?)"); //$NON-NLS-1$
-
- sqlSelectAttributesByVersion = builder.toString();
-
- // ----------- Insert Attributes -------------------------
- builder = new StringBuilder();
- builder.append("INSERT INTO "); //$NON-NLS-1$
- builder.append(getTable());
-
- builder.append("("); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_ID);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_VERSION);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_CREATED);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_RESOURCE);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_CONTAINER);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_FEATURE);
-
- for (ITypeMapping singleMapping : getValueMappings())
- {
- builder.append(", "); //$NON-NLS-1$
- builder.append(singleMapping.getField());
- }
-
- if (unsettableFields != null)
- {
- for (String fieldName : unsettableFields.values())
- {
- builder.append(", "); //$NON-NLS-1$
- builder.append(fieldName);
- }
- }
-
- if (listSizeFields != null)
- {
- for (String fieldName : listSizeFields.values())
- {
- builder.append(", "); //$NON-NLS-1$
- builder.append(fieldName);
- }
- }
-
- builder.append(") VALUES (?, ?, ?, ?, ?, ?, ?"); //$NON-NLS-1$
-
- for (int i = 0; i < getValueMappings().size(); i++)
- {
- builder.append(", ?"); //$NON-NLS-1$
- }
-
- if (unsettableFields != null)
- {
- for (int i = 0; i < unsettableFields.size(); i++)
- {
- builder.append(", ?"); //$NON-NLS-1$
- }
- }
-
- if (listSizeFields != null)
- {
- for (int i = 0; i < listSizeFields.size(); i++)
- {
- builder.append(", ?"); //$NON-NLS-1$
- }
- }
-
- builder.append(")"); //$NON-NLS-1$
- sqlInsertAttributes = builder.toString();
-
- // ----------- Update to set revised ----------------
- builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" SET "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append("=? WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append("=0"); //$NON-NLS-1$
- sqlReviseAttributes = builder.toString();
-
- // ----------- Select all unrevised Object IDs ------
- builder = new StringBuilder("SELECT "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_ID);
- builder.append(" FROM "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append("=0"); //$NON-NLS-1$
- sqlSelectAllObjectIDs = builder.toString();
- }
-
- public boolean readRevision(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- long timeStamp = revision.getTimeStamp();
- if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE)
- {
- stmt = statementCache.getPreparedStatement(sqlSelectAttributesByTime, ReuseProbability.MEDIUM);
- idHandler.setCDOID(stmt, 1, revision.getID());
- stmt.setLong(2, timeStamp);
- stmt.setLong(3, timeStamp);
- }
- else
- {
- stmt = statementCache.getPreparedStatement(sqlSelectCurrentAttributes, ReuseProbability.HIGH);
- idHandler.setCDOID(stmt, 1, revision.getID());
- }
-
- // Read singleval-attribute table always (even without modeled attributes!)
- boolean success = readValuesFromStatement(stmt, revision, accessor);
-
- // Read multival tables only if revision exists
- if (success && revision.getVersion() >= CDOBranchVersion.FIRST_VERSION)
- {
- readLists(accessor, revision, listChunk);
- }
-
- return success;
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public boolean readRevisionByVersion(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlSelectAttributesByVersion, ReuseProbability.HIGH);
- idHandler.setCDOID(stmt, 1, revision.getID());
- stmt.setInt(2, revision.getVersion());
-
- // Read singleval-attribute table always (even without modeled attributes!)
- boolean success = readValuesFromStatement(stmt, revision, accessor);
-
- // Read multival tables only if revision exists
- if (success)
- {
- readLists(accessor, revision, listChunk);
- }
-
- return success;
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public PreparedStatement createResourceQueryStatement(IDBStoreAccessor accessor, CDOID folderId, String name,
- boolean exactMatch, CDOBranchPoint branchPoint)
- {
- EStructuralFeature nameFeature = EresourcePackage.eINSTANCE.getCDOResourceNode_Name();
- long timeStamp = branchPoint.getTimeStamp();
-
- ITypeMapping nameValueMapping = getValueMapping(nameFeature);
- if (nameValueMapping == null)
- {
- throw new ImplementationError(nameFeature + " not found in ClassMapping " + this); //$NON-NLS-1$
- }
-
- StringBuilder builder = new StringBuilder();
- builder.append("SELECT "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_ID);
- builder.append(" FROM "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_VERSION);
- builder.append(">0 AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_CONTAINER);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(nameValueMapping.getField());
- if (name == null)
- {
- builder.append(" IS NULL"); //$NON-NLS-1$
- }
- else
- {
- builder.append(exactMatch ? "=? " : " LIKE ? "); //$NON-NLS-1$ //$NON-NLS-2$
- }
-
- builder.append(" AND ("); //$NON-NLS-1$
-
- if (timeStamp == CDORevision.UNSPECIFIED_DATE)
- {
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append("=0)"); //$NON-NLS-1$
- }
- else
- {
- builder.append(CDODBSchema.ATTRIBUTES_CREATED);
- builder.append("<=? AND ("); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append("=0 OR "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append(">=?))"); //$NON-NLS-1$
- }
-
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- int column = 1;
-
- stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.MEDIUM);
- idHandler.setCDOID(stmt, column++, folderId);
-
- if (name != null)
- {
- String queryName = exactMatch ? name : name + "%"; //$NON-NLS-1$
- nameValueMapping.setValue(stmt, column++, queryName);
- }
-
- if (timeStamp != CDORevision.UNSPECIFIED_DATE)
- {
- stmt.setLong(column++, timeStamp);
- stmt.setLong(column++, timeStamp);
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Created Resource Query: {0}", stmt.toString()); //$NON-NLS-1$
- }
-
- return stmt;
- }
- catch (SQLException ex)
- {
- statementCache.releasePreparedStatement(stmt); // only release on error
- throw new DBException(ex);
- }
- }
-
- public PreparedStatement createObjectIDStatement(IDBStoreAccessor accessor)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Created ObjectID Statement : {0}", sqlSelectAllObjectIDs); //$NON-NLS-1$
- }
-
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- return statementCache.getPreparedStatement(sqlSelectAllObjectIDs, ReuseProbability.HIGH);
- }
-
- @Override
- protected final void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- int column = 1;
- stmt = statementCache.getPreparedStatement(sqlInsertAttributes, ReuseProbability.HIGH);
- idHandler.setCDOID(stmt, column++, revision.getID());
- stmt.setInt(column++, revision.getVersion());
- stmt.setLong(column++, revision.getTimeStamp());
- stmt.setLong(column++, revision.getRevised());
- idHandler.setCDOID(stmt, column++, revision.getResourceID());
- idHandler.setCDOID(stmt, column++, (CDOID)revision.getContainerID());
- stmt.setInt(column++, revision.getContainingFeatureID());
-
- int isSetCol = column + getValueMappings().size();
-
- for (ITypeMapping mapping : getValueMappings())
- {
- EStructuralFeature feature = mapping.getFeature();
- if (feature.isUnsettable())
- {
- if (revision.getValue(feature) == null)
- {
- stmt.setBoolean(isSetCol++, false);
-
- // also set value column to default value
- mapping.setDefaultValue(stmt, column++);
-
- continue;
- }
-
- stmt.setBoolean(isSetCol++, true);
- }
-
- mapping.setValueFromRevision(stmt, column++, revision);
- }
-
- Map<EStructuralFeature, String> listSizeFields = getListSizeFields();
- if (listSizeFields != null)
- {
- // isSetCol now points to the first listTableSize-column
- column = isSetCol;
-
- for (EStructuralFeature feature : listSizeFields.keySet())
- {
- CDOList list = revision.getList(feature);
- stmt.setInt(column++, list.size());
- }
- }
-
- DBUtil.update(stmt, true);
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- @Override
- protected void detachAttributes(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp,
- OMMonitor mon)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlInsertAttributes, ReuseProbability.HIGH);
-
- int column = 1;
-
- idHandler.setCDOID(stmt, column++, id);
- stmt.setInt(column++, -version); // cdo_version
- stmt.setLong(column++, timeStamp); // cdo_created
- stmt.setLong(column++, CDOBranchPoint.UNSPECIFIED_DATE); // cdo_revised
- idHandler.setCDOID(stmt, column++, CDOID.NULL); // resource
- idHandler.setCDOID(stmt, column++, CDOID.NULL); // container
- stmt.setInt(column++, 0); // containing feature ID
-
- int isSetCol = column + getValueMappings().size();
-
- for (ITypeMapping mapping : getValueMappings())
- {
- EStructuralFeature feature = mapping.getFeature();
- if (feature.isUnsettable())
- {
- stmt.setBoolean(isSetCol++, false);
- }
-
- mapping.setDefaultValue(stmt, column++);
- }
-
- Map<EStructuralFeature, String> listSizeFields = getListSizeFields();
- if (listSizeFields != null)
- {
- // list size columns begin after isSet-columns
- column = isSetCol;
-
- for (int i = 0; i < listSizeFields.size(); i++)
- {
- stmt.setInt(column++, 0);
- }
- }
-
- DBUtil.update(stmt, true);
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- @Override
- protected void reviseOldRevision(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, long revised)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlReviseAttributes, ReuseProbability.HIGH);
-
- stmt.setLong(1, revised);
- idHandler.setCDOID(stmt, 2, id);
-
- DBUtil.update(stmt, true);
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public void writeRevisionDelta(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created,
- OMMonitor monitor)
- {
- Async async = null;
- monitor.begin();
-
- try
- {
- try
- {
- async = monitor.forkAsync();
- FeatureDeltaWriter writer = deltaWriter.get();
- writer.process(accessor, delta, created);
- }
- finally
- {
- if (async != null)
- {
- async.stop();
- }
- }
- }
- finally
- {
- monitor.done();
- }
- }
-
- @Override
- protected String getListXRefsWhere(QueryXRefsContext context)
- {
- if (CDOBranch.MAIN_BRANCH_ID != context.getBranch().getID())
- {
- throw new IllegalArgumentException("Non-audit mode does not support branch specification");
- }
-
- StringBuilder builder = new StringBuilder();
- long timeStamp = context.getTimeStamp();
- if (timeStamp == CDORevision.UNSPECIFIED_DATE)
- {
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append("=0"); //$NON-NLS-1$
- }
- else
- {
- builder.append(CDODBSchema.ATTRIBUTES_CREATED);
- builder.append("<=");
- builder.append(timeStamp);
- builder.append(" AND ("); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append("=0 OR "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append(">=");
- builder.append(timeStamp);
- builder.append(")"); //$NON-NLS-1$
- }
-
- return builder.toString();
- }
-
- /**
- * @author Stefan Winkler
- */
- private class FeatureDeltaWriter implements CDOFeatureDeltaVisitor
- {
- private IDBStoreAccessor accessor;
-
- private long created;
-
- private CDOID id;
-
- private int oldVersion;
-
- private InternalCDORevision newRevision;
-
- private int branchId;
-
- public void process(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created)
- {
- this.accessor = accessor;
- this.created = created;
- id = delta.getID();
- branchId = delta.getBranch().getID();
- oldVersion = delta.getVersion();
-
- if (TRACER.isEnabled())
- {
- TRACER.format("FeatureDeltaWriter: old version: {0}, new version: {1}", oldVersion, oldVersion + 1); //$NON-NLS-1$
- }
-
- InternalCDORevision originalRevision = (InternalCDORevision)accessor.getStore().getRepository()
- .getRevisionManager().getRevisionByVersion(id, delta, 0, true);
-
- newRevision = originalRevision.copy();
-
- newRevision.setVersion(oldVersion + 1);
- newRevision.setBranchPoint(delta.getBranch().getPoint(created));
-
- // process revision delta tree
- delta.accept(this);
-
- long revised = newRevision.getTimeStamp() - 1;
- reviseOldRevision(accessor, id, delta.getBranch(), revised);
-
- writeValues(accessor, newRevision);
- }
-
- public void visit(CDOMoveFeatureDelta delta)
- {
- throw new ImplementationError("Should not be called"); //$NON-NLS-1$
- }
-
- public void visit(CDOAddFeatureDelta delta)
- {
- throw new ImplementationError("Should not be called"); //$NON-NLS-1$
- }
-
- public void visit(CDORemoveFeatureDelta delta)
- {
- throw new ImplementationError("Should not be called"); //$NON-NLS-1$
- }
-
- public void visit(CDOSetFeatureDelta delta)
- {
- delta.apply(newRevision);
- }
-
- public void visit(CDOUnsetFeatureDelta delta)
- {
- delta.apply(newRevision);
- }
-
- public void visit(CDOListFeatureDelta delta)
- {
- delta.apply(newRevision);
- IListMappingDeltaSupport listMapping = (IListMappingDeltaSupport)getListMapping(delta.getFeature());
- listMapping.processDelta(accessor, id, branchId, oldVersion, oldVersion + 1, created, delta);
- }
-
- public void visit(CDOClearFeatureDelta delta)
- {
- throw new ImplementationError("Should not be called"); //$NON-NLS-1$
- }
-
- public void visit(CDOContainerFeatureDelta delta)
- {
- delta.apply(newRevision);
- }
- }
-}
+/* + * Copyright (c) 2004 - 2012 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 + * Stefan Winkler - major refactoring + * Stefan Winkler - Bug 249610: [DB] Support external references (Implementation) + * Lothar Werzinger - Bug 296440: [DB] Change RDB schema to improve scalability of to-many references in audit mode + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDOList; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta; +import org.eclipse.emf.cdo.eresource.EresourcePackage; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IClassMappingAuditSupport; +import org.eclipse.emf.cdo.server.db.mapping.IClassMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.util.ImplementationError; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Map; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class HorizontalAuditClassMapping extends AbstractHorizontalClassMapping implements IClassMappingAuditSupport, + IClassMappingDeltaSupport +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, HorizontalAuditClassMapping.class); + + private String sqlInsertAttributes; + + private String sqlSelectCurrentAttributes; + + private String sqlSelectAllObjectIDs; + + private String sqlSelectAttributesByTime; + + private String sqlSelectAttributesByVersion; + + private String sqlReviseAttributes; + + private String sqlRawDeleteAttributes; + + private ThreadLocal<FeatureDeltaWriter> deltaWriter = new ThreadLocal<FeatureDeltaWriter>() + { + @Override + protected FeatureDeltaWriter initialValue() + { + return new FeatureDeltaWriter(); + } + }; + + public HorizontalAuditClassMapping(AbstractHorizontalMappingStrategy mappingStrategy, EClass eClass) + { + super(mappingStrategy, eClass); + + initSQLStrings(); + } + + private void initSQLStrings() + { + Map<EStructuralFeature, String> unsettableFields = getUnsettableFields(); + Map<EStructuralFeature, String> listSizeFields = getListSizeFields(); + + // ----------- Select Revision --------------------------- + StringBuilder builder = new StringBuilder(); + + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_RESOURCE); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CONTAINER); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_FEATURE); + + for (ITypeMapping singleMapping : getValueMappings()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(singleMapping.getField()); + } + + if (unsettableFields != null) + { + for (String fieldName : unsettableFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + if (listSizeFields != null) + { + for (String fieldName : listSizeFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append("=? AND ("); //$NON-NLS-1$ + + String sqlSelectAttributesPrefix = builder.toString(); + + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0)"); //$NON-NLS-1$ + + sqlSelectCurrentAttributes = builder.toString(); + + builder = new StringBuilder(sqlSelectAttributesPrefix); + + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0 OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(">=?))"); //$NON-NLS-1$ + + sqlSelectAttributesByTime = builder.toString(); + + builder = new StringBuilder(sqlSelectAttributesPrefix); + + builder.append("ABS("); + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(")=?)"); //$NON-NLS-1$ + + sqlSelectAttributesByVersion = builder.toString(); + + // ----------- Insert Attributes ------------------------- + builder = new StringBuilder(); + builder.append("INSERT INTO "); //$NON-NLS-1$ + builder.append(getTable()); + + builder.append("("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_RESOURCE); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CONTAINER); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_FEATURE); + + for (ITypeMapping singleMapping : getValueMappings()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(singleMapping.getField()); + } + + if (unsettableFields != null) + { + for (String fieldName : unsettableFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + if (listSizeFields != null) + { + for (String fieldName : listSizeFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + builder.append(") VALUES (?, ?, ?, ?, ?, ?, ?"); //$NON-NLS-1$ + + for (int i = 0; i < getValueMappings().size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + + if (unsettableFields != null) + { + for (int i = 0; i < unsettableFields.size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + } + + if (listSizeFields != null) + { + for (int i = 0; i < listSizeFields.size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + } + + builder.append(")"); //$NON-NLS-1$ + sqlInsertAttributes = builder.toString(); + + // ----------- Update to set revised ---------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=? WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0"); //$NON-NLS-1$ + sqlReviseAttributes = builder.toString(); + + // ----------- Select all unrevised Object IDs ------ + builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0"); //$NON-NLS-1$ + sqlSelectAllObjectIDs = builder.toString(); + + // ----------- Raw delete one specific revision ------ + builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append("=?"); //$NON-NLS-1$ + sqlRawDeleteAttributes = builder.toString(); + } + + public boolean readRevision(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + long timeStamp = revision.getTimeStamp(); + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + stmt = statementCache.getPreparedStatement(sqlSelectAttributesByTime, ReuseProbability.MEDIUM); + idHandler.setCDOID(stmt, 1, revision.getID()); + stmt.setLong(2, timeStamp); + stmt.setLong(3, timeStamp); + } + else + { + stmt = statementCache.getPreparedStatement(sqlSelectCurrentAttributes, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, revision.getID()); + } + + // Read singleval-attribute table always (even without modeled attributes!) + boolean success = readValuesFromStatement(stmt, revision, accessor); + + // Read multival tables only if revision exists + if (success && revision.getVersion() >= CDOBranchVersion.FIRST_VERSION) + { + readLists(accessor, revision, listChunk); + } + + return success; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public boolean readRevisionByVersion(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlSelectAttributesByVersion, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, revision.getID()); + stmt.setInt(2, revision.getVersion()); + + // Read singleval-attribute table always (even without modeled attributes!) + boolean success = readValuesFromStatement(stmt, revision, accessor); + + // Read multival tables only if revision exists + if (success) + { + readLists(accessor, revision, listChunk); + } + + return success; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public PreparedStatement createResourceQueryStatement(IDBStoreAccessor accessor, CDOID folderId, String name, + boolean exactMatch, CDOBranchPoint branchPoint) + { + EStructuralFeature nameFeature = EresourcePackage.eINSTANCE.getCDOResourceNode_Name(); + long timeStamp = branchPoint.getTimeStamp(); + + ITypeMapping nameValueMapping = getValueMapping(nameFeature); + if (nameValueMapping == null) + { + throw new ImplementationError(nameFeature + " not found in ClassMapping " + this); //$NON-NLS-1$ + } + + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(">0 AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CONTAINER); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(nameValueMapping.getField()); + if (name == null) + { + builder.append(" IS NULL"); //$NON-NLS-1$ + } + else + { + builder.append(exactMatch ? "=? " : " LIKE ? "); //$NON-NLS-1$ //$NON-NLS-2$ + } + + builder.append(" AND ("); //$NON-NLS-1$ + + if (timeStamp == CDORevision.UNSPECIFIED_DATE) + { + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0)"); //$NON-NLS-1$ + } + else + { + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0 OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(">=?))"); //$NON-NLS-1$ + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + int column = 1; + + stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.MEDIUM); + idHandler.setCDOID(stmt, column++, folderId); + + if (name != null) + { + String queryName = exactMatch ? name : name + "%"; //$NON-NLS-1$ + nameValueMapping.setValue(stmt, column++, queryName); + } + + if (timeStamp != CDORevision.UNSPECIFIED_DATE) + { + stmt.setLong(column++, timeStamp); + stmt.setLong(column++, timeStamp); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Created Resource Query: {0}", stmt.toString()); //$NON-NLS-1$ + } + + return stmt; + } + catch (SQLException ex) + { + statementCache.releasePreparedStatement(stmt); // only release on error + throw new DBException(ex); + } + } + + public PreparedStatement createObjectIDStatement(IDBStoreAccessor accessor) + { + if (TRACER.isEnabled()) + { + TRACER.format("Created ObjectID Statement : {0}", sqlSelectAllObjectIDs); //$NON-NLS-1$ + } + + IPreparedStatementCache statementCache = accessor.getStatementCache(); + return statementCache.getPreparedStatement(sqlSelectAllObjectIDs, ReuseProbability.HIGH); + } + + @Override + protected final void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + int column = 1; + stmt = statementCache.getPreparedStatement(sqlInsertAttributes, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, column++, revision.getID()); + stmt.setInt(column++, revision.getVersion()); + stmt.setLong(column++, revision.getTimeStamp()); + stmt.setLong(column++, revision.getRevised()); + idHandler.setCDOID(stmt, column++, revision.getResourceID()); + idHandler.setCDOID(stmt, column++, (CDOID)revision.getContainerID()); + stmt.setInt(column++, revision.getContainingFeatureID()); + + int isSetCol = column + getValueMappings().size(); + + for (ITypeMapping mapping : getValueMappings()) + { + EStructuralFeature feature = mapping.getFeature(); + if (feature.isUnsettable()) + { + if (revision.getValue(feature) == null) + { + stmt.setBoolean(isSetCol++, false); + + // also set value column to default value + mapping.setDefaultValue(stmt, column++); + + continue; + } + + stmt.setBoolean(isSetCol++, true); + } + + mapping.setValueFromRevision(stmt, column++, revision); + } + + Map<EStructuralFeature, String> listSizeFields = getListSizeFields(); + if (listSizeFields != null) + { + // isSetCol now points to the first listTableSize-column + column = isSetCol; + + for (EStructuralFeature feature : listSizeFields.keySet()) + { + CDOList list = revision.getList(feature); + stmt.setInt(column++, list.size()); + } + } + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + protected void detachAttributes(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp, + OMMonitor mon) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlInsertAttributes, ReuseProbability.HIGH); + + int column = 1; + + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, -version); // cdo_version + stmt.setLong(column++, timeStamp); // cdo_created + stmt.setLong(column++, CDOBranchPoint.UNSPECIFIED_DATE); // cdo_revised + idHandler.setCDOID(stmt, column++, CDOID.NULL); // resource + idHandler.setCDOID(stmt, column++, CDOID.NULL); // container + stmt.setInt(column++, 0); // containing feature ID + + int isSetCol = column + getValueMappings().size(); + + for (ITypeMapping mapping : getValueMappings()) + { + EStructuralFeature feature = mapping.getFeature(); + if (feature.isUnsettable()) + { + stmt.setBoolean(isSetCol++, false); + } + + mapping.setDefaultValue(stmt, column++); + } + + Map<EStructuralFeature, String> listSizeFields = getListSizeFields(); + if (listSizeFields != null) + { + // list size columns begin after isSet-columns + column = isSetCol; + + for (int i = 0; i < listSizeFields.size(); i++) + { + stmt.setInt(column++, 0); + } + } + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + protected void rawDeleteAttributes(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version, OMMonitor fork) + { + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlRawDeleteAttributes, ReuseProbability.HIGH); + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id); + stmt.setInt(2, version); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + protected void reviseOldRevision(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, long revised) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlReviseAttributes, ReuseProbability.HIGH); + + stmt.setLong(1, revised); + idHandler.setCDOID(stmt, 2, id); + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public void writeRevisionDelta(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created, + OMMonitor monitor) + { + Async async = null; + monitor.begin(); + + try + { + try + { + async = monitor.forkAsync(); + FeatureDeltaWriter writer = deltaWriter.get(); + writer.process(accessor, delta, created); + } + finally + { + if (async != null) + { + async.stop(); + } + } + } + finally + { + monitor.done(); + } + } + + @Override + protected String getListXRefsWhere(QueryXRefsContext context) + { + if (CDOBranch.MAIN_BRANCH_ID != context.getBranch().getID()) + { + throw new IllegalArgumentException("Non-audit mode does not support branch specification"); + } + + StringBuilder builder = new StringBuilder(); + long timeStamp = context.getTimeStamp(); + if (timeStamp == CDORevision.UNSPECIFIED_DATE) + { + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0"); //$NON-NLS-1$ + } + else + { + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append("<="); + builder.append(timeStamp); + builder.append(" AND ("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append("=0 OR "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(">="); + builder.append(timeStamp); + builder.append(")"); //$NON-NLS-1$ + } + + return builder.toString(); + } + + /** + * @author Stefan Winkler + */ + private class FeatureDeltaWriter implements CDOFeatureDeltaVisitor + { + private IDBStoreAccessor accessor; + + private long created; + + private CDOID id; + + private int oldVersion; + + private InternalCDORevision newRevision; + + private int branchId; + + public void process(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created) + { + this.accessor = accessor; + this.created = created; + id = delta.getID(); + branchId = delta.getBranch().getID(); + oldVersion = delta.getVersion(); + + if (TRACER.isEnabled()) + { + TRACER.format("FeatureDeltaWriter: old version: {0}, new version: {1}", oldVersion, oldVersion + 1); //$NON-NLS-1$ + } + + InternalCDORevision originalRevision = (InternalCDORevision)accessor.getStore().getRepository() + .getRevisionManager().getRevisionByVersion(id, delta, 0, true); + + newRevision = originalRevision.copy(); + + newRevision.setVersion(oldVersion + 1); + newRevision.setBranchPoint(delta.getBranch().getPoint(created)); + + // process revision delta tree + delta.accept(this); + + long revised = newRevision.getTimeStamp() - 1; + reviseOldRevision(accessor, id, delta.getBranch(), revised); + + writeValues(accessor, newRevision); + } + + public void visit(CDOMoveFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOAddFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDORemoveFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOSetFeatureDelta delta) + { + delta.apply(newRevision); + } + + public void visit(CDOUnsetFeatureDelta delta) + { + delta.apply(newRevision); + } + + public void visit(CDOListFeatureDelta delta) + { + delta.apply(newRevision); + IListMappingDeltaSupport listMapping = (IListMappingDeltaSupport)getListMapping(delta.getFeature()); + listMapping.processDelta(accessor, id, branchId, oldVersion, oldVersion + 1, created, delta); + } + + public void visit(CDOClearFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOContainerFeatureDelta delta) + { + delta.apply(newRevision); + } + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingClassMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingClassMapping.java index b3b1ca7d79..772abfb6a1 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingClassMapping.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingClassMapping.java @@ -190,6 +190,8 @@ public class HorizontalBranchingClassMapping extends AbstractHorizontalClassMapp private String sqlSelectForChangeSet; + private String sqlRawDeleteAttributes; + private ThreadLocal<FeatureDeltaWriter> deltaWriter = new ThreadLocal<FeatureDeltaWriter>() { @Override @@ -404,6 +406,18 @@ public class HorizontalBranchingClassMapping extends AbstractHorizontalClassMapp builder.append(getTable()); builder.append(" WHERE "); //$NON-NLS-1$ sqlSelectForChangeSet = builder.toString(); + + // ----------- Raw delete one specific revision ------ + builder = new StringBuilder("DELETE FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append("=?"); //$NON-NLS-1$ + sqlRawDeleteAttributes = builder.toString(); } public boolean readRevision(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) @@ -716,6 +730,30 @@ public class HorizontalBranchingClassMapping extends AbstractHorizontalClassMapp } @Override + protected void rawDeleteAttributes(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version, OMMonitor fork) + { + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlRawDeleteAttributes, ReuseProbability.HIGH); + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id); + stmt.setInt(2, branch.getID()); + stmt.setInt(3, version); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override protected void reviseOldRevision(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, long revised) { IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditClassMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditClassMapping.java index cb0ef0113e..d90b23ba61 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditClassMapping.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditClassMapping.java @@ -1,764 +1,770 @@ -/*
- * Copyright (c) 2004 - 2012 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
- * Stefan Winkler - major refactoring
- * Stefan Winkler - 249610: [DB] Support external references (Implementation)
- * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy
- */
-package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
-
-import org.eclipse.emf.cdo.common.branch.CDOBranch;
-import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.revision.CDOList;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor;
-import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta;
-import org.eclipse.emf.cdo.eresource.EresourcePackage;
-import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext;
-import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
-import org.eclipse.emf.cdo.server.db.IIDHandler;
-import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
-import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
-import org.eclipse.emf.cdo.server.db.mapping.IClassMappingDeltaSupport;
-import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport;
-import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
-import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
-import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
-
-import org.eclipse.net4j.db.DBException;
-import org.eclipse.net4j.db.DBUtil;
-import org.eclipse.net4j.util.ImplementationError;
-import org.eclipse.net4j.util.collection.Pair;
-import org.eclipse.net4j.util.om.monitor.OMMonitor;
-import org.eclipse.net4j.util.om.monitor.OMMonitor.Async;
-import org.eclipse.net4j.util.om.trace.ContextTracer;
-
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EStructuralFeature;
-
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-/**
- * @author Eike Stepper
- * @since 2.0
- */
-public class HorizontalNonAuditClassMapping extends AbstractHorizontalClassMapping implements IClassMappingDeltaSupport
-{
- private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, HorizontalNonAuditClassMapping.class);
-
- private String sqlSelectAllObjectIDs;
-
- private String sqlSelectCurrentAttributes;
-
- private String sqlInsertAttributes;
-
- private String sqlUpdateAffix;
-
- private String sqlUpdatePrefix;
-
- private String sqlUpdateContainerPart;
-
- private ThreadLocal<FeatureDeltaWriter> deltaWriter = new ThreadLocal<FeatureDeltaWriter>()
- {
- @Override
- protected FeatureDeltaWriter initialValue()
- {
- return new FeatureDeltaWriter();
- }
- };
-
- public HorizontalNonAuditClassMapping(AbstractHorizontalMappingStrategy mappingStrategy, EClass eClass)
- {
- super(mappingStrategy, eClass);
-
- initSQLStrings();
- }
-
- private void initSQLStrings()
- {
- Map<EStructuralFeature, String> unsettableFields = getUnsettableFields();
- Map<EStructuralFeature, String> listSizeFields = getListSizeFields();
-
- // ----------- Select Revision ---------------------------
- StringBuilder builder = new StringBuilder();
-
- builder.append("SELECT "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_VERSION);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_CREATED);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_RESOURCE);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_CONTAINER);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_FEATURE);
-
- for (ITypeMapping singleMapping : getValueMappings())
- {
- builder.append(", "); //$NON-NLS-1$
- builder.append(singleMapping.getField());
- }
-
- if (unsettableFields != null)
- {
- for (String fieldName : unsettableFields.values())
- {
- builder.append(", "); //$NON-NLS-1$
- builder.append(fieldName);
- }
- }
-
- if (listSizeFields != null)
- {
- for (String fieldName : listSizeFields.values())
- {
- builder.append(", "); //$NON-NLS-1$
- builder.append(fieldName);
- }
- }
-
- builder.append(" FROM "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_ID);
- builder.append("=?"); //$NON-NLS-1$
-
- sqlSelectCurrentAttributes = builder.toString();
-
- // ----------- Insert Attributes -------------------------
- builder = new StringBuilder();
- builder.append("INSERT INTO "); //$NON-NLS-1$
- builder.append(getTable());
-
- builder.append("("); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_ID);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_VERSION);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_CREATED);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_REVISED);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_RESOURCE);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_CONTAINER);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_FEATURE);
-
- for (ITypeMapping singleMapping : getValueMappings())
- {
- builder.append(", "); //$NON-NLS-1$
- builder.append(singleMapping.getField());
- }
-
- if (unsettableFields != null)
- {
- for (String fieldName : unsettableFields.values())
- {
- builder.append(", "); //$NON-NLS-1$
- builder.append(fieldName);
- }
- }
-
- if (listSizeFields != null)
- {
- for (String fieldName : listSizeFields.values())
- {
- builder.append(", "); //$NON-NLS-1$
- builder.append(fieldName);
- }
- }
-
- builder.append(") VALUES (?, ?, ?, ?, ?, ?, ?"); //$NON-NLS-1$
- for (int i = 0; i < getValueMappings().size(); i++)
- {
- builder.append(", ?"); //$NON-NLS-1$
- }
-
- if (unsettableFields != null)
- {
- for (int i = 0; i < unsettableFields.size(); i++)
- {
- builder.append(", ?"); //$NON-NLS-1$
- }
- }
-
- if (listSizeFields != null)
- {
- for (int i = 0; i < listSizeFields.size(); i++)
- {
- builder.append(", ?"); //$NON-NLS-1$
- }
- }
-
- builder.append(")"); //$NON-NLS-1$
- sqlInsertAttributes = builder.toString();
-
- // ----------- Select all unrevised Object IDs ------
- builder = new StringBuilder("SELECT "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_ID);
- builder.append(" FROM "); //$NON-NLS-1$
- builder.append(getTable());
- sqlSelectAllObjectIDs = builder.toString();
-
- // ----------- Update attributes --------------------
- builder = new StringBuilder("UPDATE "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" SET "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_VERSION);
- builder.append("=? ,"); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_CREATED);
- builder.append("=? "); //$NON-NLS-1$
- sqlUpdatePrefix = builder.toString();
-
- builder = new StringBuilder(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_RESOURCE);
- builder.append("=? ,"); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_CONTAINER);
- builder.append("=? ,"); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_FEATURE);
- builder.append("=? "); //$NON-NLS-1$
- sqlUpdateContainerPart = builder.toString();
-
- builder = new StringBuilder(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_ID);
- builder.append("=? "); //$NON-NLS-1$
- sqlUpdateAffix = builder.toString();
- }
-
- @Override
- protected void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- int column = 1;
- stmt = statementCache.getPreparedStatement(sqlInsertAttributes, ReuseProbability.HIGH);
- idHandler.setCDOID(stmt, column++, revision.getID());
- stmt.setInt(column++, revision.getVersion());
- stmt.setLong(column++, revision.getTimeStamp());
- stmt.setLong(column++, revision.getRevised());
- idHandler.setCDOID(stmt, column++, revision.getResourceID());
- idHandler.setCDOID(stmt, column++, (CDOID)revision.getContainerID());
- stmt.setInt(column++, revision.getContainingFeatureID());
-
- int isSetCol = column + getValueMappings().size();
-
- for (ITypeMapping mapping : getValueMappings())
- {
- EStructuralFeature feature = mapping.getFeature();
- if (feature.isUnsettable())
- {
- if (revision.getValue(feature) == null)
- {
- stmt.setBoolean(isSetCol++, false);
-
- // also set value column to default value
- mapping.setDefaultValue(stmt, column++);
- continue;
- }
-
- stmt.setBoolean(isSetCol++, true);
- }
-
- mapping.setValueFromRevision(stmt, column++, revision);
- }
-
- Map<EStructuralFeature, String> listSizeFields = getListSizeFields();
- if (listSizeFields != null)
- {
- // isSetCol now points to the first listTableSize-column
- column = isSetCol;
-
- for (EStructuralFeature feature : listSizeFields.keySet())
- {
- CDOList list = revision.getList(feature);
- stmt.setInt(column++, list.size());
- }
- }
-
- DBUtil.update(stmt, true);
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public PreparedStatement createObjectIDStatement(IDBStoreAccessor accessor)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Created ObjectID Statement : {0}", sqlSelectAllObjectIDs); //$NON-NLS-1$
- }
-
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- return statementCache.getPreparedStatement(sqlSelectAllObjectIDs, ReuseProbability.HIGH);
- }
-
- public PreparedStatement createResourceQueryStatement(IDBStoreAccessor accessor, CDOID folderId, String name,
- boolean exactMatch, CDOBranchPoint branchPoint)
- {
- long timeStamp = branchPoint.getTimeStamp();
- if (timeStamp != CDORevision.UNSPECIFIED_DATE)
- {
- throw new IllegalArgumentException("Non-audit store does not support explicit timeStamp in resource query"); //$NON-NLS-1$
- }
-
- EStructuralFeature nameFeature = EresourcePackage.eINSTANCE.getCDOResourceNode_Name();
-
- ITypeMapping nameValueMapping = getValueMapping(nameFeature);
- if (nameValueMapping == null)
- {
- throw new ImplementationError(nameFeature + " not found in ClassMapping " + this); //$NON-NLS-1$
- }
-
- StringBuilder builder = new StringBuilder();
- builder.append("SELECT "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_ID);
- builder.append(" FROM "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_VERSION);
- builder.append(">0 AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.ATTRIBUTES_CONTAINER);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(nameValueMapping.getField());
- if (name == null)
- {
- builder.append(" IS NULL"); //$NON-NLS-1$
- }
- else
- {
- builder.append(exactMatch ? "=? " : " LIKE ? "); //$NON-NLS-1$ //$NON-NLS-2$
- }
-
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- int column = 1;
-
- stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.MEDIUM);
- idHandler.setCDOID(stmt, column++, folderId);
-
- if (name != null)
- {
- String queryName = exactMatch ? name : name + "%"; //$NON-NLS-1$
- nameValueMapping.setValue(stmt, column++, queryName);
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Created Resource Query: {0}", stmt.toString()); //$NON-NLS-1$
- }
-
- return stmt;
- }
- catch (SQLException ex)
- {
- statementCache.releasePreparedStatement(stmt); // only release on error
- throw new DBException(ex);
- }
- }
-
- public boolean readRevision(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk)
- {
- long timeStamp = revision.getTimeStamp();
- if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE)
- {
- throw new UnsupportedOperationException("Mapping strategy does not support audits"); //$NON-NLS-1$
- }
-
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlSelectCurrentAttributes, ReuseProbability.HIGH);
- idHandler.setCDOID(stmt, 1, revision.getID());
-
- // Read singleval-attribute table always (even without modeled attributes!)
- boolean success = readValuesFromStatement(stmt, revision, accessor);
-
- // Read multival tables only if revision exists
- if (success)
- {
- readLists(accessor, revision, listChunk);
- }
-
- return success;
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- @Override
- protected void detachAttributes(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp,
- OMMonitor mon)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlUpdatePrefix + sqlUpdateAffix, ReuseProbability.HIGH);
- stmt.setInt(1, -version);
- stmt.setLong(2, timeStamp);
- idHandler.setCDOID(stmt, 3, id);
-
- DBUtil.update(stmt, true);
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public void writeRevisionDelta(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created,
- OMMonitor monitor)
- {
- Async async = null;
- monitor.begin();
-
- try
- {
- try
- {
- async = monitor.forkAsync();
- FeatureDeltaWriter writer = deltaWriter.get();
- writer.process(accessor, delta, created);
- }
- finally
- {
- if (async != null)
- {
- async.stop();
- }
- }
- }
- finally
- {
- monitor.done();
- }
- }
-
- /**
- * @author Eike Stepper
- */
- private class FeatureDeltaWriter implements CDOFeatureDeltaVisitor
- {
- private CDOID id;
-
- private int oldVersion;
-
- private long created;
-
- private IDBStoreAccessor accessor;
-
- private boolean updateContainer;
-
- private List<Pair<ITypeMapping, Object>> attributeChanges;
-
- private List<Pair<EStructuralFeature, Integer>> listSizeChanges;
-
- private int newContainingFeatureID;
-
- private CDOID newContainerID;
-
- private CDOID newResourceID;
-
- private int branchId;
-
- private int newVersion;
-
- /*
- * this is a temporary copy of the revision to track list size changes...
- */
- private InternalCDORevision tempRevision;
-
- public FeatureDeltaWriter()
- {
- attributeChanges = new ArrayList<Pair<ITypeMapping, Object>>();
- listSizeChanges = new ArrayList<Pair<EStructuralFeature, Integer>>();
- }
-
- protected void reset()
- {
- attributeChanges.clear();
- listSizeChanges.clear();
- updateContainer = false;
- }
-
- public void process(IDBStoreAccessor a, CDORevisionDelta d, long c)
- {
- // set context
- id = d.getID();
-
- branchId = d.getBranch().getID();
- oldVersion = d.getVersion();
- newVersion = oldVersion + 1;
- created = c;
- accessor = a;
-
- tempRevision = (InternalCDORevision)accessor.getTransaction().getRevision(id).copy();
-
- // process revision delta tree
- d.accept(this);
-
- updateAttributes();
- // clean up
- reset();
- }
-
- public void visit(CDOMoveFeatureDelta delta)
- {
- throw new ImplementationError("Should not be called"); //$NON-NLS-1$
- }
-
- public void visit(CDOSetFeatureDelta delta)
- {
- if (delta.getFeature().isMany())
- {
- throw new ImplementationError("Should not be called"); //$NON-NLS-1$
- }
-
- ITypeMapping am = getValueMapping(delta.getFeature());
- if (am == null)
- {
- throw new IllegalArgumentException("AttributeMapping for " + delta.getFeature() + " is null!"); //$NON-NLS-1$ //$NON-NLS-2$
- }
-
- attributeChanges.add(new Pair<ITypeMapping, Object>(am, delta.getValue()));
- }
-
- public void visit(CDOUnsetFeatureDelta delta)
- {
- // TODO: correct this when DBStore implements unsettable features
- // see Bugs 259868 and 263010
- ITypeMapping tm = getValueMapping(delta.getFeature());
- attributeChanges.add(new Pair<ITypeMapping, Object>(tm, null));
- }
-
- public void visit(CDOListFeatureDelta delta)
- {
- EStructuralFeature feature = delta.getFeature();
-
- IListMappingDeltaSupport listMapping = (IListMappingDeltaSupport)getListMapping(feature);
- listMapping.processDelta(accessor, id, branchId, oldVersion, oldVersion + 1, created, delta);
-
- int oldSize = tempRevision.getList(feature).size();
- delta.apply(tempRevision);
- int newSize = tempRevision.getList(feature).size();
-
- if (oldSize != newSize)
- {
- listSizeChanges.add(new Pair<EStructuralFeature, Integer>(feature, newSize));
- }
- }
-
- public void visit(CDOClearFeatureDelta delta)
- {
- throw new ImplementationError("Should not be called"); //$NON-NLS-1$
- }
-
- public void visit(CDOAddFeatureDelta delta)
- {
- throw new ImplementationError("Should not be called"); //$NON-NLS-1$
- }
-
- public void visit(CDORemoveFeatureDelta delta)
- {
- throw new ImplementationError("Should not be called"); //$NON-NLS-1$
- }
-
- public void visit(CDOContainerFeatureDelta delta)
- {
- newContainingFeatureID = delta.getContainerFeatureID();
- newContainerID = (CDOID)delta.getContainerID();
- newResourceID = delta.getResourceID();
- updateContainer = true;
- }
-
- private void updateAttributes()
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- int column = 1;
-
- stmt = statementCache.getPreparedStatement(buildUpdateStatement(), ReuseProbability.MEDIUM);
- stmt.setInt(column++, newVersion);
- stmt.setLong(column++, created);
- if (updateContainer)
- {
- idHandler.setCDOID(stmt, column++, newResourceID, created);
- idHandler.setCDOID(stmt, column++, newContainerID, created);
- stmt.setInt(column++, newContainingFeatureID);
- }
-
- column = setUpdateAttributeValues(attributeChanges, stmt, column);
- column = setUpdateListSizeChanges(listSizeChanges, stmt, column);
-
- idHandler.setCDOID(stmt, column++, id);
-
- DBUtil.update(stmt, true);
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- private String buildUpdateStatement()
- {
- StringBuilder builder = new StringBuilder(sqlUpdatePrefix);
- if (updateContainer)
- {
- builder.append(sqlUpdateContainerPart);
- }
-
- for (Pair<ITypeMapping, Object> change : attributeChanges)
- {
- builder.append(", "); //$NON-NLS-1$
- ITypeMapping typeMapping = change.getElement1();
- builder.append(typeMapping.getField());
- builder.append("=?"); //$NON-NLS-1$
-
- if (typeMapping.getFeature().isUnsettable())
- {
- builder.append(", "); //$NON-NLS-1$
- builder.append(getUnsettableFields().get(typeMapping.getFeature()));
- builder.append("=?"); //$NON-NLS-1$
- }
- }
-
- for (Pair<EStructuralFeature, Integer> change : listSizeChanges)
- {
- builder.append(", "); //$NON-NLS-1$
- EStructuralFeature feature = change.getElement1();
- builder.append(getListSizeFields().get(feature));
- builder.append("=?"); //$NON-NLS-1$
- }
-
- builder.append(sqlUpdateAffix);
- return builder.toString();
- }
-
- private int setUpdateAttributeValues(List<Pair<ITypeMapping, Object>> attributeChanges, PreparedStatement stmt,
- int col) throws SQLException
- {
- for (Pair<ITypeMapping, Object> change : attributeChanges)
- {
- ITypeMapping typeMapping = change.getElement1();
- Object value = change.getElement2();
- if (typeMapping.getFeature().isUnsettable())
- {
- // feature is unsettable
- if (value == null)
- {
- // feature is unset
- typeMapping.setDefaultValue(stmt, col++);
- stmt.setBoolean(col++, false);
- }
- else
- {
- // feature is set
- typeMapping.setValue(stmt, col++, value);
- stmt.setBoolean(col++, true);
- }
- }
- else
- {
- typeMapping.setValue(stmt, col++, change.getElement2());
- }
- }
-
- return col;
- }
-
- private int setUpdateListSizeChanges(List<Pair<EStructuralFeature, Integer>> attributeChanges,
- PreparedStatement stmt, int col) throws SQLException
- {
- for (Pair<EStructuralFeature, Integer> change : listSizeChanges)
- {
- stmt.setInt(col++, change.getElement2());
- }
-
- return col;
- }
- }
-
- @Override
- protected void reviseOldRevision(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, long timeStamp)
- {
- // do nothing
- }
-
- @Override
- protected String getListXRefsWhere(QueryXRefsContext context)
- {
- if (CDORevision.UNSPECIFIED_DATE != context.getTimeStamp())
- {
- throw new IllegalArgumentException("Non-audit mode does not support timestamp specification");
- }
-
- if (!context.getBranch().isMainBranch())
- {
- throw new IllegalArgumentException("Non-audit mode does not support branch specification");
- }
-
- return CDODBSchema.ATTRIBUTES_REVISED + "=0";
- }
-}
+/* + * Copyright (c) 2004 - 2012 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 + * Stefan Winkler - major refactoring + * Stefan Winkler - 249610: [DB] Support external references (Implementation) + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDOList; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta; +import org.eclipse.emf.cdo.eresource.EresourcePackage; +import org.eclipse.emf.cdo.server.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IClassMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.util.ImplementationError; +import org.eclipse.net4j.util.collection.Pair; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * @author Eike Stepper + * @since 2.0 + */ +public class HorizontalNonAuditClassMapping extends AbstractHorizontalClassMapping implements IClassMappingDeltaSupport +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, HorizontalNonAuditClassMapping.class); + + private String sqlSelectAllObjectIDs; + + private String sqlSelectCurrentAttributes; + + private String sqlInsertAttributes; + + private String sqlUpdateAffix; + + private String sqlUpdatePrefix; + + private String sqlUpdateContainerPart; + + private ThreadLocal<FeatureDeltaWriter> deltaWriter = new ThreadLocal<FeatureDeltaWriter>() + { + @Override + protected FeatureDeltaWriter initialValue() + { + return new FeatureDeltaWriter(); + } + }; + + public HorizontalNonAuditClassMapping(AbstractHorizontalMappingStrategy mappingStrategy, EClass eClass) + { + super(mappingStrategy, eClass); + + initSQLStrings(); + } + + private void initSQLStrings() + { + Map<EStructuralFeature, String> unsettableFields = getUnsettableFields(); + Map<EStructuralFeature, String> listSizeFields = getListSizeFields(); + + // ----------- Select Revision --------------------------- + StringBuilder builder = new StringBuilder(); + + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_RESOURCE); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CONTAINER); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_FEATURE); + + for (ITypeMapping singleMapping : getValueMappings()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(singleMapping.getField()); + } + + if (unsettableFields != null) + { + for (String fieldName : unsettableFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + if (listSizeFields != null) + { + for (String fieldName : listSizeFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append("=?"); //$NON-NLS-1$ + + sqlSelectCurrentAttributes = builder.toString(); + + // ----------- Insert Attributes ------------------------- + builder = new StringBuilder(); + builder.append("INSERT INTO "); //$NON-NLS-1$ + builder.append(getTable()); + + builder.append("("); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_REVISED); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_RESOURCE); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CONTAINER); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_FEATURE); + + for (ITypeMapping singleMapping : getValueMappings()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(singleMapping.getField()); + } + + if (unsettableFields != null) + { + for (String fieldName : unsettableFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + if (listSizeFields != null) + { + for (String fieldName : listSizeFields.values()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(fieldName); + } + } + + builder.append(") VALUES (?, ?, ?, ?, ?, ?, ?"); //$NON-NLS-1$ + for (int i = 0; i < getValueMappings().size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + + if (unsettableFields != null) + { + for (int i = 0; i < unsettableFields.size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + } + + if (listSizeFields != null) + { + for (int i = 0; i < listSizeFields.size(); i++) + { + builder.append(", ?"); //$NON-NLS-1$ + } + } + + builder.append(")"); //$NON-NLS-1$ + sqlInsertAttributes = builder.toString(); + + // ----------- Select all unrevised Object IDs ------ + builder = new StringBuilder("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + sqlSelectAllObjectIDs = builder.toString(); + + // ----------- Update attributes -------------------- + builder = new StringBuilder("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append("=? ,"); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CREATED); + builder.append("=? "); //$NON-NLS-1$ + sqlUpdatePrefix = builder.toString(); + + builder = new StringBuilder(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_RESOURCE); + builder.append("=? ,"); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CONTAINER); + builder.append("=? ,"); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_FEATURE); + builder.append("=? "); //$NON-NLS-1$ + sqlUpdateContainerPart = builder.toString(); + + builder = new StringBuilder(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append("=? "); //$NON-NLS-1$ + sqlUpdateAffix = builder.toString(); + } + + @Override + protected void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + int column = 1; + stmt = statementCache.getPreparedStatement(sqlInsertAttributes, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, column++, revision.getID()); + stmt.setInt(column++, revision.getVersion()); + stmt.setLong(column++, revision.getTimeStamp()); + stmt.setLong(column++, revision.getRevised()); + idHandler.setCDOID(stmt, column++, revision.getResourceID()); + idHandler.setCDOID(stmt, column++, (CDOID)revision.getContainerID()); + stmt.setInt(column++, revision.getContainingFeatureID()); + + int isSetCol = column + getValueMappings().size(); + + for (ITypeMapping mapping : getValueMappings()) + { + EStructuralFeature feature = mapping.getFeature(); + if (feature.isUnsettable()) + { + if (revision.getValue(feature) == null) + { + stmt.setBoolean(isSetCol++, false); + + // also set value column to default value + mapping.setDefaultValue(stmt, column++); + continue; + } + + stmt.setBoolean(isSetCol++, true); + } + + mapping.setValueFromRevision(stmt, column++, revision); + } + + Map<EStructuralFeature, String> listSizeFields = getListSizeFields(); + if (listSizeFields != null) + { + // isSetCol now points to the first listTableSize-column + column = isSetCol; + + for (EStructuralFeature feature : listSizeFields.keySet()) + { + CDOList list = revision.getList(feature); + stmt.setInt(column++, list.size()); + } + } + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public PreparedStatement createObjectIDStatement(IDBStoreAccessor accessor) + { + if (TRACER.isEnabled()) + { + TRACER.format("Created ObjectID Statement : {0}", sqlSelectAllObjectIDs); //$NON-NLS-1$ + } + + IPreparedStatementCache statementCache = accessor.getStatementCache(); + return statementCache.getPreparedStatement(sqlSelectAllObjectIDs, ReuseProbability.HIGH); + } + + public PreparedStatement createResourceQueryStatement(IDBStoreAccessor accessor, CDOID folderId, String name, + boolean exactMatch, CDOBranchPoint branchPoint) + { + long timeStamp = branchPoint.getTimeStamp(); + if (timeStamp != CDORevision.UNSPECIFIED_DATE) + { + throw new IllegalArgumentException("Non-audit store does not support explicit timeStamp in resource query"); //$NON-NLS-1$ + } + + EStructuralFeature nameFeature = EresourcePackage.eINSTANCE.getCDOResourceNode_Name(); + + ITypeMapping nameValueMapping = getValueMapping(nameFeature); + if (nameValueMapping == null) + { + throw new ImplementationError(nameFeature + " not found in ClassMapping " + this); //$NON-NLS-1$ + } + + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_ID); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_VERSION); + builder.append(">0 AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.ATTRIBUTES_CONTAINER); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(nameValueMapping.getField()); + if (name == null) + { + builder.append(" IS NULL"); //$NON-NLS-1$ + } + else + { + builder.append(exactMatch ? "=? " : " LIKE ? "); //$NON-NLS-1$ //$NON-NLS-2$ + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + int column = 1; + + stmt = statementCache.getPreparedStatement(builder.toString(), ReuseProbability.MEDIUM); + idHandler.setCDOID(stmt, column++, folderId); + + if (name != null) + { + String queryName = exactMatch ? name : name + "%"; //$NON-NLS-1$ + nameValueMapping.setValue(stmt, column++, queryName); + } + + if (TRACER.isEnabled()) + { + TRACER.format("Created Resource Query: {0}", stmt.toString()); //$NON-NLS-1$ + } + + return stmt; + } + catch (SQLException ex) + { + statementCache.releasePreparedStatement(stmt); // only release on error + throw new DBException(ex); + } + } + + public boolean readRevision(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) + { + long timeStamp = revision.getTimeStamp(); + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + throw new UnsupportedOperationException("Mapping strategy does not support audits"); //$NON-NLS-1$ + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlSelectCurrentAttributes, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, revision.getID()); + + // Read singleval-attribute table always (even without modeled attributes!) + boolean success = readValuesFromStatement(stmt, revision, accessor); + + // Read multival tables only if revision exists + if (success) + { + readLists(accessor, revision, listChunk); + } + + return success; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + protected void detachAttributes(IDBStoreAccessor accessor, CDOID id, int version, CDOBranch branch, long timeStamp, + OMMonitor mon) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdatePrefix + sqlUpdateAffix, ReuseProbability.HIGH); + stmt.setInt(1, -version); + stmt.setLong(2, timeStamp); + idHandler.setCDOID(stmt, 3, id); + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + protected void rawDeleteAttributes(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version, OMMonitor fork) + { + // Not called because CDOWorkspace uses an auditing local repo + } + + public void writeRevisionDelta(IDBStoreAccessor accessor, InternalCDORevisionDelta delta, long created, + OMMonitor monitor) + { + Async async = null; + monitor.begin(); + + try + { + try + { + async = monitor.forkAsync(); + FeatureDeltaWriter writer = deltaWriter.get(); + writer.process(accessor, delta, created); + } + finally + { + if (async != null) + { + async.stop(); + } + } + } + finally + { + monitor.done(); + } + } + + /** + * @author Eike Stepper + */ + private class FeatureDeltaWriter implements CDOFeatureDeltaVisitor + { + private CDOID id; + + private int oldVersion; + + private long created; + + private IDBStoreAccessor accessor; + + private boolean updateContainer; + + private List<Pair<ITypeMapping, Object>> attributeChanges; + + private List<Pair<EStructuralFeature, Integer>> listSizeChanges; + + private int newContainingFeatureID; + + private CDOID newContainerID; + + private CDOID newResourceID; + + private int branchId; + + private int newVersion; + + /* + * this is a temporary copy of the revision to track list size changes... + */ + private InternalCDORevision tempRevision; + + public FeatureDeltaWriter() + { + attributeChanges = new ArrayList<Pair<ITypeMapping, Object>>(); + listSizeChanges = new ArrayList<Pair<EStructuralFeature, Integer>>(); + } + + protected void reset() + { + attributeChanges.clear(); + listSizeChanges.clear(); + updateContainer = false; + } + + public void process(IDBStoreAccessor a, CDORevisionDelta d, long c) + { + // set context + id = d.getID(); + + branchId = d.getBranch().getID(); + oldVersion = d.getVersion(); + newVersion = oldVersion + 1; + created = c; + accessor = a; + + tempRevision = (InternalCDORevision)accessor.getTransaction().getRevision(id).copy(); + + // process revision delta tree + d.accept(this); + + updateAttributes(); + // clean up + reset(); + } + + public void visit(CDOMoveFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOSetFeatureDelta delta) + { + if (delta.getFeature().isMany()) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + ITypeMapping am = getValueMapping(delta.getFeature()); + if (am == null) + { + throw new IllegalArgumentException("AttributeMapping for " + delta.getFeature() + " is null!"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + attributeChanges.add(new Pair<ITypeMapping, Object>(am, delta.getValue())); + } + + public void visit(CDOUnsetFeatureDelta delta) + { + // TODO: correct this when DBStore implements unsettable features + // see Bugs 259868 and 263010 + ITypeMapping tm = getValueMapping(delta.getFeature()); + attributeChanges.add(new Pair<ITypeMapping, Object>(tm, null)); + } + + public void visit(CDOListFeatureDelta delta) + { + EStructuralFeature feature = delta.getFeature(); + + IListMappingDeltaSupport listMapping = (IListMappingDeltaSupport)getListMapping(feature); + listMapping.processDelta(accessor, id, branchId, oldVersion, oldVersion + 1, created, delta); + + int oldSize = tempRevision.getList(feature).size(); + delta.apply(tempRevision); + int newSize = tempRevision.getList(feature).size(); + + if (oldSize != newSize) + { + listSizeChanges.add(new Pair<EStructuralFeature, Integer>(feature, newSize)); + } + } + + public void visit(CDOClearFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOAddFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDORemoveFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOContainerFeatureDelta delta) + { + newContainingFeatureID = delta.getContainerFeatureID(); + newContainerID = (CDOID)delta.getContainerID(); + newResourceID = delta.getResourceID(); + updateContainer = true; + } + + private void updateAttributes() + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + int column = 1; + + stmt = statementCache.getPreparedStatement(buildUpdateStatement(), ReuseProbability.MEDIUM); + stmt.setInt(column++, newVersion); + stmt.setLong(column++, created); + if (updateContainer) + { + idHandler.setCDOID(stmt, column++, newResourceID, created); + idHandler.setCDOID(stmt, column++, newContainerID, created); + stmt.setInt(column++, newContainingFeatureID); + } + + column = setUpdateAttributeValues(attributeChanges, stmt, column); + column = setUpdateListSizeChanges(listSizeChanges, stmt, column); + + idHandler.setCDOID(stmt, column++, id); + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private String buildUpdateStatement() + { + StringBuilder builder = new StringBuilder(sqlUpdatePrefix); + if (updateContainer) + { + builder.append(sqlUpdateContainerPart); + } + + for (Pair<ITypeMapping, Object> change : attributeChanges) + { + builder.append(", "); //$NON-NLS-1$ + ITypeMapping typeMapping = change.getElement1(); + builder.append(typeMapping.getField()); + builder.append("=?"); //$NON-NLS-1$ + + if (typeMapping.getFeature().isUnsettable()) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(getUnsettableFields().get(typeMapping.getFeature())); + builder.append("=?"); //$NON-NLS-1$ + } + } + + for (Pair<EStructuralFeature, Integer> change : listSizeChanges) + { + builder.append(", "); //$NON-NLS-1$ + EStructuralFeature feature = change.getElement1(); + builder.append(getListSizeFields().get(feature)); + builder.append("=?"); //$NON-NLS-1$ + } + + builder.append(sqlUpdateAffix); + return builder.toString(); + } + + private int setUpdateAttributeValues(List<Pair<ITypeMapping, Object>> attributeChanges, PreparedStatement stmt, + int col) throws SQLException + { + for (Pair<ITypeMapping, Object> change : attributeChanges) + { + ITypeMapping typeMapping = change.getElement1(); + Object value = change.getElement2(); + if (typeMapping.getFeature().isUnsettable()) + { + // feature is unsettable + if (value == null) + { + // feature is unset + typeMapping.setDefaultValue(stmt, col++); + stmt.setBoolean(col++, false); + } + else + { + // feature is set + typeMapping.setValue(stmt, col++, value); + stmt.setBoolean(col++, true); + } + } + else + { + typeMapping.setValue(stmt, col++, change.getElement2()); + } + } + + return col; + } + + private int setUpdateListSizeChanges(List<Pair<EStructuralFeature, Integer>> attributeChanges, + PreparedStatement stmt, int col) throws SQLException + { + for (Pair<EStructuralFeature, Integer> change : listSizeChanges) + { + stmt.setInt(col++, change.getElement2()); + } + + return col; + } + } + + @Override + protected void reviseOldRevision(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, long timeStamp) + { + // do nothing + } + + @Override + protected String getListXRefsWhere(QueryXRefsContext context) + { + if (CDORevision.UNSPECIFIED_DATE != context.getTimeStamp()) + { + throw new IllegalArgumentException("Non-audit mode does not support timestamp specification"); + } + + if (!context.getBranch().isMainBranch()) + { + throw new IllegalArgumentException("Non-audit mode does not support branch specification"); + } + + return CDODBSchema.ATTRIBUTES_REVISED + "=0"; + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java index 458f82f51b..6031cbd2ad 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java @@ -1,590 +1,597 @@ -/*
- * Copyright (c) 2004 - 2012 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
- * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
- * Christopher Albert - 254455: [DB] Support FeatureMaps bug 254455
- * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy
- */
-package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
-
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor;
-import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta;
-import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
-import org.eclipse.emf.cdo.server.db.IIDHandler;
-import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
-import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
-import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport;
-import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
-import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping;
-import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
-
-import org.eclipse.net4j.db.DBException;
-import org.eclipse.net4j.db.DBType;
-import org.eclipse.net4j.db.DBUtil;
-import org.eclipse.net4j.util.ImplementationError;
-
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EStructuralFeature;
-import org.eclipse.emf.ecore.util.FeatureMap;
-
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.util.Iterator;
-
-/**
- * This is a featuremap-to-table mapping optimized for non-audit-mode. It doesn't care about version and has delta
- * support.
- *
- * @author Eike Stepper
- * @since 3.0
- */
-public class NonAuditFeatureMapTableMapping extends AbstractFeatureMapTableMapping implements IListMappingDeltaSupport
-{
- private FieldInfo[] keyFields;
-
- private static final int TEMP_INDEX = -1;
-
- private static final int UNBOUNDED_MOVE = -1;
-
- private String sqlClear;
-
- private String sqlUpdateIndex;
-
- private String sqlUpdateValue;
-
- private String sqlDeleteItem;
-
- private String sqlMoveDownWithLimit;
-
- private String sqlMoveDown;
-
- private String sqlMoveUpWithLimit;
-
- private String sqlMoveUp;
-
- public NonAuditFeatureMapTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature)
- {
- super(mappingStrategy, eClass, feature);
- initSQLStrings();
- }
-
- private void initSQLStrings()
- {
- // TODO: add key fields length support
-
- StringBuilder builder = new StringBuilder();
-
- // ----------- clear list -------------------------
-
- builder.append("DELETE FROM "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
- builder.append("=? "); //$NON-NLS-1$
-
- sqlClear = builder.toString();
-
- builder.append(" AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_IDX);
- builder.append("=? "); //$NON-NLS-1$
-
- sqlDeleteItem = builder.toString();
-
- // ----------- update one item index --------------
- builder = new StringBuilder();
- builder.append("UPDATE "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" SET "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_IDX);
- builder.append("=? "); //$NON-NLS-1$
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_IDX);
- builder.append("=? "); //$NON-NLS-1$
- sqlUpdateIndex = builder.toString();
-
- // ----------- update one item value --------------
- builder = new StringBuilder();
- builder.append("UPDATE "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" SET "); //$NON-NLS-1$
-
- builder.append(CDODBSchema.FEATUREMAP_TAG);
- builder.append("=?,"); //$NON-NLS-1$
-
- Iterator<String> iter = getColumnNames().iterator();
- while (iter.hasNext())
- {
- String column = iter.next();
- builder.append(column);
- builder.append("=?"); //$NON-NLS-1$
-
- if (iter.hasNext())
- {
- builder.append(", "); //$NON-NLS-1$
- }
- }
-
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_IDX);
- builder.append("=? "); //$NON-NLS-1$
- sqlUpdateValue = builder.toString();
-
- // ----------- move down --------------
- builder = new StringBuilder();
- builder.append("UPDATE "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" SET "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_IDX);
- builder.append("="); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_IDX);
- builder.append("-1 WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_IDX);
- builder.append(">? "); //$NON-NLS-1$
- sqlMoveDown = builder.toString();
-
- builder.append(" AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_IDX);
- builder.append("<=?"); //$NON-NLS-1$
- sqlMoveDownWithLimit = builder.toString();
-
- // ----------- move up --------------
- builder = new StringBuilder();
- builder.append("UPDATE "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" SET "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_IDX);
- builder.append("="); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_IDX);
- builder.append("+1 WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_IDX);
- builder.append(">=? "); //$NON-NLS-1$
- sqlMoveUp = builder.toString();
-
- builder.append(" AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_IDX);
- builder.append("<?"); //$NON-NLS-1$
- sqlMoveUpWithLimit = builder.toString();
- }
-
- @Override
- protected FieldInfo[] getKeyFields()
- {
- if (keyFields == null)
- {
- DBType dbType = getMappingStrategy().getStore().getIDHandler().getDBType();
- keyFields = new FieldInfo[] { new FieldInfo(CDODBSchema.FEATUREMAP_REVISION_ID, dbType) };
- }
-
- return keyFields;
- }
-
- @Override
- protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException
- {
- getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID());
- }
-
- public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised)
- {
- clearList(accessor, id);
- }
-
- /**
- * Clear a list of a given revision.
- *
- * @param accessor
- * the accessor to use
- * @param id
- * the id of the revision from which to remove all items
- */
- public void clearList(IDBStoreAccessor accessor, CDOID id)
- {
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlClear, ReuseProbability.HIGH);
- getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id);
- DBUtil.update(stmt, false);
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- /**
- * Insert a list item at a specified position.
- *
- * @param accessor
- * the accessor to use
- * @param id
- * the id of the revision to insert the value
- * @param index
- * the index where to insert the element
- * @param value
- * the value to insert.
- */
- public void insertListItem(IDBStoreAccessor accessor, CDOID id, int index, Object value, long timestamp)
- {
- move1up(accessor, id, index, UNBOUNDED_MOVE);
- insertValue(accessor, id, index, value, timestamp);
- }
-
- private void insertValue(IDBStoreAccessor accessor, CDOID id, int index, Object value, long timestamp)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- FeatureMap.Entry entry = (FeatureMap.Entry)value;
- EStructuralFeature entryFeature = entry.getEStructuralFeature();
- CDOID tag = getTagByFeature(entryFeature, timestamp);
- String columnName = getColumnName(tag);
-
- String sql = sqlInsert;
-
- stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH);
-
- idHandler.setCDOID(stmt, 1, id);
- int column = getKeyFields().length + 1;
-
- for (int i = 0; i < getColumnNames().size(); i++)
- {
- if (getColumnNames().get(i).equals(columnName))
- {
- getTypeMapping(tag).setValue(stmt, column++, entry.getValue());
- }
- else
- {
- stmt.setNull(column++, getDBTypes().get(i).getCode());
- }
- }
-
- stmt.setInt(column++, index);
- idHandler.setCDOID(stmt, column++, tag);
-
- DBUtil.update(stmt, true);
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- /**
- * Move a list item from one position to another. Indices between both positions are updated so that the list remains
- * consistent.
- *
- * @param accessor
- * the accessor to use
- * @param id
- * the id of the revision in which to move the item
- * @param oldPosition
- * the old position of the item.
- * @param newPosition
- * the new position of the item.
- */
- public void moveListItem(IDBStoreAccessor accessor, CDOID id, int oldPosition, int newPosition)
- {
- if (oldPosition == newPosition)
- {
- return;
- }
-
- // move element away temporarily
- updateOneIndex(accessor, id, oldPosition, TEMP_INDEX);
-
- // move elements in between
- if (oldPosition < newPosition)
- {
- move1down(accessor, id, oldPosition, newPosition);
- }
- else
- {
- // oldPosition > newPosition -- equal case is handled above
- move1up(accessor, id, newPosition, oldPosition);
- }
-
- // move temporary element to new position
- updateOneIndex(accessor, id, TEMP_INDEX, newPosition);
- }
-
- private void updateOneIndex(IDBStoreAccessor accessor, CDOID id, int oldIndex, int newIndex)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH);
- stmt.setInt(1, newIndex);
- idHandler.setCDOID(stmt, 2, id);
- stmt.setInt(3, oldIndex);
- DBUtil.update(stmt, true);
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- /**
- * Remove a list item from a specified a position.
- *
- * @param accessor
- * the accessor to use
- * @param id
- * the id of the revision from which to remove the item
- * @param index
- * the index of the item to remove
- */
- public void removeListItem(IDBStoreAccessor accessor, CDOID id, int index)
- {
- deleteItem(accessor, id, index);
- move1down(accessor, id, index, UNBOUNDED_MOVE);
- }
-
- /**
- * Move references downwards to close a gap at position <code>index</code>. Only indexes starting with
- * <code>index + 1</code> and ending with <code>upperIndex</code> are moved down.
- */
- private void move1down(IDBStoreAccessor accessor, CDOID id, int index, int upperIndex)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(upperIndex == UNBOUNDED_MOVE ? sqlMoveDown : sqlMoveDownWithLimit,
- ReuseProbability.HIGH);
-
- idHandler.setCDOID(stmt, 1, id);
- stmt.setInt(2, index);
- if (upperIndex != UNBOUNDED_MOVE)
- {
- stmt.setInt(3, upperIndex);
- }
-
- DBUtil.update(stmt, false);
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- /**
- * Move references downwards to close a gap at position <code>index</code>. Only indexes starting with
- * <code>index + 1</code> and ending with <code>upperIndex</code> are moved down.
- */
- private void move1up(IDBStoreAccessor accessor, CDOID id, int index, int upperIndex)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(upperIndex == UNBOUNDED_MOVE ? sqlMoveUp : sqlMoveUpWithLimit,
- ReuseProbability.HIGH);
- idHandler.setCDOID(stmt, 1, id);
- stmt.setInt(2, index);
- if (upperIndex != UNBOUNDED_MOVE)
- {
- stmt.setInt(3, upperIndex);
- }
-
- DBUtil.update(stmt, false);
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- private void deleteItem(IDBStoreAccessor accessor, CDOID id, int index)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlDeleteItem, ReuseProbability.HIGH);
- idHandler.setCDOID(stmt, 1, id);
- stmt.setInt(2, index);
- DBUtil.update(stmt, true);
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- /**
- * Set a value at a specified position to the given value.
- *
- * @param accessor
- * the accessor to use
- * @param id
- * the id of the revision to set the value
- * @param index
- * the index of the item to set
- * @param value
- * the value to be set.
- */
- public void setListItem(IDBStoreAccessor accessor, CDOID id, int index, Object value, long timestamp)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- FeatureMap.Entry entry = (FeatureMap.Entry)value;
- EStructuralFeature entryFeature = entry.getEStructuralFeature();
- CDOID tag = getTagByFeature(entryFeature, timestamp);
- String columnName = getColumnName(tag);
- ITypeMapping mapping = getTypeMapping(tag);
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlUpdateValue, ReuseProbability.HIGH);
- idHandler.setCDOID(stmt, 1, tag);
- int column = 2;
-
- for (int i = 0; i < getColumnNames().size(); i++)
- {
- if (getColumnNames().get(i).equals(columnName))
- {
- mapping.setValue(stmt, column++, entry.getValue());
- }
- else
- {
- stmt.setNull(column++, getDBTypes().get(i).getCode());
- }
- }
-
- idHandler.setCDOID(stmt, column++, id);
- stmt.setInt(column++, index);
- DBUtil.update(stmt, true);
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion,
- final int newVersion, final long created, CDOListFeatureDelta listDelta)
- {
- CDOFeatureDeltaVisitor visitor = new CDOFeatureDeltaVisitor()
- {
- public void visit(CDOMoveFeatureDelta delta)
- {
- moveListItem(accessor, id, delta.getOldPosition(), delta.getNewPosition());
- }
-
- public void visit(CDOAddFeatureDelta delta)
- {
- insertListItem(accessor, id, delta.getIndex(), delta.getValue(), created);
- }
-
- public void visit(CDORemoveFeatureDelta delta)
- {
- removeListItem(accessor, id, delta.getIndex());
- }
-
- public void visit(CDOSetFeatureDelta delta)
- {
- setListItem(accessor, id, delta.getIndex(), delta.getValue(), created);
- }
-
- public void visit(CDOUnsetFeatureDelta delta)
- {
- if (delta.getFeature().isUnsettable())
- {
- throw new ImplementationError("Should not be called"); //$NON-NLS-1$
- }
-
- clearList(accessor, id);
- }
-
- public void visit(CDOListFeatureDelta delta)
- {
- throw new ImplementationError("Should not be called"); //$NON-NLS-1$
- }
-
- public void visit(CDOClearFeatureDelta delta)
- {
- clearList(accessor, id);
- }
-
- public void visit(CDOContainerFeatureDelta delta)
- {
- throw new ImplementationError("Should not be called"); //$NON-NLS-1$
- }
- };
-
- for (CDOFeatureDelta delta : listDelta.getListChanges())
- {
- delta.accept(visitor);
- }
- }
-}
+/* + * Copyright (c) 2004 - 2012 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 + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + * Christopher Albert - 254455: [DB] Support FeatureMaps bug 254455 + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.util.ImplementationError; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.FeatureMap; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Iterator; + +/** + * This is a featuremap-to-table mapping optimized for non-audit-mode. It doesn't care about version and has delta + * support. + * + * @author Eike Stepper + * @since 3.0 + */ +public class NonAuditFeatureMapTableMapping extends AbstractFeatureMapTableMapping implements IListMappingDeltaSupport +{ + private FieldInfo[] keyFields; + + private static final int TEMP_INDEX = -1; + + private static final int UNBOUNDED_MOVE = -1; + + private String sqlClear; + + private String sqlUpdateIndex; + + private String sqlUpdateValue; + + private String sqlDeleteItem; + + private String sqlMoveDownWithLimit; + + private String sqlMoveDown; + + private String sqlMoveUpWithLimit; + + private String sqlMoveUp; + + public NonAuditFeatureMapTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + initSQLStrings(); + } + + private void initSQLStrings() + { + // TODO: add key fields length support + + StringBuilder builder = new StringBuilder(); + + // ----------- clear list ------------------------- + + builder.append("DELETE FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? "); //$NON-NLS-1$ + + sqlClear = builder.toString(); + + builder.append(" AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? "); //$NON-NLS-1$ + + sqlDeleteItem = builder.toString(); + + // ----------- update one item index -------------- + builder = new StringBuilder(); + builder.append("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? "); //$NON-NLS-1$ + sqlUpdateIndex = builder.toString(); + + // ----------- update one item value -------------- + builder = new StringBuilder(); + builder.append("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + + builder.append(CDODBSchema.FEATUREMAP_TAG); + builder.append("=?,"); //$NON-NLS-1$ + + Iterator<String> iter = getColumnNames().iterator(); + while (iter.hasNext()) + { + String column = iter.next(); + builder.append(column); + builder.append("=?"); //$NON-NLS-1$ + + if (iter.hasNext()) + { + builder.append(", "); //$NON-NLS-1$ + } + } + + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("=? "); //$NON-NLS-1$ + sqlUpdateValue = builder.toString(); + + // ----------- move down -------------- + builder = new StringBuilder(); + builder.append("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("="); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("-1 WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append(">? "); //$NON-NLS-1$ + sqlMoveDown = builder.toString(); + + builder.append(" AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("<=?"); //$NON-NLS-1$ + sqlMoveDownWithLimit = builder.toString(); + + // ----------- move up -------------- + builder = new StringBuilder(); + builder.append("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("="); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("+1 WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append(">=? "); //$NON-NLS-1$ + sqlMoveUp = builder.toString(); + + builder.append(" AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_IDX); + builder.append("<?"); //$NON-NLS-1$ + sqlMoveUpWithLimit = builder.toString(); + } + + @Override + protected FieldInfo[] getKeyFields() + { + if (keyFields == null) + { + DBType dbType = getMappingStrategy().getStore().getIDHandler().getDBType(); + keyFields = new FieldInfo[] { new FieldInfo(CDODBSchema.FEATUREMAP_REVISION_ID, dbType) }; + } + + return keyFields; + } + + @Override + protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException + { + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID()); + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + clearList(accessor, id); + } + + /** + * Clear a list of a given revision. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision from which to remove all items + */ + public void clearList(IDBStoreAccessor accessor, CDOID id) + { + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlClear, ReuseProbability.HIGH); + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + clearList(accessor, id); + } + + /** + * Insert a list item at a specified position. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision to insert the value + * @param index + * the index where to insert the element + * @param value + * the value to insert. + */ + public void insertListItem(IDBStoreAccessor accessor, CDOID id, int index, Object value, long timestamp) + { + move1up(accessor, id, index, UNBOUNDED_MOVE); + insertValue(accessor, id, index, value, timestamp); + } + + private void insertValue(IDBStoreAccessor accessor, CDOID id, int index, Object value, long timestamp) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + FeatureMap.Entry entry = (FeatureMap.Entry)value; + EStructuralFeature entryFeature = entry.getEStructuralFeature(); + CDOID tag = getTagByFeature(entryFeature, timestamp); + String columnName = getColumnName(tag); + + String sql = sqlInsert; + + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH); + + idHandler.setCDOID(stmt, 1, id); + int column = getKeyFields().length + 1; + + for (int i = 0; i < getColumnNames().size(); i++) + { + if (getColumnNames().get(i).equals(columnName)) + { + getTypeMapping(tag).setValue(stmt, column++, entry.getValue()); + } + else + { + stmt.setNull(column++, getDBTypes().get(i).getCode()); + } + } + + stmt.setInt(column++, index); + idHandler.setCDOID(stmt, column++, tag); + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + /** + * Move a list item from one position to another. Indices between both positions are updated so that the list remains + * consistent. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision in which to move the item + * @param oldPosition + * the old position of the item. + * @param newPosition + * the new position of the item. + */ + public void moveListItem(IDBStoreAccessor accessor, CDOID id, int oldPosition, int newPosition) + { + if (oldPosition == newPosition) + { + return; + } + + // move element away temporarily + updateOneIndex(accessor, id, oldPosition, TEMP_INDEX); + + // move elements in between + if (oldPosition < newPosition) + { + move1down(accessor, id, oldPosition, newPosition); + } + else + { + // oldPosition > newPosition -- equal case is handled above + move1up(accessor, id, newPosition, oldPosition); + } + + // move temporary element to new position + updateOneIndex(accessor, id, TEMP_INDEX, newPosition); + } + + private void updateOneIndex(IDBStoreAccessor accessor, CDOID id, int oldIndex, int newIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH); + stmt.setInt(1, newIndex); + idHandler.setCDOID(stmt, 2, id); + stmt.setInt(3, oldIndex); + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + /** + * Remove a list item from a specified a position. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision from which to remove the item + * @param index + * the index of the item to remove + */ + public void removeListItem(IDBStoreAccessor accessor, CDOID id, int index) + { + deleteItem(accessor, id, index); + move1down(accessor, id, index, UNBOUNDED_MOVE); + } + + /** + * Move references downwards to close a gap at position <code>index</code>. Only indexes starting with + * <code>index + 1</code> and ending with <code>upperIndex</code> are moved down. + */ + private void move1down(IDBStoreAccessor accessor, CDOID id, int index, int upperIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(upperIndex == UNBOUNDED_MOVE ? sqlMoveDown : sqlMoveDownWithLimit, + ReuseProbability.HIGH); + + idHandler.setCDOID(stmt, 1, id); + stmt.setInt(2, index); + if (upperIndex != UNBOUNDED_MOVE) + { + stmt.setInt(3, upperIndex); + } + + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + /** + * Move references downwards to close a gap at position <code>index</code>. Only indexes starting with + * <code>index + 1</code> and ending with <code>upperIndex</code> are moved down. + */ + private void move1up(IDBStoreAccessor accessor, CDOID id, int index, int upperIndex) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(upperIndex == UNBOUNDED_MOVE ? sqlMoveUp : sqlMoveUpWithLimit, + ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, id); + stmt.setInt(2, index); + if (upperIndex != UNBOUNDED_MOVE) + { + stmt.setInt(3, upperIndex); + } + + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + private void deleteItem(IDBStoreAccessor accessor, CDOID id, int index) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlDeleteItem, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, id); + stmt.setInt(2, index); + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + /** + * Set a value at a specified position to the given value. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision to set the value + * @param index + * the index of the item to set + * @param value + * the value to be set. + */ + public void setListItem(IDBStoreAccessor accessor, CDOID id, int index, Object value, long timestamp) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + FeatureMap.Entry entry = (FeatureMap.Entry)value; + EStructuralFeature entryFeature = entry.getEStructuralFeature(); + CDOID tag = getTagByFeature(entryFeature, timestamp); + String columnName = getColumnName(tag); + ITypeMapping mapping = getTypeMapping(tag); + + try + { + stmt = statementCache.getPreparedStatement(sqlUpdateValue, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, tag); + int column = 2; + + for (int i = 0; i < getColumnNames().size(); i++) + { + if (getColumnNames().get(i).equals(columnName)) + { + mapping.setValue(stmt, column++, entry.getValue()); + } + else + { + stmt.setNull(column++, getDBTypes().get(i).getCode()); + } + } + + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, index); + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion, + final int newVersion, final long created, CDOListFeatureDelta listDelta) + { + CDOFeatureDeltaVisitor visitor = new CDOFeatureDeltaVisitor() + { + public void visit(CDOMoveFeatureDelta delta) + { + moveListItem(accessor, id, delta.getOldPosition(), delta.getNewPosition()); + } + + public void visit(CDOAddFeatureDelta delta) + { + insertListItem(accessor, id, delta.getIndex(), delta.getValue(), created); + } + + public void visit(CDORemoveFeatureDelta delta) + { + removeListItem(accessor, id, delta.getIndex()); + } + + public void visit(CDOSetFeatureDelta delta) + { + setListItem(accessor, id, delta.getIndex(), delta.getValue(), created); + } + + public void visit(CDOUnsetFeatureDelta delta) + { + if (delta.getFeature().isUnsettable()) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + clearList(accessor, id); + } + + public void visit(CDOListFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + + public void visit(CDOClearFeatureDelta delta) + { + clearList(accessor, id); + } + + public void visit(CDOContainerFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-1$ + } + }; + + for (CDOFeatureDelta delta : listDelta.getListChanges()) + { + delta.accept(visitor); + } + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java index 8ea191ba31..12fcbf2314 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java @@ -1,1177 +1,1185 @@ -/*
- * Copyright (c) 2004 - 2012 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
- * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
- * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy
- */
-package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
-
-import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor;
-import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta;
-import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
-import org.eclipse.emf.cdo.server.db.IIDHandler;
-import org.eclipse.emf.cdo.server.db.IPreparedStatementCache;
-import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
-import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport;
-import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
-import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
-import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
-
-import org.eclipse.net4j.db.DBException;
-import org.eclipse.net4j.db.DBType;
-import org.eclipse.net4j.db.DBUtil;
-import org.eclipse.net4j.util.om.trace.ContextTracer;
-
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EStructuralFeature;
-
-import org.eclipse.core.runtime.Assert;
-
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.ListIterator;
-
-/**
- * This is a list-to-table mapping optimized for non-audit-mode. It doesn't care about version and has delta support.
- *
- * @author Eike Stepper
- * @since 2.0
- */
-public class NonAuditListTableMapping extends AbstractListTableMapping implements IListMappingDeltaSupport
-{
- private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, NonAuditListTableMapping.class);
-
- private FieldInfo[] keyFields;
-
- private static final int UNBOUNDED_SHIFT = -1;
-
- private String sqlClear;
-
- private String sqlUpdateValue;
-
- private String sqlUpdateIndex;
-
- private String sqlInsertValue;
-
- private String sqlDeleteItem;
-
- private String sqlShiftDownIndex;
-
- private String sqlReadCurrentIndexOffset;
-
- private String sqlShiftUpIndex;
-
- public NonAuditListTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature)
- {
- super(mappingStrategy, eClass, feature);
- initSQLStrings();
- }
-
- private void initSQLStrings()
- {
- // ----------- clear list -------------------------
- StringBuilder builder = new StringBuilder();
-
- builder.append("DELETE FROM "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_ID);
- builder.append("=? "); //$NON-NLS-1$
-
- sqlClear = builder.toString();
-
- builder.append(" AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_IDX);
- builder.append("=? "); //$NON-NLS-1$
-
- sqlDeleteItem = builder.toString();
-
- // ----------- update one item --------------------
- builder = new StringBuilder();
- builder.append("UPDATE "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" SET "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_VALUE);
- builder.append("=? "); //$NON-NLS-1$
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_IDX);
- builder.append("=? "); //$NON-NLS-1$
- sqlUpdateValue = builder.toString();
-
- // ----------- insert one item --------------------
- builder = new StringBuilder();
- builder.append("INSERT INTO "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" ("); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_ID);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_IDX);
- builder.append(", "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_VALUE);
- builder.append(") VALUES(?, ?, ?) "); //$NON-NLS-1$
- sqlInsertValue = builder.toString();
-
- // ----------- update one item index --------------
- builder = new StringBuilder();
- builder.append("UPDATE "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" SET "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_IDX);
- builder.append("=? "); //$NON-NLS-1$
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_IDX);
- builder.append("=? "); //$NON-NLS-1$
- sqlUpdateIndex = builder.toString();
-
- // ----------- mass update item indexes --------------
- builder = new StringBuilder();
- builder.append("UPDATE "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" SET "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_IDX);
- builder.append("="); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_IDX);
- builder.append("+? WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_ID);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_IDX);
- builder.append(" BETWEEN ? AND ?"); //$NON-NLS-1$
- // getMappingStrategy().getStore().getDBAdapter()
-
- // needed because of MySQL:
- builder.append("/*! ORDER BY "); //$NON-NLS-1$ /
- builder.append(CDODBSchema.LIST_IDX);
- sqlShiftDownIndex = builder.toString() + " */"; //$NON-NLS-1$
- builder.append(" DESC"); //$NON-NLS-1$
- sqlShiftUpIndex = builder.toString() + " */"; //$NON-NLS-1$
-
- // ----------- read current index offset --------------
- builder = new StringBuilder();
- builder.append("SELECT MIN("); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_IDX);
- builder.append(") FROM "); //$NON-NLS-1$
- builder.append(getTable());
- builder.append(" WHERE "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_ID);
- builder.append("=?"); //$NON-NLS-1$
- sqlReadCurrentIndexOffset = builder.toString();
- }
-
- @Override
- public void addSimpleChunkWhere(IDBStoreAccessor accessor, CDOID cdoid, StringBuilder builder, int index)
- {
- int offset = getCurrentIndexOffset(accessor, cdoid);
- super.addSimpleChunkWhere(accessor, cdoid, builder, index + offset);
- }
-
- @Override
- public void addRangedChunkWhere(IDBStoreAccessor accessor, CDOID cdoid, StringBuilder builder, int fromIndex,
- int toIndex)
- {
- int offset = getCurrentIndexOffset(accessor, cdoid);
- super.addRangedChunkWhere(accessor, cdoid, builder, fromIndex + offset, toIndex + offset);
- }
-
- @Override
- protected FieldInfo[] getKeyFields()
- {
- if (keyFields == null)
- {
- DBType dbType = getMappingStrategy().getStore().getIDHandler().getDBType();
- keyFields = new FieldInfo[] { new FieldInfo(CDODBSchema.LIST_REVISION_ID, dbType) };
- }
-
- return keyFields;
- }
-
- @Override
- protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException
- {
- getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID());
- }
-
- public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised)
- {
- clearList(accessor, id);
- }
-
- /**
- * Clear a list of a given revision.
- *
- * @param accessor
- * the accessor to use
- * @param id
- * the id of the revision from which to remove all items
- */
- public void clearList(IDBStoreAccessor accessor, CDOID id)
- {
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlClear, ReuseProbability.HIGH);
- getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id);
- DBUtil.update(stmt, false);
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public int getCurrentIndexOffset(IDBStoreAccessor accessor, CDOID id)
- {
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
- ResultSet rset = null;
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlReadCurrentIndexOffset, ReuseProbability.HIGH);
- getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id);
- rset = stmt.executeQuery();
- if (!rset.next())
- {
- // list is empty. Return the default offset of 0.
- return 0;
- }
- // return the minimum index which is equal to the current offset.
- return rset.getInt(1);
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- DBUtil.close(rset);
- releaseStatement(accessor, stmt);
- }
- }
-
- public void processDelta(final IDBStoreAccessor accessor, final CDOID id, int branchId, int oldVersion,
- final int newVersion, long created, CDOListFeatureDelta delta)
- {
- CDOBranchPoint main = accessor.getStore().getRepository().getBranchManager().getMainBranch().getHead();
-
- InternalCDORevision originalRevision = (InternalCDORevision)accessor.getStore().getRepository()
- .getRevisionManager().getRevision(id, main, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true);
- int oldListSize = originalRevision.getList(getFeature()).size();
-
- if (TRACER.isEnabled())
- {
- TRACER.format("ListTableMapping.processDelta for revision {0} - previous list size: {1}", originalRevision, //$NON-NLS-1$
- oldListSize);
- }
-
- // let the visitor collect the changes
- ListDeltaVisitor visitor = new ListDeltaVisitor(oldListSize);
-
- if (TRACER.isEnabled())
- {
- TRACER.trace("Processing deltas..."); //$NON-NLS-1$
- }
-
- for (CDOFeatureDelta listDelta : delta.getListChanges())
- {
- listDelta.accept(visitor);
- }
-
- visitor.postProcess(accessor, id);
-
- // finally, write results to the database
- visitor.writeResultToDatabase(accessor, id);
- }
-
- private void releaseStatement(IDBStoreAccessor accessor, PreparedStatement... stmts)
- {
- Throwable t = null;
-
- for (PreparedStatement stmt : stmts)
- {
- try
- {
- if (stmt != null)
- {
- try
- {
- stmt.clearBatch();
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- accessor.getStatementCache().releasePreparedStatement(stmt);
- }
- }
- }
- catch (Throwable th)
- {
- if (t == null)
- {
- // remember first exception
- t = th;
- }
-
- // more exceptions go to the log
- OM.LOG.error(t);
- }
- }
-
- if (t != null)
- {
- throw new DBException(t);
- }
- }
-
- /**
- * @author Eike Stepper
- */
- private final class ListDeltaVisitor implements CDOFeatureDeltaVisitor
- {
- private boolean clearFirst;
-
- private ArrayList<ManipulationElement> manipulations;
-
- /**
- * Start of a range [tempIndex, tempIndex-1, ...] which lies outside of the normal list indexes and which serve as
- * temporary space to move items temporarily to get them out of the way of other operations.
- */
- private int tempIndex = -1;
-
- public ListDeltaVisitor(int oldListSize)
- {
- // reset the clear-flag
- clearFirst = false;
- manipulations = new ArrayList<ManipulationElement>(oldListSize);
-
- // create list and initialize with original indexes
- for (int i = 0; i < oldListSize; i++)
- {
- manipulations.add(ManipulationElement.createOriginalElement(i));
- }
- }
-
- public void visit(CDOAddFeatureDelta delta)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format(" - insert at {0} value {1}", delta.getIndex(), delta.getValue()); //$NON-NLS-1$
- }
-
- // make room for the new item
- shiftIndexes(delta.getIndex(), UNBOUNDED_SHIFT, +1);
-
- // create the item
- manipulations.add(ManipulationElement.createInsertedElement(delta.getIndex(), delta.getValue()));
- }
-
- public void visit(CDORemoveFeatureDelta delta)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format(" - remove at {0}", delta.getIndex()); //$NON-NLS-1$
- }
-
- ManipulationElement e = findElement(delta.getIndex());
- deleteItem(e);
-
- // fill the gap by shifting all subsequent items down
- shiftIndexes(delta.getIndex() + 1, UNBOUNDED_SHIFT, -1);
- }
-
- public void visit(CDOSetFeatureDelta delta)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format(" - set at {0} value {1}", delta.getIndex(), delta.getValue()); //$NON-NLS-1$
- }
-
- ManipulationElement e = findElement(delta.getIndex());
- // set the new value
- e.value = delta.getValue();
-
- // if the item is freshly inserted we do not set the SET-mark.
- // setting the value of a new item results in inserting with the
- // new value at once.
- if (!e.is(ManipulationConstants.INSERT))
- {
- // else mark the existing item to be set to a new value
- e.addType(ManipulationConstants.SET_VALUE);
- }
- }
-
- public void visit(CDOUnsetFeatureDelta delta)
- {
- if (delta.getFeature().isUnsettable())
- {
- Assert.isTrue(false);
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format(" - unset list"); //$NON-NLS-1$
- }
-
- // set the clear-flag
- clearFirst = true;
-
- // and also clear all manipulation items
- manipulations.clear();
- }
-
- public void visit(CDOClearFeatureDelta delta)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format(" - clear list"); //$NON-NLS-1$
- }
-
- // set the clear-flag
- clearFirst = true;
-
- // and also clear all manipulation items
- manipulations.clear();
- }
-
- public void visit(CDOMoveFeatureDelta delta)
- {
- int fromIdx = delta.getOldPosition();
- int toIdx = delta.getNewPosition();
-
- if (TRACER.isEnabled())
- {
- TRACER.format(" - move {0} -> {1}", fromIdx, toIdx); //$NON-NLS-1$
- }
-
- // ignore the trivial case
- if (fromIdx == toIdx)
- {
- return;
- }
-
- ManipulationElement e = findElement(fromIdx);
-
- // adjust indexes and shift either up or down
- if (fromIdx < toIdx)
- {
- shiftIndexes(fromIdx + 1, toIdx, -1);
- }
- else
- { // fromIdx > toIdx here
- shiftIndexes(toIdx, fromIdx - 1, +1);
- }
-
- // set the new index
- e.destinationIndex = toIdx;
-
- // if it is a new element, no MOVE mark needed, because we insert it
- // at the new position
- if (!e.is(ManipulationConstants.INSERT))
- {
- // else we need to handle the move of an existing item
- e.addType(ManipulationConstants.MOVE);
- }
- }
-
- public void visit(CDOListFeatureDelta delta)
- {
- // never called
- Assert.isTrue(false);
- }
-
- public void visit(CDOContainerFeatureDelta delta)
- {
- // never called
- Assert.isTrue(false);
- }
-
- /**
- * Helper method: shift all (destination) indexes in the interval [from,to] (inclusive at both ends) by offset
- * (positive or negative).
- */
- private void shiftIndexes(int from, int to, int offset)
- {
- for (ManipulationElement e : manipulations)
- {
- if (e.destinationIndex >= from && (to == UNBOUNDED_SHIFT || e.destinationIndex <= to))
- {
- e.destinationIndex += offset;
- }
- }
- }
-
- /**
- * Find a manipulation item by destination index).
- */
- private ManipulationElement findElement(int index)
- {
- for (ManipulationElement e : manipulations)
- {
- if (e.destinationIndex == index)
- {
- return e;
- }
- }
-
- // never reached
- Assert.isTrue(false);
- return null;
- }
-
- /**
- * Delete an element (used in remove and clear)
- */
- private void deleteItem(ManipulationElement e)
- {
- if (e.is(ManipulationConstants.INSERT))
- {
- // newly inserted items are simply removed, as
- // removing inserted items is equal to no change at all.
- manipulations.remove(e);
- }
- else
- {
- // mark the existing item as to be deleted.
- // (previous MOVE and SET conditions are overridden by setting
- // the exclusive DELETE type).
- e.type = ManipulationConstants.DELETE;
- e.destinationIndex = ManipulationConstants.NO_INDEX;
- }
- }
-
- /**
- * Called after all deltas are applied an before the results are written to the database. This method post-processes
- * the manipulation elements in order to minimize database access.
- */
- public void postProcess(IDBStoreAccessor accessor, CDOID id)
- {
- if (!((HorizontalNonAuditMappingStrategy)getMappingStrategy()).shallForceZeroBasedIndex())
- {
- /*
- * this is an optimization which reduces the amount of modifications on the database to maintain list indexes.
- * For the optimization, we let go of the assumption that indexes are zero-based. Instead, we work with an
- * offset at the database level which can change with every change to the list (e.g. if the second element is
- * removed from a list with 1000 elements, instead of shifting down indexes 2 to 1000 by 1, we shift up index 0
- * by 1 and have now a list with indexes starting at 1 instead of 0. This optimization is applied by modifying
- * the list of ManipulationElements, which can be seen as the database modification plan.
- */
-
- // first, get the current offset
- int offsetBefore = getCurrentIndexOffset(accessor, id);
- if (TRACER.isEnabled())
- {
- TRACER.trace("Offset optimization."); //$NON-NLS-1$
- TRACER.trace("Current offset = " + offsetBefore); //$NON-NLS-1$
- }
-
- applyOffsetToSourceIndexes(offsetBefore);
-
- int offsetAfter;
-
- if ((long)Math.abs(offsetBefore) + (long)manipulations.size() > Integer.MAX_VALUE)
- {
- // security belt for really huge collections or for collections that have been manipulated lots of times
- // -> do not optimize after this border is crossed. Instead, reset offset for the whole list to a zero-based
- // index.
- offsetAfter = 0;
- }
- else
- {
- offsetAfter = calculateOptimalOffset();
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.trace("New offset = " + -offsetAfter); //$NON-NLS-1$
- }
-
- applyOffsetToDestinationIndexes(offsetAfter);
-
- // make sure temporary indexes do not get in the way of the other operations
- tempIndex = Math.min(offsetBefore, offsetAfter) - 1;
- }
- }
-
- /**
- * Calculate the optimal offset wrt the manipulations planned. The optimal offset is the offset which occurs the
- * most in the manipulations (because letting this offset be neutral leads to the least manipulations. Note: the
- * zero offset is also regarded as an offset as any other, because selecting an offset != 0 would also lead to
- * elements with original offset 0 to be moved.
- */
- private int calculateOptimalOffset()
- {
- HashMap<Integer, Integer> occurrences = new HashMap<Integer, Integer>();
- int bestOffset = 0;
- int bestOffsetOccurrence = 0;
-
- for (ManipulationElement element : manipulations)
- {
- int srcIdx = element.sourceIndex;
- int destIdx = element.destinationIndex;
- if (srcIdx != ManipulationConstants.NO_INDEX && destIdx != ManipulationConstants.NO_INDEX)
- {
- int offset = destIdx - srcIdx;
- Integer oldOccurrence = occurrences.get(offset);
- int newOccurrence;
- if (oldOccurrence == null)
- {
- newOccurrence = 1;
- }
- else
- {
- newOccurrence = oldOccurrence + 1;
- }
- occurrences.put(offset, newOccurrence);
-
- // remember maximum along the way
- if (newOccurrence > bestOffsetOccurrence)
- {
- bestOffsetOccurrence = newOccurrence;
- bestOffset = offset;
- }
- }
- }
-
- return bestOffset;
- }
-
- private void applyOffsetToSourceIndexes(int offsetBefore)
- {
- for (ManipulationElement element : manipulations)
- {
- if (element.sourceIndex != ManipulationConstants.NO_INDEX)
- {
- element.sourceIndex += offsetBefore;
- }
- }
- }
-
- private void applyOffsetToDestinationIndexes(int offsetAfter)
- {
- for (ManipulationElement element : manipulations)
- {
- if (element.destinationIndex != ManipulationConstants.NO_INDEX)
- {
- // substract the offset from all indices to make them relative to the new offset
- element.destinationIndex -= offsetAfter;
- }
- }
- }
-
- /**
- * Write calculated changes to the database
- *
- * @param accessor
- */
- private void writeResultToDatabase(IDBStoreAccessor accessor, CDOID id)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement deleteStmt = null;
- PreparedStatement moveStmt = null;
- PreparedStatement setValueStmt = null;
- PreparedStatement insertStmt = null;
-
- int deleteCounter = 0;
- int moveCounter = 0;
- int setValueCounter = 0;
- int insertCounter = 0;
-
- if (TRACER.isEnabled())
- {
- TRACER.trace("Writing to database:"); //$NON-NLS-1$
- }
-
- if (clearFirst)
- {
- if (TRACER.isEnabled())
- {
- TRACER.trace(" - clear list"); //$NON-NLS-1$
- }
-
- clearList(accessor, id);
- }
-
- try
- {
- for (ManipulationElement element : manipulations)
- {
- if (element.is(ManipulationConstants.DELETE))
- {
- /*
- * Step 1: DELETE all elements e which have e.is(REMOVE) by e.sourceIdx
- */
-
- if (deleteStmt == null)
- {
- deleteStmt = statementCache.getPreparedStatement(sqlDeleteItem, ReuseProbability.HIGH);
- idHandler.setCDOID(deleteStmt, 1, id);
- }
-
- deleteStmt.setInt(2, element.sourceIndex);
- deleteStmt.addBatch();
- deleteCounter++;
-
- if (TRACER.isEnabled())
- {
- TRACER.format(" - delete at {0} ", element.sourceIndex); //$NON-NLS-1$
- }
- }
-
- if (element.is(ManipulationConstants.MOVE))
- {
- /*
- * Step 2: MOVE all elements e (by e.sourceIdx) which have e.is(MOVE) to temporary idx (-1, -2, -3, -4, ...)
- * and store temporary idx in e.tempIndex
- */
- if (moveStmt == null)
- {
- moveStmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH);
- idHandler.setCDOID(moveStmt, 2, id);
- }
-
- moveStmt.setInt(3, element.sourceIndex); // from index
- moveStmt.setInt(1, --tempIndex); // to index
- element.tempIndex = tempIndex;
- moveStmt.addBatch();
- moveCounter++;
-
- if (TRACER.isEnabled())
- {
- TRACER.format(" - move {0} -> {1} ", element.sourceIndex, element.tempIndex); //$NON-NLS-1$
- }
- }
- }
-
- /* now perform deletes and moves ... */
- if (deleteCounter > 0)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Performing {0} delete operations", deleteCounter); //$NON-NLS-1$
- }
-
- DBUtil.executeBatch(deleteStmt, deleteCounter);
- }
-
- if (moveCounter > 0)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Performing {0} move operations", moveCounter); //$NON-NLS-1$
- }
-
- DBUtil.executeBatch(moveStmt, moveCounter);
- moveStmt.clearBatch();
- moveCounter = 0;
- }
-
- writeShiftOperations(accessor, id);
-
- for (ManipulationElement element : manipulations)
- {
- if (element.is(ManipulationConstants.MOVE))
- {
- /*
- * Step 4: MOVE all elements e have e.is(MOVE) from e.tempIdx to e.destinationIdx (because we have moved
- * them before, moveStmt is always initialized
- */
- moveStmt.setInt(3, element.tempIndex); // from index
- moveStmt.setInt(1, element.destinationIndex); // to index
- moveStmt.addBatch();
- moveCounter++;
-
- if (TRACER.isEnabled())
- {
- TRACER.format(" - move {0} -> {1} ", element.tempIndex, element.destinationIndex); //$NON-NLS-1$
- }
- }
-
- if (element.is(ManipulationConstants.SET_VALUE))
- {
- /*
- * Step 5: SET all elements which have e.type == SET_VALUE by index == e.destinationIdx
- */
- if (setValueStmt == null)
- {
- setValueStmt = statementCache.getPreparedStatement(sqlUpdateValue, ReuseProbability.HIGH);
- idHandler.setCDOID(setValueStmt, 2, id);
- }
-
- setValueStmt.setInt(3, element.destinationIndex);
- getTypeMapping().setValue(setValueStmt, 1, element.value);
- setValueStmt.addBatch();
- setValueCounter++;
-
- if (TRACER.isEnabled())
- {
- TRACER.format(" - set value at {0} to {1} ", element.destinationIndex, element.value); //$NON-NLS-1$
- }
- }
-
- if (element.is(ManipulationConstants.INSERT))
- {
- /*
- * Step 6: INSERT all elements which have e.type == INSERT.
- */
- if (insertStmt == null)
- {
- insertStmt = statementCache.getPreparedStatement(sqlInsertValue, ReuseProbability.HIGH);
- idHandler.setCDOID(insertStmt, 1, id);
- }
-
- insertStmt.setInt(2, element.destinationIndex);
- getTypeMapping().setValue(insertStmt, 3, element.value);
- insertStmt.addBatch();
- insertCounter++;
-
- if (TRACER.isEnabled())
- {
- TRACER.format(" - insert value at {0} : value {1} ", element.destinationIndex, element.value); //$NON-NLS-1$
- }
- }
- }
-
- if (moveCounter > 0)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Performing {0} move operations", moveCounter); //$NON-NLS-1$
- }
-
- DBUtil.executeBatch(moveStmt, moveCounter);
- }
-
- if (insertCounter > 0)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Performing {0} insert operations", insertCounter); //$NON-NLS-1$
- }
-
- DBUtil.executeBatch(insertStmt, insertCounter);
- }
-
- if (setValueCounter > 0)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Performing {0} set operations", setValueCounter); //$NON-NLS-1$
- }
-
- DBUtil.executeBatch(setValueStmt, setValueCounter);
- }
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- releaseStatement(accessor, deleteStmt, moveStmt, insertStmt, setValueStmt);
- }
- }
-
- /**
- * Perform the shift operations to adjust indexes resulting from remove, insert, and move operations.
- *
- * @see #writeResultToDatabase(IDBStoreAccessor, CDOID)
- * @throws SQLException
- */
- private void writeShiftOperations(IDBStoreAccessor accessor, CDOID id) throws SQLException
- {
- /*
- * Step 3: shift all elements which have to be shifted up or down because of add, remove or move of other elements
- * to their proper position. This has to be done in two phases to avoid collisions, as the index has to be unique
- * and shift up operations have to be executed in top to bottom order.
- */
-
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- int size = manipulations.size();
-
- LinkedList<ShiftOperation> shiftOperations = new LinkedList<ShiftOperation>();
-
- /*
- * If a necessary shift is detected (source and destination indices differ), firstIndex is set to the current
- * index and currentOffset is set to the offset of the shift operation. When a new offset is detected or the range
- * is interrupted, we record the range and start a new one if needed.
- */
- int rangeStartIndex = ManipulationConstants.NO_INDEX;
- int rangeOffset = 0;
- int lastElementIndex = ManipulationConstants.NO_INDEX;
-
- // iterate through the manipulationElements and collect the necessary operations
- for (int i = 0; i < size; i++)
- {
- ManipulationElement element = manipulations.get(i);
-
- /*
- * shift applies only to elements which are not moved, inserted or deleted (i.e. only plain SET_VALUE and NONE
- * are affected)
- */
- if (element.type == ManipulationConstants.NONE || element.type == ManipulationConstants.SET_VALUE)
- {
- int elementOffset = element.destinationIndex - element.sourceIndex;
-
- /*
- * first make sure if we have to close a previous range. This is the case, if the current element's offset
- * differs from the rangeOffset and a range is open.
- */
- if (elementOffset != rangeOffset && rangeStartIndex != ManipulationConstants.NO_INDEX)
- {
- // there is an open range but the rangeOffset differs. We have to close the open range
- shiftOperations.add(new ShiftOperation(rangeStartIndex, lastElementIndex, rangeOffset));
- // and reset the state
- rangeStartIndex = ManipulationConstants.NO_INDEX;
- rangeOffset = 0;
- }
-
- /*
- * at this point, either a range is open, which means that the current element also fits in the range (i.e.
- * the offsets match) or no range is open. In the latter case, we have to open one if the current element's
- * offset is not 0.
- */
- if (elementOffset != 0 && rangeStartIndex == ManipulationConstants.NO_INDEX)
- {
- rangeStartIndex = element.sourceIndex;
- rangeOffset = elementOffset;
- }
- }
- else
- { // shift does not apply to this element because of its type
- if (rangeStartIndex != ManipulationConstants.NO_INDEX)
- {
- // if there is an open range, we have to close and remember it
- shiftOperations.add(new ShiftOperation(rangeStartIndex, lastElementIndex, rangeOffset));
- // and reset the state
- rangeStartIndex = ManipulationConstants.NO_INDEX;
- rangeOffset = 0;
- }
- }
- lastElementIndex = element.sourceIndex;
- }
-
- // after the iteration, we have to make sure that we remember the last open range, if it is there
- if (rangeStartIndex != ManipulationConstants.NO_INDEX)
- {
- shiftOperations.add(new ShiftOperation(rangeStartIndex, lastElementIndex, rangeOffset));
- }
-
- /*
- * now process the operations. Move down operations can be performed directly, move up operations need to be
- * performed later in the reverse direction
- */
- ListIterator<ShiftOperation> operationIt = shiftOperations.listIterator();
-
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement shiftDownStmt = null;
- int operationCounter = 0;
-
- try
- {
-
- while (operationIt.hasNext())
- {
- ShiftOperation operation = operationIt.next();
- if (operation.offset < 0)
- {
- if (shiftDownStmt == null)
- {
- shiftDownStmt = statementCache.getPreparedStatement(sqlShiftDownIndex, ReuseProbability.HIGH);
- idHandler.setCDOID(shiftDownStmt, 2, id);
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format(" - shift down {0} ", operation); //$NON-NLS-1$
- }
-
- shiftDownStmt.setInt(1, operation.offset);
- shiftDownStmt.setInt(3, operation.startIndex);
- shiftDownStmt.setInt(4, operation.endIndex);
- shiftDownStmt.addBatch();
- operationCounter++;
-
- operationIt.remove();
- }
- }
- if (operationCounter > 0)
- {
- DBUtil.executeBatch(shiftDownStmt, operationCounter, false);
- }
- }
- finally
- {
- releaseStatement(accessor, shiftDownStmt);
- }
-
- PreparedStatement shiftUpStmt = null;
- operationCounter = 0;
-
- try
- {
-
- while (operationIt.hasPrevious())
- {
- ShiftOperation operation = operationIt.previous();
- if (shiftUpStmt == null)
- {
- shiftUpStmt = statementCache.getPreparedStatement(sqlShiftUpIndex, ReuseProbability.HIGH);
- idHandler.setCDOID(shiftUpStmt, 2, id);
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format(" - shift up {0} ", operation); //$NON-NLS-1$
- }
-
- shiftUpStmt.setInt(1, operation.offset);
- shiftUpStmt.setInt(3, operation.startIndex);
- shiftUpStmt.setInt(4, operation.endIndex);
- shiftUpStmt.addBatch();
- operationCounter++;
- }
-
- if (operationCounter > 0)
- {
- DBUtil.executeBatch(shiftUpStmt, operationCounter, false);
- }
- }
- finally
- {
- releaseStatement(accessor, shiftUpStmt);
- }
- }
-
- }
-
- /**
- * @author Eike Stepper
- */
- private static interface ManipulationConstants
- {
- public static final int NO_INDEX = Integer.MIN_VALUE;
-
- public static final int DELETE = 1 << 4;
-
- public static final int INSERT = 1 << 3;
-
- public static final int MOVE = 1 << 2;
-
- public static final int SET_VALUE = 1 << 1;
-
- public static final Object NIL = new Object();
-
- public static final int NONE = 0;
- }
-
- /**
- * @author Eike Stepper
- */
- private static final class ManipulationElement implements ManipulationConstants
- {
- public int type;
-
- public int sourceIndex;
-
- public int tempIndex;
-
- public int destinationIndex;
-
- public Object value;
-
- public ManipulationElement(int srcIdx, int dstIdx, Object val, int t)
- {
- sourceIndex = srcIdx;
- tempIndex = NO_INDEX;
- destinationIndex = dstIdx;
- value = val;
- type = t;
- }
-
- /**
- * Create a ManipulationElement which represents an element which already is in the list.
- */
- public static ManipulationElement createOriginalElement(int index)
- {
- return new ManipulationElement(index, index, NIL, NONE);
- }
-
- /**
- * Create a ManipulationElement which represents an element which is inserted in the list.
- */
- public static ManipulationElement createInsertedElement(int index, Object value)
- {
- return new ManipulationElement(NO_INDEX, index, value, ManipulationConstants.INSERT);
- }
-
- public boolean is(int t)
- {
- return (type & t) > 0;
- }
-
- public void addType(int t)
- {
- type |= t;
- }
- }
-
- /**
- * @author Eike Stepper
- */
- private static class ShiftOperation
- {
- final int startIndex;
-
- final int endIndex;
-
- final int offset;
-
- ShiftOperation(int startIndex, int endIndex, int offset)
- {
- this.startIndex = startIndex;
- this.endIndex = endIndex;
- this.offset = offset;
- }
-
- @Override
- public String toString()
- {
- return "range [" + startIndex + ".." + endIndex + "] offset " + offset;
- }
- }
-}
+/* + * Copyright (c) 2004 - 2012 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 + * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444 + * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor; +import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta; +import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache; +import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.server.internal.db.bundle.OM; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.util.om.trace.ContextTracer; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EStructuralFeature; + +import org.eclipse.core.runtime.Assert; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.ListIterator; + +/** + * This is a list-to-table mapping optimized for non-audit-mode. It doesn't care about version and has delta support. + * + * @author Eike Stepper + * @since 2.0 + */ +public class NonAuditListTableMapping extends AbstractListTableMapping implements IListMappingDeltaSupport +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, NonAuditListTableMapping.class); + + private FieldInfo[] keyFields; + + private static final int UNBOUNDED_SHIFT = -1; + + private String sqlClear; + + private String sqlUpdateValue; + + private String sqlUpdateIndex; + + private String sqlInsertValue; + + private String sqlDeleteItem; + + private String sqlShiftDownIndex; + + private String sqlReadCurrentIndexOffset; + + private String sqlShiftUpIndex; + + public NonAuditListTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature) + { + super(mappingStrategy, eClass, feature); + initSQLStrings(); + } + + private void initSQLStrings() + { + // ----------- clear list ------------------------- + StringBuilder builder = new StringBuilder(); + + builder.append("DELETE FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? "); //$NON-NLS-1$ + + sqlClear = builder.toString(); + + builder.append(" AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? "); //$NON-NLS-1$ + + sqlDeleteItem = builder.toString(); + + // ----------- update one item -------------------- + builder = new StringBuilder(); + builder.append("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? "); //$NON-NLS-1$ + sqlUpdateValue = builder.toString(); + + // ----------- insert one item -------------------- + builder = new StringBuilder(); + builder.append("INSERT INTO "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" ("); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append(", "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_VALUE); + builder.append(") VALUES(?, ?, ?) "); //$NON-NLS-1$ + sqlInsertValue = builder.toString(); + + // ----------- update one item index -------------- + builder = new StringBuilder(); + builder.append("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? "); //$NON-NLS-1$ + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("=? "); //$NON-NLS-1$ + sqlUpdateIndex = builder.toString(); + + // ----------- mass update item indexes -------------- + builder = new StringBuilder(); + builder.append("UPDATE "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" SET "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("="); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append("+? WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append(" BETWEEN ? AND ?"); //$NON-NLS-1$ + // getMappingStrategy().getStore().getDBAdapter() + + // needed because of MySQL: + builder.append("/*! ORDER BY "); //$NON-NLS-1$ / + builder.append(CDODBSchema.LIST_IDX); + sqlShiftDownIndex = builder.toString() + " */"; //$NON-NLS-1$ + builder.append(" DESC"); //$NON-NLS-1$ + sqlShiftUpIndex = builder.toString() + " */"; //$NON-NLS-1$ + + // ----------- read current index offset -------------- + builder = new StringBuilder(); + builder.append("SELECT MIN("); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_IDX); + builder.append(") FROM "); //$NON-NLS-1$ + builder.append(getTable()); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_ID); + builder.append("=?"); //$NON-NLS-1$ + sqlReadCurrentIndexOffset = builder.toString(); + } + + @Override + public void addSimpleChunkWhere(IDBStoreAccessor accessor, CDOID cdoid, StringBuilder builder, int index) + { + int offset = getCurrentIndexOffset(accessor, cdoid); + super.addSimpleChunkWhere(accessor, cdoid, builder, index + offset); + } + + @Override + public void addRangedChunkWhere(IDBStoreAccessor accessor, CDOID cdoid, StringBuilder builder, int fromIndex, + int toIndex) + { + int offset = getCurrentIndexOffset(accessor, cdoid); + super.addRangedChunkWhere(accessor, cdoid, builder, fromIndex + offset, toIndex + offset); + } + + @Override + protected FieldInfo[] getKeyFields() + { + if (keyFields == null) + { + DBType dbType = getMappingStrategy().getStore().getIDHandler().getDBType(); + keyFields = new FieldInfo[] { new FieldInfo(CDODBSchema.LIST_REVISION_ID, dbType) }; + } + + return keyFields; + } + + @Override + protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException + { + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID()); + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + clearList(accessor, id); + } + + /** + * Clear a list of a given revision. + * + * @param accessor + * the accessor to use + * @param id + * the id of the revision from which to remove all items + */ + public void clearList(IDBStoreAccessor accessor, CDOID id) + { + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlClear, ReuseProbability.HIGH); + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id); + DBUtil.update(stmt, false); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + @Override + public void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version) + { + clearList(accessor, id); + } + + public int getCurrentIndexOffset(IDBStoreAccessor accessor, CDOID id) + { + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet rset = null; + + try + { + stmt = statementCache.getPreparedStatement(sqlReadCurrentIndexOffset, ReuseProbability.HIGH); + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id); + rset = stmt.executeQuery(); + if (!rset.next()) + { + // list is empty. Return the default offset of 0. + return 0; + } + + // return the minimum index which is equal to the current offset. + return rset.getInt(1); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(rset); + releaseStatement(accessor, stmt); + } + } + + public void processDelta(final IDBStoreAccessor accessor, final CDOID id, int branchId, int oldVersion, + final int newVersion, long created, CDOListFeatureDelta delta) + { + CDOBranchPoint main = accessor.getStore().getRepository().getBranchManager().getMainBranch().getHead(); + + InternalCDORevision originalRevision = (InternalCDORevision)accessor.getStore().getRepository() + .getRevisionManager().getRevision(id, main, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true); + int oldListSize = originalRevision.getList(getFeature()).size(); + + if (TRACER.isEnabled()) + { + TRACER.format("ListTableMapping.processDelta for revision {0} - previous list size: {1}", originalRevision, //$NON-NLS-1$ + oldListSize); + } + + // let the visitor collect the changes + ListDeltaVisitor visitor = new ListDeltaVisitor(oldListSize); + + if (TRACER.isEnabled()) + { + TRACER.trace("Processing deltas..."); //$NON-NLS-1$ + } + + for (CDOFeatureDelta listDelta : delta.getListChanges()) + { + listDelta.accept(visitor); + } + + visitor.postProcess(accessor, id); + + // finally, write results to the database + visitor.writeResultToDatabase(accessor, id); + } + + private void releaseStatement(IDBStoreAccessor accessor, PreparedStatement... stmts) + { + Throwable t = null; + + for (PreparedStatement stmt : stmts) + { + try + { + if (stmt != null) + { + try + { + stmt.clearBatch(); + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + accessor.getStatementCache().releasePreparedStatement(stmt); + } + } + } + catch (Throwable th) + { + if (t == null) + { + // remember first exception + t = th; + } + + // more exceptions go to the log + OM.LOG.error(t); + } + } + + if (t != null) + { + throw new DBException(t); + } + } + + /** + * @author Eike Stepper + */ + private final class ListDeltaVisitor implements CDOFeatureDeltaVisitor + { + private boolean clearFirst; + + private ArrayList<ManipulationElement> manipulations; + + /** + * Start of a range [tempIndex, tempIndex-1, ...] which lies outside of the normal list indexes and which serve as + * temporary space to move items temporarily to get them out of the way of other operations. + */ + private int tempIndex = -1; + + public ListDeltaVisitor(int oldListSize) + { + // reset the clear-flag + clearFirst = false; + manipulations = new ArrayList<ManipulationElement>(oldListSize); + + // create list and initialize with original indexes + for (int i = 0; i < oldListSize; i++) + { + manipulations.add(ManipulationElement.createOriginalElement(i)); + } + } + + public void visit(CDOAddFeatureDelta delta) + { + if (TRACER.isEnabled()) + { + TRACER.format(" - insert at {0} value {1}", delta.getIndex(), delta.getValue()); //$NON-NLS-1$ + } + + // make room for the new item + shiftIndexes(delta.getIndex(), UNBOUNDED_SHIFT, +1); + + // create the item + manipulations.add(ManipulationElement.createInsertedElement(delta.getIndex(), delta.getValue())); + } + + public void visit(CDORemoveFeatureDelta delta) + { + if (TRACER.isEnabled()) + { + TRACER.format(" - remove at {0}", delta.getIndex()); //$NON-NLS-1$ + } + + ManipulationElement e = findElement(delta.getIndex()); + deleteItem(e); + + // fill the gap by shifting all subsequent items down + shiftIndexes(delta.getIndex() + 1, UNBOUNDED_SHIFT, -1); + } + + public void visit(CDOSetFeatureDelta delta) + { + if (TRACER.isEnabled()) + { + TRACER.format(" - set at {0} value {1}", delta.getIndex(), delta.getValue()); //$NON-NLS-1$ + } + + ManipulationElement e = findElement(delta.getIndex()); + // set the new value + e.value = delta.getValue(); + + // if the item is freshly inserted we do not set the SET-mark. + // setting the value of a new item results in inserting with the + // new value at once. + if (!e.is(ManipulationConstants.INSERT)) + { + // else mark the existing item to be set to a new value + e.addType(ManipulationConstants.SET_VALUE); + } + } + + public void visit(CDOUnsetFeatureDelta delta) + { + if (delta.getFeature().isUnsettable()) + { + Assert.isTrue(false); + } + + if (TRACER.isEnabled()) + { + TRACER.format(" - unset list"); //$NON-NLS-1$ + } + + // set the clear-flag + clearFirst = true; + + // and also clear all manipulation items + manipulations.clear(); + } + + public void visit(CDOClearFeatureDelta delta) + { + if (TRACER.isEnabled()) + { + TRACER.format(" - clear list"); //$NON-NLS-1$ + } + + // set the clear-flag + clearFirst = true; + + // and also clear all manipulation items + manipulations.clear(); + } + + public void visit(CDOMoveFeatureDelta delta) + { + int fromIdx = delta.getOldPosition(); + int toIdx = delta.getNewPosition(); + + if (TRACER.isEnabled()) + { + TRACER.format(" - move {0} -> {1}", fromIdx, toIdx); //$NON-NLS-1$ + } + + // ignore the trivial case + if (fromIdx == toIdx) + { + return; + } + + ManipulationElement e = findElement(fromIdx); + + // adjust indexes and shift either up or down + if (fromIdx < toIdx) + { + shiftIndexes(fromIdx + 1, toIdx, -1); + } + else + { // fromIdx > toIdx here + shiftIndexes(toIdx, fromIdx - 1, +1); + } + + // set the new index + e.destinationIndex = toIdx; + + // if it is a new element, no MOVE mark needed, because we insert it + // at the new position + if (!e.is(ManipulationConstants.INSERT)) + { + // else we need to handle the move of an existing item + e.addType(ManipulationConstants.MOVE); + } + } + + public void visit(CDOListFeatureDelta delta) + { + // never called + Assert.isTrue(false); + } + + public void visit(CDOContainerFeatureDelta delta) + { + // never called + Assert.isTrue(false); + } + + /** + * Helper method: shift all (destination) indexes in the interval [from,to] (inclusive at both ends) by offset + * (positive or negative). + */ + private void shiftIndexes(int from, int to, int offset) + { + for (ManipulationElement e : manipulations) + { + if (e.destinationIndex >= from && (to == UNBOUNDED_SHIFT || e.destinationIndex <= to)) + { + e.destinationIndex += offset; + } + } + } + + /** + * Find a manipulation item by destination index). + */ + private ManipulationElement findElement(int index) + { + for (ManipulationElement e : manipulations) + { + if (e.destinationIndex == index) + { + return e; + } + } + + // never reached + Assert.isTrue(false); + return null; + } + + /** + * Delete an element (used in remove and clear) + */ + private void deleteItem(ManipulationElement e) + { + if (e.is(ManipulationConstants.INSERT)) + { + // newly inserted items are simply removed, as + // removing inserted items is equal to no change at all. + manipulations.remove(e); + } + else + { + // mark the existing item as to be deleted. + // (previous MOVE and SET conditions are overridden by setting + // the exclusive DELETE type). + e.type = ManipulationConstants.DELETE; + e.destinationIndex = ManipulationConstants.NO_INDEX; + } + } + + /** + * Called after all deltas are applied an before the results are written to the database. This method post-processes + * the manipulation elements in order to minimize database access. + */ + public void postProcess(IDBStoreAccessor accessor, CDOID id) + { + if (!((HorizontalNonAuditMappingStrategy)getMappingStrategy()).shallForceZeroBasedIndex()) + { + /* + * this is an optimization which reduces the amount of modifications on the database to maintain list indexes. + * For the optimization, we let go of the assumption that indexes are zero-based. Instead, we work with an + * offset at the database level which can change with every change to the list (e.g. if the second element is + * removed from a list with 1000 elements, instead of shifting down indexes 2 to 1000 by 1, we shift up index 0 + * by 1 and have now a list with indexes starting at 1 instead of 0. This optimization is applied by modifying + * the list of ManipulationElements, which can be seen as the database modification plan. + */ + + // first, get the current offset + int offsetBefore = getCurrentIndexOffset(accessor, id); + if (TRACER.isEnabled()) + { + TRACER.trace("Offset optimization."); //$NON-NLS-1$ + TRACER.trace("Current offset = " + offsetBefore); //$NON-NLS-1$ + } + + applyOffsetToSourceIndexes(offsetBefore); + + int offsetAfter; + + if ((long)Math.abs(offsetBefore) + (long)manipulations.size() > Integer.MAX_VALUE) + { + // security belt for really huge collections or for collections that have been manipulated lots of times + // -> do not optimize after this border is crossed. Instead, reset offset for the whole list to a zero-based + // index. + offsetAfter = 0; + } + else + { + offsetAfter = calculateOptimalOffset(); + } + + if (TRACER.isEnabled()) + { + TRACER.trace("New offset = " + -offsetAfter); //$NON-NLS-1$ + } + + applyOffsetToDestinationIndexes(offsetAfter); + + // make sure temporary indexes do not get in the way of the other operations + tempIndex = Math.min(offsetBefore, offsetAfter) - 1; + } + } + + /** + * Calculate the optimal offset wrt the manipulations planned. The optimal offset is the offset which occurs the + * most in the manipulations (because letting this offset be neutral leads to the least manipulations. Note: the + * zero offset is also regarded as an offset as any other, because selecting an offset != 0 would also lead to + * elements with original offset 0 to be moved. + */ + private int calculateOptimalOffset() + { + HashMap<Integer, Integer> occurrences = new HashMap<Integer, Integer>(); + int bestOffset = 0; + int bestOffsetOccurrence = 0; + + for (ManipulationElement element : manipulations) + { + int srcIdx = element.sourceIndex; + int destIdx = element.destinationIndex; + if (srcIdx != ManipulationConstants.NO_INDEX && destIdx != ManipulationConstants.NO_INDEX) + { + int offset = destIdx - srcIdx; + Integer oldOccurrence = occurrences.get(offset); + int newOccurrence; + if (oldOccurrence == null) + { + newOccurrence = 1; + } + else + { + newOccurrence = oldOccurrence + 1; + } + occurrences.put(offset, newOccurrence); + + // remember maximum along the way + if (newOccurrence > bestOffsetOccurrence) + { + bestOffsetOccurrence = newOccurrence; + bestOffset = offset; + } + } + } + + return bestOffset; + } + + private void applyOffsetToSourceIndexes(int offsetBefore) + { + for (ManipulationElement element : manipulations) + { + if (element.sourceIndex != ManipulationConstants.NO_INDEX) + { + element.sourceIndex += offsetBefore; + } + } + } + + private void applyOffsetToDestinationIndexes(int offsetAfter) + { + for (ManipulationElement element : manipulations) + { + if (element.destinationIndex != ManipulationConstants.NO_INDEX) + { + // substract the offset from all indices to make them relative to the new offset + element.destinationIndex -= offsetAfter; + } + } + } + + /** + * Write calculated changes to the database + * + * @param accessor + */ + private void writeResultToDatabase(IDBStoreAccessor accessor, CDOID id) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement deleteStmt = null; + PreparedStatement moveStmt = null; + PreparedStatement setValueStmt = null; + PreparedStatement insertStmt = null; + + int deleteCounter = 0; + int moveCounter = 0; + int setValueCounter = 0; + int insertCounter = 0; + + if (TRACER.isEnabled()) + { + TRACER.trace("Writing to database:"); //$NON-NLS-1$ + } + + if (clearFirst) + { + if (TRACER.isEnabled()) + { + TRACER.trace(" - clear list"); //$NON-NLS-1$ + } + + clearList(accessor, id); + } + + try + { + for (ManipulationElement element : manipulations) + { + if (element.is(ManipulationConstants.DELETE)) + { + /* + * Step 1: DELETE all elements e which have e.is(REMOVE) by e.sourceIdx + */ + + if (deleteStmt == null) + { + deleteStmt = statementCache.getPreparedStatement(sqlDeleteItem, ReuseProbability.HIGH); + idHandler.setCDOID(deleteStmt, 1, id); + } + + deleteStmt.setInt(2, element.sourceIndex); + deleteStmt.addBatch(); + deleteCounter++; + + if (TRACER.isEnabled()) + { + TRACER.format(" - delete at {0} ", element.sourceIndex); //$NON-NLS-1$ + } + } + + if (element.is(ManipulationConstants.MOVE)) + { + /* + * Step 2: MOVE all elements e (by e.sourceIdx) which have e.is(MOVE) to temporary idx (-1, -2, -3, -4, ...) + * and store temporary idx in e.tempIndex + */ + if (moveStmt == null) + { + moveStmt = statementCache.getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH); + idHandler.setCDOID(moveStmt, 2, id); + } + + moveStmt.setInt(3, element.sourceIndex); // from index + moveStmt.setInt(1, --tempIndex); // to index + element.tempIndex = tempIndex; + moveStmt.addBatch(); + moveCounter++; + + if (TRACER.isEnabled()) + { + TRACER.format(" - move {0} -> {1} ", element.sourceIndex, element.tempIndex); //$NON-NLS-1$ + } + } + } + + /* now perform deletes and moves ... */ + if (deleteCounter > 0) + { + if (TRACER.isEnabled()) + { + TRACER.format("Performing {0} delete operations", deleteCounter); //$NON-NLS-1$ + } + + DBUtil.executeBatch(deleteStmt, deleteCounter); + } + + if (moveCounter > 0) + { + if (TRACER.isEnabled()) + { + TRACER.format("Performing {0} move operations", moveCounter); //$NON-NLS-1$ + } + + DBUtil.executeBatch(moveStmt, moveCounter); + moveStmt.clearBatch(); + moveCounter = 0; + } + + writeShiftOperations(accessor, id); + + for (ManipulationElement element : manipulations) + { + if (element.is(ManipulationConstants.MOVE)) + { + /* + * Step 4: MOVE all elements e have e.is(MOVE) from e.tempIdx to e.destinationIdx (because we have moved + * them before, moveStmt is always initialized + */ + moveStmt.setInt(3, element.tempIndex); // from index + moveStmt.setInt(1, element.destinationIndex); // to index + moveStmt.addBatch(); + moveCounter++; + + if (TRACER.isEnabled()) + { + TRACER.format(" - move {0} -> {1} ", element.tempIndex, element.destinationIndex); //$NON-NLS-1$ + } + } + + if (element.is(ManipulationConstants.SET_VALUE)) + { + /* + * Step 5: SET all elements which have e.type == SET_VALUE by index == e.destinationIdx + */ + if (setValueStmt == null) + { + setValueStmt = statementCache.getPreparedStatement(sqlUpdateValue, ReuseProbability.HIGH); + idHandler.setCDOID(setValueStmt, 2, id); + } + + setValueStmt.setInt(3, element.destinationIndex); + getTypeMapping().setValue(setValueStmt, 1, element.value); + setValueStmt.addBatch(); + setValueCounter++; + + if (TRACER.isEnabled()) + { + TRACER.format(" - set value at {0} to {1} ", element.destinationIndex, element.value); //$NON-NLS-1$ + } + } + + if (element.is(ManipulationConstants.INSERT)) + { + /* + * Step 6: INSERT all elements which have e.type == INSERT. + */ + if (insertStmt == null) + { + insertStmt = statementCache.getPreparedStatement(sqlInsertValue, ReuseProbability.HIGH); + idHandler.setCDOID(insertStmt, 1, id); + } + + insertStmt.setInt(2, element.destinationIndex); + getTypeMapping().setValue(insertStmt, 3, element.value); + insertStmt.addBatch(); + insertCounter++; + + if (TRACER.isEnabled()) + { + TRACER.format(" - insert value at {0} : value {1} ", element.destinationIndex, element.value); //$NON-NLS-1$ + } + } + } + + if (moveCounter > 0) + { + if (TRACER.isEnabled()) + { + TRACER.format("Performing {0} move operations", moveCounter); //$NON-NLS-1$ + } + + DBUtil.executeBatch(moveStmt, moveCounter); + } + + if (insertCounter > 0) + { + if (TRACER.isEnabled()) + { + TRACER.format("Performing {0} insert operations", insertCounter); //$NON-NLS-1$ + } + + DBUtil.executeBatch(insertStmt, insertCounter); + } + + if (setValueCounter > 0) + { + if (TRACER.isEnabled()) + { + TRACER.format("Performing {0} set operations", setValueCounter); //$NON-NLS-1$ + } + + DBUtil.executeBatch(setValueStmt, setValueCounter); + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + releaseStatement(accessor, deleteStmt, moveStmt, insertStmt, setValueStmt); + } + } + + /** + * Perform the shift operations to adjust indexes resulting from remove, insert, and move operations. + * + * @see #writeResultToDatabase(IDBStoreAccessor, CDOID) + * @throws SQLException + */ + private void writeShiftOperations(IDBStoreAccessor accessor, CDOID id) throws SQLException + { + /* + * Step 3: shift all elements which have to be shifted up or down because of add, remove or move of other elements + * to their proper position. This has to be done in two phases to avoid collisions, as the index has to be unique + * and shift up operations have to be executed in top to bottom order. + */ + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + int size = manipulations.size(); + + LinkedList<ShiftOperation> shiftOperations = new LinkedList<ShiftOperation>(); + + /* + * If a necessary shift is detected (source and destination indices differ), firstIndex is set to the current + * index and currentOffset is set to the offset of the shift operation. When a new offset is detected or the range + * is interrupted, we record the range and start a new one if needed. + */ + int rangeStartIndex = ManipulationConstants.NO_INDEX; + int rangeOffset = 0; + int lastElementIndex = ManipulationConstants.NO_INDEX; + + // iterate through the manipulationElements and collect the necessary operations + for (int i = 0; i < size; i++) + { + ManipulationElement element = manipulations.get(i); + + /* + * shift applies only to elements which are not moved, inserted or deleted (i.e. only plain SET_VALUE and NONE + * are affected) + */ + if (element.type == ManipulationConstants.NONE || element.type == ManipulationConstants.SET_VALUE) + { + int elementOffset = element.destinationIndex - element.sourceIndex; + + /* + * first make sure if we have to close a previous range. This is the case, if the current element's offset + * differs from the rangeOffset and a range is open. + */ + if (elementOffset != rangeOffset && rangeStartIndex != ManipulationConstants.NO_INDEX) + { + // there is an open range but the rangeOffset differs. We have to close the open range + shiftOperations.add(new ShiftOperation(rangeStartIndex, lastElementIndex, rangeOffset)); + // and reset the state + rangeStartIndex = ManipulationConstants.NO_INDEX; + rangeOffset = 0; + } + + /* + * at this point, either a range is open, which means that the current element also fits in the range (i.e. + * the offsets match) or no range is open. In the latter case, we have to open one if the current element's + * offset is not 0. + */ + if (elementOffset != 0 && rangeStartIndex == ManipulationConstants.NO_INDEX) + { + rangeStartIndex = element.sourceIndex; + rangeOffset = elementOffset; + } + } + else + { // shift does not apply to this element because of its type + if (rangeStartIndex != ManipulationConstants.NO_INDEX) + { + // if there is an open range, we have to close and remember it + shiftOperations.add(new ShiftOperation(rangeStartIndex, lastElementIndex, rangeOffset)); + // and reset the state + rangeStartIndex = ManipulationConstants.NO_INDEX; + rangeOffset = 0; + } + } + lastElementIndex = element.sourceIndex; + } + + // after the iteration, we have to make sure that we remember the last open range, if it is there + if (rangeStartIndex != ManipulationConstants.NO_INDEX) + { + shiftOperations.add(new ShiftOperation(rangeStartIndex, lastElementIndex, rangeOffset)); + } + + /* + * now process the operations. Move down operations can be performed directly, move up operations need to be + * performed later in the reverse direction + */ + ListIterator<ShiftOperation> operationIt = shiftOperations.listIterator(); + + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement shiftDownStmt = null; + int operationCounter = 0; + + try + { + + while (operationIt.hasNext()) + { + ShiftOperation operation = operationIt.next(); + if (operation.offset < 0) + { + if (shiftDownStmt == null) + { + shiftDownStmt = statementCache.getPreparedStatement(sqlShiftDownIndex, ReuseProbability.HIGH); + idHandler.setCDOID(shiftDownStmt, 2, id); + } + + if (TRACER.isEnabled()) + { + TRACER.format(" - shift down {0} ", operation); //$NON-NLS-1$ + } + + shiftDownStmt.setInt(1, operation.offset); + shiftDownStmt.setInt(3, operation.startIndex); + shiftDownStmt.setInt(4, operation.endIndex); + shiftDownStmt.addBatch(); + operationCounter++; + + operationIt.remove(); + } + } + if (operationCounter > 0) + { + DBUtil.executeBatch(shiftDownStmt, operationCounter, false); + } + } + finally + { + releaseStatement(accessor, shiftDownStmt); + } + + PreparedStatement shiftUpStmt = null; + operationCounter = 0; + + try + { + + while (operationIt.hasPrevious()) + { + ShiftOperation operation = operationIt.previous(); + if (shiftUpStmt == null) + { + shiftUpStmt = statementCache.getPreparedStatement(sqlShiftUpIndex, ReuseProbability.HIGH); + idHandler.setCDOID(shiftUpStmt, 2, id); + } + + if (TRACER.isEnabled()) + { + TRACER.format(" - shift up {0} ", operation); //$NON-NLS-1$ + } + + shiftUpStmt.setInt(1, operation.offset); + shiftUpStmt.setInt(3, operation.startIndex); + shiftUpStmt.setInt(4, operation.endIndex); + shiftUpStmt.addBatch(); + operationCounter++; + } + + if (operationCounter > 0) + { + DBUtil.executeBatch(shiftUpStmt, operationCounter, false); + } + } + finally + { + releaseStatement(accessor, shiftUpStmt); + } + } + + } + + /** + * @author Eike Stepper + */ + private static interface ManipulationConstants + { + public static final int NO_INDEX = Integer.MIN_VALUE; + + public static final int DELETE = 1 << 4; + + public static final int INSERT = 1 << 3; + + public static final int MOVE = 1 << 2; + + public static final int SET_VALUE = 1 << 1; + + public static final Object NIL = new Object(); + + public static final int NONE = 0; + } + + /** + * @author Eike Stepper + */ + private static final class ManipulationElement implements ManipulationConstants + { + public int type; + + public int sourceIndex; + + public int tempIndex; + + public int destinationIndex; + + public Object value; + + public ManipulationElement(int srcIdx, int dstIdx, Object val, int t) + { + sourceIndex = srcIdx; + tempIndex = NO_INDEX; + destinationIndex = dstIdx; + value = val; + type = t; + } + + /** + * Create a ManipulationElement which represents an element which already is in the list. + */ + public static ManipulationElement createOriginalElement(int index) + { + return new ManipulationElement(index, index, NIL, NONE); + } + + /** + * Create a ManipulationElement which represents an element which is inserted in the list. + */ + public static ManipulationElement createInsertedElement(int index, Object value) + { + return new ManipulationElement(NO_INDEX, index, value, ManipulationConstants.INSERT); + } + + public boolean is(int t) + { + return (type & t) > 0; + } + + public void addType(int t) + { + type |= t; + } + } + + /** + * @author Eike Stepper + */ + private static class ShiftOperation + { + final int startIndex; + + final int endIndex; + + final int offset; + + ShiftOperation(int startIndex, int endIndex, int offset) + { + this.startIndex = startIndex; + this.endIndex = endIndex; + this.offset = offset; + } + + @Override + public String toString() + { + return "range [" + startIndex + ".." + endIndex + "] offset " + offset; + } + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.db4o/src/org/eclipse/emf/cdo/server/internal/db4o/DB4OStoreAccessor.java b/plugins/org.eclipse.emf.cdo.server.db4o/src/org/eclipse/emf/cdo/server/internal/db4o/DB4OStoreAccessor.java index 1d30927442..d8840a72f9 100644 --- a/plugins/org.eclipse.emf.cdo.server.db4o/src/org/eclipse/emf/cdo/server/internal/db4o/DB4OStoreAccessor.java +++ b/plugins/org.eclipse.emf.cdo.server.db4o/src/org/eclipse/emf/cdo/server/internal/db4o/DB4OStoreAccessor.java @@ -1,876 +1,875 @@ -/*
- * Copyright (c) 2004 - 2012 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:
- * Victor Roldan Betancort - initial API and implementation
- */
-package org.eclipse.emf.cdo.server.internal.db4o;
-
-import org.eclipse.emf.cdo.common.branch.CDOBranch;
-import org.eclipse.emf.cdo.common.branch.CDOBranchHandler;
-import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
-import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
-import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler;
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.id.CDOIDUtil;
-import org.eclipse.emf.cdo.common.lob.CDOLobHandler;
-import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea.Handler;
-import org.eclipse.emf.cdo.common.model.EMFUtil;
-import org.eclipse.emf.cdo.common.protocol.CDODataInput;
-import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.common.revision.CDORevisionCacheAdder;
-import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
-import org.eclipse.emf.cdo.common.util.CDOCommonUtil;
-import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
-import org.eclipse.emf.cdo.eresource.EresourcePackage;
-import org.eclipse.emf.cdo.server.IQueryHandler;
-import org.eclipse.emf.cdo.server.ISession;
-import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking;
-import org.eclipse.emf.cdo.server.IStoreAccessor.Raw;
-import org.eclipse.emf.cdo.server.IStoreChunkReader;
-import org.eclipse.emf.cdo.server.ITransaction;
-import org.eclipse.emf.cdo.server.db4o.IDB4OIdentifiableObject;
-import org.eclipse.emf.cdo.server.internal.db4o.bundle.OM;
-import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager;
-import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment;
-import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager;
-import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
-import org.eclipse.emf.cdo.spi.server.InternalRepository;
-import org.eclipse.emf.cdo.spi.server.LongIDStoreAccessor;
-
-import org.eclipse.net4j.util.HexUtil;
-import org.eclipse.net4j.util.ObjectUtil;
-import org.eclipse.net4j.util.StringUtil;
-import org.eclipse.net4j.util.collection.Pair;
-import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
-import org.eclipse.net4j.util.io.IOUtil;
-import org.eclipse.net4j.util.om.monitor.Monitor;
-import org.eclipse.net4j.util.om.monitor.OMMonitor;
-import org.eclipse.net4j.util.om.monitor.OMMonitor.Async;
-
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EPackage;
-import org.eclipse.emf.ecore.EReference;
-import org.eclipse.emf.ecore.EStructuralFeature;
-
-import com.db4o.ObjectContainer;
-import com.db4o.ObjectSet;
-import com.db4o.query.Predicate;
-import com.db4o.query.Query;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.CharArrayReader;
-import java.io.CharArrayWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * @author Victor Roldan Betancort
- */
-public class DB4OStoreAccessor extends LongIDStoreAccessor implements Raw, DurableLocking
-{
- private ObjectContainer objectContainer;
-
- public DB4OStoreAccessor(DB4OStore store, ISession session)
- {
- super(store, session);
- }
-
- public DB4OStoreAccessor(DB4OStore store, ITransaction transaction)
- {
- super(store, transaction);
- }
-
- @Override
- public DB4OStore getStore()
- {
- return (DB4OStore)super.getStore();
- }
-
- public ObjectContainer getObjectContainer()
- {
- return objectContainer;
- }
-
- public EPackage[] loadPackageUnit(InternalCDOPackageUnit packageUnit)
- {
- DB4OPackageUnit db4OPackageUnit = getPrimitivePackageUnitMap().get(
- packageUnit.getTopLevelPackageInfo().getPackageURI());
- return EMFUtil.getAllPackages(db4OPackageUnit.getEPackage());
- }
-
- public void writePackageUnits(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor)
- {
- monitor.begin(packageUnits.length);
-
- try
- {
- DB4OStore store = getStore();
- ObjectContainer objectContainer = getObjectContainer();
-
- for (InternalCDOPackageUnit packageUnit : packageUnits)
- {
- DB4OPackageUnit primitivePackageUnit = DB4OPackageUnit.getPrimitivePackageUnit(store, packageUnit);
- objectContainer.store(primitivePackageUnit);
- monitor.worked(1);
- }
- }
- catch (Exception ex)
- {
- OM.LOG.error(ex);
- }
- finally
- {
- monitor.done();
- }
- }
-
- public Collection<InternalCDOPackageUnit> readPackageUnits()
- {
- Map<InternalCDOPackageUnit, DB4OPackageUnit> map = getPackageUnitMap();
- return map.keySet();
- }
-
- private Map<InternalCDOPackageUnit, DB4OPackageUnit> getPackageUnitMap()
- {
- Map<InternalCDOPackageUnit, DB4OPackageUnit> map = new HashMap<InternalCDOPackageUnit, DB4OPackageUnit>();
- Collection<DB4OPackageUnit> primitivePackageUnits = getObjectContainer().query(DB4OPackageUnit.class);
-
- for (DB4OPackageUnit primitivePackageUnit : primitivePackageUnits)
- {
- InternalCDOPackageUnit packageUnit = DB4OPackageUnit.getPackageUnit(primitivePackageUnit);
- map.put(packageUnit, primitivePackageUnit);
- }
- return map;
- }
-
- private Map<String, DB4OPackageUnit> getPrimitivePackageUnitMap()
- {
- Map<String, DB4OPackageUnit> map = new HashMap<String, DB4OPackageUnit>();
- Collection<DB4OPackageUnit> primitivePackageUnits = getObjectContainer().query(DB4OPackageUnit.class);
-
- for (DB4OPackageUnit primitivePackageUnit : primitivePackageUnits)
- {
- InternalCDOPackageUnit packageUnit = DB4OPackageUnit.getPackageUnit(primitivePackageUnit);
- map.put(packageUnit.getTopLevelPackageInfo().getPackageURI(), primitivePackageUnit);
- }
- return map;
- }
-
- public InternalCDORevision readRevision(CDOID id, CDOBranchPoint branchPoint, int listChunk,
- CDORevisionCacheAdder cache)
- {
- DB4ORevision lastRevision = DB4OStore.getRevision(getObjectContainer(), id);
- if (lastRevision == null)
- {
- // Revision does not exist. Return null to signal inexistent Revision
- return null;
- }
-
- return DB4ORevision.getCDORevision(getStore(), lastRevision);
- }
-
- public void queryResources(QueryResourcesContext context)
- {
- final long folderID = CDOIDUtil.getLong(context.getFolderID());
- final String name = context.getName();
- final boolean exactMatch = context.exactMatch();
- // RootResource may not be initialized, as there may be queries during IStore activation
- CDOID rootResourceID = getStore().getRepository().getRootResourceID();
- final Object rootResourceLongID = rootResourceID != null ? DB4ORevision.getDB4OID(getStore().getRepository()
- .getRootResourceID()) : null;
-
- ObjectSet<DB4ORevision> revisionObjectSet = getObjectContainer().query(new Predicate<DB4ORevision>()
- {
- private static final long serialVersionUID = 1L;
-
- @Override
- public boolean match(DB4ORevision revision)
- {
- if (!revision.isResourceNode())
- {
- return false;
- }
-
- if (ObjectUtil.equals(rootResourceLongID, revision.getID()))
- {
- // is Root resource
- return false;
- }
-
- if (ObjectUtil.equals(revision.getContainerID(), folderID))
- {
- String candidateName = (String)revision.getValues().get(EresourcePackage.CDO_RESOURCE__NAME);
- if (exactMatch)
- {
- if (ObjectUtil.equals(candidateName, name))
- {
- return true;
- }
- }
- else
- {
- // provided name is prefix of the resource name
- if (candidateName != null && candidateName.startsWith(name))
- {
- return true;
- }
- }
- }
-
- return false;
- }
- });
-
- for (DB4ORevision revision : revisionObjectSet)
- {
- CDOID id = DB4ORevision.getCDOID(revision.getID());
- if (!context.addResource(id))
- {
- // No more results allowed
- break;
- }
- }
-
- }
-
- public IStoreChunkReader createChunkReader(InternalCDORevision revision, EStructuralFeature feature)
- {
- // TODO: implement DB4OStoreAccessor.createChunkReader(revision, feature)
- throw new UnsupportedOperationException();
- }
-
- public IQueryHandler getQueryHandler(CDOQueryInfo info)
- {
- return null;
- }
-
- @Override
- protected void doActivate() throws Exception
- {
- objectContainer = getStore().openClient();
- }
-
- @Override
- protected void doDeactivate() throws Exception
- {
- if (objectContainer != null)
- {
- objectContainer.close();
- objectContainer = null;
- }
- }
-
- @Override
- protected void doPassivate() throws Exception
- {
- if (objectContainer != null)
- {
- objectContainer.rollback();
- }
- }
-
- @Override
- protected void doUnpassivate() throws Exception
- {
- }
-
- @Override
- protected void doRollback(CommitContext commitContext)
- {
- getObjectContainer().rollback();
- }
-
- public InternalCDORevision readRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int listChunk,
- CDORevisionCacheAdder cache)
- {
- DB4ORevision revision = DB4OStore.getRevision(getObjectContainer(), id);
- if (revision == null || revision.getVersion() != branchVersion.getVersion())
- {
- return null;
- }
-
- return DB4ORevision.getCDORevision(getStore(), revision);
- }
-
- public void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime,
- CDORevisionHandler handler)
- {
- Query query = getObjectContainer().query();
- query.constrain(DB4ORevision.class);
- if (eClass != null)
- {
- query.descend(DB4ORevision.ATTRIBUTE_PACKAGE_NS_URI).constrain(eClass.getEPackage().getNsURI());
- query.descend(DB4ORevision.ATTRIBUTE_CLASS_NAME).constrain(eClass.getName());
- }
-
- ObjectSet<?> revisions = query.execute();
- if (revisions.isEmpty())
- {
- return;
- }
-
- for (Object revision : revisions.toArray())
- {
- CDORevision cdoRevision = DB4ORevision.getCDORevision(getStore(), (DB4ORevision)revision);
- handler.handleRevision(cdoRevision);
- }
- }
-
- public Set<CDOID> readChangeSet(OMMonitor monitor, CDOChangeSetSegment... segments)
- {
- // TODO: implement DB4OStoreAccessor.readChangeSet(segments)
- throw new UnsupportedOperationException();
- }
-
- public void queryXRefs(final QueryXRefsContext context)
- {
- final int branchID = context.getBranch().getID();
-
- for (final CDOID target : context.getTargetObjects().keySet())
- {
- for (final EClass eClass : context.getSourceCandidates().keySet())
- {
- final String eClassName = eClass.getName();
- final String nsURI = eClass.getEPackage().getNsURI();
- final List<EReference> eReferences = context.getSourceCandidates().get(eClass);
- getObjectContainer().query(new Predicate<DB4ORevision>()
- {
- private static final long serialVersionUID = 1L;
-
- private boolean moreResults = true;
-
- @Override
- public boolean match(DB4ORevision revision)
- {
- if (!moreResults)
- {
- return false;
- }
-
- if (!revision.getClassName().equals(eClassName))
- {
- return false;
- }
-
- if (!revision.getPackageURI().equals(nsURI))
- {
- return false;
- }
-
- if (!(revision.getBranchID() == branchID))
- {
- return false;
- }
-
- CDOID id = DB4ORevision.getCDOID(revision.getID());
- for (EReference eReference : eReferences)
- {
- Object obj = revision.getValues().get(eReference.getFeatureID());
- if (obj instanceof List)
- {
- List<?> list = (List<?>)obj;
- int index = 0;
- for (Object element : list)
- {
- CDOID ref = DB4ORevision.getCDOID(element);
- if (ObjectUtil.equals(ref, target))
- {
- moreResults = context.addXRef(target, id, eReference, index);
- }
-
- ++index;
- }
- }
- else
- {
- CDOID ref = DB4ORevision.getCDOID(obj);
- if (ObjectUtil.equals(ref, target))
- {
- moreResults = context.addXRef(target, id, eReference, 0);
- }
- }
- }
-
- return false;
- }
- });
- }
- }
- }
-
- public void queryLobs(List<byte[]> ids)
- {
- for (Iterator<byte[]> it = ids.iterator(); it.hasNext();)
- {
- byte[] id = it.next();
- String key = HexUtil.bytesToHex(id);
- if (DB4OStore.getIdentifiableObject(getObjectContainer(), key) == null)
- {
- it.remove();
- }
- }
- }
-
- public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException
- {
- for (DB4OBlob db4oBlob : DB4OStore.getElementsOfType(getObjectContainer(), DB4OBlob.class))
- {
- byte[] id = HexUtil.hexToBytes(db4oBlob.getId());
- byte[] blob = db4oBlob.getValue();
- ByteArrayInputStream in = new ByteArrayInputStream(blob);
- OutputStream out = handler.handleBlob(id, blob.length);
- if (out != null)
- {
- try
- {
- IOUtil.copyBinary(in, out, blob.length);
- }
- finally
- {
- IOUtil.close(out);
- }
- }
- }
- for (DB4OClob db4oClob : DB4OStore.getElementsOfType(getObjectContainer(), DB4OClob.class))
- {
- byte[] id = HexUtil.hexToBytes(db4oClob.getId());
- char[] clob = db4oClob.getValue();
- CharArrayReader in = new CharArrayReader(clob);
- Writer out = handler.handleClob(id, clob.length);
- if (out != null)
- {
- try
- {
- IOUtil.copyCharacter(in, out, clob.length);
- }
- finally
- {
- IOUtil.close(out);
- }
- }
- }
- }
-
- public void loadLob(byte[] id, OutputStream out) throws IOException
- {
- String key = HexUtil.bytesToHex(id);
- IDB4OIdentifiableObject identifiableObject = DB4OStore.getIdentifiableObject(getObjectContainer(), key);
- if (identifiableObject == null)
- {
- throw new IOException("Lob not found: " + key);
- }
-
- if (identifiableObject instanceof DB4OBlob)
- {
- DB4OBlob blob = (DB4OBlob)identifiableObject;
- byte[] byteArray = blob.getValue();
- ByteArrayInputStream in = new ByteArrayInputStream(byteArray);
- IOUtil.copyBinary(in, out, byteArray.length);
- }
- else
- {
- DB4OClob clob = (DB4OClob)identifiableObject;
- char[] charArray = clob.getValue();
- CharArrayReader in = new CharArrayReader(charArray);
- IOUtil.copyCharacter(in, new OutputStreamWriter(out), charArray.length);
- }
- }
-
- @Override
- protected void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException
- {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- IOUtil.copyBinary(inputStream, out, size);
- writeObject(new DB4OBlob(HexUtil.bytesToHex(id), out.toByteArray()), new Monitor());
- }
-
- @Override
- protected void writeClob(byte[] id, long size, Reader reader) throws IOException
- {
- CharArrayWriter out = new CharArrayWriter();
- IOUtil.copyCharacter(reader, out, size);
- writeObject(new DB4OClob(HexUtil.bytesToHex(id), out.toCharArray()), new Monitor());
- }
-
- public Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo)
- {
- // TODO: implement DB4OStoreAccessor.createBranch(branchID, branchInfo)
- throw new UnsupportedOperationException();
- }
-
- public BranchInfo loadBranch(int branchID)
- {
- // TODO: implement DB4OStoreAccessor.loadBranch(branchID)
- throw new UnsupportedOperationException();
- }
-
- public SubBranchInfo[] loadSubBranches(int branchID)
- {
- // TODO: implement DB4OStoreAccessor.loadSubBranches(branchID)
- throw new UnsupportedOperationException();
- }
-
- public int loadBranches(int startID, int endID, CDOBranchHandler branchHandler)
- {
- // TODO: implement DB4OStoreAccessor.loadBranches(startID, endID, branchHandler)
- throw new UnsupportedOperationException();
- }
-
- public void loadCommitInfos(final CDOBranch branch, final long startTime, final long endTime,
- CDOCommitInfoHandler handler)
- {
- ObjectSet<DB4OCommitInfo> resultSet = getObjectContainer().query(new Predicate<DB4OCommitInfo>()
- {
- private static final long serialVersionUID = 1L;
-
- @Override
- public boolean match(DB4OCommitInfo info)
- {
- if (startTime != CDOBranchPoint.UNSPECIFIED_DATE && info.getTimeStamp() < startTime)
- {
- return false;
- }
-
- if (endTime != CDOBranchPoint.UNSPECIFIED_DATE && info.getTimeStamp() > endTime)
- {
- return false;
- }
-
- if (branch != null && !(info.getBranchID() == branch.getID()))
- {
- return false;
- }
-
- return true;
- }
- });
-
- InternalRepository repository = getStore().getRepository();
- InternalCDOCommitInfoManager commitInfoManager = repository.getCommitInfoManager();
- InternalCDOBranchManager branchManager = repository.getBranchManager();
-
- // Although not specified in the API, the test suite
- // suggests CommitInfos should be returned ordered by timeStamp
- // TODO Specify this in the API!
-
- List<DB4OCommitInfo> infos = new ArrayList<DB4OCommitInfo>(resultSet);
- Collections.sort(infos, new Comparator<DB4OCommitInfo>()
- {
- public int compare(DB4OCommitInfo arg0, DB4OCommitInfo arg1)
- {
- return CDOCommonUtil.compareTimeStamps(arg0.getTimeStamp(), arg1.getTimeStamp());
- }
- });
-
- for (DB4OCommitInfo info : infos)
- {
- info.handle(branchManager, commitInfoManager, handler);
- }
- }
-
- @Override
- protected void doCommit(OMMonitor monitor)
- {
- monitor.begin();
- Async async = monitor.forkAsync();
-
- try
- {
- long start = System.currentTimeMillis();
- getObjectContainer().commit();
- long end = System.currentTimeMillis();
- OM.LOG.debug("Commit took -> " + (end - start) + " milliseconds");
- }
- catch (Exception e)
- {
- OM.LOG.error(e);
- }
- finally
- {
- async.stop();
- monitor.done();
- }
- }
-
- @Override
- protected void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID,
- String comment, OMMonitor monitor)
- {
- DB4OCommitInfo commitInfo = new DB4OCommitInfo(branch.getID(), timeStamp, previousTimeStamp, userID, comment);
- writeObject(commitInfo, monitor);
- }
-
- @Override
- protected void writeRevisions(InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor)
- {
- monitor.begin(revisions.length);
-
- try
- {
- long start = System.currentTimeMillis();
- for (InternalCDORevision revision : revisions)
- {
- writeRevision(revision, monitor.fork());
- }
- long end = System.currentTimeMillis();
- OM.LOG.debug("Storage of " + revisions.length + " revisions took: " + (end - start) + " milliseconds");
- }
- finally
- {
- monitor.done();
- }
- }
-
- protected void writeRevision(InternalCDORevision revision, OMMonitor monitor)
- {
- Async async = null;
- monitor.begin(10);
-
- try
- {
- try
- {
- async = monitor.forkAsync();
- if (revision.isResourceNode())
- {
- checkDuplicateResources(revision);
- }
- }
- finally
- {
- if (async != null)
- {
- async.stop();
- }
- }
-
- // TODO removal of previous version implies query, this should be optimized
-
- long start = System.currentTimeMillis();
- CDOID id = revision.getID();
- DB4OStore.removeRevision(getObjectContainer(), id);
- DB4ORevision primitiveRevision = DB4ORevision.getDB4ORevision(revision);
- writeObject(primitiveRevision, monitor);
- long end = System.currentTimeMillis();
- OM.LOG.debug("Writing revision " + id + " took: " + (end - start) + " milliseconds");
- }
- finally
- {
- monitor.done();
- }
- }
-
- protected void writeObject(Object object, OMMonitor monitor)
- {
- monitor.begin();
- Async async = monitor.forkAsync();
-
- try
- {
- getObjectContainer().store(object);
- }
- catch (Throwable t)
- {
- OM.LOG.error(t);
- }
- finally
- {
- async.stop();
- monitor.done();
- }
- }
-
- @Override
- protected void writeRevisionDeltas(InternalCDORevisionDelta[] revisionDeltas, CDOBranch branch, long created,
- OMMonitor monitor)
- {
- for (InternalCDORevisionDelta revisionDelta : revisionDeltas)
- {
- writeRevisionDelta(revisionDelta, branch, created);
- }
- }
-
- protected void writeRevisionDelta(InternalCDORevisionDelta revisionDelta, CDOBranch branch, long created)
- {
- CDOID id = revisionDelta.getID();
- InternalCDORevision revision = DB4ORevision.getCDORevision(getStore(),
- DB4OStore.getRevision(getObjectContainer(), id));
- InternalCDORevision newRevision = revision.copy();
- newRevision.adjustForCommit(branch, created);
-
- revisionDelta.apply(newRevision);
- writeRevision(newRevision, new Monitor());
- }
-
- @Override
- protected void detachObjects(CDOID[] detachedObjects, CDOBranch branch, long timeStamp, OMMonitor monitor)
- {
- monitor.begin(detachedObjects.length);
-
- try
- {
- for (CDOID id : detachedObjects)
- {
- DB4OStore.removeRevision(getObjectContainer(), id);
- monitor.worked();
- }
- }
- finally
- {
- monitor.done();
- }
- }
-
- protected void checkDuplicateResources(CDORevision revision) throws IllegalStateException
- {
- final long folderID = CDOIDUtil.getLong((CDOID)revision.data().getContainerID());
- final long revisionID = CDOIDUtil.getLong(revision.getID());
- final String name = (String)revision.data().get(EresourcePackage.eINSTANCE.getCDOResourceNode_Name(), 0);
-
- ObjectSet<DB4ORevision> resultSet = getObjectContainer().query(new Predicate<DB4ORevision>()
- {
- private static final long serialVersionUID = 1L;
-
- @Override
- public boolean match(DB4ORevision revision)
- {
- if (revision.isResourceNode() && ObjectUtil.equals(revision.getContainerID(), folderID))
- {
- String candidateName = (String)revision.getValues().get(EresourcePackage.CDO_RESOURCE__NAME);
- if (StringUtil.compare(name, candidateName) == 0)
- {
- if (!ObjectUtil.equals(revision.getID(), revisionID))
- {
- return true;
- }
- }
- }
-
- return false;
- }
- });
-
- if (!resultSet.isEmpty())
- {
- throw new IllegalStateException("Duplicate resource or folder: " + name + " in folder " + folderID); //$NON-NLS-1$ //$NON-NLS-2$
- }
- }
-
- public void rawExport(CDODataOutput out, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime)
- throws IOException
- {
- // TODO: Implement DB4OStoreAccessor.rawExport(CDODataOutput, int, int, long, long)
- throw new UnsupportedOperationException();
- }
-
- public void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime,
- OMMonitor monitor) throws IOException
- {
- // TODO: Implement DB4OStoreAccessor.rawImport(CDODataInput, int, int, long, long, OMMonitor)
- throw new UnsupportedOperationException();
- }
-
- public void rawStore(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor)
- {
- writePackageUnits(packageUnits, monitor);
- }
-
- public void rawStore(InternalCDORevision revision, OMMonitor monitor)
- {
- writeRevision(revision, monitor);
- }
-
- public void rawStore(byte[] id, long size, InputStream inputStream) throws IOException
- {
- // TODO: Implement DB4OStoreAccessor.rawExport(CDODataOutput, int, int, long, long)
- throw new UnsupportedOperationException();
- }
-
- public void rawStore(byte[] id, long size, Reader reader) throws IOException
- {
- // TODO: Implement DB4OStoreAccessor.rawStore(byte[], long, Reader)
- throw new UnsupportedOperationException();
- }
-
- public void rawStore(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment,
- OMMonitor monitor)
- {
- writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, monitor);
- }
-
- @Deprecated
- public void rawDelete(CDOID id, int version, CDOBranch branch, EClass eClass, OMMonitor monitor)
- {
- throw new UnsupportedOperationException();
- }
-
- public void rawCommit(double commitWork, OMMonitor monitor)
- {
- doCommit(monitor);
- }
-
- public LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly,
- Map<CDOID, LockGrade> locks)
- {
- DB4ODurableLockingManager manager = getStore().getDurableLockingManager();
- return manager.createLockArea(this, userID, branchPoint, readOnly, locks);
- }
-
- public LockArea getLockArea(String durableLockingID) throws LockAreaNotFoundException
- {
- DB4ODurableLockingManager manager = getStore().getDurableLockingManager();
- return manager.getLockArea(this, durableLockingID);
- }
-
- public void getLockAreas(String userIDPrefix, Handler handler)
- {
- DB4ODurableLockingManager manager = getStore().getDurableLockingManager();
- manager.getLockAreas(this, userIDPrefix, handler);
- }
-
- public void deleteLockArea(String durableLockingID)
- {
- DB4ODurableLockingManager manager = getStore().getDurableLockingManager();
- manager.deleteLockArea(this, durableLockingID);
- }
-
- public void lock(String durableLockingID, LockType type, Collection<? extends Object> objectsToLock)
- {
- DB4ODurableLockingManager manager = getStore().getDurableLockingManager();
- manager.lock(this, durableLockingID, type, objectsToLock);
- }
-
- public void unlock(String durableLockingID, LockType type, Collection<? extends Object> objectsToUnlock)
- {
- DB4ODurableLockingManager manager = getStore().getDurableLockingManager();
- manager.unlock(this, durableLockingID, type, objectsToUnlock);
- }
-
- public void unlock(String durableLockingID)
- {
- DB4ODurableLockingManager manager = getStore().getDurableLockingManager();
- manager.unlock(this, durableLockingID);
- }
-}
+/* + * Copyright (c) 2004 - 2012 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: + * Victor Roldan Betancort - initial API and implementation + */ +package org.eclipse.emf.cdo.server.internal.db4o; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchHandler; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.lob.CDOLobHandler; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea.Handler; +import org.eclipse.emf.cdo.common.model.EMFUtil; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionCacheAdder; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.util.CDOCommonUtil; +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +import org.eclipse.emf.cdo.eresource.EresourcePackage; +import org.eclipse.emf.cdo.server.IQueryHandler; +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking; +import org.eclipse.emf.cdo.server.IStoreAccessor.Raw; +import org.eclipse.emf.cdo.server.IStoreChunkReader; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.server.db4o.IDB4OIdentifiableObject; +import org.eclipse.emf.cdo.server.internal.db4o.bundle.OM; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; +import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.LongIDStoreAccessor; + +import org.eclipse.net4j.util.HexUtil; +import org.eclipse.net4j.util.ObjectUtil; +import org.eclipse.net4j.util.StringUtil; +import org.eclipse.net4j.util.collection.Pair; +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.io.IOUtil; +import org.eclipse.net4j.util.om.monitor.Monitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; + +import com.db4o.ObjectContainer; +import com.db4o.ObjectSet; +import com.db4o.query.Predicate; +import com.db4o.query.Query; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.CharArrayReader; +import java.io.CharArrayWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Victor Roldan Betancort + */ +public class DB4OStoreAccessor extends LongIDStoreAccessor implements Raw, DurableLocking +{ + private ObjectContainer objectContainer; + + public DB4OStoreAccessor(DB4OStore store, ISession session) + { + super(store, session); + } + + public DB4OStoreAccessor(DB4OStore store, ITransaction transaction) + { + super(store, transaction); + } + + @Override + public DB4OStore getStore() + { + return (DB4OStore)super.getStore(); + } + + public ObjectContainer getObjectContainer() + { + return objectContainer; + } + + public EPackage[] loadPackageUnit(InternalCDOPackageUnit packageUnit) + { + DB4OPackageUnit db4OPackageUnit = getPrimitivePackageUnitMap().get( + packageUnit.getTopLevelPackageInfo().getPackageURI()); + return EMFUtil.getAllPackages(db4OPackageUnit.getEPackage()); + } + + public void writePackageUnits(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor) + { + monitor.begin(packageUnits.length); + + try + { + DB4OStore store = getStore(); + ObjectContainer objectContainer = getObjectContainer(); + + for (InternalCDOPackageUnit packageUnit : packageUnits) + { + DB4OPackageUnit primitivePackageUnit = DB4OPackageUnit.getPrimitivePackageUnit(store, packageUnit); + objectContainer.store(primitivePackageUnit); + monitor.worked(1); + } + } + catch (Exception ex) + { + OM.LOG.error(ex); + } + finally + { + monitor.done(); + } + } + + public Collection<InternalCDOPackageUnit> readPackageUnits() + { + Map<InternalCDOPackageUnit, DB4OPackageUnit> map = getPackageUnitMap(); + return map.keySet(); + } + + private Map<InternalCDOPackageUnit, DB4OPackageUnit> getPackageUnitMap() + { + Map<InternalCDOPackageUnit, DB4OPackageUnit> map = new HashMap<InternalCDOPackageUnit, DB4OPackageUnit>(); + Collection<DB4OPackageUnit> primitivePackageUnits = getObjectContainer().query(DB4OPackageUnit.class); + + for (DB4OPackageUnit primitivePackageUnit : primitivePackageUnits) + { + InternalCDOPackageUnit packageUnit = DB4OPackageUnit.getPackageUnit(primitivePackageUnit); + map.put(packageUnit, primitivePackageUnit); + } + return map; + } + + private Map<String, DB4OPackageUnit> getPrimitivePackageUnitMap() + { + Map<String, DB4OPackageUnit> map = new HashMap<String, DB4OPackageUnit>(); + Collection<DB4OPackageUnit> primitivePackageUnits = getObjectContainer().query(DB4OPackageUnit.class); + + for (DB4OPackageUnit primitivePackageUnit : primitivePackageUnits) + { + InternalCDOPackageUnit packageUnit = DB4OPackageUnit.getPackageUnit(primitivePackageUnit); + map.put(packageUnit.getTopLevelPackageInfo().getPackageURI(), primitivePackageUnit); + } + return map; + } + + public InternalCDORevision readRevision(CDOID id, CDOBranchPoint branchPoint, int listChunk, + CDORevisionCacheAdder cache) + { + DB4ORevision lastRevision = DB4OStore.getRevision(getObjectContainer(), id); + if (lastRevision == null) + { + // Revision does not exist. Return null to signal inexistent Revision + return null; + } + + return DB4ORevision.getCDORevision(getStore(), lastRevision); + } + + public void queryResources(QueryResourcesContext context) + { + final long folderID = CDOIDUtil.getLong(context.getFolderID()); + final String name = context.getName(); + final boolean exactMatch = context.exactMatch(); + // RootResource may not be initialized, as there may be queries during IStore activation + CDOID rootResourceID = getStore().getRepository().getRootResourceID(); + final Object rootResourceLongID = rootResourceID != null ? DB4ORevision.getDB4OID(getStore().getRepository() + .getRootResourceID()) : null; + + ObjectSet<DB4ORevision> revisionObjectSet = getObjectContainer().query(new Predicate<DB4ORevision>() + { + private static final long serialVersionUID = 1L; + + @Override + public boolean match(DB4ORevision revision) + { + if (!revision.isResourceNode()) + { + return false; + } + + if (ObjectUtil.equals(rootResourceLongID, revision.getID())) + { + // is Root resource + return false; + } + + if (ObjectUtil.equals(revision.getContainerID(), folderID)) + { + String candidateName = (String)revision.getValues().get(EresourcePackage.CDO_RESOURCE__NAME); + if (exactMatch) + { + if (ObjectUtil.equals(candidateName, name)) + { + return true; + } + } + else + { + // provided name is prefix of the resource name + if (candidateName != null && candidateName.startsWith(name)) + { + return true; + } + } + } + + return false; + } + }); + + for (DB4ORevision revision : revisionObjectSet) + { + CDOID id = DB4ORevision.getCDOID(revision.getID()); + if (!context.addResource(id)) + { + // No more results allowed + break; + } + } + + } + + public IStoreChunkReader createChunkReader(InternalCDORevision revision, EStructuralFeature feature) + { + // TODO: implement DB4OStoreAccessor.createChunkReader(revision, feature) + throw new UnsupportedOperationException(); + } + + public IQueryHandler getQueryHandler(CDOQueryInfo info) + { + return null; + } + + @Override + protected void doActivate() throws Exception + { + objectContainer = getStore().openClient(); + } + + @Override + protected void doDeactivate() throws Exception + { + if (objectContainer != null) + { + objectContainer.close(); + objectContainer = null; + } + } + + @Override + protected void doPassivate() throws Exception + { + if (objectContainer != null) + { + objectContainer.rollback(); + } + } + + @Override + protected void doUnpassivate() throws Exception + { + } + + @Override + protected void doRollback(CommitContext commitContext) + { + getObjectContainer().rollback(); + } + + public InternalCDORevision readRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int listChunk, + CDORevisionCacheAdder cache) + { + DB4ORevision revision = DB4OStore.getRevision(getObjectContainer(), id); + if (revision == null || revision.getVersion() != branchVersion.getVersion()) + { + return null; + } + + return DB4ORevision.getCDORevision(getStore(), revision); + } + + public void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime, + CDORevisionHandler handler) + { + Query query = getObjectContainer().query(); + query.constrain(DB4ORevision.class); + if (eClass != null) + { + query.descend(DB4ORevision.ATTRIBUTE_PACKAGE_NS_URI).constrain(eClass.getEPackage().getNsURI()); + query.descend(DB4ORevision.ATTRIBUTE_CLASS_NAME).constrain(eClass.getName()); + } + + ObjectSet<?> revisions = query.execute(); + if (revisions.isEmpty()) + { + return; + } + + for (Object revision : revisions.toArray()) + { + CDORevision cdoRevision = DB4ORevision.getCDORevision(getStore(), (DB4ORevision)revision); + handler.handleRevision(cdoRevision); + } + } + + public Set<CDOID> readChangeSet(OMMonitor monitor, CDOChangeSetSegment... segments) + { + // TODO: implement DB4OStoreAccessor.readChangeSet(segments) + throw new UnsupportedOperationException(); + } + + public void queryXRefs(final QueryXRefsContext context) + { + final int branchID = context.getBranch().getID(); + + for (final CDOID target : context.getTargetObjects().keySet()) + { + for (final EClass eClass : context.getSourceCandidates().keySet()) + { + final String eClassName = eClass.getName(); + final String nsURI = eClass.getEPackage().getNsURI(); + final List<EReference> eReferences = context.getSourceCandidates().get(eClass); + getObjectContainer().query(new Predicate<DB4ORevision>() + { + private static final long serialVersionUID = 1L; + + private boolean moreResults = true; + + @Override + public boolean match(DB4ORevision revision) + { + if (!moreResults) + { + return false; + } + + if (!revision.getClassName().equals(eClassName)) + { + return false; + } + + if (!revision.getPackageURI().equals(nsURI)) + { + return false; + } + + if (!(revision.getBranchID() == branchID)) + { + return false; + } + + CDOID id = DB4ORevision.getCDOID(revision.getID()); + for (EReference eReference : eReferences) + { + Object obj = revision.getValues().get(eReference.getFeatureID()); + if (obj instanceof List) + { + List<?> list = (List<?>)obj; + int index = 0; + for (Object element : list) + { + CDOID ref = DB4ORevision.getCDOID(element); + if (ObjectUtil.equals(ref, target)) + { + moreResults = context.addXRef(target, id, eReference, index); + } + + ++index; + } + } + else + { + CDOID ref = DB4ORevision.getCDOID(obj); + if (ObjectUtil.equals(ref, target)) + { + moreResults = context.addXRef(target, id, eReference, 0); + } + } + } + + return false; + } + }); + } + } + } + + public void queryLobs(List<byte[]> ids) + { + for (Iterator<byte[]> it = ids.iterator(); it.hasNext();) + { + byte[] id = it.next(); + String key = HexUtil.bytesToHex(id); + if (DB4OStore.getIdentifiableObject(getObjectContainer(), key) == null) + { + it.remove(); + } + } + } + + public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException + { + for (DB4OBlob db4oBlob : DB4OStore.getElementsOfType(getObjectContainer(), DB4OBlob.class)) + { + byte[] id = HexUtil.hexToBytes(db4oBlob.getId()); + byte[] blob = db4oBlob.getValue(); + ByteArrayInputStream in = new ByteArrayInputStream(blob); + OutputStream out = handler.handleBlob(id, blob.length); + if (out != null) + { + try + { + IOUtil.copyBinary(in, out, blob.length); + } + finally + { + IOUtil.close(out); + } + } + } + for (DB4OClob db4oClob : DB4OStore.getElementsOfType(getObjectContainer(), DB4OClob.class)) + { + byte[] id = HexUtil.hexToBytes(db4oClob.getId()); + char[] clob = db4oClob.getValue(); + CharArrayReader in = new CharArrayReader(clob); + Writer out = handler.handleClob(id, clob.length); + if (out != null) + { + try + { + IOUtil.copyCharacter(in, out, clob.length); + } + finally + { + IOUtil.close(out); + } + } + } + } + + public void loadLob(byte[] id, OutputStream out) throws IOException + { + String key = HexUtil.bytesToHex(id); + IDB4OIdentifiableObject identifiableObject = DB4OStore.getIdentifiableObject(getObjectContainer(), key); + if (identifiableObject == null) + { + throw new IOException("Lob not found: " + key); + } + + if (identifiableObject instanceof DB4OBlob) + { + DB4OBlob blob = (DB4OBlob)identifiableObject; + byte[] byteArray = blob.getValue(); + ByteArrayInputStream in = new ByteArrayInputStream(byteArray); + IOUtil.copyBinary(in, out, byteArray.length); + } + else + { + DB4OClob clob = (DB4OClob)identifiableObject; + char[] charArray = clob.getValue(); + CharArrayReader in = new CharArrayReader(charArray); + IOUtil.copyCharacter(in, new OutputStreamWriter(out), charArray.length); + } + } + + @Override + protected void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + IOUtil.copyBinary(inputStream, out, size); + writeObject(new DB4OBlob(HexUtil.bytesToHex(id), out.toByteArray()), new Monitor()); + } + + @Override + protected void writeClob(byte[] id, long size, Reader reader) throws IOException + { + CharArrayWriter out = new CharArrayWriter(); + IOUtil.copyCharacter(reader, out, size); + writeObject(new DB4OClob(HexUtil.bytesToHex(id), out.toCharArray()), new Monitor()); + } + + public Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo) + { + // TODO: implement DB4OStoreAccessor.createBranch(branchID, branchInfo) + throw new UnsupportedOperationException(); + } + + public BranchInfo loadBranch(int branchID) + { + // TODO: implement DB4OStoreAccessor.loadBranch(branchID) + throw new UnsupportedOperationException(); + } + + public SubBranchInfo[] loadSubBranches(int branchID) + { + // TODO: implement DB4OStoreAccessor.loadSubBranches(branchID) + throw new UnsupportedOperationException(); + } + + public int loadBranches(int startID, int endID, CDOBranchHandler branchHandler) + { + // TODO: implement DB4OStoreAccessor.loadBranches(startID, endID, branchHandler) + throw new UnsupportedOperationException(); + } + + public void loadCommitInfos(final CDOBranch branch, final long startTime, final long endTime, + CDOCommitInfoHandler handler) + { + ObjectSet<DB4OCommitInfo> resultSet = getObjectContainer().query(new Predicate<DB4OCommitInfo>() + { + private static final long serialVersionUID = 1L; + + @Override + public boolean match(DB4OCommitInfo info) + { + if (startTime != CDOBranchPoint.UNSPECIFIED_DATE && info.getTimeStamp() < startTime) + { + return false; + } + + if (endTime != CDOBranchPoint.UNSPECIFIED_DATE && info.getTimeStamp() > endTime) + { + return false; + } + + if (branch != null && !(info.getBranchID() == branch.getID())) + { + return false; + } + + return true; + } + }); + + InternalRepository repository = getStore().getRepository(); + InternalCDOCommitInfoManager commitInfoManager = repository.getCommitInfoManager(); + InternalCDOBranchManager branchManager = repository.getBranchManager(); + + // Although not specified in the API, the test suite + // suggests CommitInfos should be returned ordered by timeStamp + // TODO Specify this in the API! + + List<DB4OCommitInfo> infos = new ArrayList<DB4OCommitInfo>(resultSet); + Collections.sort(infos, new Comparator<DB4OCommitInfo>() + { + public int compare(DB4OCommitInfo arg0, DB4OCommitInfo arg1) + { + return CDOCommonUtil.compareTimeStamps(arg0.getTimeStamp(), arg1.getTimeStamp()); + } + }); + + for (DB4OCommitInfo info : infos) + { + info.handle(branchManager, commitInfoManager, handler); + } + } + + @Override + protected void doCommit(OMMonitor monitor) + { + monitor.begin(); + Async async = monitor.forkAsync(); + + try + { + long start = System.currentTimeMillis(); + getObjectContainer().commit(); + long end = System.currentTimeMillis(); + OM.LOG.debug("Commit took -> " + (end - start) + " milliseconds"); + } + catch (Exception e) + { + OM.LOG.error(e); + } + finally + { + async.stop(); + monitor.done(); + } + } + + @Override + protected void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, + String comment, OMMonitor monitor) + { + DB4OCommitInfo commitInfo = new DB4OCommitInfo(branch.getID(), timeStamp, previousTimeStamp, userID, comment); + writeObject(commitInfo, monitor); + } + + @Override + protected void writeRevisions(InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor) + { + monitor.begin(revisions.length); + + try + { + long start = System.currentTimeMillis(); + for (InternalCDORevision revision : revisions) + { + writeRevision(revision, monitor.fork()); + } + long end = System.currentTimeMillis(); + OM.LOG.debug("Storage of " + revisions.length + " revisions took: " + (end - start) + " milliseconds"); + } + finally + { + monitor.done(); + } + } + + protected void writeRevision(InternalCDORevision revision, OMMonitor monitor) + { + Async async = null; + monitor.begin(10); + + try + { + try + { + async = monitor.forkAsync(); + if (revision.isResourceNode()) + { + checkDuplicateResources(revision); + } + } + finally + { + if (async != null) + { + async.stop(); + } + } + + // TODO removal of previous version implies query, this should be optimized + + long start = System.currentTimeMillis(); + CDOID id = revision.getID(); + DB4OStore.removeRevision(getObjectContainer(), id); + DB4ORevision primitiveRevision = DB4ORevision.getDB4ORevision(revision); + writeObject(primitiveRevision, monitor); + long end = System.currentTimeMillis(); + OM.LOG.debug("Writing revision " + id + " took: " + (end - start) + " milliseconds"); + } + finally + { + monitor.done(); + } + } + + protected void writeObject(Object object, OMMonitor monitor) + { + monitor.begin(); + Async async = monitor.forkAsync(); + + try + { + getObjectContainer().store(object); + } + catch (Throwable t) + { + OM.LOG.error(t); + } + finally + { + async.stop(); + monitor.done(); + } + } + + @Override + protected void writeRevisionDeltas(InternalCDORevisionDelta[] revisionDeltas, CDOBranch branch, long created, + OMMonitor monitor) + { + for (InternalCDORevisionDelta revisionDelta : revisionDeltas) + { + writeRevisionDelta(revisionDelta, branch, created); + } + } + + protected void writeRevisionDelta(InternalCDORevisionDelta revisionDelta, CDOBranch branch, long created) + { + CDOID id = revisionDelta.getID(); + InternalCDORevision revision = DB4ORevision.getCDORevision(getStore(), + DB4OStore.getRevision(getObjectContainer(), id)); + InternalCDORevision newRevision = revision.copy(); + newRevision.adjustForCommit(branch, created); + + revisionDelta.apply(newRevision); + writeRevision(newRevision, new Monitor()); + } + + @Override + protected void detachObjects(CDOID[] detachedObjects, CDOBranch branch, long timeStamp, OMMonitor monitor) + { + monitor.begin(detachedObjects.length); + + try + { + for (CDOID id : detachedObjects) + { + DB4OStore.removeRevision(getObjectContainer(), id); + monitor.worked(); + } + } + finally + { + monitor.done(); + } + } + + protected void checkDuplicateResources(CDORevision revision) throws IllegalStateException + { + final long folderID = CDOIDUtil.getLong((CDOID)revision.data().getContainerID()); + final long revisionID = CDOIDUtil.getLong(revision.getID()); + final String name = (String)revision.data().get(EresourcePackage.eINSTANCE.getCDOResourceNode_Name(), 0); + + ObjectSet<DB4ORevision> resultSet = getObjectContainer().query(new Predicate<DB4ORevision>() + { + private static final long serialVersionUID = 1L; + + @Override + public boolean match(DB4ORevision revision) + { + if (revision.isResourceNode() && ObjectUtil.equals(revision.getContainerID(), folderID)) + { + String candidateName = (String)revision.getValues().get(EresourcePackage.CDO_RESOURCE__NAME); + if (StringUtil.compare(name, candidateName) == 0) + { + if (!ObjectUtil.equals(revision.getID(), revisionID)) + { + return true; + } + } + } + + return false; + } + }); + + if (!resultSet.isEmpty()) + { + throw new IllegalStateException("Duplicate resource or folder: " + name + " in folder " + folderID); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + public void rawExport(CDODataOutput out, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime) + throws IOException + { + // TODO: Implement DB4OStoreAccessor.rawExport(CDODataOutput, int, int, long, long) + throw new UnsupportedOperationException(); + } + + public void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime, + OMMonitor monitor) throws IOException + { + // TODO: Implement DB4OStoreAccessor.rawImport(CDODataInput, int, int, long, long, OMMonitor) + throw new UnsupportedOperationException(); + } + + public void rawStore(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor) + { + writePackageUnits(packageUnits, monitor); + } + + public void rawStore(InternalCDORevision revision, OMMonitor monitor) + { + writeRevision(revision, monitor); + } + + public void rawStore(byte[] id, long size, InputStream inputStream) throws IOException + { + // TODO: Implement DB4OStoreAccessor.rawExport(CDODataOutput, int, int, long, long) + throw new UnsupportedOperationException(); + } + + public void rawStore(byte[] id, long size, Reader reader) throws IOException + { + // TODO: Implement DB4OStoreAccessor.rawStore(byte[], long, Reader) + throw new UnsupportedOperationException(); + } + + public void rawStore(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, + OMMonitor monitor) + { + writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, monitor); + } + + public void rawDelete(CDOID id, int version, CDOBranch branch, EClass eClass, OMMonitor monitor) + { + throw new UnsupportedOperationException(); + } + + public void rawCommit(double commitWork, OMMonitor monitor) + { + doCommit(monitor); + } + + public LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, + Map<CDOID, LockGrade> locks) + { + DB4ODurableLockingManager manager = getStore().getDurableLockingManager(); + return manager.createLockArea(this, userID, branchPoint, readOnly, locks); + } + + public LockArea getLockArea(String durableLockingID) throws LockAreaNotFoundException + { + DB4ODurableLockingManager manager = getStore().getDurableLockingManager(); + return manager.getLockArea(this, durableLockingID); + } + + public void getLockAreas(String userIDPrefix, Handler handler) + { + DB4ODurableLockingManager manager = getStore().getDurableLockingManager(); + manager.getLockAreas(this, userIDPrefix, handler); + } + + public void deleteLockArea(String durableLockingID) + { + DB4ODurableLockingManager manager = getStore().getDurableLockingManager(); + manager.deleteLockArea(this, durableLockingID); + } + + public void lock(String durableLockingID, LockType type, Collection<? extends Object> objectsToLock) + { + DB4ODurableLockingManager manager = getStore().getDurableLockingManager(); + manager.lock(this, durableLockingID, type, objectsToLock); + } + + public void unlock(String durableLockingID, LockType type, Collection<? extends Object> objectsToUnlock) + { + DB4ODurableLockingManager manager = getStore().getDurableLockingManager(); + manager.unlock(this, durableLockingID, type, objectsToUnlock); + } + + public void unlock(String durableLockingID) + { + DB4ODurableLockingManager manager = getStore().getDurableLockingManager(); + manager.unlock(this, durableLockingID); + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.hibernate/src/org/eclipse/emf/cdo/server/internal/hibernate/HibernateStoreAccessor.java b/plugins/org.eclipse.emf.cdo.server.hibernate/src/org/eclipse/emf/cdo/server/internal/hibernate/HibernateStoreAccessor.java index 4739a5c7d9..1fb56f7019 100644 --- a/plugins/org.eclipse.emf.cdo.server.hibernate/src/org/eclipse/emf/cdo/server/internal/hibernate/HibernateStoreAccessor.java +++ b/plugins/org.eclipse.emf.cdo.server.hibernate/src/org/eclipse/emf/cdo/server/internal/hibernate/HibernateStoreAccessor.java @@ -1074,7 +1074,6 @@ public class HibernateStoreAccessor extends StoreAccessor implements IHibernateS commit(monitor); } - @Deprecated public void rawDelete(CDOID id, int version, CDOBranch branch, EClass eClass, OMMonitor monitor) { throw new UnsupportedOperationException(); diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java index f171552888..8650fbd03b 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java @@ -1,502 +1,499 @@ -/*
- * Copyright (c) 2004 - 2012 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:
- * Simon McDuff - initial API and implementation
- * Eike Stepper - maintenance
- * Simon McDuff - bug 213402
- */
-package org.eclipse.emf.cdo.internal.server.mem;
-
-import org.eclipse.emf.cdo.common.branch.CDOBranch;
-import org.eclipse.emf.cdo.common.branch.CDOBranchHandler;
-import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
-import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
-import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler;
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.lob.CDOLobHandler;
-import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea.Handler;
-import org.eclipse.emf.cdo.common.protocol.CDODataInput;
-import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
-import org.eclipse.emf.cdo.common.revision.CDORevisionCacheAdder;
-import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
-import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
-import org.eclipse.emf.cdo.server.IQueryContext;
-import org.eclipse.emf.cdo.server.IQueryHandler;
-import org.eclipse.emf.cdo.server.ISession;
-import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking2;
-import org.eclipse.emf.cdo.server.IStoreAccessor.Raw;
-import org.eclipse.emf.cdo.server.ITransaction;
-import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment;
-import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
-import org.eclipse.emf.cdo.spi.server.InternalCommitContext;
-import org.eclipse.emf.cdo.spi.server.LongIDStoreAccessor;
-
-import org.eclipse.net4j.util.WrappedException;
-import org.eclipse.net4j.util.collection.Pair;
-import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
-import org.eclipse.net4j.util.om.monitor.OMMonitor;
-
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EPackage;
-import org.eclipse.emf.ecore.EStructuralFeature;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.ConcurrentModificationException;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * @author Simon McDuff
- */
-public class MEMStoreAccessor extends LongIDStoreAccessor implements Raw, DurableLocking2
-{
- private final IQueryHandler testQueryHandler = new IQueryHandler()
- {
- public void executeQuery(CDOQueryInfo info, IQueryContext queryContext)
- {
- List<Object> filters = new ArrayList<Object>();
- Object context = info.getParameters().get("context"); //$NON-NLS-1$
- Long sleep = (Long)info.getParameters().get("sleep"); //$NON-NLS-1$
- if (context != null)
- {
- if (context instanceof EClass)
- {
- final EClass eClass = (EClass)context;
- filters.add(new Object()
- {
- @Override
- public int hashCode()
- {
- return eClass.hashCode();
- }
-
- @Override
- public boolean equals(Object obj)
- {
- InternalCDORevision revision = (InternalCDORevision)obj;
- return revision.getEClass().equals(eClass);
- }
- });
- }
- }
-
- for (InternalCDORevision revision : getStore().getCurrentRevisions())
- {
- if (sleep != null)
- {
- try
- {
- Thread.sleep(sleep);
- }
- catch (InterruptedException ex)
- {
- throw WrappedException.wrap(ex);
- }
- }
-
- boolean valid = true;
-
- for (Object filter : filters)
- {
- if (!filter.equals(revision))
- {
- valid = false;
- break;
- }
- }
-
- if (valid)
- {
- if (!queryContext.addResult(revision))
- {
- // No more results allowed
- break;
- }
- }
- }
- }
- };
-
- private List<InternalCDORevision> newRevisions = new ArrayList<InternalCDORevision>();
-
- public MEMStoreAccessor(MEMStore store, ISession session)
- {
- super(store, session);
- }
-
- /**
- * @since 2.0
- */
- public MEMStoreAccessor(MEMStore store, ITransaction transaction)
- {
- super(store, transaction);
- }
-
- @Override
- public MEMStore getStore()
- {
- return (MEMStore)super.getStore();
- }
-
- /**
- * @since 2.0
- */
- public MEMStoreChunkReader createChunkReader(InternalCDORevision revision, EStructuralFeature feature)
- {
- return new MEMStoreChunkReader(this, revision, feature);
- }
-
- public Collection<InternalCDOPackageUnit> readPackageUnits()
- {
- return Collections.emptySet();
- }
-
- public EPackage[] loadPackageUnit(InternalCDOPackageUnit packageUnit)
- {
- throw new UnsupportedOperationException();
- }
-
- public Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo)
- {
- return getStore().createBranch(branchID, branchInfo);
- }
-
- public BranchInfo loadBranch(int branchID)
- {
- return getStore().loadBranch(branchID);
- }
-
- public SubBranchInfo[] loadSubBranches(int branchID)
- {
- return getStore().loadSubBranches(branchID);
- }
-
- public int loadBranches(int startID, int endID, CDOBranchHandler branchHandler)
- {
- return getStore().loadBranches(startID, endID, branchHandler);
- }
-
- public void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler)
- {
- getStore().loadCommitInfos(branch, startTime, endTime, handler);
- }
-
- public Set<CDOID> readChangeSet(OMMonitor monitor, CDOChangeSetSegment... segments)
- {
- return getStore().readChangeSet(segments);
- }
-
- public InternalCDORevision readRevision(CDOID id, CDOBranchPoint branchPoint, int listChunk,
- CDORevisionCacheAdder cache)
- {
- return getStore().getRevision(id, branchPoint);
- }
-
- public InternalCDORevision readRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int listChunk,
- CDORevisionCacheAdder cache)
- {
- return getStore().getRevisionByVersion(id, branchVersion);
- }
-
- public void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime,
- CDORevisionHandler handler)
- {
- getStore().handleRevisions(eClass, branch, timeStamp, exactTime, handler);
- }
-
- /**
- * @since 2.0
- */
- @Override
- protected void doCommit(OMMonitor monitor)
- {
- // Do nothing
- }
-
- @Override
- public void doWrite(InternalCommitContext context, OMMonitor monitor)
- {
- MEMStore store = getStore();
- synchronized (store)
- {
- super.doWrite(context, monitor);
- }
- }
-
- @Override
- protected void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID,
- String comment, OMMonitor monitor)
- {
- getStore().addCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment);
- }
-
- @Override
- protected void doRollback(CommitContext context)
- {
- MEMStore store = getStore();
- synchronized (store)
- {
- for (InternalCDORevision revision : newRevisions)
- {
- store.rollbackRevision(revision);
- }
- }
- }
-
- public void writePackageUnits(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor)
- {
- // Do nothing
- }
-
- @Override
- protected void writeRevisions(InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor)
- {
- for (InternalCDORevision revision : revisions)
- {
- writeRevision(revision);
- }
- }
-
- protected void writeRevision(InternalCDORevision revision)
- {
- newRevisions.add(revision);
- getStore().addRevision(revision, false);
- }
-
- /**
- * @since 2.0
- */
- @Override
- protected void writeRevisionDeltas(InternalCDORevisionDelta[] revisionDeltas, CDOBranch branch, long created,
- OMMonitor monitor)
- {
- for (InternalCDORevisionDelta revisionDelta : revisionDeltas)
- {
- writeRevisionDelta(revisionDelta, branch, created);
- }
- }
-
- /**
- * @since 2.0
- */
- protected void writeRevisionDelta(InternalCDORevisionDelta revisionDelta, CDOBranch branch, long created)
- {
- CDOID id = revisionDelta.getID();
- CDOBranchVersion version = revisionDelta.getBranch().getVersion(revisionDelta.getVersion());
- InternalCDORevision revision = getStore().getRevisionByVersion(id, version);
- if (revision.getVersion() != revisionDelta.getVersion())
- {
- throw new ConcurrentModificationException("Trying to update object " + id //$NON-NLS-1$
- + " that was already modified"); //$NON-NLS-1$
- }
-
- InternalCDORevision newRevision = revision.copy();
- newRevision.adjustForCommit(branch, created);
-
- revisionDelta.apply(newRevision);
- writeRevision(newRevision);
- }
-
- @Override
- protected void detachObjects(CDOID[] detachedObjects, CDOBranch branch, long timeStamp, OMMonitor monitor)
- {
- for (CDOID id : detachedObjects)
- {
- detachObject(id, branch, timeStamp);
- }
- }
-
- /**
- * @since 3.0
- */
- protected void detachObject(CDOID id, CDOBranch branch, long timeStamp)
- {
- getStore().detachObject(id, branch, timeStamp);
- }
-
- /**
- * @since 2.0
- */
- public void queryResources(QueryResourcesContext context)
- {
- getStore().queryResources(context);
- }
-
- public void queryXRefs(QueryXRefsContext context)
- {
- getStore().queryXRefs(context);
- }
-
- public IQueryHandler getQueryHandler(CDOQueryInfo info)
- {
- if ("TEST".equals(info.getQueryLanguage())) //$NON-NLS-1$
- {
- return testQueryHandler;
- }
-
- return null;
- }
-
- public void rawExport(CDODataOutput out, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime)
- throws IOException
- {
- getStore().rawExport(out, fromBranchID, toBranchID, fromCommitTime, toCommitTime);
- }
-
- public void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime,
- OMMonitor monitor) throws IOException
- {
- getStore().rawImport(in, fromBranchID, toBranchID, fromCommitTime, toCommitTime, monitor);
- }
-
- public void rawStore(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor)
- {
- writePackageUnits(packageUnits, monitor);
- }
-
- public void rawStore(InternalCDORevision revision, OMMonitor monitor)
- {
- getStore().addRevision(revision, true);
- }
-
- public void rawStore(byte[] id, long size, InputStream inputStream) throws IOException
- {
- writeBlob(id, size, inputStream);
- }
-
- public void rawStore(byte[] id, long size, Reader reader) throws IOException
- {
- writeClob(id, size, reader);
- }
-
- public void rawStore(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment,
- OMMonitor monitor)
- {
- writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, monitor);
- }
-
- @Deprecated
- public void rawDelete(CDOID id, int version, CDOBranch branch, EClass eClass, OMMonitor monitor)
- {
- throw new UnsupportedOperationException();
-
- // getStore().rawDelete(id, version, branch);
- }
-
- public void rawCommit(double commitWork, OMMonitor monitor)
- {
- // Do nothing
- }
-
- public LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly,
- Map<CDOID, LockGrade> locks)
- {
- return getStore().createLockArea(userID, branchPoint, readOnly, locks);
- }
-
- public LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly,
- Map<CDOID, LockGrade> locks)
- {
- return getStore().createLockArea(durableLockingID, userID, branchPoint, readOnly, locks);
- }
-
- public void updateLockArea(LockArea lockArea)
- {
- getStore().updateLockArea(lockArea);
- }
-
- public LockArea getLockArea(String durableLockingID) throws LockAreaNotFoundException
- {
- return getStore().getLockArea(durableLockingID);
- }
-
- public void getLockAreas(String userIDPrefix, Handler handler)
- {
- getStore().getLockAreas(userIDPrefix, handler);
- }
-
- public void deleteLockArea(String durableLockingID)
- {
- getStore().deleteLockArea(durableLockingID);
- }
-
- public void lock(String durableLockingID, LockType type, Collection<? extends Object> objectsToLock)
- {
- getStore().lock(durableLockingID, type, objectsToLock);
- }
-
- public void unlock(String durableLockingID, LockType type, Collection<? extends Object> objectsToUnlock)
- {
- getStore().unlock(durableLockingID, type, objectsToUnlock);
- }
-
- public void unlock(String durableLockingID)
- {
- getStore().unlock(durableLockingID);
- }
-
- public void queryLobs(List<byte[]> ids)
- {
- getStore().queryLobs(ids);
- }
-
- public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException
- {
- getStore().handleLobs(fromTime, toTime, handler);
- }
-
- public void loadLob(byte[] id, OutputStream out) throws IOException
- {
- getStore().loadLob(id, out);
- }
-
- @Override
- protected void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException
- {
- getStore().writeBlob(id, size, inputStream);
- }
-
- @Override
- protected void writeClob(byte[] id, long size, Reader reader) throws IOException
- {
- getStore().writeClob(id, size, reader);
- }
-
- @Override
- protected void doActivate() throws Exception
- {
- // Do nothing
- }
-
- @Override
- protected void doDeactivate() throws Exception
- {
- newRevisions.clear();
- }
-
- @Override
- protected void doPassivate() throws Exception
- {
- // Pooling of store accessors not supported
- }
-
- @Override
- protected void doUnpassivate() throws Exception
- {
- // Pooling of store accessors not supported
- }
-}
+/* + * Copyright (c) 2004 - 2012 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: + * Simon McDuff - initial API and implementation + * Eike Stepper - maintenance + * Simon McDuff - bug 213402 + */ +package org.eclipse.emf.cdo.internal.server.mem; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchHandler; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.lob.CDOLobHandler; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockArea.Handler; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.revision.CDORevisionCacheAdder; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +import org.eclipse.emf.cdo.server.IQueryContext; +import org.eclipse.emf.cdo.server.IQueryHandler; +import org.eclipse.emf.cdo.server.ISession; +import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking2; +import org.eclipse.emf.cdo.server.IStoreAccessor.Raw; +import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment; +import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; +import org.eclipse.emf.cdo.spi.server.InternalCommitContext; +import org.eclipse.emf.cdo.spi.server.LongIDStoreAccessor; + +import org.eclipse.net4j.util.WrappedException; +import org.eclipse.net4j.util.collection.Pair; +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Simon McDuff + */ +public class MEMStoreAccessor extends LongIDStoreAccessor implements Raw, DurableLocking2 +{ + private final IQueryHandler testQueryHandler = new IQueryHandler() + { + public void executeQuery(CDOQueryInfo info, IQueryContext queryContext) + { + List<Object> filters = new ArrayList<Object>(); + Object context = info.getParameters().get("context"); //$NON-NLS-1$ + Long sleep = (Long)info.getParameters().get("sleep"); //$NON-NLS-1$ + if (context != null) + { + if (context instanceof EClass) + { + final EClass eClass = (EClass)context; + filters.add(new Object() + { + @Override + public int hashCode() + { + return eClass.hashCode(); + } + + @Override + public boolean equals(Object obj) + { + InternalCDORevision revision = (InternalCDORevision)obj; + return revision.getEClass().equals(eClass); + } + }); + } + } + + for (InternalCDORevision revision : getStore().getCurrentRevisions()) + { + if (sleep != null) + { + try + { + Thread.sleep(sleep); + } + catch (InterruptedException ex) + { + throw WrappedException.wrap(ex); + } + } + + boolean valid = true; + + for (Object filter : filters) + { + if (!filter.equals(revision)) + { + valid = false; + break; + } + } + + if (valid) + { + if (!queryContext.addResult(revision)) + { + // No more results allowed + break; + } + } + } + } + }; + + private List<InternalCDORevision> newRevisions = new ArrayList<InternalCDORevision>(); + + public MEMStoreAccessor(MEMStore store, ISession session) + { + super(store, session); + } + + /** + * @since 2.0 + */ + public MEMStoreAccessor(MEMStore store, ITransaction transaction) + { + super(store, transaction); + } + + @Override + public MEMStore getStore() + { + return (MEMStore)super.getStore(); + } + + /** + * @since 2.0 + */ + public MEMStoreChunkReader createChunkReader(InternalCDORevision revision, EStructuralFeature feature) + { + return new MEMStoreChunkReader(this, revision, feature); + } + + public Collection<InternalCDOPackageUnit> readPackageUnits() + { + return Collections.emptySet(); + } + + public EPackage[] loadPackageUnit(InternalCDOPackageUnit packageUnit) + { + throw new UnsupportedOperationException(); + } + + public Pair<Integer, Long> createBranch(int branchID, BranchInfo branchInfo) + { + return getStore().createBranch(branchID, branchInfo); + } + + public BranchInfo loadBranch(int branchID) + { + return getStore().loadBranch(branchID); + } + + public SubBranchInfo[] loadSubBranches(int branchID) + { + return getStore().loadSubBranches(branchID); + } + + public int loadBranches(int startID, int endID, CDOBranchHandler branchHandler) + { + return getStore().loadBranches(startID, endID, branchHandler); + } + + public void loadCommitInfos(CDOBranch branch, long startTime, long endTime, CDOCommitInfoHandler handler) + { + getStore().loadCommitInfos(branch, startTime, endTime, handler); + } + + public Set<CDOID> readChangeSet(OMMonitor monitor, CDOChangeSetSegment... segments) + { + return getStore().readChangeSet(segments); + } + + public InternalCDORevision readRevision(CDOID id, CDOBranchPoint branchPoint, int listChunk, + CDORevisionCacheAdder cache) + { + return getStore().getRevision(id, branchPoint); + } + + public InternalCDORevision readRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int listChunk, + CDORevisionCacheAdder cache) + { + return getStore().getRevisionByVersion(id, branchVersion); + } + + public void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime, + CDORevisionHandler handler) + { + getStore().handleRevisions(eClass, branch, timeStamp, exactTime, handler); + } + + /** + * @since 2.0 + */ + @Override + protected void doCommit(OMMonitor monitor) + { + // Do nothing + } + + @Override + public void doWrite(InternalCommitContext context, OMMonitor monitor) + { + MEMStore store = getStore(); + synchronized (store) + { + super.doWrite(context, monitor); + } + } + + @Override + protected void writeCommitInfo(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, + String comment, OMMonitor monitor) + { + getStore().addCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment); + } + + @Override + protected void doRollback(CommitContext context) + { + MEMStore store = getStore(); + synchronized (store) + { + for (InternalCDORevision revision : newRevisions) + { + store.rollbackRevision(revision); + } + } + } + + public void writePackageUnits(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor) + { + // Do nothing + } + + @Override + protected void writeRevisions(InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor) + { + for (InternalCDORevision revision : revisions) + { + writeRevision(revision); + } + } + + protected void writeRevision(InternalCDORevision revision) + { + newRevisions.add(revision); + getStore().addRevision(revision, false); + } + + /** + * @since 2.0 + */ + @Override + protected void writeRevisionDeltas(InternalCDORevisionDelta[] revisionDeltas, CDOBranch branch, long created, + OMMonitor monitor) + { + for (InternalCDORevisionDelta revisionDelta : revisionDeltas) + { + writeRevisionDelta(revisionDelta, branch, created); + } + } + + /** + * @since 2.0 + */ + protected void writeRevisionDelta(InternalCDORevisionDelta revisionDelta, CDOBranch branch, long created) + { + CDOID id = revisionDelta.getID(); + CDOBranchVersion version = revisionDelta.getBranch().getVersion(revisionDelta.getVersion()); + InternalCDORevision revision = getStore().getRevisionByVersion(id, version); + if (revision.getVersion() != revisionDelta.getVersion()) + { + throw new ConcurrentModificationException("Trying to update object " + id //$NON-NLS-1$ + + " that was already modified"); //$NON-NLS-1$ + } + + InternalCDORevision newRevision = revision.copy(); + newRevision.adjustForCommit(branch, created); + + revisionDelta.apply(newRevision); + writeRevision(newRevision); + } + + @Override + protected void detachObjects(CDOID[] detachedObjects, CDOBranch branch, long timeStamp, OMMonitor monitor) + { + for (CDOID id : detachedObjects) + { + detachObject(id, branch, timeStamp); + } + } + + /** + * @since 3.0 + */ + protected void detachObject(CDOID id, CDOBranch branch, long timeStamp) + { + getStore().detachObject(id, branch, timeStamp); + } + + /** + * @since 2.0 + */ + public void queryResources(QueryResourcesContext context) + { + getStore().queryResources(context); + } + + public void queryXRefs(QueryXRefsContext context) + { + getStore().queryXRefs(context); + } + + public IQueryHandler getQueryHandler(CDOQueryInfo info) + { + if ("TEST".equals(info.getQueryLanguage())) //$NON-NLS-1$ + { + return testQueryHandler; + } + + return null; + } + + public void rawExport(CDODataOutput out, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime) + throws IOException + { + getStore().rawExport(out, fromBranchID, toBranchID, fromCommitTime, toCommitTime); + } + + public void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime, + OMMonitor monitor) throws IOException + { + getStore().rawImport(in, fromBranchID, toBranchID, fromCommitTime, toCommitTime, monitor); + } + + public void rawStore(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor) + { + writePackageUnits(packageUnits, monitor); + } + + public void rawStore(InternalCDORevision revision, OMMonitor monitor) + { + getStore().addRevision(revision, true); + } + + public void rawStore(byte[] id, long size, InputStream inputStream) throws IOException + { + writeBlob(id, size, inputStream); + } + + public void rawStore(byte[] id, long size, Reader reader) throws IOException + { + writeClob(id, size, reader); + } + + public void rawStore(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, + OMMonitor monitor) + { + writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, comment, monitor); + } + + public void rawDelete(CDOID id, int version, CDOBranch branch, EClass eClass, OMMonitor monitor) + { + getStore().rawDelete(id, version, branch); + } + + public void rawCommit(double commitWork, OMMonitor monitor) + { + // Do nothing + } + + public LockArea createLockArea(String userID, CDOBranchPoint branchPoint, boolean readOnly, + Map<CDOID, LockGrade> locks) + { + return getStore().createLockArea(userID, branchPoint, readOnly, locks); + } + + public LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly, + Map<CDOID, LockGrade> locks) + { + return getStore().createLockArea(durableLockingID, userID, branchPoint, readOnly, locks); + } + + public void updateLockArea(LockArea lockArea) + { + getStore().updateLockArea(lockArea); + } + + public LockArea getLockArea(String durableLockingID) throws LockAreaNotFoundException + { + return getStore().getLockArea(durableLockingID); + } + + public void getLockAreas(String userIDPrefix, Handler handler) + { + getStore().getLockAreas(userIDPrefix, handler); + } + + public void deleteLockArea(String durableLockingID) + { + getStore().deleteLockArea(durableLockingID); + } + + public void lock(String durableLockingID, LockType type, Collection<? extends Object> objectsToLock) + { + getStore().lock(durableLockingID, type, objectsToLock); + } + + public void unlock(String durableLockingID, LockType type, Collection<? extends Object> objectsToUnlock) + { + getStore().unlock(durableLockingID, type, objectsToUnlock); + } + + public void unlock(String durableLockingID) + { + getStore().unlock(durableLockingID); + } + + public void queryLobs(List<byte[]> ids) + { + getStore().queryLobs(ids); + } + + public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException + { + getStore().handleLobs(fromTime, toTime, handler); + } + + public void loadLob(byte[] id, OutputStream out) throws IOException + { + getStore().loadLob(id, out); + } + + @Override + protected void writeBlob(byte[] id, long size, InputStream inputStream) throws IOException + { + getStore().writeBlob(id, size, inputStream); + } + + @Override + protected void writeClob(byte[] id, long size, Reader reader) throws IOException + { + getStore().writeClob(id, size, reader); + } + + @Override + protected void doActivate() throws Exception + { + // Do nothing + } + + @Override + protected void doDeactivate() throws Exception + { + newRevisions.clear(); + } + + @Override + protected void doPassivate() throws Exception + { + // Pooling of store accessors not supported + } + + @Override + protected void doUnpassivate() throws Exception + { + // Pooling of store accessors not supported + } +} diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java index 85908e0d73..2c9f205e5f 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java @@ -1,757 +1,755 @@ -/*
- * Copyright (c) 2004 - 2012 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.server;
-
-import org.eclipse.emf.cdo.common.branch.CDOBranch;
-import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
-import org.eclipse.emf.cdo.common.branch.CDOBranchVersion;
-import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.id.CDOIDReference;
-import org.eclipse.emf.cdo.common.lob.CDOBlob;
-import org.eclipse.emf.cdo.common.lob.CDOClob;
-import org.eclipse.emf.cdo.common.lob.CDOLob;
-import org.eclipse.emf.cdo.common.lob.CDOLobHandler;
-import org.eclipse.emf.cdo.common.lock.CDOLockState;
-import org.eclipse.emf.cdo.common.lock.IDurableLockingManager;
-import org.eclipse.emf.cdo.common.model.CDOPackageUnit;
-import org.eclipse.emf.cdo.common.protocol.CDODataInput;
-import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
-import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.common.revision.CDORevisionCacheAdder;
-import org.eclipse.emf.cdo.common.revision.CDORevisionHandler;
-import org.eclipse.emf.cdo.common.revision.CDORevisionKey;
-import org.eclipse.emf.cdo.common.revision.CDORevisionProvider;
-import org.eclipse.emf.cdo.common.util.CDOQueryInfo;
-import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager.BranchLoader;
-import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment;
-import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager.CommitInfoLoader;
-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.InternalCDORevision;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
-import org.eclipse.emf.cdo.spi.server.InternalCommitContext;
-import org.eclipse.emf.cdo.spi.server.InternalSession;
-
-import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
-import org.eclipse.net4j.util.concurrent.RWOLockManager.LockState;
-import org.eclipse.net4j.util.io.ExtendedDataInputStream;
-import org.eclipse.net4j.util.io.IOUtil;
-import org.eclipse.net4j.util.om.monitor.OMMonitor;
-
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EPackage;
-import org.eclipse.emf.ecore.EReference;
-import org.eclipse.emf.ecore.EStructuralFeature;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Represents a <i>connection</i> to a physical data storage back-end.
- *
- * @author Eike Stepper
- * @apiviz.uses {@link IStoreChunkReader} - - creates
- */
-public interface IStoreAccessor extends IQueryHandlerProvider, BranchLoader, CommitInfoLoader
-{
- /**
- * Returns the store this accessor is associated with.
- */
- public IStore getStore();
-
- /**
- * Returns the session this accessor is associated with.
- *
- * @since 3.0
- */
- public InternalSession getSession();
-
- /**
- * Returns the transaction this accessor is associated with if {@link #isReader()} returns <code>false</code>,
- * <code>null</code> otherwise.
- *
- * @since 2.0
- */
- public ITransaction getTransaction();
-
- /**
- * Returns <code>true</code> if this accessor has been configured for read-only access to the back-end,
- * <code>false</code> otherwise.
- *
- * @since 2.0
- */
- public boolean isReader();
-
- /**
- * @since 2.0
- */
- public IStoreChunkReader createChunkReader(InternalCDORevision revision, EStructuralFeature feature);
-
- /**
- * @since 2.0
- */
- public Collection<InternalCDOPackageUnit> readPackageUnits();
-
- /**
- * Demand loads a given package proxy that has been created on startup of the repository.
- * <p>
- * This method must only load the given package, <b>not</b> possible contained packages.
- *
- * @since 2.0
- */
- public EPackage[] loadPackageUnit(InternalCDOPackageUnit packageUnit);
-
- /**
- * Reads a revision from the back-end that was valid at the given timeStamp in the given branch.
- *
- * @since 4.0
- */
- public InternalCDORevision readRevision(CDOID id, CDOBranchPoint branchPoint, int listChunk,
- CDORevisionCacheAdder cache);
-
- /**
- * Reads a revision with the given version in the given branch from the back-end.
- *
- * @since 4.0
- */
- public InternalCDORevision readRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int listChunk,
- CDORevisionCacheAdder cache);
-
- /**
- * Passes all revisions of the store to the {@link CDORevisionHandler handler} if <b>all</b> of the following
- * conditions are met:
- * <ul>
- * <li>The <code>eClass</code> parameter is <code>null</code> or equal to <code>revision.getEClass()</code>.
- * <li>The <code>branch</code> parameter is <code>null</code> or equal to <code>revision.getBranch()</code>.
- * <li><b>One</b> of the following conditions is met:
- * <ul>
- * <li>The <code>timeStamp</code> parameter is {@link CDOBranchPoint#INVALID_DATE INVALID}.
- * <li>The <code>exactTime</code> parameter is <code>true</code> and the <code>timeStamp</code> parameter is
- * {@link CDOBranchPoint#UNSPECIFIED_DATE UNSPECIFIED} or equal to <code>revision.getTimeStamp()</code>.
- * <li>The <code>exactTime</code> parameter is <code>false</code> and the <code>timeStamp</code> parameter is between
- * <code>revision.getTimeStamp()</code> and <code>revision.getRevised()</code>.
- * </ul>
- * </ul>
- *
- * @since 4.0
- */
- public void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime,
- CDORevisionHandler handler);
-
- /**
- * Returns a set of CDOIDs that have at least one revision in any of the passed branches and time ranges.
- * DetachedCDORevisions must also be considered!
- *
- * @since 4.0
- */
- public Set<CDOID> readChangeSet(OMMonitor monitor, CDOChangeSetSegment... segments);
-
- /**
- * Returns the <code>CDOID</code> of the resource node with the given folderID and name if a resource with this
- * folderID and name exists in the store, <code>null</code> otherwise.
- *
- * @since 3.0
- */
- public CDOID readResourceID(CDOID folderID, String name, CDOBranchPoint branchPoint);
-
- /**
- * @since 2.0
- */
- public void queryResources(QueryResourcesContext context);
-
- /**
- * @since 3.0
- */
- public void queryXRefs(QueryXRefsContext context);
-
- /**
- * Determines which of the large objects identified by the given {@link CDOLob#getID() IDs} are known in the backend
- * represented by this {@link IStoreAccessor} by removing the unknown IDs from the passed collection.
- * <p>
- * The identifier of a {@link CDOLob large object} is the SHA-1 digest of the content of this large object.
- * <p>
- * <b>Usage context:</b> This method is only called in the context of a commit operation of a client transaction if
- * that transaction contains additions of or changes to large objects.
- *
- * @param ids
- * the collection of large object IDs that the unknown IDs are supposed to be removed from.
- * @since 4.0
- */
- public void queryLobs(List<byte[]> ids);
-
- /**
- * Serializes the content of the large object identified by the given {@link CDOLob#getID() ID} to the given
- * <i>stream</i>.
- * <p>
- * The identifier of a {@link CDOLob large object} is the SHA-1 digest of the content of this large object.
- *
- * @param id
- * the ID of the large object whose content is to be written to the <i>stream</i>.
- * @throws IOException
- * if the <i>stream</i> could not be written to.
- * @since 4.0
- */
- public void loadLob(byte[] id, OutputStream out) throws IOException;
-
- /**
- * @since 4.0
- */
- public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException;
-
- /**
- * @since 2.0
- */
- public void writePackageUnits(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor);
-
- /**
- * Called before committing. An instance of this accessor represents an instance of a back-end transaction. Could be
- * called multiple times before commit it called. {@link IStoreAccessor#commit(OMMonitor)} or
- * {@link IStoreAccessor#rollback()} will be called after any numbers of
- * {@link IStoreAccessor#write(InternalCommitContext, OMMonitor)}.
- * <p>
- * <b>Note</b>: {@link IStoreAccessor#write(InternalCommitContext, OMMonitor)} and
- * {@link IStoreAccessor#commit(OMMonitor)} could be called from different threads.
- *
- * @since 3.0
- */
- public void write(InternalCommitContext context, OMMonitor monitor);
-
- /**
- * Flushes to the back-end and makes available the data for others.
- * <p>
- * <b>Note</b>: {@link IStoreAccessor#write(InternalCommitContext, OMMonitor)} and
- * {@link IStoreAccessor#commit(OMMonitor)} could be called from different threads.
- * <p>
- * <b>Note</b>: Implementors should detect if dirty write occurred. In this case it should throw an exception.
- *
- * <pre>
- * if (revision.getVersion() != revisionDelta.getOriginVersion())
- * {
- * throw new ConcurrentModificationException("Trying to update object " + revisionDelta.getID()
- * + " that was already modified");
- * }
- * </pre>
- *
- * @since 2.0
- */
- public void commit(OMMonitor monitor);
-
- /**
- * <b>Note</b>: {@link IStoreAccessor#write(InternalCommitContext, OMMonitor)} and {@link IStoreAccessor#rollback()}
- * could be called from different threads.
- *
- * @since 2.0
- */
- public void rollback();
-
- public void release();
-
- /**
- * Represents the state of a single, logical commit operation which is driven through multiple calls to several
- * methods on the {@link IStoreAccessor} API. All these method calls get the same <code>CommitContext</code> instance
- * passed so that the implementor of the {@link IStoreAccessor} can track the state and progress of the commit
- * operation.
- *
- * @author Eike Stepper
- * @since 2.0
- * @noimplement This interface is not intended to be implemented by clients.
- * @noextend This interface is not intended to be extended by clients.
- * @apiviz.exclude
- */
- public interface CommitContext extends CDORevisionProvider
- {
- /**
- * Returns the transactional view (<code>ITransaction</code>) which is the scope of the commit operation represented
- * by this <code>CommitContext</code>.
- *
- * @since 4.0
- */
- public ITransaction getTransaction();
-
- /**
- * Returns the branch ID and timestamp of this commit operation.
- *
- * @since 3.0
- */
- public CDOBranchPoint getBranchPoint();
-
- /**
- * @since 4.0
- */
- public long getPreviousTimeStamp();
-
- /**
- * @since 3.0
- */
- public String getUserID();
-
- /**
- * @since 3.0
- */
- public String getCommitComment();
-
- /**
- * @since 3.0
- */
- public boolean isAutoReleaseLocksEnabled();
-
- /**
- * Returns the temporary, transactional package manager associated with the commit operation represented by this
- * <code>CommitContext</code>. In addition to the packages registered with the session this package manager also
- * contains the new packages that are part of this commit operation.
- */
- public InternalCDOPackageRegistry getPackageRegistry();
-
- /**
- * Returns an array of the new package units that are part of the commit operation represented by this
- * <code>CommitContext</code>.
- */
- public InternalCDOPackageUnit[] getNewPackageUnits();
-
- /**
- * Returns an array of the locks on the new objects that are part of the commit operation represented by this
- * <code>CommitContext</code>.
- *
- * @since 4.1
- */
- public CDOLockState[] getLocksOnNewObjects();
-
- /**
- * Returns an array of the new objects that are part of the commit operation represented by this
- * <code>CommitContext</code>.
- */
- public InternalCDORevision[] getNewObjects();
-
- /**
- * Returns an array of the dirty objects that are part of the commit operation represented by this
- * <code>CommitContext</code>.
- */
- public InternalCDORevision[] getDirtyObjects();
-
- /**
- * Returns an array of the dirty object deltas that are part of the commit operation represented by this
- * <code>CommitContext</code>.
- */
- public InternalCDORevisionDelta[] getDirtyObjectDeltas();
-
- /**
- * Returns an array of the removed object that are part of the commit operation represented by this
- * <code>CommitContext</code>.
- *
- * @since 2.0
- */
- public CDOID[] getDetachedObjects();
-
- /**
- * Returns a map with an {@link EClass} value per {@link CDOID} type.
- *
- * @since 4.0
- */
- public Map<CDOID, EClass> getDetachedObjectTypes();
-
- /**
- * Returns a stream that all {@link CDOLob lobs} can be read from. The format of the data delivered through the
- * stream is:
- * <p>
- * <ol>
- * <li> {@link ExtendedDataInputStream#readInt() int}: the number of lobs to be read from the stream.
- * <li>The following data can be read from the stream in a loop with one iteration per lob in the stream:
- * <ol>
- * <li> {@link ExtendedDataInputStream#readByteArray() int + byte[]}: the id of the lob (prepended by the size of the
- * id).
- * <li> {@link ExtendedDataInputStream#readLong() long}: the size of the lob. The foollowing interpretation applies:
- * <ul>
- * <li>A positive size indicates a {@link CDOBlob blob} and means the number of bytes that can be
- * {@link IOUtil#copyBinary(java.io.InputStream, java.io.OutputStream) read}.
- * <li>A negative size indicates a {@link CDOClob clob} and means the number of characters that can be
- * {@link IOUtil#copyCharacter(java.io.Reader, java.io.Writer) read}.
- * </ul>
- * </ol>
- * </ol>
- *
- * @since 4.0
- */
- public ExtendedDataInputStream getLobs();
-
- /**
- * Returns an unmodifiable map from all temporary IDs to their persistent counter parts.
- */
- public Map<CDOID, CDOID> getIDMappings();
-
- /**
- * @since 4.0
- */
- public CDOCommitInfo createCommitInfo();
-
- /**
- * @since 3.0
- */
- public String getRollbackMessage();
-
- /**
- * @since 4.0
- */
- public List<CDOIDReference> getXRefs();
-
- /**
- * @since 4.1
- */
- public List<LockState<Object, IView>> getPostCommmitLockStates();
- }
-
- /**
- * Represents the query execution state of a {@link IStoreAccessor#queryResources(QueryResourcesContext) resources
- * query}.
- *
- * @author Eike Stepper
- * @since 2.0
- * @noimplement This interface is not intended to be implemented by clients.
- * @apiviz.exclude
- */
- public interface QueryResourcesContext extends CDOBranchPoint
- {
- public CDOID getFolderID();
-
- public String getName();
-
- public boolean exactMatch();
-
- /**
- * Returns the maximum number of results expected by the client or {@link CDOQueryInfo#UNLIMITED_RESULTS} for no
- * limitation.
- */
- public int getMaxResults();
-
- /**
- * Adds the CDOID of one resource to the results of the underlying query.
- *
- * @return <code>true</code> to indicate that more results can be passed subsequently, <code>false</code> otherwise
- * (i.e. maxResults has been reached or an asynchronous query has been canceled).
- */
- public boolean addResource(CDOID resourceID);
-
- /**
- * Represents the query execution state of a {@link IStoreAccessor#queryResources(QueryResourcesContext) resources
- * query} that is supposed to deliver one exact resource, or <code>null</code>.
- *
- * @author Eike Stepper
- * @since 2.0
- * @apiviz.exclude
- */
- public interface ExactMatch extends QueryResourcesContext
- {
- public CDOID getResourceID();
- }
- }
-
- /**
- * Represents the query execution state of a {@link IStoreAccessor#queryXRefs(QueryXRefsContext) XRefs query}.
- *
- * @author Eike Stepper
- * @since 3.0
- * @noimplement This interface is not intended to be implemented by clients.
- * @apiviz.exclude
- */
- public interface QueryXRefsContext extends CDOBranchPoint
- {
- /**
- * @since 4.0
- */
- public Map<CDOID, EClass> getTargetObjects();
-
- public EReference[] getSourceReferences();
-
- /**
- * @since 4.0
- */
- public Map<EClass, List<EReference>> getSourceCandidates();
-
- /**
- * Returns the maximum number of results expected by the client or {@link CDOQueryInfo#UNLIMITED_RESULTS} for no
- * limitation.
- */
- public int getMaxResults();
-
- /**
- * Adds the data of one cross reference to the results of the underlying query.
- *
- * @return <code>true</code> to indicate that more results can be passed subsequently, <code>false</code> otherwise
- * (i.e. maxResults has been reached or an asynchronous query has been canceled).
- */
- public boolean addXRef(CDOID targetID, CDOID sourceID, EReference sourceReference, int sourceIndex);
- }
-
- /**
- * An extension interface for {@link IStoreAccessor store accessors} that support <i>raw data access</i> as needed by
- * {@link IRepositorySynchronizer repository synchronizers} or {@link CDOServerImporter server importers}.
- *
- * @author Eike Stepper
- * @since 4.0
- * @apiviz.exclude
- */
- public interface Raw extends IStoreAccessor
- {
- /**
- * Serializes all backend data within the given ranges such that it can be deserialized by the
- * {@link #rawImport(CDODataInput, int, int, long, long, OMMonitor) rawImport()} method of a different instance of
- * the same implementation of {@link IStoreAccessor.Raw raw store accessor}.
- * <p>
- * <b>Implementation note:</b> The implementor of this method is free to choose a serialization format as it only
- * needs to be understood by different instances of the same implementation of {@link IStoreAccessor.Raw raw store
- * accessor}.
- * <p>
- * <b>Usage context:</b> This method is only called in the context of a
- * {@link CDOProtocolConstants#SIGNAL_REPLICATE_REPOSITORY_RAW REPLICATE_REPOSITORY_RAW} signal that is triggered
- * from {@link IRepositorySynchronizer}.
- *
- * @param out
- * the <i>stream</i> to serialize the data to.
- * @param fromBranchID
- * the {@link CDOBranch#getID() ID} of the first branch to be exported.
- * @param toBranchID
- * the {@link CDOBranch#getID() ID} of the last branch to be exported.
- * @param fromCommitTime
- * the first {@link CDOBranchPoint#getTimeStamp() time stamp} of all non-branch data (e.g.
- * {@link CDORevision revisions}, {@link CDOCommitInfo commit infos}, {@link CDOPackageUnit package units},
- * etc...) to be exported.
- * @param toCommitTime
- * the last {@link CDOBranchPoint#getTimeStamp() time stamp} of all non-branch data (e.g.
- * {@link CDORevision revisions}, {@link CDOCommitInfo commit infos}, {@link CDOPackageUnit package units},
- * etc...) to be exported.
- * @throws IOException
- * if the <i>stream</i> could not be written to.
- * @throws UnsupportedOperationException
- * if this {@link IStoreAccessor.Raw raw store accessor} does not support branching.
- */
- public void rawExport(CDODataOutput out, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime)
- throws IOException;
-
- /**
- * Deserializes backend data that has been serialized by the {@link #rawExport(CDODataOutput, int, int, long, long)
- * rawExport()} method of a different instance of the same implementation of {@link IStoreAccessor.Raw raw store
- * accessor}.
- * <p>
- * <b>Implementation note:</b> The implementor of this method is free to choose a serialization format as it only
- * needs to be understood by different instances of the same implementation of {@link IStoreAccessor.Raw raw store
- * accessor}.
- * <p>
- * <b>Usage context:</b> This method is only called in the context of a
- * {@link CDOProtocolConstants#SIGNAL_REPLICATE_REPOSITORY_RAW REPLICATE_REPOSITORY_RAW} signal that is triggered
- * from {@link IRepositorySynchronizer}.
- *
- * @param in
- * the <i>stream</i> to deserialize the data from.
- * @param fromBranchID
- * the {@link CDOBranch#getID() ID} of the first branch to be imported.
- * @param toBranchID
- * the {@link CDOBranch#getID() ID} of the last branch to be imported.
- * @param fromCommitTime
- * the first {@link CDOBranchPoint#getTimeStamp() time stamp} of all non-branch data (e.g.
- * {@link CDORevision revisions}, {@link CDOCommitInfo commit infos}, {@link CDOPackageUnit package units},
- * etc...) to be imported.
- * @param toCommitTime
- * the last {@link CDOBranchPoint#getTimeStamp() time stamp} of all non-branch data (e.g.
- * {@link CDORevision revisions}, {@link CDOCommitInfo commit infos}, {@link CDOPackageUnit package units},
- * etc...) to be imported.
- * @throws IOException
- * if the <i>stream</i> could not be read from.
- * @throws UnsupportedOperationException
- * if this {@link IStoreAccessor.Raw raw store accessor} does not support branching.
- */
- public void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime,
- OMMonitor monitor) throws IOException;
-
- /**
- * Stores the given {@link CDOPackageUnit package units} in the backend represented by this
- * {@link IStoreAccessor.Raw raw store accessor} without going through a regular
- * {@link IStoreAccessor #commit(OMMonitor) commit}. A regular commit operation would assign new
- * {@link CDOPackageUnit#getTimeStamp() time stamps}, which is not desired in the context of a replication
- * operation.
- * <p>
- * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to
- * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method
- * where the accumulated backend changes can be committed atomically.
- *
- * @param packageUnits
- * the package units to be stored in the backend represented by this {@link IStoreAccessor.Raw raw store
- * accessor}.
- * @param monitor
- * a progress monitor that <b>may be</b> used to report proper progress of this operation to the caller and
- * <b>may be</b> used to react to cancelation requests of the caller and <b>must be</b> touched regularly
- * to prevent timeouts from expiring in the caller.
- * @see #rawCommit(double, OMMonitor)
- */
- public void rawStore(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor);
-
- /**
- * Stores the given {@link CDORevision revision} in the backend represented by this {@link IStoreAccessor.Raw raw
- * store accessor} without going through a regular {@link IStoreAccessor#commit(OMMonitor) commit}. A regular commit
- * operation would assign new {@link CDORevisionKey#getID() IDs} and {@link CDOBranchPoint#getTimeStamp() time
- * stamps}, which is not desired in the context of a replication operation.
- * <p>
- * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to
- * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method
- * where the accumulated backend changes can be committed atomically.
- *
- * @param revision
- * the revision to be stored in the backend represented by this {@link IStoreAccessor.Raw raw store
- * accessor}.
- * @param monitor
- * a progress monitor that <b>may be</b> used to report proper progress of this operation to the caller and
- * <b>may be</b> used to react to cancelation requests of the caller and <b>must be</b> touched regularly
- * to prevent timeouts from expiring in the caller.
- * @see #rawCommit(double, OMMonitor)
- */
- public void rawStore(InternalCDORevision revision, OMMonitor monitor);
-
- /**
- * Stores the given {@link CDOBlob blob} in the backend represented by this {@link IStoreAccessor.Raw raw store
- * accessor} without going through a regular {@link IStoreAccessor#commit(OMMonitor) commit}.
- * <p>
- * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to
- * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method
- * where the accumulated backend changes can be committed atomically.
- *
- * @param id
- * the {@link CDOBlob#getID() ID} of the blob to be stored in the backend represented by this
- * {@link IStoreAccessor.Raw raw store accessor}.
- * @param size
- * the {@link CDOBlob#getSize() size} of the blob to be stored in the backend represented by this
- * {@link IStoreAccessor.Raw raw store accessor}.
- * @param inputStream
- * the {@link CDOBlob#getContents() contents} of the blob to be stored in the backend represented by this
- * {@link IStoreAccessor.Raw raw store accessor}.
- * @see #rawCommit(double, OMMonitor)
- */
- public void rawStore(byte[] id, long size, InputStream inputStream) throws IOException;
-
- /**
- * Stores the given {@link CDOClob clob} in the backend represented by this {@link IStoreAccessor.Raw raw store
- * accessor} without going through a regular {@link IStoreAccessor#commit(OMMonitor) commit}.
- * <p>
- * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to
- * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method
- * where the accumulated backend changes can be committed atomically.
- *
- * @param id
- * the {@link CDOClob#getID() ID} of the clob to be stored in the backend represented by this
- * {@link IStoreAccessor.Raw raw store accessor}.
- * @param size
- * the {@link CDOClob#getSize() size} of the clob to be stored in the backend represented by this
- * {@link IStoreAccessor.Raw raw store accessor}.
- * @param reader
- * the {@link CDOClob#getContents() contents} of the clob to be stored in the backend represented by this
- * {@link IStoreAccessor.Raw raw store accessor}.
- * @see #rawCommit(double, OMMonitor)
- */
- public void rawStore(byte[] id, long size, Reader reader) throws IOException;
-
- /**
- * Stores the given {@link CDOCommitInfo commit} in the backend represented by this {@link IStoreAccessor.Raw raw
- * store accessor} without going through a regular {@link IStoreAccessor#commit(OMMonitor) commit}.
- * <p>
- * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to
- * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method
- * where the accumulated backend changes can be committed atomically.
- *
- * @param branch
- * the {@link CDOCommitInfo#getBranch() branch} of the commit info to be stored in the backend represented
- * by this {@link IStoreAccessor.Raw raw store accessor}.
- * @param timeStamp
- * the {@link CDOCommitInfo#getTimeStamp() time stamp} of the commit info to be stored in the backend
- * represented by this {@link IStoreAccessor.Raw raw store accessor}.
- * @param previousTimeStamp
- * the {@link CDOCommitInfo#getPreviousTimeStamp() previous time stamp} of the commit info to be stored in
- * the backend represented by this {@link IStoreAccessor.Raw raw store accessor}.
- * @param userID
- * the {@link CDOCommitInfo#getUserID() user ID} of the commit info to be stored in the backend represented
- * by this {@link IStoreAccessor.Raw raw store accessor}.
- * @param comment
- * the {@link CDOCommitInfo#getComment() comment} of the commit info to be stored in the backend
- * represented by this {@link IStoreAccessor.Raw raw store accessor}.
- * @see #rawCommit(double, OMMonitor)
- */
- public void rawStore(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment,
- OMMonitor monitor);
-
- /**
- * Deletes the revision identified by the given {@link CDORevisionKey key} from the backend represented by this
- * {@link IStoreAccessor.Raw raw store accessor} without going through a regular
- * {@link IStoreAccessor#commit(OMMonitor) commit}.
- * <p>
- * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to
- * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method
- * where the accumulated backend changes can be committed atomically.
- *
- * @see #rawCommit(double, OMMonitor)
- * @deprecated Not used anymore
- */
- @Deprecated
- public void rawDelete(CDOID id, int version, CDOBranch branch, EClass eClass, OMMonitor monitor);
-
- /**
- * Atomically commits the accumulated backend changes resulting from previous calls to the rawStore() methods.
- *
- * @param commitWork
- * the amount of work to use up from the monitor while executing the commit.
- * @param monitor
- * a progress monitor that <b>may be</b> used to report proper progress of this operation to the caller and
- * <b>may be</b> used to react to cancelation requests of the caller and <b>must be</b> touched regularly
- * to prevent timeouts from expiring in the caller.
- * @see #rawStore(InternalCDOPackageUnit[], OMMonitor)
- * @see #rawStore(InternalCDORevision, OMMonitor)
- * @see #rawStore(byte[], long, InputStream)
- * @see #rawStore(byte[], long, Reader)
- * @see #rawStore(CDOBranch, long, long, String, String, OMMonitor)
- */
- public void rawCommit(double commitWork, OMMonitor monitor);
- }
-
- /**
- * An extension interface for {@link IStoreAccessor store accessors} that support <i>durable locking</i>.
- *
- * @see DurableLocking2
- * @author Eike Stepper
- * @since 4.0
- * @apiviz.exclude
- */
- public interface DurableLocking extends IDurableLockingManager
- {
- public void lock(String durableLockingID, LockType type, Collection<? extends Object> objectsToLock);
-
- public void unlock(String durableLockingID, LockType type, Collection<? extends Object> objectsToUnlock);
-
- public void unlock(String durableLockingID);
- }
-
- /**
- * An extension interface for {@link IStoreAccessor store accessors} that support <i>durable locking</i>.
- *
- * @author Caspar De Groot
- * @since 4.1
- * @apiviz.exclude
- */
- public interface DurableLocking2 extends DurableLocking
- {
- LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly,
- Map<CDOID, LockGrade> locks);
-
- public void updateLockArea(LockArea lockArea);
- }
-}
+/* + * Copyright (c) 2004 - 2012 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.server; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; +import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDReference; +import org.eclipse.emf.cdo.common.lob.CDOBlob; +import org.eclipse.emf.cdo.common.lob.CDOClob; +import org.eclipse.emf.cdo.common.lob.CDOLob; +import org.eclipse.emf.cdo.common.lob.CDOLobHandler; +import org.eclipse.emf.cdo.common.lock.CDOLockState; +import org.eclipse.emf.cdo.common.lock.IDurableLockingManager; +import org.eclipse.emf.cdo.common.model.CDOPackageUnit; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionCacheAdder; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionKey; +import org.eclipse.emf.cdo.common.revision.CDORevisionProvider; +import org.eclipse.emf.cdo.common.util.CDOQueryInfo; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager.BranchLoader; +import org.eclipse.emf.cdo.spi.common.commit.CDOChangeSetSegment; +import org.eclipse.emf.cdo.spi.common.commit.InternalCDOCommitInfoManager.CommitInfoLoader; +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.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; +import org.eclipse.emf.cdo.spi.server.InternalCommitContext; +import org.eclipse.emf.cdo.spi.server.InternalSession; + +import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; +import org.eclipse.net4j.util.concurrent.RWOLockManager.LockState; +import org.eclipse.net4j.util.io.ExtendedDataInputStream; +import org.eclipse.net4j.util.io.IOUtil; +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Represents a <i>connection</i> to a physical data storage back-end. + * + * @author Eike Stepper + * @apiviz.uses {@link IStoreChunkReader} - - creates + */ +public interface IStoreAccessor extends IQueryHandlerProvider, BranchLoader, CommitInfoLoader +{ + /** + * Returns the store this accessor is associated with. + */ + public IStore getStore(); + + /** + * Returns the session this accessor is associated with. + * + * @since 3.0 + */ + public InternalSession getSession(); + + /** + * Returns the transaction this accessor is associated with if {@link #isReader()} returns <code>false</code>, + * <code>null</code> otherwise. + * + * @since 2.0 + */ + public ITransaction getTransaction(); + + /** + * Returns <code>true</code> if this accessor has been configured for read-only access to the back-end, + * <code>false</code> otherwise. + * + * @since 2.0 + */ + public boolean isReader(); + + /** + * @since 2.0 + */ + public IStoreChunkReader createChunkReader(InternalCDORevision revision, EStructuralFeature feature); + + /** + * @since 2.0 + */ + public Collection<InternalCDOPackageUnit> readPackageUnits(); + + /** + * Demand loads a given package proxy that has been created on startup of the repository. + * <p> + * This method must only load the given package, <b>not</b> possible contained packages. + * + * @since 2.0 + */ + public EPackage[] loadPackageUnit(InternalCDOPackageUnit packageUnit); + + /** + * Reads a revision from the back-end that was valid at the given timeStamp in the given branch. + * + * @since 4.0 + */ + public InternalCDORevision readRevision(CDOID id, CDOBranchPoint branchPoint, int listChunk, + CDORevisionCacheAdder cache); + + /** + * Reads a revision with the given version in the given branch from the back-end. + * + * @since 4.0 + */ + public InternalCDORevision readRevisionByVersion(CDOID id, CDOBranchVersion branchVersion, int listChunk, + CDORevisionCacheAdder cache); + + /** + * Passes all revisions of the store to the {@link CDORevisionHandler handler} if <b>all</b> of the following + * conditions are met: + * <ul> + * <li>The <code>eClass</code> parameter is <code>null</code> or equal to <code>revision.getEClass()</code>. + * <li>The <code>branch</code> parameter is <code>null</code> or equal to <code>revision.getBranch()</code>. + * <li><b>One</b> of the following conditions is met: + * <ul> + * <li>The <code>timeStamp</code> parameter is {@link CDOBranchPoint#INVALID_DATE INVALID}. + * <li>The <code>exactTime</code> parameter is <code>true</code> and the <code>timeStamp</code> parameter is + * {@link CDOBranchPoint#UNSPECIFIED_DATE UNSPECIFIED} or equal to <code>revision.getTimeStamp()</code>. + * <li>The <code>exactTime</code> parameter is <code>false</code> and the <code>timeStamp</code> parameter is between + * <code>revision.getTimeStamp()</code> and <code>revision.getRevised()</code>. + * </ul> + * </ul> + * + * @since 4.0 + */ + public void handleRevisions(EClass eClass, CDOBranch branch, long timeStamp, boolean exactTime, + CDORevisionHandler handler); + + /** + * Returns a set of CDOIDs that have at least one revision in any of the passed branches and time ranges. + * DetachedCDORevisions must also be considered! + * + * @since 4.0 + */ + public Set<CDOID> readChangeSet(OMMonitor monitor, CDOChangeSetSegment... segments); + + /** + * Returns the <code>CDOID</code> of the resource node with the given folderID and name if a resource with this + * folderID and name exists in the store, <code>null</code> otherwise. + * + * @since 3.0 + */ + public CDOID readResourceID(CDOID folderID, String name, CDOBranchPoint branchPoint); + + /** + * @since 2.0 + */ + public void queryResources(QueryResourcesContext context); + + /** + * @since 3.0 + */ + public void queryXRefs(QueryXRefsContext context); + + /** + * Determines which of the large objects identified by the given {@link CDOLob#getID() IDs} are known in the backend + * represented by this {@link IStoreAccessor} by removing the unknown IDs from the passed collection. + * <p> + * The identifier of a {@link CDOLob large object} is the SHA-1 digest of the content of this large object. + * <p> + * <b>Usage context:</b> This method is only called in the context of a commit operation of a client transaction if + * that transaction contains additions of or changes to large objects. + * + * @param ids + * the collection of large object IDs that the unknown IDs are supposed to be removed from. + * @since 4.0 + */ + public void queryLobs(List<byte[]> ids); + + /** + * Serializes the content of the large object identified by the given {@link CDOLob#getID() ID} to the given + * <i>stream</i>. + * <p> + * The identifier of a {@link CDOLob large object} is the SHA-1 digest of the content of this large object. + * + * @param id + * the ID of the large object whose content is to be written to the <i>stream</i>. + * @throws IOException + * if the <i>stream</i> could not be written to. + * @since 4.0 + */ + public void loadLob(byte[] id, OutputStream out) throws IOException; + + /** + * @since 4.0 + */ + public void handleLobs(long fromTime, long toTime, CDOLobHandler handler) throws IOException; + + /** + * @since 2.0 + */ + public void writePackageUnits(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor); + + /** + * Called before committing. An instance of this accessor represents an instance of a back-end transaction. Could be + * called multiple times before commit it called. {@link IStoreAccessor#commit(OMMonitor)} or + * {@link IStoreAccessor#rollback()} will be called after any numbers of + * {@link IStoreAccessor#write(InternalCommitContext, OMMonitor)}. + * <p> + * <b>Note</b>: {@link IStoreAccessor#write(InternalCommitContext, OMMonitor)} and + * {@link IStoreAccessor#commit(OMMonitor)} could be called from different threads. + * + * @since 3.0 + */ + public void write(InternalCommitContext context, OMMonitor monitor); + + /** + * Flushes to the back-end and makes available the data for others. + * <p> + * <b>Note</b>: {@link IStoreAccessor#write(InternalCommitContext, OMMonitor)} and + * {@link IStoreAccessor#commit(OMMonitor)} could be called from different threads. + * <p> + * <b>Note</b>: Implementors should detect if dirty write occurred. In this case it should throw an exception. + * + * <pre> + * if (revision.getVersion() != revisionDelta.getOriginVersion()) + * { + * throw new ConcurrentModificationException("Trying to update object " + revisionDelta.getID() + * + " that was already modified"); + * } + * </pre> + * + * @since 2.0 + */ + public void commit(OMMonitor monitor); + + /** + * <b>Note</b>: {@link IStoreAccessor#write(InternalCommitContext, OMMonitor)} and {@link IStoreAccessor#rollback()} + * could be called from different threads. + * + * @since 2.0 + */ + public void rollback(); + + public void release(); + + /** + * Represents the state of a single, logical commit operation which is driven through multiple calls to several + * methods on the {@link IStoreAccessor} API. All these method calls get the same <code>CommitContext</code> instance + * passed so that the implementor of the {@link IStoreAccessor} can track the state and progress of the commit + * operation. + * + * @author Eike Stepper + * @since 2.0 + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + * @apiviz.exclude + */ + public interface CommitContext extends CDORevisionProvider + { + /** + * Returns the transactional view (<code>ITransaction</code>) which is the scope of the commit operation represented + * by this <code>CommitContext</code>. + * + * @since 4.0 + */ + public ITransaction getTransaction(); + + /** + * Returns the branch ID and timestamp of this commit operation. + * + * @since 3.0 + */ + public CDOBranchPoint getBranchPoint(); + + /** + * @since 4.0 + */ + public long getPreviousTimeStamp(); + + /** + * @since 3.0 + */ + public String getUserID(); + + /** + * @since 3.0 + */ + public String getCommitComment(); + + /** + * @since 3.0 + */ + public boolean isAutoReleaseLocksEnabled(); + + /** + * Returns the temporary, transactional package manager associated with the commit operation represented by this + * <code>CommitContext</code>. In addition to the packages registered with the session this package manager also + * contains the new packages that are part of this commit operation. + */ + public InternalCDOPackageRegistry getPackageRegistry(); + + /** + * Returns an array of the new package units that are part of the commit operation represented by this + * <code>CommitContext</code>. + */ + public InternalCDOPackageUnit[] getNewPackageUnits(); + + /** + * Returns an array of the locks on the new objects that are part of the commit operation represented by this + * <code>CommitContext</code>. + * + * @since 4.1 + */ + public CDOLockState[] getLocksOnNewObjects(); + + /** + * Returns an array of the new objects that are part of the commit operation represented by this + * <code>CommitContext</code>. + */ + public InternalCDORevision[] getNewObjects(); + + /** + * Returns an array of the dirty objects that are part of the commit operation represented by this + * <code>CommitContext</code>. + */ + public InternalCDORevision[] getDirtyObjects(); + + /** + * Returns an array of the dirty object deltas that are part of the commit operation represented by this + * <code>CommitContext</code>. + */ + public InternalCDORevisionDelta[] getDirtyObjectDeltas(); + + /** + * Returns an array of the removed object that are part of the commit operation represented by this + * <code>CommitContext</code>. + * + * @since 2.0 + */ + public CDOID[] getDetachedObjects(); + + /** + * Returns a map with an {@link EClass} value per {@link CDOID} type. + * + * @since 4.0 + */ + public Map<CDOID, EClass> getDetachedObjectTypes(); + + /** + * Returns a stream that all {@link CDOLob lobs} can be read from. The format of the data delivered through the + * stream is: + * <p> + * <ol> + * <li> {@link ExtendedDataInputStream#readInt() int}: the number of lobs to be read from the stream. + * <li>The following data can be read from the stream in a loop with one iteration per lob in the stream: + * <ol> + * <li> {@link ExtendedDataInputStream#readByteArray() int + byte[]}: the id of the lob (prepended by the size of the + * id). + * <li> {@link ExtendedDataInputStream#readLong() long}: the size of the lob. The foollowing interpretation applies: + * <ul> + * <li>A positive size indicates a {@link CDOBlob blob} and means the number of bytes that can be + * {@link IOUtil#copyBinary(java.io.InputStream, java.io.OutputStream) read}. + * <li>A negative size indicates a {@link CDOClob clob} and means the number of characters that can be + * {@link IOUtil#copyCharacter(java.io.Reader, java.io.Writer) read}. + * </ul> + * </ol> + * </ol> + * + * @since 4.0 + */ + public ExtendedDataInputStream getLobs(); + + /** + * Returns an unmodifiable map from all temporary IDs to their persistent counter parts. + */ + public Map<CDOID, CDOID> getIDMappings(); + + /** + * @since 4.0 + */ + public CDOCommitInfo createCommitInfo(); + + /** + * @since 3.0 + */ + public String getRollbackMessage(); + + /** + * @since 4.0 + */ + public List<CDOIDReference> getXRefs(); + + /** + * @since 4.1 + */ + public List<LockState<Object, IView>> getPostCommmitLockStates(); + } + + /** + * Represents the query execution state of a {@link IStoreAccessor#queryResources(QueryResourcesContext) resources + * query}. + * + * @author Eike Stepper + * @since 2.0 + * @noimplement This interface is not intended to be implemented by clients. + * @apiviz.exclude + */ + public interface QueryResourcesContext extends CDOBranchPoint + { + public CDOID getFolderID(); + + public String getName(); + + public boolean exactMatch(); + + /** + * Returns the maximum number of results expected by the client or {@link CDOQueryInfo#UNLIMITED_RESULTS} for no + * limitation. + */ + public int getMaxResults(); + + /** + * Adds the CDOID of one resource to the results of the underlying query. + * + * @return <code>true</code> to indicate that more results can be passed subsequently, <code>false</code> otherwise + * (i.e. maxResults has been reached or an asynchronous query has been canceled). + */ + public boolean addResource(CDOID resourceID); + + /** + * Represents the query execution state of a {@link IStoreAccessor#queryResources(QueryResourcesContext) resources + * query} that is supposed to deliver one exact resource, or <code>null</code>. + * + * @author Eike Stepper + * @since 2.0 + * @apiviz.exclude + */ + public interface ExactMatch extends QueryResourcesContext + { + public CDOID getResourceID(); + } + } + + /** + * Represents the query execution state of a {@link IStoreAccessor#queryXRefs(QueryXRefsContext) XRefs query}. + * + * @author Eike Stepper + * @since 3.0 + * @noimplement This interface is not intended to be implemented by clients. + * @apiviz.exclude + */ + public interface QueryXRefsContext extends CDOBranchPoint + { + /** + * @since 4.0 + */ + public Map<CDOID, EClass> getTargetObjects(); + + public EReference[] getSourceReferences(); + + /** + * @since 4.0 + */ + public Map<EClass, List<EReference>> getSourceCandidates(); + + /** + * Returns the maximum number of results expected by the client or {@link CDOQueryInfo#UNLIMITED_RESULTS} for no + * limitation. + */ + public int getMaxResults(); + + /** + * Adds the data of one cross reference to the results of the underlying query. + * + * @return <code>true</code> to indicate that more results can be passed subsequently, <code>false</code> otherwise + * (i.e. maxResults has been reached or an asynchronous query has been canceled). + */ + public boolean addXRef(CDOID targetID, CDOID sourceID, EReference sourceReference, int sourceIndex); + } + + /** + * An extension interface for {@link IStoreAccessor store accessors} that support <i>raw data access</i> as needed by + * {@link IRepositorySynchronizer repository synchronizers} or {@link CDOServerImporter server importers}. + * + * @author Eike Stepper + * @since 4.0 + * @apiviz.exclude + */ + public interface Raw extends IStoreAccessor + { + /** + * Serializes all backend data within the given ranges such that it can be deserialized by the + * {@link #rawImport(CDODataInput, int, int, long, long, OMMonitor) rawImport()} method of a different instance of + * the same implementation of {@link IStoreAccessor.Raw raw store accessor}. + * <p> + * <b>Implementation note:</b> The implementor of this method is free to choose a serialization format as it only + * needs to be understood by different instances of the same implementation of {@link IStoreAccessor.Raw raw store + * accessor}. + * <p> + * <b>Usage context:</b> This method is only called in the context of a + * {@link CDOProtocolConstants#SIGNAL_REPLICATE_REPOSITORY_RAW REPLICATE_REPOSITORY_RAW} signal that is triggered + * from {@link IRepositorySynchronizer}. + * + * @param out + * the <i>stream</i> to serialize the data to. + * @param fromBranchID + * the {@link CDOBranch#getID() ID} of the first branch to be exported. + * @param toBranchID + * the {@link CDOBranch#getID() ID} of the last branch to be exported. + * @param fromCommitTime + * the first {@link CDOBranchPoint#getTimeStamp() time stamp} of all non-branch data (e.g. + * {@link CDORevision revisions}, {@link CDOCommitInfo commit infos}, {@link CDOPackageUnit package units}, + * etc...) to be exported. + * @param toCommitTime + * the last {@link CDOBranchPoint#getTimeStamp() time stamp} of all non-branch data (e.g. + * {@link CDORevision revisions}, {@link CDOCommitInfo commit infos}, {@link CDOPackageUnit package units}, + * etc...) to be exported. + * @throws IOException + * if the <i>stream</i> could not be written to. + * @throws UnsupportedOperationException + * if this {@link IStoreAccessor.Raw raw store accessor} does not support branching. + */ + public void rawExport(CDODataOutput out, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime) + throws IOException; + + /** + * Deserializes backend data that has been serialized by the {@link #rawExport(CDODataOutput, int, int, long, long) + * rawExport()} method of a different instance of the same implementation of {@link IStoreAccessor.Raw raw store + * accessor}. + * <p> + * <b>Implementation note:</b> The implementor of this method is free to choose a serialization format as it only + * needs to be understood by different instances of the same implementation of {@link IStoreAccessor.Raw raw store + * accessor}. + * <p> + * <b>Usage context:</b> This method is only called in the context of a + * {@link CDOProtocolConstants#SIGNAL_REPLICATE_REPOSITORY_RAW REPLICATE_REPOSITORY_RAW} signal that is triggered + * from {@link IRepositorySynchronizer}. + * + * @param in + * the <i>stream</i> to deserialize the data from. + * @param fromBranchID + * the {@link CDOBranch#getID() ID} of the first branch to be imported. + * @param toBranchID + * the {@link CDOBranch#getID() ID} of the last branch to be imported. + * @param fromCommitTime + * the first {@link CDOBranchPoint#getTimeStamp() time stamp} of all non-branch data (e.g. + * {@link CDORevision revisions}, {@link CDOCommitInfo commit infos}, {@link CDOPackageUnit package units}, + * etc...) to be imported. + * @param toCommitTime + * the last {@link CDOBranchPoint#getTimeStamp() time stamp} of all non-branch data (e.g. + * {@link CDORevision revisions}, {@link CDOCommitInfo commit infos}, {@link CDOPackageUnit package units}, + * etc...) to be imported. + * @throws IOException + * if the <i>stream</i> could not be read from. + * @throws UnsupportedOperationException + * if this {@link IStoreAccessor.Raw raw store accessor} does not support branching. + */ + public void rawImport(CDODataInput in, int fromBranchID, int toBranchID, long fromCommitTime, long toCommitTime, + OMMonitor monitor) throws IOException; + + /** + * Stores the given {@link CDOPackageUnit package units} in the backend represented by this + * {@link IStoreAccessor.Raw raw store accessor} without going through a regular + * {@link IStoreAccessor #commit(OMMonitor) commit}. A regular commit operation would assign new + * {@link CDOPackageUnit#getTimeStamp() time stamps}, which is not desired in the context of a replication + * operation. + * <p> + * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to + * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method + * where the accumulated backend changes can be committed atomically. + * + * @param packageUnits + * the package units to be stored in the backend represented by this {@link IStoreAccessor.Raw raw store + * accessor}. + * @param monitor + * a progress monitor that <b>may be</b> used to report proper progress of this operation to the caller and + * <b>may be</b> used to react to cancelation requests of the caller and <b>must be</b> touched regularly + * to prevent timeouts from expiring in the caller. + * @see #rawCommit(double, OMMonitor) + */ + public void rawStore(InternalCDOPackageUnit[] packageUnits, OMMonitor monitor); + + /** + * Stores the given {@link CDORevision revision} in the backend represented by this {@link IStoreAccessor.Raw raw + * store accessor} without going through a regular {@link IStoreAccessor#commit(OMMonitor) commit}. A regular commit + * operation would assign new {@link CDORevisionKey#getID() IDs} and {@link CDOBranchPoint#getTimeStamp() time + * stamps}, which is not desired in the context of a replication operation. + * <p> + * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to + * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method + * where the accumulated backend changes can be committed atomically. + * + * @param revision + * the revision to be stored in the backend represented by this {@link IStoreAccessor.Raw raw store + * accessor}. + * @param monitor + * a progress monitor that <b>may be</b> used to report proper progress of this operation to the caller and + * <b>may be</b> used to react to cancelation requests of the caller and <b>must be</b> touched regularly + * to prevent timeouts from expiring in the caller. + * @see #rawCommit(double, OMMonitor) + */ + public void rawStore(InternalCDORevision revision, OMMonitor monitor); + + /** + * Stores the given {@link CDOBlob blob} in the backend represented by this {@link IStoreAccessor.Raw raw store + * accessor} without going through a regular {@link IStoreAccessor#commit(OMMonitor) commit}. + * <p> + * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to + * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method + * where the accumulated backend changes can be committed atomically. + * + * @param id + * the {@link CDOBlob#getID() ID} of the blob to be stored in the backend represented by this + * {@link IStoreAccessor.Raw raw store accessor}. + * @param size + * the {@link CDOBlob#getSize() size} of the blob to be stored in the backend represented by this + * {@link IStoreAccessor.Raw raw store accessor}. + * @param inputStream + * the {@link CDOBlob#getContents() contents} of the blob to be stored in the backend represented by this + * {@link IStoreAccessor.Raw raw store accessor}. + * @see #rawCommit(double, OMMonitor) + */ + public void rawStore(byte[] id, long size, InputStream inputStream) throws IOException; + + /** + * Stores the given {@link CDOClob clob} in the backend represented by this {@link IStoreAccessor.Raw raw store + * accessor} without going through a regular {@link IStoreAccessor#commit(OMMonitor) commit}. + * <p> + * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to + * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method + * where the accumulated backend changes can be committed atomically. + * + * @param id + * the {@link CDOClob#getID() ID} of the clob to be stored in the backend represented by this + * {@link IStoreAccessor.Raw raw store accessor}. + * @param size + * the {@link CDOClob#getSize() size} of the clob to be stored in the backend represented by this + * {@link IStoreAccessor.Raw raw store accessor}. + * @param reader + * the {@link CDOClob#getContents() contents} of the clob to be stored in the backend represented by this + * {@link IStoreAccessor.Raw raw store accessor}. + * @see #rawCommit(double, OMMonitor) + */ + public void rawStore(byte[] id, long size, Reader reader) throws IOException; + + /** + * Stores the given {@link CDOCommitInfo commit} in the backend represented by this {@link IStoreAccessor.Raw raw + * store accessor} without going through a regular {@link IStoreAccessor#commit(OMMonitor) commit}. + * <p> + * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to + * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method + * where the accumulated backend changes can be committed atomically. + * + * @param branch + * the {@link CDOCommitInfo#getBranch() branch} of the commit info to be stored in the backend represented + * by this {@link IStoreAccessor.Raw raw store accessor}. + * @param timeStamp + * the {@link CDOCommitInfo#getTimeStamp() time stamp} of the commit info to be stored in the backend + * represented by this {@link IStoreAccessor.Raw raw store accessor}. + * @param previousTimeStamp + * the {@link CDOCommitInfo#getPreviousTimeStamp() previous time stamp} of the commit info to be stored in + * the backend represented by this {@link IStoreAccessor.Raw raw store accessor}. + * @param userID + * the {@link CDOCommitInfo#getUserID() user ID} of the commit info to be stored in the backend represented + * by this {@link IStoreAccessor.Raw raw store accessor}. + * @param comment + * the {@link CDOCommitInfo#getComment() comment} of the commit info to be stored in the backend + * represented by this {@link IStoreAccessor.Raw raw store accessor}. + * @see #rawCommit(double, OMMonitor) + */ + public void rawStore(CDOBranch branch, long timeStamp, long previousTimeStamp, String userID, String comment, + OMMonitor monitor); + + /** + * Deletes the revision identified by the given {@link CDORevisionKey key} from the backend represented by this + * {@link IStoreAccessor.Raw raw store accessor} without going through a regular + * {@link IStoreAccessor#commit(OMMonitor) commit}. + * <p> + * <b>Implementation note:</b> The implementor of this method may rely on the fact that multiple subsequent calls to + * this method are followed by a single final call to the {@link #rawCommit(double, OMMonitor) rawCommit()} method + * where the accumulated backend changes can be committed atomically. + * + * @see #rawCommit(double, OMMonitor) + */ + public void rawDelete(CDOID id, int version, CDOBranch branch, EClass eClass, OMMonitor monitor); + + /** + * Atomically commits the accumulated backend changes resulting from previous calls to the rawStore() methods. + * + * @param commitWork + * the amount of work to use up from the monitor while executing the commit. + * @param monitor + * a progress monitor that <b>may be</b> used to report proper progress of this operation to the caller and + * <b>may be</b> used to react to cancelation requests of the caller and <b>must be</b> touched regularly + * to prevent timeouts from expiring in the caller. + * @see #rawStore(InternalCDOPackageUnit[], OMMonitor) + * @see #rawStore(InternalCDORevision, OMMonitor) + * @see #rawStore(byte[], long, InputStream) + * @see #rawStore(byte[], long, Reader) + * @see #rawStore(CDOBranch, long, long, String, String, OMMonitor) + */ + public void rawCommit(double commitWork, OMMonitor monitor); + } + + /** + * An extension interface for {@link IStoreAccessor store accessors} that support <i>durable locking</i>. + * + * @see DurableLocking2 + * @author Eike Stepper + * @since 4.0 + * @apiviz.exclude + */ + public interface DurableLocking extends IDurableLockingManager + { + public void lock(String durableLockingID, LockType type, Collection<? extends Object> objectsToLock); + + public void unlock(String durableLockingID, LockType type, Collection<? extends Object> objectsToUnlock); + + public void unlock(String durableLockingID); + } + + /** + * An extension interface for {@link IStoreAccessor store accessors} that support <i>durable locking</i>. + * + * @author Caspar De Groot + * @since 4.1 + * @apiviz.exclude + */ + public interface DurableLocking2 extends DurableLocking + { + LockArea createLockArea(String durableLockingID, String userID, CDOBranchPoint branchPoint, boolean readOnly, + Map<CDOID, LockGrade> locks); + + public void updateLockArea(LockArea lockArea); + } +} diff --git a/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/AllTestsDBH2Audit.java b/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/AllTestsDBH2Audit.java index dd8bc33e29..58cf583b62 100644 --- a/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/AllTestsDBH2Audit.java +++ b/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/AllTestsDBH2Audit.java @@ -1,41 +1,44 @@ -/*
- * Copyright (c) 2004 - 2012 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
- * Stefan Winkler - introduced variable mapping strategies
- */
-package org.eclipse.emf.cdo.tests.db;
-
-import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation;
-import org.eclipse.emf.cdo.tests.config.impl.ConfigTestSuite;
-
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
-/**
- * @author Eike Stepper
- */
-public class AllTestsDBH2Audit extends DBConfigs
-{
- public static Test suite()
- {
- return new AllTestsDBH2Audit().getTestSuite("CDO Tests (DBStore H2 Horizontal)");
- }
-
- public static void initConfigSuites(ConfigTestSuite suite, TestSuite parent, IDGenerationLocation idGenerationLocation)
- {
- suite.addScenario(parent, COMBINED, new H2Config(true, false, false, false, idGenerationLocation), JVM, NATIVE);
- suite.addScenario(parent, COMBINED, new H2Config(true, false, true, false, idGenerationLocation), JVM, NATIVE);
- }
-
- @Override
- protected void initConfigSuites(TestSuite parent)
- {
- initConfigSuites(this, parent, IDGenerationLocation.STORE);
- }
-}
+/* + * Copyright (c) 2004 - 2012 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 + * Stefan Winkler - introduced variable mapping strategies + */ +package org.eclipse.emf.cdo.tests.db; + +import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTestSuite; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * @author Eike Stepper + */ +public class AllTestsDBH2Audit extends DBConfigs +{ + public static Test suite() + { + return new AllTestsDBH2Audit().getTestSuite("CDO Tests (DBStore H2 Horizontal)"); + } + + public static void initConfigSuites(ConfigTestSuite suite, TestSuite parent, IDGenerationLocation idGenerationLocation) + { + // Without ranges + suite.addScenario(parent, COMBINED, new H2Config(true, false, false, false, idGenerationLocation), JVM, NATIVE); + + // With ranges + suite.addScenario(parent, COMBINED, new H2Config(true, false, true, false, idGenerationLocation), JVM, NATIVE); + } + + @Override + protected void initConfigSuites(TestSuite parent) + { + initConfigSuites(this, parent, IDGenerationLocation.STORE); + } +} diff --git a/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/AllTestsDBH2Branching.java b/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/AllTestsDBH2Branching.java index 90aad2246d..e8fcdd5364 100644 --- a/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/AllTestsDBH2Branching.java +++ b/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/AllTestsDBH2Branching.java @@ -1,41 +1,46 @@ -/*
- * Copyright (c) 2004 - 2012 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.tests.db;
-
-import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation;
-import org.eclipse.emf.cdo.tests.config.impl.ConfigTestSuite;
-
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
-/**
- * @author Eike Stepper
- */
-public class AllTestsDBH2Branching extends DBConfigs
-{
- public static Test suite()
- {
- return new AllTestsDBH2Branching().getTestSuite("CDO Tests (DBStore H2 Horizontal - branching mode)");
- }
-
- public static void initConfigSuites(ConfigTestSuite suite, TestSuite parent, IDGenerationLocation idGenerationLocation)
- {
- // suite.addScenario(parent, COMBINED, new H2Config(true, true, false, false, idGenerationLocation), JVM, NATIVE);
- suite.addScenario(parent, COMBINED, new H2Config(true, true, true, false, idGenerationLocation), JVM, NATIVE);
- // suite.addScenario(parent, COMBINED, new H2Config(true, true, true, true, idGenerationLocation), JVM, NATIVE);
- }
-
- @Override
- protected void initConfigSuites(TestSuite parent)
- {
- initConfigSuites(this, parent, IDGenerationLocation.STORE);
- }
-}
+/* + * Copyright (c) 2004 - 2012 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.tests.db; + +import org.eclipse.emf.cdo.common.CDOCommonRepository.IDGenerationLocation; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTestSuite; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * @author Eike Stepper + */ +public class AllTestsDBH2Branching extends DBConfigs +{ + public static Test suite() + { + return new AllTestsDBH2Branching().getTestSuite("CDO Tests (DBStore H2 Horizontal - branching mode)"); + } + + public static void initConfigSuites(ConfigTestSuite suite, TestSuite parent, IDGenerationLocation idGenerationLocation) + { + // Without ranges + suite.addScenario(parent, COMBINED, new H2Config(true, true, false, false, idGenerationLocation), JVM, NATIVE); + + // With ranges + suite.addScenario(parent, COMBINED, new H2Config(true, true, true, false, idGenerationLocation), JVM, NATIVE); + + // With ranges and copy-on-branch + // suite.addScenario(parent, COMBINED, new H2Config(true, true, true, true, idGenerationLocation), JVM, NATIVE); + } + + @Override + protected void initConfigSuites(TestSuite parent) + { + initConfigSuites(this, parent, IDGenerationLocation.STORE); + } +} diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/RevisionManagerTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/RevisionManagerTest.java index 8255acca54..eb8d6a691e 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/RevisionManagerTest.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/RevisionManagerTest.java @@ -1,848 +1,858 @@ -/*
- * Copyright (c) 2004 - 2012 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.tests;
-
-import org.eclipse.emf.cdo.common.branch.CDOBranch;
-import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.id.CDOIDUtil;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
-import org.eclipse.emf.cdo.internal.common.revision.AbstractCDORevisionCache;
-import org.eclipse.emf.cdo.internal.common.revision.CDORevisionImpl;
-import org.eclipse.emf.cdo.internal.server.mem.MEMStore;
-import org.eclipse.emf.cdo.server.IRepository;
-import org.eclipse.emf.cdo.server.StoreThreadLocal;
-import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager;
-import org.eclipse.emf.cdo.spi.server.InternalRepository;
-import org.eclipse.emf.cdo.spi.server.InternalSession;
-import org.eclipse.emf.cdo.tests.config.IRepositoryConfig;
-import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.Requires;
-import org.eclipse.emf.cdo.tests.util.TestRevisionManager;
-
-import org.eclipse.net4j.util.ReflectUtil;
-import org.eclipse.net4j.util.io.IOUtil;
-
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EcorePackage;
-import org.eclipse.emf.spi.cdo.InternalCDOSession;
-
-import java.lang.reflect.Field;
-import java.util.Map;
-
-/**
- * @author Eike Stepper
- */
-@Requires({ IRepositoryConfig.CAPABILITY_BRANCHING, "MEM" })
-public class RevisionManagerTest extends AbstractCDOTest
-{
- private static final CDOID ID = CDOIDUtil.createLong(2);
-
- private static final EClass CLASS = EcorePackage.eINSTANCE.getEAnnotation();
-
- private static final int DETACH = -1;
-
- protected InternalRepository repository;
-
- private MEMStore store;
-
- private InternalCDOSession session;
-
- private InternalSession serverSession;
-
- private InternalCDOBranchManager branchManager;
-
- private int branchID;
-
- private CDOBranch branch0;
-
- private CDOBranch branch1;
-
- private CDOBranch branch2;
-
- private CDOBranch branch3;
-
- private CDOBranch branch4;
-
- private InternalCDORevision[] revisions0;
-
- private InternalCDORevision[] revisions1;
-
- private InternalCDORevision[] revisions4;
-
- private TestRevisionManager revisionManager;
-
- @Override
- public synchronized Map<String, Object> getTestProperties()
- {
- Map<String, Object> testProperties = super.getTestProperties();
- testProperties.put(IRepository.Props.SUPPORTING_AUDITS, "true");
- testProperties.put(IRepository.Props.SUPPORTING_BRANCHES, "true");
- return testProperties;
- }
-
- @Override
- protected void doSetUp() throws Exception
- {
- super.doSetUp();
-
- Field disableGC = ReflectUtil.getField(AbstractCDORevisionCache.class, "disableGC");
- ReflectUtil.setValue(disableGC, null, true);
-
- repository = getRepository();
- store = (MEMStore)repository.getStore();
-
- session = (InternalCDOSession)openSession();
- serverSession = repository.getSessionManager().getSession(session.getSessionID());
- StoreThreadLocal.setSession(serverSession);
-
- branchManager = session.getBranchManager();
- branchID = 0;
-
- branch0 = branchManager.getMainBranch();
- revisions0 = fillBranch(branch0, 10, 10, 20, 50, 20, DETACH);
-
- revisions1 = createBranch(revisions0[1], 5, 10, 20, 10, DETACH);
- branch1 = revisions1[0].getBranch();
-
- branch2 = createBranch(revisions1[3]);
-
- branch3 = createBranch(revisions1[1]);
-
- revisions4 = createBranch(branch3, branch3.getBase().getTimeStamp() + 10, 30, DETACH);
- branch4 = revisions4[0].getBranch();
-
- BranchingTest.dump("MEMStore", store.getAllRevisions());
- revisionManager = (TestRevisionManager)getRevisionManager(repository, session);
- }
-
- @Override
- protected void doTearDown() throws Exception
- {
- StoreThreadLocal.release();
- IOUtil.close(session);
-
- Field disableGC = ReflectUtil.getField(AbstractCDORevisionCache.class, "disableGC");
- ReflectUtil.setValue(disableGC, null, false);
-
- super.doTearDown();
- }
-
- protected InternalCDORevisionManager getRevisionManager(InternalRepository repository, InternalCDOSession session)
- {
- return repository.getRevisionManager();
- }
-
- protected String getLocation()
- {
- return "Server";
- }
-
- protected void dumpCache(CDOBranchPoint branchPoint)
- {
- BranchingTest.dump(getLocation() + "Cache: Getting " + branchPoint, revisionManager.getCache().getAllRevisions());
- }
-
- private InternalCDORevision[] fillBranch(CDOBranch branch, long offset, long... durations)
- {
- InternalCDORevision[] revisions = new InternalCDORevision[durations.length];
- long timeStamp = branch.getBase().getTimeStamp() + offset;
- for (int i = 0; i < durations.length; i++)
- {
- long duration = durations[i];
- CDOBranchPoint branchPoint = branch.getPoint(timeStamp);
-
- if (duration != DETACH)
- {
- timeStamp += duration;
-
- revisions[i] = new CDORevisionImpl(CLASS);
- revisions[i].setID(ID);
- revisions[i].setBranchPoint(branchPoint);
- revisions[i].setRevised(timeStamp - 1);
- revisions[i].setVersion(i + 1);
- store.addRevision(revisions[i], false);
- }
- else
- {
- revisions[i] = store.detachObject(ID, branch, timeStamp - 1);
- }
- }
-
- return revisions;
- }
-
- private InternalCDORevision[] createBranch(CDOBranch baseBranch, long baseTimeStamp, long offset, long... durations)
- {
- CDOBranch branch = baseBranch.createBranch("branch" + ++branchID, baseTimeStamp);
- return fillBranch(branch, offset, durations);
- }
-
- private InternalCDORevision[] createBranch(InternalCDORevision revision, long offset, long... durations)
- {
- CDOBranch baseBranch = revision.getBranch();
- long baseTimeStamp = getMiddleOfValidity(revision);
- return createBranch(baseBranch, baseTimeStamp, offset, durations);
- }
-
- private CDOBranch createBranch(InternalCDORevision revision)
- {
- CDOBranch baseBranch = revision.getBranch();
- long baseTimeStamp = getMiddleOfValidity(revision);
- return baseBranch.createBranch("branch" + ++branchID, baseTimeStamp);
- }
-
- private long getMiddleOfValidity(InternalCDORevision revision)
- {
- long timeStamp = revision.getTimeStamp();
- long revised = revision.getRevised();
- if (revised == CDOBranchPoint.UNSPECIFIED_DATE)
- {
- revised = timeStamp + 10000;
- }
-
- return timeStamp / 2 + revised / 2;
- }
-
- private InternalCDORevision getRevision(CDOBranch branch, long timeStamp)
- {
- CDOBranchPoint branchPoint = branch.getPoint(timeStamp);
- dumpCache(branchPoint);
- return revisionManager.getRevision(ID, branchPoint, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true);
- }
-
- private void prefetchBaseline(CDOBranch branch, int levelsUp)
- {
- long timeStamp = 0;
- while (--levelsUp >= 0)
- {
- timeStamp = branch.getBase().getTimeStamp();
- branch = branch.getBase().getBranch();
- }
-
- getRevision(branch, timeStamp);
- assertLoads(1);
- }
-
- private static void assertRevision(InternalCDORevision expected, InternalCDORevision actual)
- {
- if (expected == null)
- {
- assertEquals(null, actual);
- }
- else
- {
- assertEquals( //
- CDORevisionUtil.copyRevisionKey(expected), //
- CDORevisionUtil.copyRevisionKey(actual));
- }
- }
-
- private void assertLoads(int expected)
- {
- assertEquals(expected, revisionManager.getLoadCounter());
- revisionManager.resetLoadCounter();
- }
-
- public void testBranch0_Initial() throws Exception
- {
- CDOBranch branch = branch0;
- long timeStamp = revisions0[0].getTimeStamp() - 1;
- InternalCDORevision expected = null;
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1); // TODO Shouldn't this be 0?
- }
-
- public void testBranch0_Normal() throws Exception
- {
- CDOBranch branch = branch0;
- for (int i = 0; i < revisions0.length - 1; i++)
- {
- InternalCDORevision expected = revisions0[i];
- long timeStamp = getMiddleOfValidity(expected);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
- }
-
- public void testBranch0_Detached() throws Exception
- {
- CDOBranch branch = branch0;
- long timeStamp = revisions0[4].getTimeStamp() + 1;
- InternalCDORevision expected = null;
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testBranch0_Head() throws Exception
- {
- CDOBranch branch = branch0;
- long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE;
- InternalCDORevision expected = null;
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testBranch1_Initial() throws Exception
- {
- CDOBranch branch = branch1;
- long timeStamp = revisions1[0].getTimeStamp() - 1;
- InternalCDORevision expected = revisions0[1];
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testBranch1_Normal() throws Exception
- {
- CDOBranch branch = branch1;
- for (int i = 0; i < revisions1.length - 1; i++)
- {
- InternalCDORevision expected = revisions1[i];
- long timeStamp = getMiddleOfValidity(expected);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
- }
-
- public void testBranch1_Detached() throws Exception
- {
- CDOBranch branch = branch1;
- long timeStamp = revisions1[3].getTimeStamp() + 1;
- InternalCDORevision expected = null;
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testBranch1_Head() throws Exception
- {
- CDOBranch branch = branch1;
- long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE;
- InternalCDORevision expected = null;
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testBranch2_Initial() throws Exception
- {
- CDOBranch branch = branch2;
- long timeStamp = branch2.getBase().getTimeStamp() + 2;
- InternalCDORevision expected = null;
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testBranch2_Head() throws Exception
- {
- CDOBranch branch = branch2;
- long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE;
- InternalCDORevision expected = null;
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testBranch3_Initial() throws Exception
- {
- CDOBranch branch = branch3;
- long timeStamp = branch3.getBase().getTimeStamp() + 2;
- InternalCDORevision expected = revisions1[1];
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testBranch3_Head() throws Exception
- {
- CDOBranch branch = branch3;
- long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE;
- InternalCDORevision expected = revisions1[1];
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testBranch4_Initial() throws Exception
- {
- CDOBranch branch = branch4;
- long timeStamp = revisions4[0].getTimeStamp() - 1;
- InternalCDORevision expected = revisions1[1];
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testBranch4_Detached() throws Exception
- {
- CDOBranch branch = branch4;
- long timeStamp = revisions4[0].getTimeStamp() + 1;
- InternalCDORevision expected = null;
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testBranch4_Head() throws Exception
- {
- CDOBranch branch = branch4;
- long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE;
- InternalCDORevision expected = null;
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp1_Branch1_Initial() throws Exception
- {
- CDOBranch branch = branch1;
- long timeStamp = revisions1[0].getTimeStamp() - 1;
- InternalCDORevision expected = revisions0[1];
-
- prefetchBaseline(branch, 1);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp1_Branch1_Normal() throws Exception
- {
- CDOBranch branch = branch1;
-
- prefetchBaseline(branch, 1);
-
- for (int i = 0; i < revisions1.length - 1; i++)
- {
- InternalCDORevision expected = revisions1[i];
- long timeStamp = getMiddleOfValidity(expected);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
- }
-
- public void testAvailableUp1_Branch1_Detached() throws Exception
- {
- CDOBranch branch = branch1;
- long timeStamp = revisions1[3].getTimeStamp() + 1;
- InternalCDORevision expected = null;
-
- prefetchBaseline(branch, 1);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp1_Branch1_Head() throws Exception
- {
- CDOBranch branch = branch1;
- long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE;
- InternalCDORevision expected = null;
-
- prefetchBaseline(branch, 1);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp1_Branch2_Initial() throws Exception
- {
- CDOBranch branch = branch2;
- long timeStamp = branch2.getBase().getTimeStamp() + 2;
- InternalCDORevision expected = null;
-
- prefetchBaseline(branch, 1);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp1_Branch2_Head() throws Exception
- {
- CDOBranch branch = branch2;
- long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE;
- InternalCDORevision expected = null;
-
- prefetchBaseline(branch, 1);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp1_Branch3_Initial() throws Exception
- {
- CDOBranch branch = branch3;
- long timeStamp = branch3.getBase().getTimeStamp() + 2;
- InternalCDORevision expected = revisions1[1];
-
- prefetchBaseline(branch, 1);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp1_Branch3_Head() throws Exception
- {
- CDOBranch branch = branch3;
- long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE;
- InternalCDORevision expected = revisions1[1];
-
- prefetchBaseline(branch, 1);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp1_Branch4_Initial() throws Exception
- {
- CDOBranch branch = branch4;
- long timeStamp = revisions4[0].getTimeStamp() - 1;
- InternalCDORevision expected = revisions1[1];
-
- prefetchBaseline(branch, 1);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp1_Branch4_Detached() throws Exception
- {
- CDOBranch branch = branch4;
- long timeStamp = revisions4[0].getTimeStamp() + 1;
- InternalCDORevision expected = null;
-
- prefetchBaseline(branch, 1);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp1_Branch4_Head() throws Exception
- {
- CDOBranch branch = branch4;
- long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE;
- InternalCDORevision expected = null;
-
- prefetchBaseline(branch, 1);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp2_Branch2_Initial() throws Exception
- {
- CDOBranch branch = branch2;
- long timeStamp = branch2.getBase().getTimeStamp() + 2;
- InternalCDORevision expected = null;
-
- prefetchBaseline(branch, 2);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp2_Branch2_Head() throws Exception
- {
- CDOBranch branch = branch2;
- long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE;
- InternalCDORevision expected = null;
-
- prefetchBaseline(branch, 2);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp2_Branch3_Initial() throws Exception
- {
- CDOBranch branch = branch3;
- long timeStamp = branch3.getBase().getTimeStamp() + 2;
- InternalCDORevision expected = revisions1[1];
-
- prefetchBaseline(branch, 2);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp2_Branch3_Head() throws Exception
- {
- CDOBranch branch = branch3;
- long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE;
- InternalCDORevision expected = revisions1[1];
-
- prefetchBaseline(branch, 2);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp2_Branch4_Initial() throws Exception
- {
- CDOBranch branch = branch4;
- long timeStamp = revisions4[0].getTimeStamp() - 1;
- InternalCDORevision expected = revisions1[1];
-
- prefetchBaseline(branch, 2);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp2_Branch4_Detached() throws Exception
- {
- CDOBranch branch = branch4;
- long timeStamp = revisions4[0].getTimeStamp() + 1;
- InternalCDORevision expected = null;
-
- prefetchBaseline(branch, 2);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp2_Branch4_Head() throws Exception
- {
- CDOBranch branch = branch4;
- long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE;
- InternalCDORevision expected = null;
-
- prefetchBaseline(branch, 2);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp3_Branch4_Initial() throws Exception
- {
- CDOBranch branch = branch4;
- long timeStamp = revisions4[0].getTimeStamp() - 1;
- InternalCDORevision expected = revisions1[1];
-
- prefetchBaseline(branch, 3);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp3_Branch4_Detached() throws Exception
- {
- CDOBranch branch = branch4;
- long timeStamp = revisions4[0].getTimeStamp() + 1;
- InternalCDORevision expected = null;
-
- prefetchBaseline(branch, 3);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-
- public void testAvailableUp3_Branch4_Head() throws Exception
- {
- CDOBranch branch = branch4;
- long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE;
- InternalCDORevision expected = null;
-
- prefetchBaseline(branch, 3);
-
- InternalCDORevision revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(1);
-
- revision = getRevision(branch, timeStamp);
- assertRevision(expected, revision);
- assertLoads(0);
- }
-}
+/* + * Copyright (c) 2004 - 2012 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.tests; + +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.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.internal.common.revision.AbstractCDORevisionCache; +import org.eclipse.emf.cdo.internal.common.revision.CDORevisionImpl; +import org.eclipse.emf.cdo.internal.server.mem.MEMStore; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.StoreThreadLocal; +import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalSession; +import org.eclipse.emf.cdo.tests.config.IRepositoryConfig; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.Requires; +import org.eclipse.emf.cdo.tests.util.TestRevisionManager; + +import org.eclipse.net4j.util.ReflectUtil; +import org.eclipse.net4j.util.io.IOUtil; + +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EcorePackage; +import org.eclipse.emf.spi.cdo.InternalCDOSession; + +import java.lang.reflect.Field; +import java.util.Map; + +/** + * @author Eike Stepper + */ +@Requires({ IRepositoryConfig.CAPABILITY_BRANCHING, "MEM" }) +public class RevisionManagerTest extends AbstractCDOTest +{ + private static final EClass CLASS = EcorePackage.eINSTANCE.getEAnnotation(); + + private static final int DETACH = -1; + + protected InternalRepository repository; + + private MEMStore store; + + private InternalCDOSession session; + + private InternalSession serverSession; + + private InternalCDOBranchManager branchManager; + + private CDOID objectID; + + private int branchID; + + private CDOBranch branch0; + + private CDOBranch branch1; + + private CDOBranch branch2; + + private CDOBranch branch3; + + private CDOBranch branch4; + + private InternalCDORevision[] revisions0; + + private InternalCDORevision[] revisions1; + + private InternalCDORevision[] revisions4; + + private TestRevisionManager revisionManager; + + @Override + public synchronized Map<String, Object> getTestProperties() + { + Map<String, Object> testProperties = super.getTestProperties(); + testProperties.put(IRepository.Props.SUPPORTING_AUDITS, "true"); + testProperties.put(IRepository.Props.SUPPORTING_BRANCHES, "true"); + return testProperties; + } + + @Override + protected void doSetUp() throws Exception + { + super.doSetUp(); + + if (getRepositoryConfig().getIDGenerationLocation() == IDGenerationLocation.STORE) + { + objectID = CDOIDUtil.createLong(2); + } + else + { + objectID = CDOIDUtil.createUUID(); + } + + Field disableGC = ReflectUtil.getField(AbstractCDORevisionCache.class, "disableGC"); + ReflectUtil.setValue(disableGC, null, true); + + repository = getRepository(); + store = (MEMStore)repository.getStore(); + + session = (InternalCDOSession)openSession(); + serverSession = repository.getSessionManager().getSession(session.getSessionID()); + StoreThreadLocal.setSession(serverSession); + + branchManager = session.getBranchManager(); + branchID = 0; + + branch0 = branchManager.getMainBranch(); + revisions0 = fillBranch(branch0, 10, 10, 20, 50, 20, DETACH); + + revisions1 = createBranch(revisions0[1], 5, 10, 20, 10, DETACH); + branch1 = revisions1[0].getBranch(); + + branch2 = createBranch(revisions1[3]); + + branch3 = createBranch(revisions1[1]); + + revisions4 = createBranch(branch3, branch3.getBase().getTimeStamp() + 10, 30, DETACH); + branch4 = revisions4[0].getBranch(); + + BranchingTest.dump("MEMStore", store.getAllRevisions()); + revisionManager = (TestRevisionManager)getRevisionManager(repository, session); + } + + @Override + protected void doTearDown() throws Exception + { + StoreThreadLocal.release(); + IOUtil.close(session); + + Field disableGC = ReflectUtil.getField(AbstractCDORevisionCache.class, "disableGC"); + ReflectUtil.setValue(disableGC, null, false); + + super.doTearDown(); + } + + protected InternalCDORevisionManager getRevisionManager(InternalRepository repository, InternalCDOSession session) + { + return repository.getRevisionManager(); + } + + protected String getLocation() + { + return "Server"; + } + + protected void dumpCache(CDOBranchPoint branchPoint) + { + BranchingTest.dump(getLocation() + "Cache: Getting " + branchPoint, revisionManager.getCache().getAllRevisions()); + } + + private InternalCDORevision[] fillBranch(CDOBranch branch, long offset, long... durations) + { + InternalCDORevision[] revisions = new InternalCDORevision[durations.length]; + long timeStamp = branch.getBase().getTimeStamp() + offset; + for (int i = 0; i < durations.length; i++) + { + long duration = durations[i]; + CDOBranchPoint branchPoint = branch.getPoint(timeStamp); + + if (duration != DETACH) + { + timeStamp += duration; + + revisions[i] = new CDORevisionImpl(CLASS); + revisions[i].setID(objectID); + revisions[i].setBranchPoint(branchPoint); + revisions[i].setRevised(timeStamp - 1); + revisions[i].setVersion(i + 1); + store.addRevision(revisions[i], false); + } + else + { + revisions[i] = store.detachObject(objectID, branch, timeStamp - 1); + } + } + + return revisions; + } + + private InternalCDORevision[] createBranch(CDOBranch baseBranch, long baseTimeStamp, long offset, long... durations) + { + CDOBranch branch = baseBranch.createBranch("branch" + ++branchID, baseTimeStamp); + return fillBranch(branch, offset, durations); + } + + private InternalCDORevision[] createBranch(InternalCDORevision revision, long offset, long... durations) + { + CDOBranch baseBranch = revision.getBranch(); + long baseTimeStamp = getMiddleOfValidity(revision); + return createBranch(baseBranch, baseTimeStamp, offset, durations); + } + + private CDOBranch createBranch(InternalCDORevision revision) + { + CDOBranch baseBranch = revision.getBranch(); + long baseTimeStamp = getMiddleOfValidity(revision); + return baseBranch.createBranch("branch" + ++branchID, baseTimeStamp); + } + + private long getMiddleOfValidity(InternalCDORevision revision) + { + long timeStamp = revision.getTimeStamp(); + long revised = revision.getRevised(); + if (revised == CDOBranchPoint.UNSPECIFIED_DATE) + { + revised = timeStamp + 10000; + } + + return timeStamp / 2 + revised / 2; + } + + private InternalCDORevision getRevision(CDOBranch branch, long timeStamp) + { + CDOBranchPoint branchPoint = branch.getPoint(timeStamp); + dumpCache(branchPoint); + return revisionManager.getRevision(objectID, branchPoint, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true); + } + + private void prefetchBaseline(CDOBranch branch, int levelsUp) + { + long timeStamp = 0; + while (--levelsUp >= 0) + { + timeStamp = branch.getBase().getTimeStamp(); + branch = branch.getBase().getBranch(); + } + + getRevision(branch, timeStamp); + assertLoads(1); + } + + private static void assertRevision(InternalCDORevision expected, InternalCDORevision actual) + { + if (expected == null) + { + assertEquals(null, actual); + } + else + { + assertEquals( // + CDORevisionUtil.copyRevisionKey(expected), // + CDORevisionUtil.copyRevisionKey(actual)); + } + } + + private void assertLoads(int expected) + { + assertEquals(expected, revisionManager.getLoadCounter()); + revisionManager.resetLoadCounter(); + } + + public void testBranch0_Initial() throws Exception + { + CDOBranch branch = branch0; + long timeStamp = revisions0[0].getTimeStamp() - 1; + InternalCDORevision expected = null; + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); // TODO Shouldn't this be 0? + } + + public void testBranch0_Normal() throws Exception + { + CDOBranch branch = branch0; + for (int i = 0; i < revisions0.length - 1; i++) + { + InternalCDORevision expected = revisions0[i]; + long timeStamp = getMiddleOfValidity(expected); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + } + + public void testBranch0_Detached() throws Exception + { + CDOBranch branch = branch0; + long timeStamp = revisions0[4].getTimeStamp() + 1; + InternalCDORevision expected = null; + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testBranch0_Head() throws Exception + { + CDOBranch branch = branch0; + long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE; + InternalCDORevision expected = null; + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testBranch1_Initial() throws Exception + { + CDOBranch branch = branch1; + long timeStamp = revisions1[0].getTimeStamp() - 1; + InternalCDORevision expected = revisions0[1]; + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testBranch1_Normal() throws Exception + { + CDOBranch branch = branch1; + for (int i = 0; i < revisions1.length - 1; i++) + { + InternalCDORevision expected = revisions1[i]; + long timeStamp = getMiddleOfValidity(expected); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + } + + public void testBranch1_Detached() throws Exception + { + CDOBranch branch = branch1; + long timeStamp = revisions1[3].getTimeStamp() + 1; + InternalCDORevision expected = null; + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testBranch1_Head() throws Exception + { + CDOBranch branch = branch1; + long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE; + InternalCDORevision expected = null; + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testBranch2_Initial() throws Exception + { + CDOBranch branch = branch2; + long timeStamp = branch2.getBase().getTimeStamp() + 2; + InternalCDORevision expected = null; + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testBranch2_Head() throws Exception + { + CDOBranch branch = branch2; + long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE; + InternalCDORevision expected = null; + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testBranch3_Initial() throws Exception + { + CDOBranch branch = branch3; + long timeStamp = branch3.getBase().getTimeStamp() + 2; + InternalCDORevision expected = revisions1[1]; + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testBranch3_Head() throws Exception + { + CDOBranch branch = branch3; + long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE; + InternalCDORevision expected = revisions1[1]; + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testBranch4_Initial() throws Exception + { + CDOBranch branch = branch4; + long timeStamp = revisions4[0].getTimeStamp() - 1; + InternalCDORevision expected = revisions1[1]; + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testBranch4_Detached() throws Exception + { + CDOBranch branch = branch4; + long timeStamp = revisions4[0].getTimeStamp() + 1; + InternalCDORevision expected = null; + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testBranch4_Head() throws Exception + { + CDOBranch branch = branch4; + long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE; + InternalCDORevision expected = null; + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp1_Branch1_Initial() throws Exception + { + CDOBranch branch = branch1; + long timeStamp = revisions1[0].getTimeStamp() - 1; + InternalCDORevision expected = revisions0[1]; + + prefetchBaseline(branch, 1); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp1_Branch1_Normal() throws Exception + { + CDOBranch branch = branch1; + + prefetchBaseline(branch, 1); + + for (int i = 0; i < revisions1.length - 1; i++) + { + InternalCDORevision expected = revisions1[i]; + long timeStamp = getMiddleOfValidity(expected); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + } + + public void testAvailableUp1_Branch1_Detached() throws Exception + { + CDOBranch branch = branch1; + long timeStamp = revisions1[3].getTimeStamp() + 1; + InternalCDORevision expected = null; + + prefetchBaseline(branch, 1); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp1_Branch1_Head() throws Exception + { + CDOBranch branch = branch1; + long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE; + InternalCDORevision expected = null; + + prefetchBaseline(branch, 1); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp1_Branch2_Initial() throws Exception + { + CDOBranch branch = branch2; + long timeStamp = branch2.getBase().getTimeStamp() + 2; + InternalCDORevision expected = null; + + prefetchBaseline(branch, 1); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp1_Branch2_Head() throws Exception + { + CDOBranch branch = branch2; + long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE; + InternalCDORevision expected = null; + + prefetchBaseline(branch, 1); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp1_Branch3_Initial() throws Exception + { + CDOBranch branch = branch3; + long timeStamp = branch3.getBase().getTimeStamp() + 2; + InternalCDORevision expected = revisions1[1]; + + prefetchBaseline(branch, 1); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp1_Branch3_Head() throws Exception + { + CDOBranch branch = branch3; + long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE; + InternalCDORevision expected = revisions1[1]; + + prefetchBaseline(branch, 1); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp1_Branch4_Initial() throws Exception + { + CDOBranch branch = branch4; + long timeStamp = revisions4[0].getTimeStamp() - 1; + InternalCDORevision expected = revisions1[1]; + + prefetchBaseline(branch, 1); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp1_Branch4_Detached() throws Exception + { + CDOBranch branch = branch4; + long timeStamp = revisions4[0].getTimeStamp() + 1; + InternalCDORevision expected = null; + + prefetchBaseline(branch, 1); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp1_Branch4_Head() throws Exception + { + CDOBranch branch = branch4; + long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE; + InternalCDORevision expected = null; + + prefetchBaseline(branch, 1); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp2_Branch2_Initial() throws Exception + { + CDOBranch branch = branch2; + long timeStamp = branch2.getBase().getTimeStamp() + 2; + InternalCDORevision expected = null; + + prefetchBaseline(branch, 2); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp2_Branch2_Head() throws Exception + { + CDOBranch branch = branch2; + long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE; + InternalCDORevision expected = null; + + prefetchBaseline(branch, 2); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp2_Branch3_Initial() throws Exception + { + CDOBranch branch = branch3; + long timeStamp = branch3.getBase().getTimeStamp() + 2; + InternalCDORevision expected = revisions1[1]; + + prefetchBaseline(branch, 2); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp2_Branch3_Head() throws Exception + { + CDOBranch branch = branch3; + long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE; + InternalCDORevision expected = revisions1[1]; + + prefetchBaseline(branch, 2); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp2_Branch4_Initial() throws Exception + { + CDOBranch branch = branch4; + long timeStamp = revisions4[0].getTimeStamp() - 1; + InternalCDORevision expected = revisions1[1]; + + prefetchBaseline(branch, 2); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp2_Branch4_Detached() throws Exception + { + CDOBranch branch = branch4; + long timeStamp = revisions4[0].getTimeStamp() + 1; + InternalCDORevision expected = null; + + prefetchBaseline(branch, 2); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp2_Branch4_Head() throws Exception + { + CDOBranch branch = branch4; + long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE; + InternalCDORevision expected = null; + + prefetchBaseline(branch, 2); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp3_Branch4_Initial() throws Exception + { + CDOBranch branch = branch4; + long timeStamp = revisions4[0].getTimeStamp() - 1; + InternalCDORevision expected = revisions1[1]; + + prefetchBaseline(branch, 3); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp3_Branch4_Detached() throws Exception + { + CDOBranch branch = branch4; + long timeStamp = revisions4[0].getTimeStamp() + 1; + InternalCDORevision expected = null; + + prefetchBaseline(branch, 3); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } + + public void testAvailableUp3_Branch4_Head() throws Exception + { + CDOBranch branch = branch4; + long timeStamp = CDOBranchPoint.UNSPECIFIED_DATE; + InternalCDORevision expected = null; + + prefetchBaseline(branch, 3); + + InternalCDORevision revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(1); + + revision = getRevision(branch, timeStamp); + assertRevision(expected, revision); + assertLoads(0); + } +} diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/WorkspaceTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/WorkspaceTest.java index ec1eb79920..c6ba03758e 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/WorkspaceTest.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/WorkspaceTest.java @@ -1,1464 +1,1514 @@ -/*
- * Copyright (c) 2004 - 2012 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.tests;
-
-import org.eclipse.emf.cdo.common.branch.CDOBranch;
-import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
-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.revision.CDOAllRevisionsProvider;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
-import org.eclipse.emf.cdo.eresource.CDOResource;
-import org.eclipse.emf.cdo.server.IRepository;
-import org.eclipse.emf.cdo.server.IStore;
-import org.eclipse.emf.cdo.session.CDOSession;
-import org.eclipse.emf.cdo.session.CDOSessionConfiguration;
-import org.eclipse.emf.cdo.session.CDOSessionConfigurationFactory;
-import org.eclipse.emf.cdo.spi.server.InternalRepository;
-import org.eclipse.emf.cdo.spi.workspace.InternalCDOWorkspace;
-import org.eclipse.emf.cdo.tests.config.IRepositoryConfig;
-import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.Requires;
-import org.eclipse.emf.cdo.tests.model1.Customer;
-import org.eclipse.emf.cdo.tests.model1.OrderDetail;
-import org.eclipse.emf.cdo.tests.model1.Product1;
-import org.eclipse.emf.cdo.tests.model1.SalesOrder;
-import org.eclipse.emf.cdo.tests.model1.VAT;
-import org.eclipse.emf.cdo.tests.util.TestSessionConfiguration;
-import org.eclipse.emf.cdo.transaction.CDOMerger.ConflictException;
-import org.eclipse.emf.cdo.transaction.CDOTransaction;
-import org.eclipse.emf.cdo.util.CDOUtil;
-import org.eclipse.emf.cdo.util.CommitException;
-import org.eclipse.emf.cdo.view.CDOView;
-import org.eclipse.emf.cdo.workspace.CDOWorkspace;
-import org.eclipse.emf.cdo.workspace.CDOWorkspaceBase;
-import org.eclipse.emf.cdo.workspace.CDOWorkspaceConfiguration;
-import org.eclipse.emf.cdo.workspace.CDOWorkspaceUtil;
-
-import org.eclipse.net4j.jvm.JVMUtil;
-import org.eclipse.net4j.util.io.IOUtil;
-import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
-
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.spi.cdo.DefaultCDOMerger;
-import org.eclipse.emf.spi.cdo.DefaultCDOMerger.Conflict;
-
-import java.io.File;
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-/**
- * @author Eike Stepper
- */
-@Requires(IRepositoryConfig.CAPABILITY_AUDITING)
-public class WorkspaceTest extends AbstractCDOTest
-{
- private static final String RESOURCE = "/test1";
-
- private static final int PRODUCTS = 5;
-
- private static final int CUSTOMERS = 2;
-
- private static final int SALES_ORDERS_PER_CUSTOMER = 1;
-
- private static final int SALES_ORDERS = CUSTOMERS * SALES_ORDERS_PER_CUSTOMER;
-
- private static final int ORDER_DETAILS = SALES_ORDERS * PRODUCTS;
-
- private List<CDOWorkspace> workspaces = new ArrayList<CDOWorkspace>();
-
- private CDOTransaction transaction;
-
- private List<Product1> products;
-
- private List<Customer> customers;
-
- private List<SalesOrder> salesOrders;
-
- private List<OrderDetail> orderDetails;
-
- private int totalObjects;
-
- private IStore localStore;
-
- @Override
- protected void doSetUp() throws Exception
- {
- super.doSetUp();
- skipStoreWithoutHandleRevisions();
-
- CDOSession session = openSession();
- transaction = session.openTransaction();
-
- products = new ArrayList<Product1>();
- customers = new ArrayList<Customer>();
- orderDetails = new ArrayList<OrderDetail>();
- salesOrders = new ArrayList<SalesOrder>();
-
- createTestSet(transaction);
- assertEquals(PRODUCTS, products.size());
- assertEquals(CUSTOMERS, customers.size());
- assertEquals(SALES_ORDERS, salesOrders.size());
- assertEquals(ORDER_DETAILS, orderDetails.size());
-
- JVMUtil.prepareContainer(getClientContainer());
- localStore = createLocalStore();
- CDOUtil.setLegacyModeDefault(true);
- }
-
- @Override
- protected void doTearDown() throws Exception
- {
- disableConsole();
- CDOUtil.setLegacyModeDefault(false);
- for (CDOWorkspace workspace : workspaces)
- {
- IOUtil.closeSilent(workspace);
- }
-
- workspaces.clear();
- super.doTearDown();
- }
-
- @Override
- public synchronized Map<String, Object> getTestProperties()
- {
- Map<String, Object> map = super.getTestProperties();
- map.put(IRepository.Props.ENSURE_REFERENTIAL_INTEGRITY, "false");
- return map;
- }
-
- @CleanRepositoriesBefore
- public void testCheckout() throws Exception
- {
- checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
- assertEquals(2 + totalObjects, dumpLocalStore(null)); // Root resource + test folder + totalObjects
- }
-
- public void testReadObjects() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOView view = workspace.openView();
- CDOResource resource = view.getResource(getResourcePath(RESOURCE));
- assertEquals(totalObjects, dumpObjects(null, resource));
- }
-
- public void testModifyObjects() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (EObject object : resource.getContents())
- {
- if (object instanceof Product1)
- {
- Product1 product = (Product1)object;
- product.setName("MODIFIED_" + product.getName());
- }
- }
-
- assertEquals(true, transaction.isDirty());
- assertEquals(PRODUCTS, transaction.getDirtyObjects().size());
-
- transaction.commit();
- assertEquals(false, transaction.isDirty());
- assertEquals(0, transaction.getDirtyObjects().size());
-
- clearCache(transaction.getSession().getRevisionManager());
-
- CDOView view = workspace.openView();
- resource = view.getResource(getResourcePath(RESOURCE));
- assertEquals(totalObjects, dumpObjects(null, resource));
- for (EObject object : resource.getContents())
- {
- if (object instanceof Product1)
- {
- Product1 product = (Product1)object;
- assertEquals(true, product.getName().startsWith("MODIFIED_"));
- }
- }
- }
-
- @Requires(IRepositoryConfig.CAPABILITY_UUIDS)
- public void testAddObjects() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (int i = 0; i < PRODUCTS; i++)
- {
- resource.getContents().add(createProduct(i));
- }
-
- assertEquals(true, transaction.isDirty());
- assertEquals(1, transaction.getDirtyObjects().size());
- assertEquals(PRODUCTS, transaction.getNewObjects().size());
-
- transaction.commit();
- assertEquals(false, transaction.isDirty());
- assertEquals(0, transaction.getDirtyObjects().size());
- assertEquals(0, transaction.getNewObjects().size());
-
- clearCache(transaction.getSession().getRevisionManager());
-
- CDOView view = workspace.openView();
- resource = view.getResource(getResourcePath(RESOURCE));
- assertEquals(totalObjects + PRODUCTS, dumpObjects("--> ", resource));
- }
-
- public void testDetachObjects() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (EObject object : resource.getContents())
- {
- if (object instanceof SalesOrder)
- {
- ((SalesOrder)object).getOrderDetails().clear();
- }
-
- if (object instanceof Product1)
- {
- ((Product1)object).getOrderDetails().clear();
- }
- }
-
- assertEquals(true, transaction.isDirty());
- assertEquals(SALES_ORDERS + PRODUCTS, transaction.getDirtyObjects().size());
- assertEquals(ORDER_DETAILS, transaction.getDetachedObjects().size());
-
- transaction.commit();
- assertEquals(false, transaction.isDirty());
- assertEquals(0, transaction.getDirtyObjects().size());
- assertEquals(0, transaction.getDetachedObjects().size());
-
- clearCache(transaction.getSession().getRevisionManager());
-
- CDOView view = workspace.openView();
- resource = view.getResource(getResourcePath(RESOURCE));
- assertEquals(totalObjects - ORDER_DETAILS, dumpObjects(null, resource));
- for (EObject object : resource.getContents())
- {
- if (object instanceof SalesOrder)
- {
- if (!((SalesOrder)object).getOrderDetails().isEmpty())
- {
- fail("All order details should be detached");
- }
- }
- }
- }
-
- public void testModifyObjects2() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (EObject object : resource.getContents())
- {
- if (object instanceof Product1)
- {
- Product1 product = (Product1)object;
- product.setName("MODIFIED_" + product.getName());
- }
- }
-
- assertEquals(true, transaction.isDirty());
- assertEquals(PRODUCTS, transaction.getDirtyObjects().size());
-
- transaction.commit();
- assertEquals(false, transaction.isDirty());
- assertEquals(0, transaction.getDirtyObjects().size());
-
- for (EObject object : resource.getContents())
- {
- if (object instanceof Product1)
- {
- Product1 product = (Product1)object;
- product.setName("MODIFIED2_" + product.getName());
- }
- }
-
- assertEquals(true, transaction.isDirty());
- assertEquals(PRODUCTS, transaction.getDirtyObjects().size());
-
- transaction.commit();
- assertEquals(false, transaction.isDirty());
- assertEquals(0, transaction.getDirtyObjects().size());
-
- clearCache(transaction.getSession().getRevisionManager());
-
- CDOView view = workspace.openView();
- resource = view.getResource(getResourcePath(RESOURCE));
- assertEquals(totalObjects, dumpObjects(null, resource));
- for (EObject object : resource.getContents())
- {
- if (object instanceof Product1)
- {
- Product1 product = (Product1)object;
- assertEquals(true, product.getName().startsWith("MODIFIED2_"));
- }
- }
- }
-
- @Requires(IRepositoryConfig.CAPABILITY_UUIDS)
- public void testAddObjects2() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (int i = 0; i < PRODUCTS; i++)
- {
- resource.getContents().add(createProduct(i));
- }
-
- assertEquals(true, transaction.isDirty());
- assertEquals(1, transaction.getDirtyObjects().size());
- assertEquals(PRODUCTS, transaction.getNewObjects().size());
-
- transaction.commit();
- assertEquals(false, transaction.isDirty());
- assertEquals(0, transaction.getDirtyObjects().size());
- assertEquals(0, transaction.getNewObjects().size());
-
- for (int i = 0; i < PRODUCTS; i++)
- {
- resource.getContents().add(createProduct(i));
- }
-
- assertEquals(true, transaction.isDirty());
- assertEquals(1, transaction.getDirtyObjects().size());
- assertEquals(PRODUCTS, transaction.getNewObjects().size());
-
- transaction.commit();
- assertEquals(false, transaction.isDirty());
- assertEquals(0, transaction.getDirtyObjects().size());
- assertEquals(0, transaction.getNewObjects().size());
-
- clearCache(transaction.getSession().getRevisionManager());
-
- CDOView view = workspace.openView();
- resource = view.getResource(getResourcePath(RESOURCE));
- assertEquals(totalObjects + 2 * PRODUCTS, dumpObjects("--> ", resource));
- }
-
- public void testDetachObjects2() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (EObject object : resource.getContents())
- {
- if (object instanceof SalesOrder)
- {
- ((SalesOrder)object).getOrderDetails().clear();
- }
-
- if (object instanceof Product1)
- {
- ((Product1)object).getOrderDetails().clear();
- }
- }
-
- assertEquals(true, transaction.isDirty());
- assertEquals(SALES_ORDERS + PRODUCTS, transaction.getDirtyObjects().size());
- assertEquals(ORDER_DETAILS, transaction.getDetachedObjects().size());
-
- transaction.commit();
- assertEquals(false, transaction.isDirty());
- assertEquals(0, transaction.getDirtyObjects().size());
- assertEquals(0, transaction.getDetachedObjects().size());
-
- for (Iterator<EObject> it = resource.getContents().iterator(); it.hasNext();)
- {
- EObject object = it.next();
- if (object instanceof SalesOrder)
- {
- it.remove();
- }
-
- if (object instanceof Customer)
- {
- ((Customer)object).getSalesOrders().clear();
- }
- }
-
- assertEquals(true, transaction.isDirty());
- assertEquals(1 + CUSTOMERS, transaction.getDirtyObjects().size());
- assertEquals(SALES_ORDERS, transaction.getDetachedObjects().size());
-
- transaction.commit();
- assertEquals(false, transaction.isDirty());
- assertEquals(0, transaction.getDirtyObjects().size());
- assertEquals(0, transaction.getDetachedObjects().size());
-
- clearCache(transaction.getSession().getRevisionManager());
-
- CDOView view = workspace.openView();
- resource = view.getResource(getResourcePath(RESOURCE));
- assertEquals(totalObjects - ORDER_DETAILS - SALES_ORDERS, dumpObjects(null, resource));
- for (EObject object : resource.getContents())
- {
- if (object instanceof SalesOrder)
- {
- if (!((SalesOrder)object).getOrderDetails().isEmpty())
- {
- fail("All order details should be detached");
- }
- }
-
- if (object instanceof Customer)
- {
- if (!((Customer)object).getSalesOrders().isEmpty())
- {
- fail("All sales orders should be detached");
- }
- }
- }
- }
-
- public void testLocalChangesAfterModify() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (EObject object : resource.getContents())
- {
- if (object instanceof Product1)
- {
- Product1 product = (Product1)object;
- product.setName("MODIFIED_" + product.getName());
- }
- }
-
- transaction.commit();
-
- CDOChangeSetData changes = workspace.getLocalChanges();
- assertEquals(0, changes.getNewObjects().size());
- assertEquals(PRODUCTS, changes.getChangedObjects().size());
- assertEquals(0, changes.getDetachedObjects().size());
- }
-
- @Requires(IRepositoryConfig.CAPABILITY_UUIDS)
- public void testLocalChangesAfterAdd() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (int i = 0; i < PRODUCTS; i++)
- {
- resource.getContents().add(createProduct(i));
- }
-
- transaction.commit();
-
- CDOChangeSetData changes = workspace.getLocalChanges();
- assertEquals(PRODUCTS, changes.getNewObjects().size());
- assertEquals(1, changes.getChangedObjects().size());
- assertEquals(0, changes.getDetachedObjects().size());
- }
-
- public void testLocalChangesAfterDetach() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (EObject object : resource.getContents())
- {
- if (object instanceof SalesOrder)
- {
- ((SalesOrder)object).getOrderDetails().clear();
- }
-
- if (object instanceof Product1)
- {
- ((Product1)object).getOrderDetails().clear();
- }
- }
-
- transaction.commit();
-
- CDOChangeSetData changes = workspace.getLocalChanges();
- assertEquals(0, changes.getNewObjects().size());
- assertEquals(SALES_ORDERS + PRODUCTS, changes.getChangedObjects().size());
- assertEquals(ORDER_DETAILS, changes.getDetachedObjects().size());
- }
-
- public void testLocalChangesAfterModify2() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (EObject object : resource.getContents())
- {
- if (object instanceof Product1)
- {
- Product1 product = (Product1)object;
- product.setName("MODIFIED_" + product.getName());
- }
- }
-
- transaction.commit();
-
- for (EObject object : resource.getContents())
- {
- if (object instanceof Product1)
- {
- Product1 product = (Product1)object;
- product.setName("MODIFIED2_" + product.getName());
- }
- }
-
- transaction.commit();
-
- CDOChangeSetData changes = workspace.getLocalChanges();
- assertEquals(0, changes.getNewObjects().size());
- assertEquals(PRODUCTS, changes.getChangedObjects().size());
- assertEquals(0, changes.getDetachedObjects().size());
- }
-
- @Requires(IRepositoryConfig.CAPABILITY_UUIDS)
- public void testLocalChangesAfterAdd2() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (int i = 0; i < PRODUCTS; i++)
- {
- resource.getContents().add(createProduct(i));
- }
-
- transaction.commit();
-
- for (int i = 0; i < PRODUCTS; i++)
- {
- resource.getContents().add(createProduct(i));
- }
-
- transaction.commit();
-
- CDOChangeSetData changes = workspace.getLocalChanges();
- assertEquals(2 * PRODUCTS, changes.getNewObjects().size());
- assertEquals(1, changes.getChangedObjects().size());
- assertEquals(0, changes.getDetachedObjects().size());
- }
-
- public void testLocalChangesAfterDetach2() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (EObject object : resource.getContents())
- {
- if (object instanceof SalesOrder)
- {
- ((SalesOrder)object).getOrderDetails().clear();
- }
-
- if (object instanceof Product1)
- {
- ((Product1)object).getOrderDetails().clear();
- }
- }
-
- transaction.commit();
-
- for (Iterator<EObject> it = resource.getContents().iterator(); it.hasNext();)
- {
- EObject object = it.next();
- if (object instanceof SalesOrder)
- {
- it.remove();
- }
-
- if (object instanceof Customer)
- {
- ((Customer)object).getSalesOrders().clear();
- }
- }
-
- transaction.commit();
-
- CDOChangeSetData changes = workspace.getLocalChanges();
- assertEquals(0, changes.getNewObjects().size());
- assertEquals(1 + CUSTOMERS + PRODUCTS, changes.getChangedObjects().size());
- assertEquals(ORDER_DETAILS + SALES_ORDERS, changes.getDetachedObjects().size());
- }
-
- public void testCheckinAfterModify() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (EObject object : resource.getContents())
- {
- if (object instanceof Product1)
- {
- Product1 product = (Product1)object;
- product.setName("MODIFIED_" + product.getName());
- }
- }
-
- transaction.commit();
-
- CDOCommitInfo info = workspace.checkin();
- assertEquals(0, info.getNewObjects().size());
- assertEquals(PRODUCTS, info.getChangedObjects().size());
- assertEquals(0, info.getDetachedObjects().size());
-
- CDOWorkspaceBase base = workspace.getBase();
- assertEquals(0, base.getIDs().size());
- assertEquals(info.getBranch().getPathName(), workspace.getBranchPath());
- assertEquals(info.getTimeStamp(), workspace.getTimeStamp());
- }
-
- @Requires(IRepositoryConfig.CAPABILITY_UUIDS)
- public void testCheckinAfterAdd() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
- IOUtil.ERR().println("Checkout done");
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (int i = 0; i < PRODUCTS; i++)
- {
- resource.getContents().add(createProduct(i));
- }
-
- transaction.commit();
-
- CDOCommitInfo info = workspace.checkin();
- assertEquals(PRODUCTS, info.getNewObjects().size());
- assertEquals(1, info.getChangedObjects().size());
- assertEquals(0, info.getDetachedObjects().size());
-
- CDOWorkspaceBase base = workspace.getBase();
- assertEquals(0, base.getIDs().size());
- assertEquals(info.getBranch().getPathName(), workspace.getBranchPath());
- assertEquals(info.getTimeStamp(), workspace.getTimeStamp());
- }
-
- public void testCheckinAfterDetach() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (EObject object : resource.getContents())
- {
- if (object instanceof SalesOrder)
- {
- ((SalesOrder)object).getOrderDetails().clear();
- }
-
- if (object instanceof Product1)
- {
- ((Product1)object).getOrderDetails().clear();
- }
- }
-
- transaction.commit();
-
- CDOCommitInfo info = workspace.checkin();
- assertEquals(0, info.getNewObjects().size());
- assertEquals(SALES_ORDERS + PRODUCTS, info.getChangedObjects().size());
- assertEquals(ORDER_DETAILS, info.getDetachedObjects().size());
-
- CDOWorkspaceBase base = workspace.getBase();
- assertEquals(0, base.getIDs().size());
- assertEquals(info.getBranch().getPathName(), workspace.getBranchPath());
- assertEquals(info.getTimeStamp(), workspace.getTimeStamp());
- }
-
- public void testCheckinAfterModify2() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (EObject object : resource.getContents())
- {
- if (object instanceof Product1)
- {
- Product1 product = (Product1)object;
- product.setName("MODIFIED_" + product.getName());
- }
- }
-
- transaction.commit();
-
- for (EObject object : resource.getContents())
- {
- if (object instanceof Product1)
- {
- Product1 product = (Product1)object;
- product.setName("MODIFIED2_" + product.getName());
- }
- }
-
- transaction.commit();
-
- CDOCommitInfo info = workspace.checkin();
- assertEquals(0, info.getNewObjects().size());
- assertEquals(PRODUCTS, info.getChangedObjects().size());
- assertEquals(0, info.getDetachedObjects().size());
-
- CDOWorkspaceBase base = workspace.getBase();
- assertEquals(0, base.getIDs().size());
- assertEquals(info.getBranch().getPathName(), workspace.getBranchPath());
- assertEquals(info.getTimeStamp(), workspace.getTimeStamp());
- }
-
- @Requires(IRepositoryConfig.CAPABILITY_UUIDS)
- public void testCheckinAfterAdd2() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (int i = 0; i < PRODUCTS; i++)
- {
- resource.getContents().add(createProduct(i));
- }
-
- transaction.commit();
-
- for (int i = 0; i < PRODUCTS; i++)
- {
- resource.getContents().add(createProduct(i));
- }
-
- transaction.commit();
-
- CDOCommitInfo info = workspace.checkin();
- assertEquals(2 * PRODUCTS, info.getNewObjects().size());
- assertEquals(1, info.getChangedObjects().size());
- assertEquals(0, info.getDetachedObjects().size());
-
- CDOWorkspaceBase base = workspace.getBase();
- assertEquals(0, base.getIDs().size());
- assertEquals(info.getBranch().getPathName(), workspace.getBranchPath());
- assertEquals(info.getTimeStamp(), workspace.getTimeStamp());
- }
-
- public void testCheckinAfterDetach2() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (EObject object : resource.getContents())
- {
- if (object instanceof SalesOrder)
- {
- ((SalesOrder)object).getOrderDetails().clear();
- }
-
- if (object instanceof Product1)
- {
- ((Product1)object).getOrderDetails().clear();
- }
- }
-
- transaction.commit();
-
- for (Iterator<EObject> it = resource.getContents().iterator(); it.hasNext();)
- {
- EObject object = it.next();
- if (object instanceof SalesOrder)
- {
- it.remove();
- }
-
- if (object instanceof Customer)
- {
- ((Customer)object).getSalesOrders().clear();
- }
- }
-
- transaction.commit();
-
- CDOCommitInfo info = workspace.checkin();
- assertEquals(0, info.getNewObjects().size());
- assertEquals(1 + CUSTOMERS + PRODUCTS, info.getChangedObjects().size());
- assertEquals(ORDER_DETAILS + SALES_ORDERS, info.getDetachedObjects().size());
-
- CDOWorkspaceBase base = workspace.getBase();
- assertEquals(0, base.getIDs().size());
- assertEquals(info.getBranch().getPathName(), workspace.getBranchPath());
- assertEquals(info.getTimeStamp(), workspace.getTimeStamp());
- }
-
- public void testCheckin2AfterModify() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (EObject object : resource.getContents())
- {
- if (object instanceof Product1)
- {
- Product1 product = (Product1)object;
- product.setName("MODIFIED_" + product.getName());
- }
- }
-
- transaction.commit();
-
- CDOCommitInfo info = workspace.checkin();
- assertEquals(0, info.getNewObjects().size());
- assertEquals(PRODUCTS, info.getChangedObjects().size());
- assertEquals(0, info.getDetachedObjects().size());
-
- CDOWorkspaceBase base = workspace.getBase();
- assertEquals(0, base.getIDs().size());
- assertEquals(info.getBranch().getPathName(), workspace.getBranchPath());
- assertEquals(info.getTimeStamp(), workspace.getTimeStamp());
-
- transaction = workspace.openTransaction();
- resource = transaction.getResource(getResourcePath(RESOURCE));
- for (EObject object : resource.getContents())
- {
- if (object instanceof Product1)
- {
- Product1 product = (Product1)object;
- product.setName("MODIFIED2_" + product.getName());
- }
- }
-
- transaction.commit();
-
- info = workspace.checkin();
- assertEquals(0, info.getNewObjects().size());
- assertEquals(PRODUCTS, info.getChangedObjects().size());
- assertEquals(0, info.getDetachedObjects().size());
-
- base = workspace.getBase();
- assertEquals(0, base.getIDs().size());
- assertEquals(info.getBranch().getPathName(), workspace.getBranchPath());
- assertEquals(info.getTimeStamp(), workspace.getTimeStamp());
- }
-
- @Requires(IRepositoryConfig.CAPABILITY_UUIDS)
- public void testCheckin2AfterAdd() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (int i = 0; i < PRODUCTS; i++)
- {
- resource.getContents().add(createProduct(i));
- }
-
- transaction.commit();
-
- CDOCommitInfo info = workspace.checkin();
- assertEquals(PRODUCTS, info.getNewObjects().size());
- assertEquals(1, info.getChangedObjects().size());
- assertEquals(0, info.getDetachedObjects().size());
-
- CDOWorkspaceBase base = workspace.getBase();
- assertEquals(0, base.getIDs().size());
- assertEquals(info.getBranch().getPathName(), workspace.getBranchPath());
- assertEquals(info.getTimeStamp(), workspace.getTimeStamp());
-
- transaction = workspace.openTransaction();
- resource = transaction.getResource(getResourcePath(RESOURCE));
- for (int i = 0; i < PRODUCTS; i++)
- {
- resource.getContents().add(createProduct(i));
- }
-
- transaction.commit();
-
- info = workspace.checkin();
- assertEquals(PRODUCTS, info.getNewObjects().size());
- assertEquals(1, info.getChangedObjects().size());
- assertEquals(0, info.getDetachedObjects().size());
-
- base = workspace.getBase();
- assertEquals(0, base.getIDs().size());
- assertEquals(info.getBranch().getPathName(), workspace.getBranchPath());
- assertEquals(info.getTimeStamp(), workspace.getTimeStamp());
- }
-
- public void testCheckin2AfterDetach() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
-
- CDOTransaction transaction = workspace.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (EObject object : resource.getContents())
- {
- if (object instanceof SalesOrder)
- {
- ((SalesOrder)object).getOrderDetails().clear();
- }
-
- if (object instanceof Product1)
- {
- ((Product1)object).getOrderDetails().clear();
- }
- }
-
- transaction.commit();
-
- CDOCommitInfo info = workspace.checkin();
- assertEquals(0, info.getNewObjects().size());
- assertEquals(SALES_ORDERS + PRODUCTS, info.getChangedObjects().size());
- assertEquals(ORDER_DETAILS, info.getDetachedObjects().size());
-
- CDOWorkspaceBase base = workspace.getBase();
- assertEquals(0, base.getIDs().size());
- assertEquals(info.getBranch().getPathName(), workspace.getBranchPath());
- assertEquals(info.getTimeStamp(), workspace.getTimeStamp());
-
- transaction = workspace.openTransaction();
- resource = transaction.getResource(getResourcePath(RESOURCE));
- for (Iterator<EObject> it = resource.getContents().iterator(); it.hasNext();)
- {
- EObject object = it.next();
- if (object instanceof SalesOrder)
- {
- it.remove();
- }
-
- if (object instanceof Customer)
- {
- ((Customer)object).getSalesOrders().clear();
- }
- }
-
- transaction.commit();
-
- info = workspace.checkin();
- assertEquals(0, info.getNewObjects().size());
- assertEquals(1 + CUSTOMERS, info.getChangedObjects().size());
- assertEquals(SALES_ORDERS, info.getDetachedObjects().size());
-
- base = workspace.getBase();
- assertEquals(0, base.getIDs().size());
- assertEquals(info.getBranch().getPathName(), workspace.getBranchPath());
- assertEquals(info.getTimeStamp(), workspace.getTimeStamp());
- }
-
- public void testUpdateAfterMasterModify() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
- assertNotSame(CDOBranchPoint.UNSPECIFIED_DATE, workspace.getTimeStamp());
-
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (EObject object : resource.getContents())
- {
- if (object instanceof Product1)
- {
- Product1 product = (Product1)object;
- product.setName("MODIFIED_" + product.getName());
- }
- }
-
- transaction.commit();
-
- CDOTransaction local = workspace.update(null);
- assertEquals(true, local.isDirty());
- assertEquals(0, local.getNewObjects().size());
- assertEquals(PRODUCTS, local.getDirtyObjects().size());
- assertEquals(0, local.getDetachedObjects().size());
-
- local.commit();
- assertEquals(false, local.isDirty());
- assertEquals(0, local.getNewObjects().size());
- assertEquals(0, local.getDirtyObjects().size());
- assertEquals(0, local.getDetachedObjects().size());
- assertEquals(0, workspace.getBase().getIDs().size());
-
- CDOView view = workspace.openView();
- resource = view.getResource(getResourcePath(RESOURCE));
- assertEquals(totalObjects, dumpObjects(null, resource));
- }
-
- @Requires(IRepositoryConfig.CAPABILITY_UUIDS)
- public void testUpdateAfterMasterAdd() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
- assertNotSame(CDOBranchPoint.UNSPECIFIED_DATE, workspace.getTimeStamp());
-
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- resource.getContents().add(createProduct(9999));
- transaction.commit();
-
- CDOTransaction local = workspace.update(null);
- assertEquals(true, local.isDirty());
- assertEquals(1, local.getNewObjects().size());
- assertEquals(1, local.getDirtyObjects().size());
- assertEquals(0, local.getDetachedObjects().size());
-
- local.commit();
- assertEquals(false, local.isDirty());
- assertEquals(0, local.getNewObjects().size());
- assertEquals(0, local.getDirtyObjects().size());
- assertEquals(0, local.getDetachedObjects().size());
- assertEquals(0, workspace.getBase().getIDs().size());
-
- CDOView view = workspace.openView();
- resource = view.getResource(getResourcePath(RESOURCE));
- assertEquals(totalObjects + 1, dumpObjects(null, resource));
- }
-
- @Requires(IRepositoryConfig.CAPABILITY_UUIDS)
- public void testUpdateTwiceAfterMasterAdd() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
- assertNotSame(CDOBranchPoint.UNSPECIFIED_DATE, workspace.getTimeStamp());
-
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- resource.getContents().add(createProduct(9999));
- transaction.commit();
-
- CDOTransaction local = workspace.update(null);
- assertEquals(true, local.isDirty());
- assertEquals(1, local.getNewObjects().size());
- assertEquals(1, local.getDirtyObjects().size());
- assertEquals(0, local.getDetachedObjects().size());
-
- local.commit();
- assertEquals(false, local.isDirty());
- assertEquals(0, local.getNewObjects().size());
- assertEquals(0, local.getDirtyObjects().size());
- assertEquals(0, local.getDetachedObjects().size());
- assertEquals(0, workspace.getBase().getIDs().size());
- local.close();
-
- local = workspace.update(null);
- assertEquals(false, local.isDirty());
- assertEquals(0, local.getNewObjects().size());
- assertEquals(0, local.getDirtyObjects().size());
- assertEquals(0, local.getDetachedObjects().size());
- assertEquals(0, workspace.getBase().getIDs().size());
-
- local.commit();
- assertEquals(false, local.isDirty());
- assertEquals(0, local.getNewObjects().size());
- assertEquals(0, local.getDirtyObjects().size());
- assertEquals(0, local.getDetachedObjects().size());
- assertEquals(0, workspace.getBase().getIDs().size());
-
- CDOView view = workspace.openView();
- resource = view.getResource(getResourcePath(RESOURCE));
- assertEquals(totalObjects + 1, dumpObjects(null, resource));
- }
-
- public void testUpdateAfterMasterDetach() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
- assertNotSame(CDOBranchPoint.UNSPECIFIED_DATE, workspace.getTimeStamp());
-
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- for (EObject object : resource.getContents())
- {
- if (object instanceof SalesOrder)
- {
- ((SalesOrder)object).getOrderDetails().clear();
- }
-
- if (object instanceof Product1)
- {
- ((Product1)object).getOrderDetails().clear();
- }
- }
-
- transaction.commit();
-
- CDOTransaction local = workspace.update(null);
- assertEquals(true, local.isDirty());
- assertEquals(SALES_ORDERS + PRODUCTS, local.getDirtyObjects().size());
- assertEquals(ORDER_DETAILS, local.getDetachedObjects().size());
-
- local.commit();
- assertEquals(false, local.isDirty());
- assertEquals(0, local.getNewObjects().size());
- assertEquals(0, local.getDirtyObjects().size());
- assertEquals(0, local.getDetachedObjects().size());
- assertEquals(0, workspace.getBase().getIDs().size());
-
- CDOView view = workspace.openView();
- resource = view.getResource(getResourcePath(RESOURCE));
- assertEquals(totalObjects - ORDER_DETAILS, dumpObjects(null, resource));
- }
-
- public void testUpdateAfterMasterAndLocalModify() throws Exception
- {
- // Checkout local
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
- assertNotSame(CDOBranchPoint.UNSPECIFIED_DATE, workspace.getTimeStamp());
-
- // Modify master
- assertEquals(1, modifyProduct(transaction, 1, "MODIFIED_"));
- transaction.commit();
-
- // Modify local
- CDOTransaction local = workspace.openTransaction();
- assertEquals(1, modifyProduct(local, 2, "MODIFIED_"));
- local.commit();
- local.close();
-
- // Update local
- local = workspace.update(new DefaultCDOMerger.PerFeature.ManyValued());
- assertEquals(true, local.isDirty());
- assertEquals(0, local.getNewObjects().size());
- assertEquals(1, local.getDirtyObjects().size());
- assertEquals(0, local.getDetachedObjects().size());
-
- // Commit local
- local.commit();
- assertEquals(false, local.isDirty());
- assertEquals(0, local.getNewObjects().size());
- assertEquals(0, local.getDirtyObjects().size());
- assertEquals(0, local.getDetachedObjects().size());
- assertEquals(1, workspace.getBase().getIDs().size());
-
- // Verify local
- CDOView view = workspace.openView();
- assertEquals(2, countModifiedProduct(view));
- }
-
- @Requires(IRepositoryConfig.CAPABILITY_UUIDS)
- public void testUpdateAfterMasterAndLocalAdd() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
- assertNotSame(CDOBranchPoint.UNSPECIFIED_DATE, workspace.getTimeStamp());
-
- CDOResource resource = transaction.getResource(getResourcePath(RESOURCE));
- resource.getContents().add(createProduct(9999));
- transaction.commit();
-
- CDOTransaction local = workspace.openTransaction();
- resource = local.getResource(getResourcePath(RESOURCE));
- for (int i = 0; i < PRODUCTS; i++)
- {
- resource.getContents().add(createProduct(i));
- }
-
- local.commit();
- local.close();
-
- local = workspace.update(new DefaultCDOMerger.PerFeature.ManyValued());
- assertEquals(true, local.isDirty());
- assertEquals(1, local.getNewObjects().size());
- assertEquals(1, local.getDirtyObjects().size());
- assertEquals(0, local.getDetachedObjects().size());
-
- local.commit();
- assertEquals(false, local.isDirty());
- assertEquals(0, local.getNewObjects().size());
- assertEquals(0, local.getDirtyObjects().size());
- assertEquals(0, local.getDetachedObjects().size());
- assertEquals(6, workspace.getBase().getIDs().size());
-
- CDOView view = workspace.openView();
- resource = view.getResource(getResourcePath(RESOURCE));
- assertEquals(totalObjects + 1 + PRODUCTS, dumpObjects(null, resource));
- }
-
- public void testNoConflictMasterAndLocalModify() throws Exception
- {
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
- assertNotSame(CDOBranchPoint.UNSPECIFIED_DATE, workspace.getTimeStamp());
-
- assertEquals(1, modifyProduct(transaction, 1, "MODIFIED_"));
- transaction.commit();
-
- CDOTransaction local = workspace.openTransaction();
- assertEquals(1, modifyProduct(local, 1, "MODIFIED_"));
- local.commit();
- local.close();
-
- DefaultCDOMerger.PerFeature.ManyValued merger = new DefaultCDOMerger.PerFeature.ManyValued();
- local = workspace.update(merger);
- assertEquals(0, merger.getConflicts().size());
- assertEquals(false, local.isDirty());
- assertEquals(0, local.getNewObjects().size());
- assertEquals(0, local.getDirtyObjects().size());
- assertEquals(0, local.getDetachedObjects().size());
-
- CDOView view = workspace.openView();
- assertEquals(1, countModifiedProduct(view));
- }
-
- public void testConflictMasterAndLocalModify() throws Exception
- {
- CDOID id = CDOUtil.getCDOObject(products.get(1)).cdoID();
-
- InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE);
- assertNotSame(CDOBranchPoint.UNSPECIFIED_DATE, workspace.getTimeStamp());
-
- assertEquals(1, modifyProduct(transaction, 1, "MODIFIED_1_"));
- transaction.commit();
-
- CDOTransaction local = workspace.openTransaction();
- assertEquals(1, modifyProduct(local, 1, "MODIFIED_2_"));
- local.commit();
- local.close();
-
- DefaultCDOMerger.PerFeature.ManyValued merger = new DefaultCDOMerger.PerFeature.ManyValued();
-
- try
- {
- local = workspace.update(merger);
- fail("ConflictException expected");
- }
- catch (ConflictException expected)
- {
- // SUCCESS
- }
-
- Map<CDOID, Conflict> conflicts = merger.getConflicts();
- assertEquals(1, conflicts.size());
- assertEquals(id, conflicts.values().iterator().next().getID());
- assertInactive(local);
-
- CDOView view = workspace.openView();
- assertEquals(1, countModifiedProduct(view));
- }
-
- protected IStore createLocalStore()
- {
- return getRepositoryConfig().createStore(CDOWorkspaceConfiguration.DEFAULT_LOCAL_REPOSITORY_NAME);
- }
-
- protected InternalCDOWorkspace checkout(String branchPath, long timeStamp)
- {
- disableConsole();
- CDOSessionConfigurationFactory remote = new RemoteSessionConfigurationFactory();
-
- File folder = createTempFolder("cdo-");
- CDOWorkspaceBase base = CDOWorkspaceUtil.createFolderWorkspaceBase(folder);
- IOUtil.ERR().println("CDOWorkspaceBaseline: " + folder.getAbsolutePath());
-
- CDOWorkspaceConfiguration config = CDOWorkspaceUtil.createWorkspaceConfiguration();
- config.setStore(localStore);
- config.setBase(base);
- config.setRemote(remote);
- config.setBranchPath(branchPath);
- config.setTimeStamp(timeStamp);
- config.setIDGenerationLocation(getRepository().getIDGenerationLocation());
-
- InternalCDOWorkspace workspace = (InternalCDOWorkspace)config.checkout();
- workspaces.add(workspace);
-
- InternalRepository localRepository = workspace.getLocalRepository();
- registerRepository(localRepository);
- LifecycleUtil.activate(localRepository);
-
- return workspace;
- }
-
- private int dumpLocalStore(PrintStream out)
- {
- if (localStore instanceof CDOAllRevisionsProvider)
- {
- CDOAllRevisionsProvider provider = (CDOAllRevisionsProvider)localStore;
- Map<CDOBranch, List<CDORevision>> allRevisions = provider.getAllRevisions();
- int size = allRevisions.values().iterator().next().size();
-
- if (out != null)
- {
- CDORevisionUtil.dumpAllRevisions(allRevisions, out);
- }
-
- return size;
- }
-
- return 0;
- }
-
- private int dumpObjects(String prefix, EObject object)
- {
- if (prefix != null)
- {
- IOUtil.OUT().println(prefix + object);
- }
-
- int sum = 1;
- for (EObject content : object.eContents())
- {
- sum += dumpObjects(prefix != null ? prefix + " " : null, content);
- }
-
- return sum;
- }
-
- private CDOResource createTestSet(CDOTransaction transaction) throws CommitException
- {
- disableConsole();
- CDOResource resource = transaction.createResource(getResourcePath(RESOURCE));
- fillResource(resource);
-
- totalObjects = 1;
- for (Iterator<EObject> it = resource.eAllContents(); it.hasNext();)
- {
- it.next();
- ++totalObjects;
- }
-
- transaction.commit();
- enableConsole();
- return resource;
- }
-
- private void fillResource(CDOResource resource)
- {
- for (int i = 0; i < PRODUCTS; i++)
- {
- Product1 product = createProduct(i);
- products.add(product);
- resource.getContents().add(product);
- }
-
- int id = 100;
- for (int i = 0; i < CUSTOMERS; i++)
- {
- Customer customer = createCustomer(i);
- customers.add(customer);
- resource.getContents().add(customer);
-
- for (int k = 0; k < SALES_ORDERS_PER_CUSTOMER; k++)
- {
- SalesOrder salesOrder = createSalesOrder(id++, customer);
- salesOrders.add(salesOrder);
- resource.getContents().add(salesOrder);
-
- for (Product1 product : products)
- {
- OrderDetail orderDetail = createOrderDetail(product, 55.123f);
- orderDetails.add(orderDetail);
- salesOrder.getOrderDetails().add(orderDetail);
- }
- }
- }
- }
-
- private Product1 createProduct(int index)
- {
- Product1 product = getModel1Factory().createProduct1();
- product.setName(getProductName(index));
- product.setVat(VAT.VAT15);
- return product;
- }
-
- private Customer createCustomer(int i)
- {
- Customer customer = getModel1Factory().createCustomer();
- customer.setCity("City " + i);
- customer.setName("" + i);
- customer.setStreet("Street " + i);
- return customer;
- }
-
- private SalesOrder createSalesOrder(int id, Customer customer)
- {
- SalesOrder salesOrder = getModel1Factory().createSalesOrder();
- salesOrder.setId(id);
- salesOrder.setCustomer(customer);
- return salesOrder;
- }
-
- private OrderDetail createOrderDetail(Product1 product, float price)
- {
- OrderDetail orderDetail = getModel1Factory().createOrderDetail();
- orderDetail.setPrice(price);
- orderDetail.setProduct(product);
- return orderDetail;
- }
-
- private String getProductName(int index)
- {
- return "Product No" + index;
- }
-
- private int modifyProduct(CDOTransaction transaction, int i, String prefix)
- {
- int count = 0;
- for (EObject object : transaction.getResource(getResourcePath(RESOURCE)).getContents())
- {
- if (object instanceof Product1)
- {
- Product1 product = (Product1)object;
- String name = product.getName();
- if (getProductName(i).equals(name))
- {
- product.setName(prefix + name);
- ++count;
- }
- }
- }
-
- return count;
- }
-
- private int countModifiedProduct(CDOView view)
- {
- int count = 0;
- for (EObject object : view.getResource(getResourcePath(RESOURCE)).getContents())
- {
- if (object instanceof Product1)
- {
- Product1 product = (Product1)object;
- String name = product.getName();
- if (name.startsWith("MODIFIED"))
- {
- IOUtil.ERR().println(name);
- ++count;
- }
- }
- }
-
- return count;
- }
-
- /**
- * @author Eike Stepper
- */
- private final class RemoteSessionConfigurationFactory implements CDOSessionConfigurationFactory
- {
- public CDOSessionConfiguration createSessionConfiguration()
- {
- return new TestSessionConfiguration()
- {
- public CDOSession openSession()
- {
- return WorkspaceTest.this.openSession();
- }
- };
- }
- }
-}
+/* + * Copyright (c) 2004 - 2012 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.tests; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +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.revision.CDOAllRevisionsProvider; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.eresource.CDOResource; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IStore; +import org.eclipse.emf.cdo.session.CDOSession; +import org.eclipse.emf.cdo.session.CDOSessionConfiguration; +import org.eclipse.emf.cdo.session.CDOSessionConfigurationFactory; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.workspace.InternalCDOWorkspace; +import org.eclipse.emf.cdo.tests.config.IRepositoryConfig; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.Requires; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.Skips; +import org.eclipse.emf.cdo.tests.model1.Customer; +import org.eclipse.emf.cdo.tests.model1.OrderDetail; +import org.eclipse.emf.cdo.tests.model1.Product1; +import org.eclipse.emf.cdo.tests.model1.SalesOrder; +import org.eclipse.emf.cdo.tests.model1.VAT; +import org.eclipse.emf.cdo.tests.util.TestSessionConfiguration; +import org.eclipse.emf.cdo.transaction.CDOMerger.ConflictException; +import org.eclipse.emf.cdo.transaction.CDOTransaction; +import org.eclipse.emf.cdo.util.CDOUtil; +import org.eclipse.emf.cdo.util.CommitException; +import org.eclipse.emf.cdo.view.CDOView; +import org.eclipse.emf.cdo.workspace.CDOWorkspace; +import org.eclipse.emf.cdo.workspace.CDOWorkspaceBase; +import org.eclipse.emf.cdo.workspace.CDOWorkspaceConfiguration; +import org.eclipse.emf.cdo.workspace.CDOWorkspaceUtil; + +import org.eclipse.net4j.jvm.JVMUtil; +import org.eclipse.net4j.util.io.IOUtil; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.spi.cdo.DefaultCDOMerger; +import org.eclipse.emf.spi.cdo.DefaultCDOMerger.Conflict; + +import java.io.File; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * @author Eike Stepper + */ +@Requires(IRepositoryConfig.CAPABILITY_AUDITING) +@Skips("DB.ranges") +public class WorkspaceTest extends AbstractCDOTest +{ + private static final String RESOURCE = "/test1"; + + private static final int PRODUCTS = 5; + + private static final int CUSTOMERS = 2; + + private static final int SALES_ORDERS_PER_CUSTOMER = 1; + + private static final int SALES_ORDERS = CUSTOMERS * SALES_ORDERS_PER_CUSTOMER; + + private static final int ORDER_DETAILS = SALES_ORDERS * PRODUCTS; + + private List<CDOWorkspace> workspaces = new ArrayList<CDOWorkspace>(); + + private CDOTransaction transaction; + + private List<Product1> products; + + private List<Customer> customers; + + private List<SalesOrder> salesOrders; + + private List<OrderDetail> orderDetails; + + private int totalObjects; + + private IStore localStore; + + @Override + protected void doSetUp() throws Exception + { + super.doSetUp(); + skipStoreWithoutHandleRevisions(); + + CDOSession session = openSession(); + transaction = session.openTransaction(); + + products = new ArrayList<Product1>(); + customers = new ArrayList<Customer>(); + orderDetails = new ArrayList<OrderDetail>(); + salesOrders = new ArrayList<SalesOrder>(); + + createTestSet(transaction); + assertEquals(PRODUCTS, products.size()); + assertEquals(CUSTOMERS, customers.size()); + assertEquals(SALES_ORDERS, salesOrders.size()); + assertEquals(ORDER_DETAILS, orderDetails.size()); + + JVMUtil.prepareContainer(getClientContainer()); + localStore = createLocalStore(); + CDOUtil.setLegacyModeDefault(true); + } + + @Override + protected void doTearDown() throws Exception + { + disableConsole(); + CDOUtil.setLegacyModeDefault(false); + for (CDOWorkspace workspace : workspaces) + { + IOUtil.closeSilent(workspace); + } + + workspaces.clear(); + super.doTearDown(); + } + + @Override + public synchronized Map<String, Object> getTestProperties() + { + Map<String, Object> map = super.getTestProperties(); + map.put(IRepository.Props.ENSURE_REFERENTIAL_INTEGRITY, "false"); + return map; + } + + @CleanRepositoriesBefore + public void testCheckout() throws Exception + { + checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + assertEquals(2 + totalObjects, dumpLocalStore(null)); // Root resource + test folder + totalObjects + } + + public void testReadObjects() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOView view = workspace.openView(); + CDOResource resource = view.getResource(getResourcePath(RESOURCE)); + assertEquals(totalObjects, dumpObjects(null, resource)); + } + + public void testModifyObjects() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (EObject object : resource.getContents()) + { + if (object instanceof Product1) + { + Product1 product = (Product1)object; + product.setName("MODIFIED_" + product.getName()); + } + } + + assertEquals(true, transaction.isDirty()); + assertEquals(PRODUCTS, transaction.getDirtyObjects().size()); + + transaction.commit(); + assertEquals(false, transaction.isDirty()); + assertEquals(0, transaction.getDirtyObjects().size()); + + clearCache(transaction.getSession().getRevisionManager()); + + CDOView view = workspace.openView(); + resource = view.getResource(getResourcePath(RESOURCE)); + assertEquals(totalObjects, dumpObjects(null, resource)); + for (EObject object : resource.getContents()) + { + if (object instanceof Product1) + { + Product1 product = (Product1)object; + assertEquals(true, product.getName().startsWith("MODIFIED_")); + } + } + } + + @Requires(IRepositoryConfig.CAPABILITY_UUIDS) + public void testAddObjects() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (int i = 0; i < PRODUCTS; i++) + { + resource.getContents().add(createProduct(i)); + } + + assertEquals(true, transaction.isDirty()); + assertEquals(1, transaction.getDirtyObjects().size()); + assertEquals(PRODUCTS, transaction.getNewObjects().size()); + + transaction.commit(); + assertEquals(false, transaction.isDirty()); + assertEquals(0, transaction.getDirtyObjects().size()); + assertEquals(0, transaction.getNewObjects().size()); + + clearCache(transaction.getSession().getRevisionManager()); + + CDOView view = workspace.openView(); + resource = view.getResource(getResourcePath(RESOURCE)); + assertEquals(totalObjects + PRODUCTS, dumpObjects("--> ", resource)); + } + + public void testDetachObjects() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (EObject object : resource.getContents()) + { + if (object instanceof SalesOrder) + { + ((SalesOrder)object).getOrderDetails().clear(); + } + + if (object instanceof Product1) + { + ((Product1)object).getOrderDetails().clear(); + } + } + + assertEquals(true, transaction.isDirty()); + assertEquals(SALES_ORDERS + PRODUCTS, transaction.getDirtyObjects().size()); + assertEquals(ORDER_DETAILS, transaction.getDetachedObjects().size()); + + transaction.commit(); + assertEquals(false, transaction.isDirty()); + assertEquals(0, transaction.getDirtyObjects().size()); + assertEquals(0, transaction.getDetachedObjects().size()); + + clearCache(transaction.getSession().getRevisionManager()); + + CDOView view = workspace.openView(); + resource = view.getResource(getResourcePath(RESOURCE)); + assertEquals(totalObjects - ORDER_DETAILS, dumpObjects(null, resource)); + for (EObject object : resource.getContents()) + { + if (object instanceof SalesOrder) + { + if (!((SalesOrder)object).getOrderDetails().isEmpty()) + { + fail("All order details should be detached"); + } + } + } + } + + public void testModifyObjects2() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (EObject object : resource.getContents()) + { + if (object instanceof Product1) + { + Product1 product = (Product1)object; + product.setName("MODIFIED_" + product.getName()); + } + } + + assertEquals(true, transaction.isDirty()); + assertEquals(PRODUCTS, transaction.getDirtyObjects().size()); + + transaction.commit(); + assertEquals(false, transaction.isDirty()); + assertEquals(0, transaction.getDirtyObjects().size()); + + for (EObject object : resource.getContents()) + { + if (object instanceof Product1) + { + Product1 product = (Product1)object; + product.setName("MODIFIED2_" + product.getName()); + } + } + + assertEquals(true, transaction.isDirty()); + assertEquals(PRODUCTS, transaction.getDirtyObjects().size()); + + transaction.commit(); + assertEquals(false, transaction.isDirty()); + assertEquals(0, transaction.getDirtyObjects().size()); + + clearCache(transaction.getSession().getRevisionManager()); + + CDOView view = workspace.openView(); + resource = view.getResource(getResourcePath(RESOURCE)); + assertEquals(totalObjects, dumpObjects(null, resource)); + for (EObject object : resource.getContents()) + { + if (object instanceof Product1) + { + Product1 product = (Product1)object; + assertEquals(true, product.getName().startsWith("MODIFIED2_")); + } + } + } + + @Requires(IRepositoryConfig.CAPABILITY_UUIDS) + public void testAddObjects2() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (int i = 0; i < PRODUCTS; i++) + { + resource.getContents().add(createProduct(i)); + } + + assertEquals(true, transaction.isDirty()); + assertEquals(1, transaction.getDirtyObjects().size()); + assertEquals(PRODUCTS, transaction.getNewObjects().size()); + + transaction.commit(); + assertEquals(false, transaction.isDirty()); + assertEquals(0, transaction.getDirtyObjects().size()); + assertEquals(0, transaction.getNewObjects().size()); + + for (int i = 0; i < PRODUCTS; i++) + { + resource.getContents().add(createProduct(i)); + } + + assertEquals(true, transaction.isDirty()); + assertEquals(1, transaction.getDirtyObjects().size()); + assertEquals(PRODUCTS, transaction.getNewObjects().size()); + + transaction.commit(); + assertEquals(false, transaction.isDirty()); + assertEquals(0, transaction.getDirtyObjects().size()); + assertEquals(0, transaction.getNewObjects().size()); + + clearCache(transaction.getSession().getRevisionManager()); + + CDOView view = workspace.openView(); + resource = view.getResource(getResourcePath(RESOURCE)); + assertEquals(totalObjects + 2 * PRODUCTS, dumpObjects("--> ", resource)); + } + + public void testDetachObjects2() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (EObject object : resource.getContents()) + { + if (object instanceof SalesOrder) + { + ((SalesOrder)object).getOrderDetails().clear(); + } + + if (object instanceof Product1) + { + ((Product1)object).getOrderDetails().clear(); + } + } + + assertEquals(true, transaction.isDirty()); + assertEquals(SALES_ORDERS + PRODUCTS, transaction.getDirtyObjects().size()); + assertEquals(ORDER_DETAILS, transaction.getDetachedObjects().size()); + + transaction.commit(); + assertEquals(false, transaction.isDirty()); + assertEquals(0, transaction.getDirtyObjects().size()); + assertEquals(0, transaction.getDetachedObjects().size()); + + for (Iterator<EObject> it = resource.getContents().iterator(); it.hasNext();) + { + EObject object = it.next(); + if (object instanceof SalesOrder) + { + it.remove(); + } + + if (object instanceof Customer) + { + ((Customer)object).getSalesOrders().clear(); + } + } + + assertEquals(true, transaction.isDirty()); + assertEquals(1 + CUSTOMERS, transaction.getDirtyObjects().size()); + assertEquals(SALES_ORDERS, transaction.getDetachedObjects().size()); + + transaction.commit(); + assertEquals(false, transaction.isDirty()); + assertEquals(0, transaction.getDirtyObjects().size()); + assertEquals(0, transaction.getDetachedObjects().size()); + + clearCache(transaction.getSession().getRevisionManager()); + + CDOView view = workspace.openView(); + resource = view.getResource(getResourcePath(RESOURCE)); + assertEquals(totalObjects - ORDER_DETAILS - SALES_ORDERS, dumpObjects(null, resource)); + for (EObject object : resource.getContents()) + { + if (object instanceof SalesOrder) + { + if (!((SalesOrder)object).getOrderDetails().isEmpty()) + { + fail("All order details should be detached"); + } + } + + if (object instanceof Customer) + { + if (!((Customer)object).getSalesOrders().isEmpty()) + { + fail("All sales orders should be detached"); + } + } + } + } + + public void testLocalChangesAfterModify() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (EObject object : resource.getContents()) + { + if (object instanceof Product1) + { + Product1 product = (Product1)object; + product.setName("MODIFIED_" + product.getName()); + } + } + + transaction.commit(); + + CDOChangeSetData changes = workspace.getLocalChanges(); + assertEquals(0, changes.getNewObjects().size()); + assertEquals(PRODUCTS, changes.getChangedObjects().size()); + assertEquals(0, changes.getDetachedObjects().size()); + } + + @Requires(IRepositoryConfig.CAPABILITY_UUIDS) + public void testLocalChangesAfterAdd() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (int i = 0; i < PRODUCTS; i++) + { + resource.getContents().add(createProduct(i)); + } + + transaction.commit(); + + CDOChangeSetData changes = workspace.getLocalChanges(); + assertEquals(PRODUCTS, changes.getNewObjects().size()); + assertEquals(1, changes.getChangedObjects().size()); + assertEquals(0, changes.getDetachedObjects().size()); + } + + public void testLocalChangesAfterDetach() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (EObject object : resource.getContents()) + { + if (object instanceof SalesOrder) + { + ((SalesOrder)object).getOrderDetails().clear(); + } + + if (object instanceof Product1) + { + ((Product1)object).getOrderDetails().clear(); + } + } + + transaction.commit(); + + CDOChangeSetData changes = workspace.getLocalChanges(); + assertEquals(0, changes.getNewObjects().size()); + assertEquals(SALES_ORDERS + PRODUCTS, changes.getChangedObjects().size()); + assertEquals(ORDER_DETAILS, changes.getDetachedObjects().size()); + } + + public void testLocalChangesAfterModify2() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (EObject object : resource.getContents()) + { + if (object instanceof Product1) + { + Product1 product = (Product1)object; + product.setName("MODIFIED_" + product.getName()); + } + } + + transaction.commit(); + + for (EObject object : resource.getContents()) + { + if (object instanceof Product1) + { + Product1 product = (Product1)object; + product.setName("MODIFIED2_" + product.getName()); + } + } + + transaction.commit(); + + CDOChangeSetData changes = workspace.getLocalChanges(); + assertEquals(0, changes.getNewObjects().size()); + assertEquals(PRODUCTS, changes.getChangedObjects().size()); + assertEquals(0, changes.getDetachedObjects().size()); + } + + @Requires(IRepositoryConfig.CAPABILITY_UUIDS) + public void testLocalChangesAfterAdd2() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (int i = 0; i < PRODUCTS; i++) + { + resource.getContents().add(createProduct(i)); + } + + transaction.commit(); + + for (int i = 0; i < PRODUCTS; i++) + { + resource.getContents().add(createProduct(i)); + } + + transaction.commit(); + + CDOChangeSetData changes = workspace.getLocalChanges(); + assertEquals(2 * PRODUCTS, changes.getNewObjects().size()); + assertEquals(1, changes.getChangedObjects().size()); + assertEquals(0, changes.getDetachedObjects().size()); + } + + public void testLocalChangesAfterDetach2() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (EObject object : resource.getContents()) + { + if (object instanceof SalesOrder) + { + ((SalesOrder)object).getOrderDetails().clear(); + } + + if (object instanceof Product1) + { + ((Product1)object).getOrderDetails().clear(); + } + } + + transaction.commit(); + + for (Iterator<EObject> it = resource.getContents().iterator(); it.hasNext();) + { + EObject object = it.next(); + if (object instanceof SalesOrder) + { + it.remove(); + } + + if (object instanceof Customer) + { + ((Customer)object).getSalesOrders().clear(); + } + } + + transaction.commit(); + + CDOChangeSetData changes = workspace.getLocalChanges(); + assertEquals(0, changes.getNewObjects().size()); + assertEquals(1 + CUSTOMERS + PRODUCTS, changes.getChangedObjects().size()); + assertEquals(ORDER_DETAILS + SALES_ORDERS, changes.getDetachedObjects().size()); + } + + public void testCheckinAfterModify() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (EObject object : resource.getContents()) + { + if (object instanceof Product1) + { + Product1 product = (Product1)object; + product.setName("MODIFIED_" + product.getName()); + } + } + + transaction.commit(); + + CDOCommitInfo info = workspace.checkin(); + assertEquals(0, info.getNewObjects().size()); + assertEquals(PRODUCTS, info.getChangedObjects().size()); + assertEquals(0, info.getDetachedObjects().size()); + + CDOWorkspaceBase base = workspace.getBase(); + assertEquals(0, base.getIDs().size()); + assertEquals(info.getBranch().getPathName(), workspace.getBranchPath()); + assertEquals(info.getTimeStamp(), workspace.getTimeStamp()); + } + + @Requires(IRepositoryConfig.CAPABILITY_UUIDS) + public void testCheckinAfterAdd() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + IOUtil.ERR().println("Checkout done"); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (int i = 0; i < PRODUCTS; i++) + { + resource.getContents().add(createProduct(i)); + } + + transaction.commit(); + + CDOCommitInfo info = workspace.checkin(); + assertEquals(PRODUCTS, info.getNewObjects().size()); + assertEquals(1, info.getChangedObjects().size()); + assertEquals(0, info.getDetachedObjects().size()); + + CDOWorkspaceBase base = workspace.getBase(); + assertEquals(0, base.getIDs().size()); + assertEquals(info.getBranch().getPathName(), workspace.getBranchPath()); + assertEquals(info.getTimeStamp(), workspace.getTimeStamp()); + } + + @Requires(IRepositoryConfig.CAPABILITY_UUIDS) + public void testCheckinAfterAddExt() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + IOUtil.ERR().println("Checkout done"); + + { + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (int i = 0; i < PRODUCTS; i++) + { + resource.getContents().add(createProduct(i)); + } + + transaction.commit(); + transaction.close(); + } + + { + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (int i = 0; i < PRODUCTS; i++) + { + resource.getContents().add(createProduct(i)); + } + + transaction.commit(); + transaction.close(); + } + + workspace.checkin(); + // workspace.update(null); + + { + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (int i = 0; i < PRODUCTS; i++) + { + resource.getContents().add(createProduct(i)); + } + + transaction.commit(); + transaction.close(); + } + + workspace.checkin(); + } + + public void testCheckinAfterDetach() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (EObject object : resource.getContents()) + { + if (object instanceof SalesOrder) + { + ((SalesOrder)object).getOrderDetails().clear(); + } + + if (object instanceof Product1) + { + ((Product1)object).getOrderDetails().clear(); + } + } + + transaction.commit(); + + CDOCommitInfo info = workspace.checkin(); + assertEquals(0, info.getNewObjects().size()); + assertEquals(SALES_ORDERS + PRODUCTS, info.getChangedObjects().size()); + assertEquals(ORDER_DETAILS, info.getDetachedObjects().size()); + + CDOWorkspaceBase base = workspace.getBase(); + assertEquals(0, base.getIDs().size()); + assertEquals(info.getBranch().getPathName(), workspace.getBranchPath()); + assertEquals(info.getTimeStamp(), workspace.getTimeStamp()); + } + + public void testCheckinAfterModify2() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (EObject object : resource.getContents()) + { + if (object instanceof Product1) + { + Product1 product = (Product1)object; + product.setName("MODIFIED_" + product.getName()); + } + } + + transaction.commit(); + + for (EObject object : resource.getContents()) + { + if (object instanceof Product1) + { + Product1 product = (Product1)object; + product.setName("MODIFIED2_" + product.getName()); + } + } + + transaction.commit(); + + CDOCommitInfo info = workspace.checkin(); + assertEquals(0, info.getNewObjects().size()); + assertEquals(PRODUCTS, info.getChangedObjects().size()); + assertEquals(0, info.getDetachedObjects().size()); + + CDOWorkspaceBase base = workspace.getBase(); + assertEquals(0, base.getIDs().size()); + assertEquals(info.getBranch().getPathName(), workspace.getBranchPath()); + assertEquals(info.getTimeStamp(), workspace.getTimeStamp()); + } + + @Requires(IRepositoryConfig.CAPABILITY_UUIDS) + public void testCheckinAfterAdd2() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (int i = 0; i < PRODUCTS; i++) + { + resource.getContents().add(createProduct(i)); + } + + transaction.commit(); + + for (int i = 0; i < PRODUCTS; i++) + { + resource.getContents().add(createProduct(i)); + } + + transaction.commit(); + + CDOCommitInfo info = workspace.checkin(); + assertEquals(2 * PRODUCTS, info.getNewObjects().size()); + assertEquals(1, info.getChangedObjects().size()); + assertEquals(0, info.getDetachedObjects().size()); + + CDOWorkspaceBase base = workspace.getBase(); + assertEquals(0, base.getIDs().size()); + assertEquals(info.getBranch().getPathName(), workspace.getBranchPath()); + assertEquals(info.getTimeStamp(), workspace.getTimeStamp()); + } + + public void testCheckinAfterDetach2() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (EObject object : resource.getContents()) + { + if (object instanceof SalesOrder) + { + ((SalesOrder)object).getOrderDetails().clear(); + } + + if (object instanceof Product1) + { + ((Product1)object).getOrderDetails().clear(); + } + } + + transaction.commit(); + + for (Iterator<EObject> it = resource.getContents().iterator(); it.hasNext();) + { + EObject object = it.next(); + if (object instanceof SalesOrder) + { + it.remove(); + } + + if (object instanceof Customer) + { + ((Customer)object).getSalesOrders().clear(); + } + } + + transaction.commit(); + + CDOCommitInfo info = workspace.checkin(); + assertEquals(0, info.getNewObjects().size()); + assertEquals(1 + CUSTOMERS + PRODUCTS, info.getChangedObjects().size()); + assertEquals(ORDER_DETAILS + SALES_ORDERS, info.getDetachedObjects().size()); + + CDOWorkspaceBase base = workspace.getBase(); + assertEquals(0, base.getIDs().size()); + assertEquals(info.getBranch().getPathName(), workspace.getBranchPath()); + assertEquals(info.getTimeStamp(), workspace.getTimeStamp()); + } + + public void testCheckin2AfterModify() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (EObject object : resource.getContents()) + { + if (object instanceof Product1) + { + Product1 product = (Product1)object; + product.setName("MODIFIED_" + product.getName()); + } + } + + transaction.commit(); + + CDOCommitInfo info = workspace.checkin(); + assertEquals(0, info.getNewObjects().size()); + assertEquals(PRODUCTS, info.getChangedObjects().size()); + assertEquals(0, info.getDetachedObjects().size()); + + CDOWorkspaceBase base = workspace.getBase(); + assertEquals(0, base.getIDs().size()); + assertEquals(info.getBranch().getPathName(), workspace.getBranchPath()); + assertEquals(info.getTimeStamp(), workspace.getTimeStamp()); + + transaction = workspace.openTransaction(); + resource = transaction.getResource(getResourcePath(RESOURCE)); + for (EObject object : resource.getContents()) + { + if (object instanceof Product1) + { + Product1 product = (Product1)object; + product.setName("MODIFIED2_" + product.getName()); + } + } + + transaction.commit(); + + info = workspace.checkin(); + assertEquals(0, info.getNewObjects().size()); + assertEquals(PRODUCTS, info.getChangedObjects().size()); + assertEquals(0, info.getDetachedObjects().size()); + + base = workspace.getBase(); + assertEquals(0, base.getIDs().size()); + assertEquals(info.getBranch().getPathName(), workspace.getBranchPath()); + assertEquals(info.getTimeStamp(), workspace.getTimeStamp()); + } + + @Requires(IRepositoryConfig.CAPABILITY_UUIDS) + public void testCheckin2AfterAdd() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (int i = 0; i < PRODUCTS; i++) + { + resource.getContents().add(createProduct(i)); + } + + transaction.commit(); + + CDOCommitInfo info = workspace.checkin(); + assertEquals(PRODUCTS, info.getNewObjects().size()); + assertEquals(1, info.getChangedObjects().size()); + assertEquals(0, info.getDetachedObjects().size()); + + CDOWorkspaceBase base = workspace.getBase(); + assertEquals(0, base.getIDs().size()); + assertEquals(info.getBranch().getPathName(), workspace.getBranchPath()); + assertEquals(info.getTimeStamp(), workspace.getTimeStamp()); + + transaction = workspace.openTransaction(); + resource = transaction.getResource(getResourcePath(RESOURCE)); + for (int i = 0; i < PRODUCTS; i++) + { + resource.getContents().add(createProduct(i)); + } + + transaction.commit(); + + info = workspace.checkin(); + assertEquals(PRODUCTS, info.getNewObjects().size()); + assertEquals(1, info.getChangedObjects().size()); + assertEquals(0, info.getDetachedObjects().size()); + + base = workspace.getBase(); + assertEquals(0, base.getIDs().size()); + assertEquals(info.getBranch().getPathName(), workspace.getBranchPath()); + assertEquals(info.getTimeStamp(), workspace.getTimeStamp()); + } + + public void testCheckin2AfterDetach() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + + CDOTransaction transaction = workspace.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (EObject object : resource.getContents()) + { + if (object instanceof SalesOrder) + { + ((SalesOrder)object).getOrderDetails().clear(); + } + + if (object instanceof Product1) + { + ((Product1)object).getOrderDetails().clear(); + } + } + + transaction.commit(); + + CDOCommitInfo info = workspace.checkin(); + assertEquals(0, info.getNewObjects().size()); + assertEquals(SALES_ORDERS + PRODUCTS, info.getChangedObjects().size()); + assertEquals(ORDER_DETAILS, info.getDetachedObjects().size()); + + CDOWorkspaceBase base = workspace.getBase(); + assertEquals(0, base.getIDs().size()); + assertEquals(info.getBranch().getPathName(), workspace.getBranchPath()); + assertEquals(info.getTimeStamp(), workspace.getTimeStamp()); + + transaction = workspace.openTransaction(); + resource = transaction.getResource(getResourcePath(RESOURCE)); + for (Iterator<EObject> it = resource.getContents().iterator(); it.hasNext();) + { + EObject object = it.next(); + if (object instanceof SalesOrder) + { + it.remove(); + } + + if (object instanceof Customer) + { + ((Customer)object).getSalesOrders().clear(); + } + } + + transaction.commit(); + + info = workspace.checkin(); + assertEquals(0, info.getNewObjects().size()); + assertEquals(1 + CUSTOMERS, info.getChangedObjects().size()); + assertEquals(SALES_ORDERS, info.getDetachedObjects().size()); + + base = workspace.getBase(); + assertEquals(0, base.getIDs().size()); + assertEquals(info.getBranch().getPathName(), workspace.getBranchPath()); + assertEquals(info.getTimeStamp(), workspace.getTimeStamp()); + } + + public void testUpdateAfterMasterModify() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + assertNotSame(CDOBranchPoint.UNSPECIFIED_DATE, workspace.getTimeStamp()); + + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (EObject object : resource.getContents()) + { + if (object instanceof Product1) + { + Product1 product = (Product1)object; + product.setName("MODIFIED_" + product.getName()); + } + } + + transaction.commit(); + + CDOTransaction local = workspace.update(null); + assertEquals(true, local.isDirty()); + assertEquals(0, local.getNewObjects().size()); + assertEquals(PRODUCTS, local.getDirtyObjects().size()); + assertEquals(0, local.getDetachedObjects().size()); + + local.commit(); + assertEquals(false, local.isDirty()); + assertEquals(0, local.getNewObjects().size()); + assertEquals(0, local.getDirtyObjects().size()); + assertEquals(0, local.getDetachedObjects().size()); + assertEquals(0, workspace.getBase().getIDs().size()); + + CDOView view = workspace.openView(); + resource = view.getResource(getResourcePath(RESOURCE)); + assertEquals(totalObjects, dumpObjects(null, resource)); + } + + @Requires(IRepositoryConfig.CAPABILITY_UUIDS) + public void testUpdateAfterMasterAdd() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + assertNotSame(CDOBranchPoint.UNSPECIFIED_DATE, workspace.getTimeStamp()); + + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + resource.getContents().add(createProduct(9999)); + transaction.commit(); + + CDOTransaction local = workspace.update(null); + assertEquals(true, local.isDirty()); + assertEquals(1, local.getNewObjects().size()); + assertEquals(1, local.getDirtyObjects().size()); + assertEquals(0, local.getDetachedObjects().size()); + + local.commit(); + assertEquals(false, local.isDirty()); + assertEquals(0, local.getNewObjects().size()); + assertEquals(0, local.getDirtyObjects().size()); + assertEquals(0, local.getDetachedObjects().size()); + assertEquals(0, workspace.getBase().getIDs().size()); + + CDOView view = workspace.openView(); + resource = view.getResource(getResourcePath(RESOURCE)); + assertEquals(totalObjects + 1, dumpObjects(null, resource)); + } + + @Requires(IRepositoryConfig.CAPABILITY_UUIDS) + public void testUpdateTwiceAfterMasterAdd() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + assertNotSame(CDOBranchPoint.UNSPECIFIED_DATE, workspace.getTimeStamp()); + + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + resource.getContents().add(createProduct(9999)); + transaction.commit(); + + CDOTransaction local = workspace.update(null); + assertEquals(true, local.isDirty()); + assertEquals(1, local.getNewObjects().size()); + assertEquals(1, local.getDirtyObjects().size()); + assertEquals(0, local.getDetachedObjects().size()); + + local.commit(); + assertEquals(false, local.isDirty()); + assertEquals(0, local.getNewObjects().size()); + assertEquals(0, local.getDirtyObjects().size()); + assertEquals(0, local.getDetachedObjects().size()); + assertEquals(0, workspace.getBase().getIDs().size()); + local.close(); + + local = workspace.update(null); + assertEquals(false, local.isDirty()); + assertEquals(0, local.getNewObjects().size()); + assertEquals(0, local.getDirtyObjects().size()); + assertEquals(0, local.getDetachedObjects().size()); + assertEquals(0, workspace.getBase().getIDs().size()); + + local.commit(); + assertEquals(false, local.isDirty()); + assertEquals(0, local.getNewObjects().size()); + assertEquals(0, local.getDirtyObjects().size()); + assertEquals(0, local.getDetachedObjects().size()); + assertEquals(0, workspace.getBase().getIDs().size()); + + CDOView view = workspace.openView(); + resource = view.getResource(getResourcePath(RESOURCE)); + assertEquals(totalObjects + 1, dumpObjects(null, resource)); + } + + public void testUpdateAfterMasterDetach() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + assertNotSame(CDOBranchPoint.UNSPECIFIED_DATE, workspace.getTimeStamp()); + + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + for (EObject object : resource.getContents()) + { + if (object instanceof SalesOrder) + { + ((SalesOrder)object).getOrderDetails().clear(); + } + + if (object instanceof Product1) + { + ((Product1)object).getOrderDetails().clear(); + } + } + + transaction.commit(); + + CDOTransaction local = workspace.update(null); + assertEquals(true, local.isDirty()); + assertEquals(SALES_ORDERS + PRODUCTS, local.getDirtyObjects().size()); + assertEquals(ORDER_DETAILS, local.getDetachedObjects().size()); + + local.commit(); + assertEquals(false, local.isDirty()); + assertEquals(0, local.getNewObjects().size()); + assertEquals(0, local.getDirtyObjects().size()); + assertEquals(0, local.getDetachedObjects().size()); + assertEquals(0, workspace.getBase().getIDs().size()); + + CDOView view = workspace.openView(); + resource = view.getResource(getResourcePath(RESOURCE)); + assertEquals(totalObjects - ORDER_DETAILS, dumpObjects(null, resource)); + } + + public void testUpdateAfterMasterAndLocalModify() throws Exception + { + // Checkout local + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + assertNotSame(CDOBranchPoint.UNSPECIFIED_DATE, workspace.getTimeStamp()); + + // Modify master + assertEquals(1, modifyProduct(transaction, 1, "MODIFIED_")); + transaction.commit(); + + // Modify local + CDOTransaction local = workspace.openTransaction(); + assertEquals(1, modifyProduct(local, 2, "MODIFIED_")); + local.commit(); + local.close(); + + // Update local + local = workspace.update(new DefaultCDOMerger.PerFeature.ManyValued()); + assertEquals(true, local.isDirty()); + assertEquals(0, local.getNewObjects().size()); + assertEquals(1, local.getDirtyObjects().size()); + assertEquals(0, local.getDetachedObjects().size()); + + // Commit local + local.commit(); + assertEquals(false, local.isDirty()); + assertEquals(0, local.getNewObjects().size()); + assertEquals(0, local.getDirtyObjects().size()); + assertEquals(0, local.getDetachedObjects().size()); + assertEquals(1, workspace.getBase().getIDs().size()); + + // Verify local + CDOView view = workspace.openView(); + assertEquals(2, countModifiedProduct(view)); + } + + @Requires(IRepositoryConfig.CAPABILITY_UUIDS) + public void testUpdateAfterMasterAndLocalAdd() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + assertNotSame(CDOBranchPoint.UNSPECIFIED_DATE, workspace.getTimeStamp()); + + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE)); + resource.getContents().add(createProduct(9999)); + transaction.commit(); + + CDOTransaction local = workspace.openTransaction(); + resource = local.getResource(getResourcePath(RESOURCE)); + for (int i = 0; i < PRODUCTS; i++) + { + resource.getContents().add(createProduct(i)); + } + + local.commit(); + local.close(); + + local = workspace.update(new DefaultCDOMerger.PerFeature.ManyValued()); + assertEquals(true, local.isDirty()); + assertEquals(1, local.getNewObjects().size()); + assertEquals(1, local.getDirtyObjects().size()); + assertEquals(0, local.getDetachedObjects().size()); + + local.commit(); + assertEquals(false, local.isDirty()); + assertEquals(0, local.getNewObjects().size()); + assertEquals(0, local.getDirtyObjects().size()); + assertEquals(0, local.getDetachedObjects().size()); + assertEquals(6, workspace.getBase().getIDs().size()); + + CDOView view = workspace.openView(); + resource = view.getResource(getResourcePath(RESOURCE)); + assertEquals(totalObjects + 1 + PRODUCTS, dumpObjects(null, resource)); + } + + public void testNoConflictMasterAndLocalModify() throws Exception + { + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + assertNotSame(CDOBranchPoint.UNSPECIFIED_DATE, workspace.getTimeStamp()); + + assertEquals(1, modifyProduct(transaction, 1, "MODIFIED_")); + transaction.commit(); + + CDOTransaction local = workspace.openTransaction(); + assertEquals(1, modifyProduct(local, 1, "MODIFIED_")); + local.commit(); + local.close(); + + DefaultCDOMerger.PerFeature.ManyValued merger = new DefaultCDOMerger.PerFeature.ManyValued(); + local = workspace.update(merger); + assertEquals(0, merger.getConflicts().size()); + assertEquals(false, local.isDirty()); + assertEquals(0, local.getNewObjects().size()); + assertEquals(0, local.getDirtyObjects().size()); + assertEquals(0, local.getDetachedObjects().size()); + + CDOView view = workspace.openView(); + assertEquals(1, countModifiedProduct(view)); + } + + public void testConflictMasterAndLocalModify() throws Exception + { + CDOID id = CDOUtil.getCDOObject(products.get(1)).cdoID(); + + InternalCDOWorkspace workspace = checkout("MAIN", CDOBranchPoint.UNSPECIFIED_DATE); + assertNotSame(CDOBranchPoint.UNSPECIFIED_DATE, workspace.getTimeStamp()); + + assertEquals(1, modifyProduct(transaction, 1, "MODIFIED_1_")); + transaction.commit(); + + CDOTransaction local = workspace.openTransaction(); + assertEquals(1, modifyProduct(local, 1, "MODIFIED_2_")); + local.commit(); + local.close(); + + DefaultCDOMerger.PerFeature.ManyValued merger = new DefaultCDOMerger.PerFeature.ManyValued(); + + try + { + local = workspace.update(merger); + fail("ConflictException expected"); + } + catch (ConflictException expected) + { + // SUCCESS + } + + Map<CDOID, Conflict> conflicts = merger.getConflicts(); + assertEquals(1, conflicts.size()); + assertEquals(id, conflicts.values().iterator().next().getID()); + assertInactive(local); + + CDOView view = workspace.openView(); + assertEquals(1, countModifiedProduct(view)); + } + + protected IStore createLocalStore() + { + return getRepositoryConfig().createStore(CDOWorkspaceConfiguration.DEFAULT_LOCAL_REPOSITORY_NAME); + } + + protected InternalCDOWorkspace checkout(String branchPath, long timeStamp) + { + disableConsole(); + CDOSessionConfigurationFactory remote = new RemoteSessionConfigurationFactory(); + + File folder = createTempFolder("cdo-"); + CDOWorkspaceBase base = CDOWorkspaceUtil.createFolderWorkspaceBase(folder); + IOUtil.ERR().println("CDOWorkspaceBaseline: " + folder.getAbsolutePath()); + + CDOWorkspaceConfiguration config = CDOWorkspaceUtil.createWorkspaceConfiguration(); + config.setStore(localStore); + config.setBase(base); + config.setRemote(remote); + config.setBranchPath(branchPath); + config.setTimeStamp(timeStamp); + config.setIDGenerationLocation(getRepository().getIDGenerationLocation()); + + InternalCDOWorkspace workspace = (InternalCDOWorkspace)config.checkout(); + workspaces.add(workspace); + + InternalRepository localRepository = workspace.getLocalRepository(); + registerRepository(localRepository); + LifecycleUtil.activate(localRepository); + + return workspace; + } + + private int dumpLocalStore(PrintStream out) + { + if (localStore instanceof CDOAllRevisionsProvider) + { + CDOAllRevisionsProvider provider = (CDOAllRevisionsProvider)localStore; + Map<CDOBranch, List<CDORevision>> allRevisions = provider.getAllRevisions(); + int size = allRevisions.values().iterator().next().size(); + + if (out != null) + { + CDORevisionUtil.dumpAllRevisions(allRevisions, out); + } + + return size; + } + + return 0; + } + + private int dumpObjects(String prefix, EObject object) + { + if (prefix != null) + { + IOUtil.OUT().println(prefix + object); + } + + int sum = 1; + for (EObject content : object.eContents()) + { + sum += dumpObjects(prefix != null ? prefix + " " : null, content); + } + + return sum; + } + + private CDOResource createTestSet(CDOTransaction transaction) throws CommitException + { + disableConsole(); + CDOResource resource = transaction.createResource(getResourcePath(RESOURCE)); + fillResource(resource); + + totalObjects = 1; + for (Iterator<EObject> it = resource.eAllContents(); it.hasNext();) + { + it.next(); + ++totalObjects; + } + + transaction.commit(); + enableConsole(); + return resource; + } + + private void fillResource(CDOResource resource) + { + for (int i = 0; i < PRODUCTS; i++) + { + Product1 product = createProduct(i); + products.add(product); + resource.getContents().add(product); + } + + int id = 100; + for (int i = 0; i < CUSTOMERS; i++) + { + Customer customer = createCustomer(i); + customers.add(customer); + resource.getContents().add(customer); + + for (int k = 0; k < SALES_ORDERS_PER_CUSTOMER; k++) + { + SalesOrder salesOrder = createSalesOrder(id++, customer); + salesOrders.add(salesOrder); + resource.getContents().add(salesOrder); + + for (Product1 product : products) + { + OrderDetail orderDetail = createOrderDetail(product, 55.123f); + orderDetails.add(orderDetail); + salesOrder.getOrderDetails().add(orderDetail); + } + } + } + } + + private Product1 createProduct(int index) + { + Product1 product = getModel1Factory().createProduct1(); + product.setName(getProductName(index)); + product.setVat(VAT.VAT15); + return product; + } + + private Customer createCustomer(int i) + { + Customer customer = getModel1Factory().createCustomer(); + customer.setCity("City " + i); + customer.setName("" + i); + customer.setStreet("Street " + i); + return customer; + } + + private SalesOrder createSalesOrder(int id, Customer customer) + { + SalesOrder salesOrder = getModel1Factory().createSalesOrder(); + salesOrder.setId(id); + salesOrder.setCustomer(customer); + return salesOrder; + } + + private OrderDetail createOrderDetail(Product1 product, float price) + { + OrderDetail orderDetail = getModel1Factory().createOrderDetail(); + orderDetail.setPrice(price); + orderDetail.setProduct(product); + return orderDetail; + } + + private String getProductName(int index) + { + return "Product No" + index; + } + + private int modifyProduct(CDOTransaction transaction, int i, String prefix) + { + int count = 0; + for (EObject object : transaction.getResource(getResourcePath(RESOURCE)).getContents()) + { + if (object instanceof Product1) + { + Product1 product = (Product1)object; + String name = product.getName(); + if (getProductName(i).equals(name)) + { + product.setName(prefix + name); + ++count; + } + } + } + + return count; + } + + private int countModifiedProduct(CDOView view) + { + int count = 0; + for (EObject object : view.getResource(getResourcePath(RESOURCE)).getContents()) + { + if (object instanceof Product1) + { + Product1 product = (Product1)object; + String name = product.getName(); + if (name.startsWith("MODIFIED")) + { + IOUtil.ERR().println(name); + ++count; + } + } + } + + return count; + } + + /** + * @author Eike Stepper + */ + private final class RemoteSessionConfigurationFactory implements CDOSessionConfigurationFactory + { + public CDOSessionConfiguration createSessionConfiguration() + { + return new TestSessionConfiguration() + { + public CDOSession openSession() + { + return WorkspaceTest.this.openSession(); + } + }; + } + } +} diff --git a/plugins/org.eclipse.emf.cdo.workspace/src/org/eclipse/emf/cdo/internal/workspace/CDOWorkspaceImpl.java b/plugins/org.eclipse.emf.cdo.workspace/src/org/eclipse/emf/cdo/internal/workspace/CDOWorkspaceImpl.java index 26284b9ad3..8c4b34780e 100644 --- a/plugins/org.eclipse.emf.cdo.workspace/src/org/eclipse/emf/cdo/internal/workspace/CDOWorkspaceImpl.java +++ b/plugins/org.eclipse.emf.cdo.workspace/src/org/eclipse/emf/cdo/internal/workspace/CDOWorkspaceImpl.java @@ -1,961 +1,1016 @@ -/*
- * Copyright (c) 2004 - 2012 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.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.model.CDOPackageUnit;
-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.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.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.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.ManagedRevisionProvider;
-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.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.StringUtil;
-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.emf.ecore.EClass;
-import org.eclipse.emf.ecore.resource.ResourceSet;
-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.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;
-
-/**
- * @author Eike Stepper
- */
-public class CDOWorkspaceImpl extends Notifier implements InternalCDOWorkspace
-{
- 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 InternalCDOSession localSession;
-
- private CDOBranchPoint head;
-
- private String branchPath;
-
- private long timeStamp;
-
- private boolean fixed;
-
- private boolean dirty;
-
- private CDOSessionConfigurationFactory remoteSessionConfigurationFactory;
-
- private Set<InternalCDOView> views = new HashSet<InternalCDOView>();
-
- public CDOWorkspaceImpl(String localRepositoryName, IStore local, IDGenerationLocation idGenerationLocation,
- CDOIDGenerator idGenerator, InternalCDOWorkspaceBase base, CDOSessionConfigurationFactory remote,
- String branchPath, long timeStamp)
- {
- init(localRepositoryName, local, idGenerationLocation, idGenerator, base, remote);
-
- this.branchPath = StringUtil.isEmpty(branchPath) ? CDOBranch.MAIN_BRANCH_NAME : branchPath;
- this.timeStamp = timeStamp;
- fixed = timeStamp != CDOBranchPoint.UNSPECIFIED_DATE;
-
- checkout();
- saveProperties();
- }
-
- public CDOWorkspaceImpl(String localRepositoryName, IStore local, IDGenerationLocation idGenerationLocation,
- CDOIDGenerator idGenerator, InternalCDOWorkspaceBase base, CDOSessionConfigurationFactory remote)
- {
- init(localRepositoryName, local, idGenerationLocation, idGenerator, base, remote);
- loadProperties();
- }
-
- 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;
-
- localRepository = createLocalRepository(localRepositoryName, local);
-
- this.base = base;
- this.base.init(this);
- setDirtyFromBase();
- }
-
- private void setDirtyFromBase()
- {
- setDirty(!base.isEmpty());
- }
-
- protected void checkout()
- {
- final OMMonitor monitor = new Monitor();
- final IStoreAccessor.Raw accessor = (IStoreAccessor.Raw)localRepository.getStore().getWriter(null);
- StoreThreadLocal.setAccessor(accessor);
-
- try
- {
- InternalCDOSession remoteSession = openRemoteSession();
-
- try
- {
- localRepository.setRootResourceID(remoteSession.getRepositoryInfo().getRootResourceID());
-
- InternalCDOPackageUnit[] packageUnits = remoteSession.getPackageRegistry().getPackageUnits(false);
- localRepository.getPackageRegistry(false).putPackageUnits(packageUnits, CDOPackageUnit.State.LOADED);
- accessor.rawStore(packageUnits, monitor);
-
- CDORevisionHandler handler = new CDORevisionHandler()
- {
- public boolean handleRevision(CDORevision revision)
- {
- InternalCDORevision rev = (InternalCDORevision)revision;
- accessor.rawStore(rev, monitor);
-
- long commitTime = revision.getTimeStamp();
- if (commitTime > timeStamp)
- {
- timeStamp = commitTime;
- }
-
- return true;
- }
- };
-
- CDOBranch branch = remoteSession.getBranchManager().getBranch(branchPath);
- remoteSession.getSessionProtocol().handleRevisions(null, branch, false, timeStamp, false, handler);
- }
- finally
- {
- LifecycleUtil.deactivate(remoteSession);
- }
-
- accessor.rawCommit(1, monitor);
- }
- finally
- {
- StoreThreadLocal.release();
- monitor.done();
- }
- }
-
- 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 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)
- {
- 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)
- {
- 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();
- boolean isDetached = detachedObjects.contains(id);
-
- if (isDetached && base.isAddedObject(id))
- {
- base.deregisterObject(id);
- }
-
- if (dirtyObjects.contains(id) || isDetached)
- {
- 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);
- }
-
- setDirtyFromBase();
- }
- });
- }
-
- 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)
- {
- final InternalCDOSession remoteSession = openRemoteSession();
- if (timeStamp == CDOBranchPoint.UNSPECIFIED_DATE)
- {
- timeStamp = remoteSession.getLastUpdateTime();
- }
-
- final long newTimeStamp = timeStamp;
-
- final InternalCDOBranchManager branchManager = remoteSession.getBranchManager();
- final CDOBranchPoint basePoint = branchManager.getBranch(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 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);
- }
- finally
- {
- LifecycleUtil.deactivate(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
- base.registerChangedOrDetachedObject((InternalCDORevision)remoteRevision);
- }
- }
- else
- {
- if (remoteRevision == null)
- {
- // Added
- base.registerAddedObject(id);
- }
- else
- {
- CDORevisionDelta delta = localRevision.compare(remoteRevision);
- if (delta.isEmpty())
- {
- // Unchanged
- base.deregisterObject(id);
- }
- else
- {
- // Changed
- base.registerChangedOrDetachedObject((InternalCDORevision)remoteRevision);
- }
- }
- }
- }
- }
-
- 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;
- }
- });
-
- return transaction;
- }
-
- 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()
- {
- // TODO: implement CDOWorkspaceImpl.revert()
- throw new UnsupportedOperationException();
- }
-
- public void replace(String branchPath, long timeStamp)
- {
- // TODO: implement CDOWorkspaceImpl.replace(branchPath, timeStamp)
- throw new UnsupportedOperationException();
- }
-
- public CDOCommitInfo checkin() throws CommitException
- {
- return checkin(null);
- }
-
- public CDOCommitInfo checkin(String comment) throws CommitException
- {
- InternalCDOSession remoteSession = openRemoteSession();
-
- try
- {
- InternalCDOBranch branch = remoteSession.getBranchManager().getBranch(branchPath);
- InternalCDOTransaction transaction = (InternalCDOTransaction)remoteSession.openTransaction(branch);
-
- CDOChangeSetData changes = getLocalChanges();
-
- try
- {
- ApplyChangeSetResult result = transaction.applyChangeSet(changes, base, this, head, true);
- if (!result.getIDMappings().isEmpty())
- {
- throw new IllegalStateException("Attaching new objects is only supported for IDGenerationLocation.CLIENT");
- }
- }
- catch (ChangeSetOutdatedException ex)
- {
- throw new CommitException(ex);
- }
-
- transaction.setCommitComment(comment);
- CDOCommitInfo info = transaction.commit();
-
- clearBase();
- setTimeStamp(info.getTimeStamp());
- return info;
- }
- finally
- {
- LifecycleUtil.deactivate(remoteSession);
- }
- }
-
- /**
- * @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 = (IStoreAccessor.Raw)localRepository.getStore().getWriter(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, head, 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, head, 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;
-
- LifecycleUtil.deactivate(localRepository);
- localRepository = 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, head, 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()
- {
- Set<CDOID> ids = base.getIDs();
- return CDORevisionUtil.createChangeSetData(ids, base, this, true);
- }
-
- public CDOSessionConfigurationFactory getRemoteSessionConfigurationFactory()
- {
- return remoteSessionConfigurationFactory;
- }
-
- protected IManagedContainer createContainer(IStore local)
- {
- IManagedContainer container = ContainerUtil.createContainer();
- Net4jUtil.prepareContainer(container);
- JVMUtil.prepareContainer(container);
- CDONet4jServerUtil.prepareContainer(container);
- container.activate();
- return container;
- }
-
- protected IManagedContainer getContainer()
- {
- 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 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);
- configuration.setRevisionManager(CDORevisionUtil.createRevisionManager(CDORevisionCache.NOOP)); // Use repo's cache
-
- if (idGenerationLocation == IDGenerationLocation.STORE)
- {
- ((InternalCDOSessionConfiguration)configuration).setMainBranchLocal(true);
- }
-
- InternalCDOSession session = (InternalCDOSession)configuration.openNet4jSession();
- ((ISignalProtocol<?>)session.getSessionProtocol()).setTimeout(ISignalProtocol.NO_TIMEOUT);
- session.setPackageRegistry(localRepository.getPackageRegistry(false)); // Use repo's registry
-
- head = session.getBranchManager().getMainBranch().getHead();
- return session;
- }
-
- protected InternalCDOView[] getViews()
- {
- synchronized (views)
- {
- return views.toArray(new InternalCDOView[views.size()]);
- }
- }
-
- protected InternalCDOSession openRemoteSession()
- {
- CDOSessionConfiguration configuration = remoteSessionConfigurationFactory.createSessionConfiguration();
- InternalCDOSession session = (InternalCDOSession)configuration.openSession();
-
- CDORepositoryInfo repositoryInfo = session.getRepositoryInfo();
- if (!repositoryInfo.isSupportingAudits())
- {
- session.close();
- throw new IllegalStateException("Remote repository does not support auditing");
- }
-
- IDGenerationLocation remoteLocation = repositoryInfo.getIDGenerationLocation();
- if (!remoteLocation.equals(idGenerationLocation))
- {
- session.close();
- throw new IllegalStateException("Remote repository uses different ID generation location: " + remoteLocation);
- }
-
- return session;
- }
-
- 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_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_PATH, PROP_TIME_STAMP, PROP_FIXED));
- Map<String, String> props = localRepository.getStore().getPersistentProperties(names);
- 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 boolean dirty;
-
- public DirtyStateChangedEventImpl(CDOWorkspace workspace, boolean dirty)
- {
- super(workspace);
- this.dirty = dirty;
- }
-
- public boolean isDirty()
- {
- return dirty;
- }
- }
-}
+/* + * Copyright (c) 2004 - 2012 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.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.model.CDOPackageUnit; +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.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.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.ManagedRevisionProvider; +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.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.StringUtil; +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.emf.ecore.EClass; +import org.eclipse.emf.ecore.resource.ResourceSet; +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.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; + +/** + * @author Eike Stepper + */ +public class CDOWorkspaceImpl extends Notifier implements InternalCDOWorkspace +{ + 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 InternalCDOSession localSession; + + private CDOBranchPoint head; + + private String branchPath; + + private long timeStamp; + + private boolean fixed; + + private boolean dirty; + + private CDOSessionConfigurationFactory remoteSessionConfigurationFactory; + + private Set<InternalCDOView> views = new HashSet<InternalCDOView>(); + + public CDOWorkspaceImpl(String localRepositoryName, IStore local, IDGenerationLocation idGenerationLocation, + CDOIDGenerator idGenerator, InternalCDOWorkspaceBase base, CDOSessionConfigurationFactory remote, + String branchPath, long timeStamp) + { + init(localRepositoryName, local, idGenerationLocation, idGenerator, base, remote); + + this.branchPath = StringUtil.isEmpty(branchPath) ? CDOBranch.MAIN_BRANCH_NAME : branchPath; + this.timeStamp = timeStamp; + fixed = timeStamp != CDOBranchPoint.UNSPECIFIED_DATE; + + checkout(); + saveProperties(); + } + + public CDOWorkspaceImpl(String localRepositoryName, IStore local, IDGenerationLocation idGenerationLocation, + CDOIDGenerator idGenerator, InternalCDOWorkspaceBase base, CDOSessionConfigurationFactory remote) + { + init(localRepositoryName, local, idGenerationLocation, idGenerator, base, remote); + loadProperties(); + } + + 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; + + localRepository = createLocalRepository(localRepositoryName, local); + + this.base = base; + this.base.init(this); + setDirtyFromBase(); + } + + private void setDirtyFromBase() + { + setDirty(!base.isEmpty()); + } + + protected void checkout() + { + final OMMonitor monitor = new Monitor(); + final IStoreAccessor.Raw accessor = getLocalWriter(null); + StoreThreadLocal.setAccessor(accessor); + + try + { + InternalCDOSession remoteSession = openRemoteSession(); + + try + { + localRepository.setRootResourceID(remoteSession.getRepositoryInfo().getRootResourceID()); + + InternalCDOPackageUnit[] packageUnits = remoteSession.getPackageRegistry().getPackageUnits(false); + localRepository.getPackageRegistry(false).putPackageUnits(packageUnits, CDOPackageUnit.State.LOADED); + accessor.rawStore(packageUnits, monitor); + + CDORevisionHandler handler = new CDORevisionHandler() + { + public boolean handleRevision(CDORevision revision) + { + InternalCDORevision rev = (InternalCDORevision)revision; + accessor.rawStore(rev, monitor); + + long commitTime = revision.getTimeStamp(); + if (commitTime > timeStamp) + { + timeStamp = commitTime; + } + + return true; + } + }; + + CDOBranch branch = remoteSession.getBranchManager().getBranch(branchPath); + remoteSession.getSessionProtocol().handleRevisions(null, branch, false, timeStamp, false, handler); + } + finally + { + LifecycleUtil.deactivate(remoteSession); + } + + accessor.rawCommit(1, monitor); + } + finally + { + StoreThreadLocal.release(); + monitor.done(); + } + } + + 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 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) + { + 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) + { + 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(); + boolean isDetached = detachedObjects.contains(id); + + if (isDetached && base.isAddedObject(id)) + { + base.deregisterObject(id); + } + + if (dirtyObjects.contains(id) || isDetached) + { + 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); + } + + setDirtyFromBase(); + } + }); + } + + 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) + { + final InternalCDOSession remoteSession = openRemoteSession(); + if (timeStamp == CDOBranchPoint.UNSPECIFIED_DATE) + { + timeStamp = remoteSession.getLastUpdateTime(); + } + + final long newTimeStamp = timeStamp; + + final InternalCDOBranchManager branchManager = remoteSession.getBranchManager(); + final CDOBranchPoint basePoint = branchManager.getBranch(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 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); + } + finally + { + LifecycleUtil.deactivate(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 + base.registerChangedOrDetachedObject((InternalCDORevision)remoteRevision); + } + } + else + { + if (remoteRevision == null) + { + // Added + base.registerAddedObject(id); + } + else + { + CDORevisionDelta delta = localRevision.compare(remoteRevision); + if (delta.isEmpty()) + { + // Unchanged + base.deregisterObject(id); + } + else + { + // Changed + base.registerChangedOrDetachedObject((InternalCDORevision)remoteRevision); + } + } + } + } + } + + 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; + } + }); + + return transaction; + } + + 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() + { + // TODO: implement CDOWorkspaceImpl.revert() + throw new UnsupportedOperationException(); + } + + public void replace(String branchPath, long timeStamp) + { + // TODO: implement CDOWorkspaceImpl.replace(branchPath, timeStamp) + throw new UnsupportedOperationException(); + } + + public CDOCommitInfo checkin() throws CommitException + { + return checkin(null); + } + + public CDOCommitInfo checkin(String comment) throws CommitException + { + InternalCDOSession remoteSession = openRemoteSession(); + + try + { + InternalCDOBranch branch = remoteSession.getBranchManager().getBranch(branchPath); + InternalCDOTransaction transaction = (InternalCDOTransaction)remoteSession.openTransaction(branch); + + CDOChangeSetData changes = getLocalChanges(); + + try + { + ApplyChangeSetResult result = transaction.applyChangeSet(changes, base, this, head, true); + if (!result.getIDMappings().isEmpty()) + { + throw new IllegalStateException("Attaching new objects is only supported for IDGenerationLocation.CLIENT"); + } + } + catch (ChangeSetOutdatedException ex) + { + throw new CommitException(ex); + } + + transaction.setCommitComment(comment); + CDOCommitInfo info = transaction.commit(); + + adjustLocalRevisions(transaction, info); + clearBase(); + setTimeStamp(info.getTimeStamp()); + + return info; + } + finally + { + LifecycleUtil.deactivate(remoteSession); + } + } + + protected void adjustLocalRevisions(InternalCDOTransaction transaction, CDOCommitInfo info) + { + IStoreAccessor.Raw accessor = null; + for (CDORevisionKey key : info.getChangedObjects()) + { + CDOID id = key.getID(); + InternalCDORevision localRevision = (InternalCDORevision)getRevision(id); + CDORevision baseRevision = base.getRevision(id); + CDORevision remoteRevision = transaction.getObject(id).cdoRevision(); + + CDOBranch localBranch = head.getBranch(); + EClass eClass = localRevision.getEClass(); + + for (int v = baseRevision.getVersion(); v < localRevision.getVersion(); v++) + { + if (accessor == null) + { + accessor = getLocalWriter(null); + StoreThreadLocal.setAccessor(accessor); + } + + accessor.rawDelete(id, v, localBranch, eClass, new Monitor()); + } + + if (localRevision.getVersion() != remoteRevision.getVersion()) + { + if (accessor == null) + { + accessor = getLocalWriter(null); + StoreThreadLocal.setAccessor(accessor); + } + + accessor.rawDelete(id, localRevision.getVersion(), localBranch, eClass, new Monitor()); + localRevision.setVersion(remoteRevision.getVersion()); + accessor.rawStore(localRevision, new Monitor()); + } + } + + if (accessor != null) + { + accessor.rawCommit(1, new Monitor()); + StoreThreadLocal.release(); + localRepository.getRevisionManager().getCache().clear(); + localSession.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, head, 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, head, 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; + + LifecycleUtil.deactivate(localRepository); + localRepository = 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, head, 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() + { + Set<CDOID> ids = base.getIDs(); + return CDORevisionUtil.createChangeSetData(ids, base, this, true); + } + + public CDOSessionConfigurationFactory getRemoteSessionConfigurationFactory() + { + return remoteSessionConfigurationFactory; + } + + protected IManagedContainer createContainer(IStore local) + { + IManagedContainer container = ContainerUtil.createContainer(); + Net4jUtil.prepareContainer(container); + JVMUtil.prepareContainer(container); + CDONet4jServerUtil.prepareContainer(container); + container.activate(); + return container; + } + + protected IManagedContainer getContainer() + { + 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); + configuration.setRevisionManager(CDORevisionUtil.createRevisionManager(CDORevisionCache.NOOP)); // Use repo's cache + + if (idGenerationLocation == IDGenerationLocation.STORE) + { + ((InternalCDOSessionConfiguration)configuration).setMainBranchLocal(true); + } + + InternalCDOSession session = (InternalCDOSession)configuration.openNet4jSession(); + ((ISignalProtocol<?>)session.getSessionProtocol()).setTimeout(ISignalProtocol.NO_TIMEOUT); + session.setPackageRegistry(localRepository.getPackageRegistry(false)); // Use repo's registry + + head = session.getBranchManager().getMainBranch().getHead(); + return session; + } + + protected InternalCDOView[] getViews() + { + synchronized (views) + { + return views.toArray(new InternalCDOView[views.size()]); + } + } + + protected InternalCDOSession openRemoteSession() + { + CDOSessionConfiguration configuration = remoteSessionConfigurationFactory.createSessionConfiguration(); + InternalCDOSession session = (InternalCDOSession)configuration.openSession(); + + CDORepositoryInfo repositoryInfo = session.getRepositoryInfo(); + if (!repositoryInfo.isSupportingAudits()) + { + session.close(); + throw new IllegalStateException("Remote repository does not support auditing"); + } + + IDGenerationLocation remoteLocation = repositoryInfo.getIDGenerationLocation(); + if (!remoteLocation.equals(idGenerationLocation)) + { + session.close(); + throw new IllegalStateException("Remote repository uses different ID generation location: " + remoteLocation); + } + + return session; + } + + 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_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_PATH, PROP_TIME_STAMP, PROP_FIXED)); + Map<String, String> props = localRepository.getStore().getPersistentProperties(names); + 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 boolean dirty; + + public DirtyStateChangedEventImpl(CDOWorkspace workspace, boolean dirty) + { + super(workspace); + this.dirty = dirty; + } + + public boolean isDirty() + { + return dirty; + } + } +} |