Skip to main content

This CGIT instance is deprecated, and repositories have been moved to Gitlab or Github. See the repository descriptions for specific locations.

summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java2814
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java1784
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractListTableMapping.java970
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMapping.java181
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditFeatureMapTableMappingWithRanges.java2430
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMapping.java179
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMappingWithRanges.java2244
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BasicAbstractListTableMapping.java148
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMapping.java192
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingFeatureMapTableMappingWithRanges.java6
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMapping.java190
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMappingWithRanges.java6
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditClassMapping.java1519
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalBranchingClassMapping.java38
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditClassMapping.java1534
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditFeatureMapTableMapping.java1187
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java2362
-rw-r--r--plugins/org.eclipse.emf.cdo.server.db4o/src/org/eclipse/emf/cdo/server/internal/db4o/DB4OStoreAccessor.java1751
-rw-r--r--plugins/org.eclipse.emf.cdo.server.hibernate/src/org/eclipse/emf/cdo/server/internal/hibernate/HibernateStoreAccessor.java1
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/mem/MEMStoreAccessor.java1001
-rw-r--r--plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java1512
-rw-r--r--plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/AllTestsDBH2Audit.java85
-rw-r--r--plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/AllTestsDBH2Branching.java87
-rw-r--r--plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/RevisionManagerTest.java1706
-rw-r--r--plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/WorkspaceTest.java2978
-rw-r--r--plugins/org.eclipse.emf.cdo.workspace/src/org/eclipse/emf/cdo/internal/workspace/CDOWorkspaceImpl.java1977
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(&quot;Trying to update object &quot; + revisionDelta.getID()
- * + &quot; that was already modified&quot;);
- * }
- * </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(&quot;Trying to update object &quot; + revisionDelta.getID()
+ * + &quot; that was already modified&quot;);
+ * }
+ * </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;
+ }
+ }
+}

Back to the top