diff options
author | Eike Stepper | 2012-06-27 04:58:52 +0000 |
---|---|---|
committer | Eike Stepper | 2012-06-27 04:58:52 +0000 |
commit | cb0b9e117562afd34dbb50470a5da87f1983bea1 (patch) | |
tree | 8d0933a9f01cfff460e783013d2a76076e676a6d /plugins | |
parent | 79ee2993d0aa71e97973103ce6689e9134ec1c88 (diff) | |
download | cdo-cb0b9e117562afd34dbb50470a5da87f1983bea1.tar.gz cdo-cb0b9e117562afd34dbb50470a5da87f1983bea1.tar.xz cdo-cb0b9e117562afd34dbb50470a5da87f1983bea1.zip |
[383602] Branch with base after the last finished commit can be created
https://bugs.eclipse.org/bugs/show_bug.cgi?id=383602
Diffstat (limited to 'plugins')
8 files changed, 4984 insertions, 4892 deletions
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 fc91648d11..607f60eb69 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 @@ -1,1502 +1,1516 @@ -/*
- * 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 taken from AuditFeatureMapTableMappingWithRanges
- * 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.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.IStoreAccessor.QueryXRefsContext;
-import org.eclipse.emf.cdo.server.IStoreChunkReader;
-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 BranchingFeatureMapTableMappingWithRanges extends BasicAbstractListTableMapping implements
- IListMappingDeltaSupport
-{
- private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG,
- BranchingFeatureMapTableMappingWithRanges.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;
-
- public BranchingFeatureMapTableMappingWithRanges(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());
-
- IDBField branchField = table.addField(CDODBSchema.LIST_REVISION_BRANCH, DBType.INTEGER);
-
- // 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);
- }
-
- table.addIndex(Type.NON_UNIQUE, idField);
- table.addIndex(Type.NON_UNIQUE, branchField);
- table.addIndex(Type.NON_UNIQUE, versionAddedField);
- table.addIndex(Type.NON_UNIQUE, versionRemovedField);
- table.addIndex(Type.NON_UNIQUE, idxField);
- table.addIndex(Type.NON_UNIQUE, tagField);
- }
-
- public Collection<IDBTable> getDBTables()
- {
- return Arrays.asList(table);
- }
-
- 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_IDX);
- builder.append(", "); //$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_BRANCH);
- 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_BRANCH);
- 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_BRANCH);
- 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_BRANCH);
- 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_BRANCH);
- 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_BRANCH);
- 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_BRANCH);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED);
- builder.append(" IS NULL"); //$NON-NLS-1$
- sqlClearList = builder.toString();
- }
-
- protected List<DBType> getDBTypes()
- {
- return dbTypes;
- }
-
- 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());
- int valuesToRead = list.size();
-
- if (listChunk != CDORevision.UNCHUNKED && listChunk < valuesToRead)
- {
- valuesToRead = listChunk;
- }
-
- if (valuesToRead == 0)
- {
- // nothing to read take shortcut
- return;
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading list values for feature {0}.{1} of {2}", getContainingClass().getName(), getFeature() //$NON-NLS-1$
- .getName(), revision);
- }
-
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
- ResultSet resultSet = null;
-
- IStoreChunkReader baseReader = null;
-
- try
- {
- String sql = sqlSelectChunksPrefix + sqlOrderByIndex;
-
- CDOID id = revision.getID();
- int branchID = revision.getBranch().getID();
-
- stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH);
- idHandler.setCDOID(stmt, 1, id);
- stmt.setInt(2, branchID);
- stmt.setInt(3, revision.getVersion());
- stmt.setInt(4, revision.getVersion());
-
- stmt.setMaxRows(valuesToRead); // optimization - don't read unneeded rows.
-
- resultSet = stmt.executeQuery();
-
- int currentIndex = 0;
-
- while (valuesToRead > 0 && resultSet.next())
- {
- int index = resultSet.getInt(1);
- if (index > currentIndex)
- {
- if (baseReader == null)
- {
- baseReader = createBaseChunkReader(accessor, id, branchID);
- }
-
- baseReader.addRangedChunk(currentIndex, index);
- if (TRACER.isEnabled())
- {
- TRACER.format("Scheduling range {0}-{1} to be read from base revision", currentIndex, index); //$NON-NLS-1$
- }
-
- valuesToRead -= index - currentIndex;
- currentIndex = index;
- }
-
- CDOID tag = idHandler.getCDOID(resultSet, 2);
- Object value = getTypeMapping(tag).readValue(resultSet);
- if (TRACER.isEnabled())
- {
- TRACER.format("Read value for index {0} from result set: {1}", currentIndex, value); //$NON-NLS-1$
- }
-
- list.set(currentIndex++, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value));
- valuesToRead--;
- }
-
- if (valuesToRead > 0)
- {
- if (baseReader == null)
- {
- baseReader = createBaseChunkReader(accessor, id, branchID);
- }
-
- baseReader.addRangedChunk(currentIndex, currentIndex + valuesToRead);
- }
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- DBUtil.close(resultSet);
- statementCache.releasePreparedStatement(stmt);
- }
-
- if (baseReader != null)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading base revision chunks for featureMap {0}.{1} of {2} from base revision {3}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), revision, baseReader.getRevision());
- }
-
- List<Chunk> baseChunks = baseReader.executeRead();
- for (Chunk chunk : baseChunks)
- {
- int startIndex = chunk.getStartIndex();
- for (int i = 0; i < chunk.size(); i++)
- {
- list.set(startIndex + i, chunk.get(i));
- }
- }
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading list values done for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$
- getFeature().getName(), revision);
- }
- }
-
- 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)
- {
- CDORevision revision = chunkReader.getRevision();
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading list chunk values for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$
- getFeature().getName(), revision);
- }
-
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache();
- PreparedStatement stmt = null;
- ResultSet resultSet = null;
-
- IStoreChunkReader baseReader = 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, revision.getID());
- stmt.setInt(2, revision.getBranch().getID());
- stmt.setInt(3, revision.getVersion());
- stmt.setInt(4, revision.getVersion());
-
- resultSet = stmt.executeQuery();
-
- int nextDBIndex = Integer.MAX_VALUE; // next available DB index
- if (resultSet.next())
- {
- nextDBIndex = resultSet.getInt(1);
- }
-
- for (Chunk chunk : chunks)
- {
- int startIndex = chunk.getStartIndex();
- int missingValueStartIndex = -1;
-
- for (int i = 0; i < chunk.size(); i++)
- {
- int nextListIndex = startIndex + i; // next expected list index
-
- if (nextDBIndex == nextListIndex)
- {
- // DB value is available. check first if missing indexes were present before.
- if (missingValueStartIndex != -1)
- {
- // read missing indexes from missingValueStartIndex to currentIndex
- if (baseReader == null)
- {
- baseReader = createBaseChunkReader(chunkReader.getAccessor(), chunkReader.getRevision().getID(),
- chunkReader.getRevision().getBranch().getID());
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format(
- "Scheduling range {0}-{1} to be read from base revision", missingValueStartIndex, nextListIndex); //$NON-NLS-1$
- }
-
- baseReader.addRangedChunk(missingValueStartIndex, nextListIndex);
-
- // reset missingValueStartIndex
- missingValueStartIndex = -1;
- }
-
- // now read value and set to chunk
- CDOID tag = idHandler.getCDOID(resultSet, 2);
- Object value = getTypeMapping(tag).readValue(resultSet);
- if (TRACER.isEnabled())
- {
- TRACER.format("ChunkReader read value for index {0} from result set: {1}", nextDBIndex, value); //$NON-NLS-1$
- }
-
- chunk.add(i, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value));
-
- // advance DB cursor and read next available index
- if (resultSet.next())
- {
- nextDBIndex = resultSet.getInt(1);
- }
- else
- {
- // no more DB indexes available, but we have to continue checking for gaps, therefore set to MAX_VALUE
- nextDBIndex = Integer.MAX_VALUE;
- }
- }
- else
- {
- // gap between next DB index and next list index detected.
- // skip until end of chunk or until DB value becomes available
- if (missingValueStartIndex == -1)
- {
- missingValueStartIndex = nextListIndex;
- }
- }
- }
-
- // chunk complete. check for missing values at the end of the chunk.
- if (missingValueStartIndex != -1)
- {
- // read missing indexes from missingValueStartIndex to last chunk index
- if (baseReader == null)
- {
- baseReader = createBaseChunkReader(chunkReader.getAccessor(), chunkReader.getRevision().getID(),
- chunkReader.getRevision().getBranch().getID());
- }
- baseReader.addRangedChunk(missingValueStartIndex, chunk.getStartIndex() + chunk.size());
- }
- }
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- DBUtil.close(resultSet);
- statementCache.releasePreparedStatement(stmt);
- }
-
- // now read missing values from base revision.
- if (baseReader != null)
- {
- List<Chunk> baseChunks = baseReader.executeRead();
-
- Iterator<Chunk> thisIterator = chunks.iterator();
- Chunk thisChunk = thisIterator.next();
-
- for (Chunk baseChunk : baseChunks)
- {
- int baseStartIndex = baseChunk.getStartIndex();
-
- while (baseStartIndex > thisChunk.getStartIndex() + thisChunk.size())
- {
- // advance thisChunk, because it does not match baseChunk
- thisChunk = thisIterator.next();
- }
-
- // baseChunk now corresponds to this chunk, but startIndex of baseChunk may be higher.
- // therefore calculate offset
- int offset = thisChunk.getStartIndex() - baseStartIndex;
-
- // and copy values.
- for (int i = 0; i < baseChunk.size(); i++)
- {
- thisChunk.add(i + offset, baseChunk.get(i));
- }
- } // finally, continue with the next baseChunk
-
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading list chunk values done for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$
- getFeature(), revision);
- }
- }
-
- 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.getBranch().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 created)
- {
- return getMappingStrategy().getStore().getMetaDataManager().getMetaID(feature, created);
- }
-
- /**
- * 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 branchId, int oldVersion, int newVersion,
- int lastIndex, long timestamp)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmtDeleteTemp = null;
- PreparedStatement stmtClear = null;
-
- try
- {
- // check for each index if the value exists in the current branch
- for (int i = 0; i <= lastIndex; i++)
- {
- if (getValue(accessor, id, branchId, i, false) == null)
- {
- // if not, add a historic entry for missing ones.
- addHistoricEntry(accessor, id, branchId, 0, newVersion, i, getValueFromBase(accessor, id, branchId, i),
- timestamp);
- }
- }
-
- // clear rest of the list
- stmtClear = statementCache.getPreparedStatement(sqlClearList, ReuseProbability.HIGH);
- stmtClear.setInt(1, newVersion);
- idHandler.setCDOID(stmtClear, 2, id);
- stmtClear.setInt(3, branchId);
-
- int 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)
- {
- InternalCDORevision revision = (InternalCDORevision)accessor.getTransaction().getRevision(id);
- int branchId = accessor.getTransaction().getBranch().getID();
-
- if (TRACER.isEnabled())
- {
- TRACER.format("objectDetached {1}", revision); //$NON-NLS-1$
- }
-
- clearList(accessor, id, branchId, revision.getVersion(), FINAL_VERSION, revision.getList(getFeature()).size() - 1,
- revised);
- }
-
- public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion,
- final int newVersion, long created, CDOListFeatureDelta delta)
- {
- List<CDOFeatureDelta> listChanges = delta.getListChanges();
- if (listChanges.size() == 0)
- {
- // nothing to do.
- return;
- }
-
- InternalCDORevision originalRevision = (InternalCDORevision)accessor.getTransaction().getRevision(id);
- 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, branchId, oldVersion, newVersion,
- created);
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Processing deltas..."); //$NON-NLS-1$
- }
-
- // optimization: it's only necessary to process deltas
- // starting with the last feature delta which clears the list
- // (any operation before the clear is cascaded by it anyway)
- int index = listChanges.size() - 1;
- while (index > 0)
- {
- CDOFeatureDelta listDelta = listChanges.get(index);
- if (listDelta instanceof CDOClearFeatureDelta || listDelta instanceof CDOUnsetFeatureDelta)
- {
- break;
- }
- index--;
- }
- while (index < listChanges.size())
- {
- listChanges.get(index++).accept(visitor);
- }
- }
-
- private class ListDeltaVisitor implements CDOFeatureDeltaVisitor
- {
- private IDBStoreAccessor accessor;
-
- private InternalCDORevision originalRevision;
-
- private CDOID id;
-
- private int branchID;
-
- private int oldVersion;
-
- private int newVersion;
-
- private int lastIndex;
-
- private long timestamp;
-
- public ListDeltaVisitor(IDBStoreAccessor accessor, InternalCDORevision originalRevision, int targetBranchID,
- int oldVersion, int newVersion, long timestamp)
- {
- this.accessor = accessor;
- this.originalRevision = originalRevision;
- id = this.originalRevision.getID();
- branchID = targetBranchID;
- 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, branchID, fromIdx, true);
-
- // remove the item
- removeEntry(accessor, id, branchID, oldVersion, newVersion, fromIdx, timestamp);
-
- // adjust indexes and shift either up or down
- if (fromIdx < toIdx)
- {
- moveOneUp(accessor, id, branchID, oldVersion, newVersion, fromIdx + 1, toIdx);
- }
- else
- { // fromIdx > toIdx here
- moveOneDown(accessor, id, branchID, oldVersion, newVersion, toIdx, fromIdx - 1);
- }
-
- // create the item
- addEntry(accessor, id, branchID, 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, branchID, oldVersion, newVersion, startIndex, endIndex);
- }
-
- // create the item
- addEntry(accessor, id, branchID, 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, branchID, oldVersion, newVersion, startIndex, timestamp);
-
- // make room for the new item
- moveOneUp(accessor, id, branchID, 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, branchID, oldVersion, newVersion, index, timestamp);
-
- // create the item
- addEntry(accessor, id, branchID, 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, branchID, oldVersion, newVersion, lastIndex, timestamp);
- 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, branchID, oldVersion, newVersion, lastIndex, timestamp);
- 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 branchId, 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, startIndex++, id);
- stmt.setInt(column++, branchId);
- stmt.setInt(column++, newVersion);
- stmt.setInt(column++, index);
-
- int result = DBUtil.update(stmt, false);
- switch (result)
- {
- case 1:
- // entry for current revision was already present.
- // index update succeeded.
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneUp updated: {0} -> {1}", index, index - 1); //$NON-NLS-1$
- }
-
- break;
- case 0:
- Object value = getValue(accessor, id, branchId, index, false);
-
- if (value != null)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneUp remove: {0}", index); //$NON-NLS-1$
- }
-
- removeEntry(accessor, id, branchId, oldVersion, newVersion, index, timestamp);
- }
- else
- {
- value = getValueFromBase(accessor, id, branchId, index);
- {
- TRACER.format("moveOneUp add historic entry at: {0}", index); //$NON-NLS-1$
- }
-
- addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value, timestamp);
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneUp add: {0}", index - 1); //$NON-NLS-1$
- }
-
- addEntry(accessor, id, branchId, newVersion, index - 1, value, timestamp);
- 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 branchId, 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++, branchId);
- stmt.setInt(column++, newVersion);
- stmt.setInt(column++, index);
-
- int result = DBUtil.update(stmt, false);
- switch (result)
- {
- case 1:
- // entry for current revision was already present.
- // index update succeeded.
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneDown updated: {0} -> {1}", index, index + 1); //$NON-NLS-1$
- }
-
- break;
- case 0:
- Object value = getValue(accessor, id, branchId, index, false);
- if (value != null)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneDown remove: {0}", index); //$NON-NLS-1$
- }
-
- removeEntry(accessor, id, branchId, oldVersion, newVersion, index, timestamp);
- }
- else
- {
- value = getValueFromBase(accessor, id, branchId, index);
- {
- TRACER.format("moveOneDown add historic entry at: {0}", index); //$NON-NLS-1$
- }
-
- addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value, timestamp);
- }
-
- addEntry(accessor, id, branchId, newVersion, index + 1, value, timestamp);
- 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 branchId, 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++, branchId);
- 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 addHistoricEntry(IDBStoreAccessor accessor, CDOID id, int branchId, int versionAdded,
- int versionRemoved, int index, Object value, long timestamp)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- if (TRACER.isEnabled())
- {
- TRACER.format(
- "Adding historic value for feature {0}.{1} index {2} of {3}:{4}v{5}-v{6} : {7}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), index, id, branchId, versionAdded, versionRemoved,
- 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++, branchId);
- stmt.setInt(column++, versionAdded);
- stmt.setNull(column++, 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 branchId, int oldVersion, int newVersion,
- int index, long timestamp)
- {
- 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++, branchId);
- 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++, branchId);
- stmt.setInt(column++, index);
- result = DBUtil.update(stmt, false);
-
- if (result == 0)
- {
- // no entry removed -> this means that we are in a branch and
- // the entry has not been modified since the branch fork.
- // therefore, we have to copy the base value and mark it as removed
- Object value = getValueFromBase(accessor, id, branchId, index);
- addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value, timestamp);
- }
- }
- }
- 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 branchId, int index, boolean getFromBase)
- {
- 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++, branchId);
- stmt.setInt(column++, index);
-
- ResultSet resultSet = stmt.executeQuery();
- if (resultSet.next())
- {
- CDOID tag = idHandler.getCDOID(resultSet, 1);
- Object value = getTypeMapping(tag).readValue(resultSet);
- result = CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value);
- }
- else
- {
- // value is not in this branch.
- // -> read from base revision
- if (getFromBase)
- {
- result = getValueFromBase(accessor, id, branchId, index);
- } // else: result remains null
- }
- 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;
- }
-
- /**
- * Read a single value (at a given index) from the base revision
- *
- * @param accessor
- * the DBStoreAccessor
- * @param id
- * the ID of the revision
- * @param branchID
- * the ID of the current (child) branch
- * @param index
- * the index to read the value from
- * @return the value which is at index <code>index</code> in revision with ID <code>id</code> in the parent branch at
- * the base of this branch (indicated by <code>branchID</code>).
- */
- private FeatureMap.Entry getValueFromBase(IDBStoreAccessor accessor, CDOID id, int branchID, int index)
- {
- IStoreChunkReader chunkReader = createBaseChunkReader(accessor, id, branchID);
- chunkReader.addSimpleChunk(index);
- List<Chunk> chunks = chunkReader.executeRead();
- return (FeatureMap.Entry)chunks.get(0).get(0);
- }
-
- private IStoreChunkReader createBaseChunkReader(IDBStoreAccessor accessor, CDOID id, int branchID)
- {
- CDOBranchPoint base = accessor.getStore().getRepository().getBranchManager().getBranch(branchID).getBase();
- InternalCDORevision baseRevision = (InternalCDORevision)accessor.getStore().getRepository().getRevisionManager()
- .getRevision(id, base, /* referenceChunk = */0, /* prefetchDepth = */CDORevision.DEPTH_NONE, true);
- IStoreChunkReader chunkReader = accessor.createChunkReader(baseRevision, getFeature());
- return chunkReader;
- }
-
- 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: + * Stefan Winkler - initial API and implementation taken from AuditFeatureMapTableMappingWithRanges + * 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.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.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.IStoreChunkReader; +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.emf.cdo.spi.common.revision.InternalCDORevisionManager; +import org.eclipse.emf.cdo.spi.server.InternalRepository; + +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 BranchingFeatureMapTableMappingWithRanges extends BasicAbstractListTableMapping implements + IListMappingDeltaSupport +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, + BranchingFeatureMapTableMappingWithRanges.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; + + public BranchingFeatureMapTableMappingWithRanges(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()); + + IDBField branchField = table.addField(CDODBSchema.LIST_REVISION_BRANCH, DBType.INTEGER); + + // 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); + } + + table.addIndex(Type.NON_UNIQUE, idField); + table.addIndex(Type.NON_UNIQUE, branchField); + table.addIndex(Type.NON_UNIQUE, versionAddedField); + table.addIndex(Type.NON_UNIQUE, versionRemovedField); + table.addIndex(Type.NON_UNIQUE, idxField); + table.addIndex(Type.NON_UNIQUE, tagField); + } + + public Collection<IDBTable> getDBTables() + { + return Arrays.asList(table); + } + + 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_IDX); + builder.append(", "); //$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_BRANCH); + 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_BRANCH); + 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_BRANCH); + 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_BRANCH); + 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_BRANCH); + 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_BRANCH); + 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_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.FEATUREMAP_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlClearList = builder.toString(); + } + + protected List<DBType> getDBTypes() + { + return dbTypes; + } + + 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()); + int valuesToRead = list.size(); + + if (listChunk != CDORevision.UNCHUNKED && listChunk < valuesToRead) + { + valuesToRead = listChunk; + } + + if (valuesToRead == 0) + { + // nothing to read take shortcut + return; + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list values for feature {0}.{1} of {2}", getContainingClass().getName(), getFeature() //$NON-NLS-1$ + .getName(), revision); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + IStoreChunkReader baseReader = null; + + try + { + String sql = sqlSelectChunksPrefix + sqlOrderByIndex; + + CDOID id = revision.getID(); + int branchID = revision.getBranch().getID(); + + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, id); + stmt.setInt(2, branchID); + stmt.setInt(3, revision.getVersion()); + stmt.setInt(4, revision.getVersion()); + + stmt.setMaxRows(valuesToRead); // optimization - don't read unneeded rows. + + resultSet = stmt.executeQuery(); + + int currentIndex = 0; + + while (valuesToRead > 0 && resultSet.next()) + { + int index = resultSet.getInt(1); + if (index > currentIndex) + { + if (baseReader == null) + { + baseReader = createBaseChunkReader(accessor, id, branchID); + } + + baseReader.addRangedChunk(currentIndex, index); + if (TRACER.isEnabled()) + { + TRACER.format("Scheduling range {0}-{1} to be read from base revision", currentIndex, index); //$NON-NLS-1$ + } + + valuesToRead -= index - currentIndex; + currentIndex = index; + } + + CDOID tag = idHandler.getCDOID(resultSet, 2); + Object value = getTypeMapping(tag).readValue(resultSet); + if (TRACER.isEnabled()) + { + TRACER.format("Read value for index {0} from result set: {1}", currentIndex, value); //$NON-NLS-1$ + } + + list.set(currentIndex++, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value)); + valuesToRead--; + } + + if (valuesToRead > 0) + { + if (baseReader == null) + { + baseReader = createBaseChunkReader(accessor, id, branchID); + } + + baseReader.addRangedChunk(currentIndex, currentIndex + valuesToRead); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + + if (baseReader != null) + { + if (TRACER.isEnabled()) + { + TRACER.format("Reading base revision chunks for featureMap {0}.{1} of {2} from base revision {3}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), revision, baseReader.getRevision()); + } + + List<Chunk> baseChunks = baseReader.executeRead(); + for (Chunk chunk : baseChunks) + { + int startIndex = chunk.getStartIndex(); + for (int i = 0; i < chunk.size(); i++) + { + list.set(startIndex + i, chunk.get(i)); + } + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list values done for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), revision); + } + } + + 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) + { + CDORevision revision = chunkReader.getRevision(); + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), revision); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + IStoreChunkReader baseReader = 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, revision.getID()); + stmt.setInt(2, revision.getBranch().getID()); + stmt.setInt(3, revision.getVersion()); + stmt.setInt(4, revision.getVersion()); + + resultSet = stmt.executeQuery(); + + int nextDBIndex = Integer.MAX_VALUE; // next available DB index + if (resultSet.next()) + { + nextDBIndex = resultSet.getInt(1); + } + + for (Chunk chunk : chunks) + { + int startIndex = chunk.getStartIndex(); + int missingValueStartIndex = -1; + + for (int i = 0; i < chunk.size(); i++) + { + int nextListIndex = startIndex + i; // next expected list index + + if (nextDBIndex == nextListIndex) + { + // DB value is available. check first if missing indexes were present before. + if (missingValueStartIndex != -1) + { + // read missing indexes from missingValueStartIndex to currentIndex + if (baseReader == null) + { + baseReader = createBaseChunkReader(chunkReader.getAccessor(), chunkReader.getRevision().getID(), + chunkReader.getRevision().getBranch().getID()); + } + + if (TRACER.isEnabled()) + { + TRACER.format( + "Scheduling range {0}-{1} to be read from base revision", missingValueStartIndex, nextListIndex); //$NON-NLS-1$ + } + + baseReader.addRangedChunk(missingValueStartIndex, nextListIndex); + + // reset missingValueStartIndex + missingValueStartIndex = -1; + } + + // now read value and set to chunk + CDOID tag = idHandler.getCDOID(resultSet, 2); + Object value = getTypeMapping(tag).readValue(resultSet); + if (TRACER.isEnabled()) + { + TRACER.format("ChunkReader read value for index {0} from result set: {1}", nextDBIndex, value); //$NON-NLS-1$ + } + + chunk.add(i, CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value)); + + // advance DB cursor and read next available index + if (resultSet.next()) + { + nextDBIndex = resultSet.getInt(1); + } + else + { + // no more DB indexes available, but we have to continue checking for gaps, therefore set to MAX_VALUE + nextDBIndex = Integer.MAX_VALUE; + } + } + else + { + // gap between next DB index and next list index detected. + // skip until end of chunk or until DB value becomes available + if (missingValueStartIndex == -1) + { + missingValueStartIndex = nextListIndex; + } + } + } + + // chunk complete. check for missing values at the end of the chunk. + if (missingValueStartIndex != -1) + { + // read missing indexes from missingValueStartIndex to last chunk index + if (baseReader == null) + { + baseReader = createBaseChunkReader(chunkReader.getAccessor(), chunkReader.getRevision().getID(), + chunkReader.getRevision().getBranch().getID()); + } + baseReader.addRangedChunk(missingValueStartIndex, chunk.getStartIndex() + chunk.size()); + } + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + + // now read missing values from base revision. + if (baseReader != null) + { + List<Chunk> baseChunks = baseReader.executeRead(); + + Iterator<Chunk> thisIterator = chunks.iterator(); + Chunk thisChunk = thisIterator.next(); + + for (Chunk baseChunk : baseChunks) + { + int baseStartIndex = baseChunk.getStartIndex(); + + while (baseStartIndex > thisChunk.getStartIndex() + thisChunk.size()) + { + // advance thisChunk, because it does not match baseChunk + thisChunk = thisIterator.next(); + } + + // baseChunk now corresponds to this chunk, but startIndex of baseChunk may be higher. + // therefore calculate offset + int offset = thisChunk.getStartIndex() - baseStartIndex; + + // and copy values. + for (int i = 0; i < baseChunk.size(); i++) + { + thisChunk.add(i + offset, baseChunk.get(i)); + } + } // finally, continue with the next baseChunk + + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values done for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature(), revision); + } + } + + 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.getBranch().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 created) + { + return getMappingStrategy().getStore().getMetaDataManager().getMetaID(feature, created); + } + + /** + * 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 branchId, int oldVersion, int newVersion, + int lastIndex, long timestamp) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmtDeleteTemp = null; + PreparedStatement stmtClear = null; + + try + { + // check for each index if the value exists in the current branch + for (int i = 0; i <= lastIndex; i++) + { + if (getValue(accessor, id, branchId, i, false) == null) + { + // if not, add a historic entry for missing ones. + addHistoricEntry(accessor, id, branchId, 0, newVersion, i, getValueFromBase(accessor, id, branchId, i), + timestamp); + } + } + + // clear rest of the list + stmtClear = statementCache.getPreparedStatement(sqlClearList, ReuseProbability.HIGH); + stmtClear.setInt(1, newVersion); + idHandler.setCDOID(stmtClear, 2, id); + stmtClear.setInt(3, branchId); + + int 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) + { + InternalCDORevision revision = (InternalCDORevision)accessor.getTransaction().getRevision(id); + int branchId = accessor.getTransaction().getBranch().getID(); + + if (TRACER.isEnabled()) + { + TRACER.format("objectDetached {1}", revision); //$NON-NLS-1$ + } + + clearList(accessor, id, branchId, revision.getVersion(), FINAL_VERSION, revision.getList(getFeature()).size() - 1, + revised); + } + + public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion, + final int newVersion, long created, CDOListFeatureDelta delta) + { + List<CDOFeatureDelta> listChanges = delta.getListChanges(); + if (listChanges.size() == 0) + { + // nothing to do. + return; + } + + InternalCDORevision originalRevision = (InternalCDORevision)accessor.getTransaction().getRevision(id); + 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, branchId, oldVersion, newVersion, + created); + + if (TRACER.isEnabled()) + { + TRACER.format("Processing deltas..."); //$NON-NLS-1$ + } + + // optimization: it's only necessary to process deltas + // starting with the last feature delta which clears the list + // (any operation before the clear is cascaded by it anyway) + int index = listChanges.size() - 1; + while (index > 0) + { + CDOFeatureDelta listDelta = listChanges.get(index); + if (listDelta instanceof CDOClearFeatureDelta || listDelta instanceof CDOUnsetFeatureDelta) + { + break; + } + index--; + } + while (index < listChanges.size()) + { + listChanges.get(index++).accept(visitor); + } + } + + private class ListDeltaVisitor implements CDOFeatureDeltaVisitor + { + private IDBStoreAccessor accessor; + + private InternalCDORevision originalRevision; + + private CDOID id; + + private int branchID; + + private int oldVersion; + + private int newVersion; + + private int lastIndex; + + private long timestamp; + + public ListDeltaVisitor(IDBStoreAccessor accessor, InternalCDORevision originalRevision, int targetBranchID, + int oldVersion, int newVersion, long timestamp) + { + this.accessor = accessor; + this.originalRevision = originalRevision; + id = this.originalRevision.getID(); + branchID = targetBranchID; + 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, branchID, fromIdx, true); + + // remove the item + removeEntry(accessor, id, branchID, oldVersion, newVersion, fromIdx, timestamp); + + // adjust indexes and shift either up or down + if (fromIdx < toIdx) + { + moveOneUp(accessor, id, branchID, oldVersion, newVersion, fromIdx + 1, toIdx); + } + else + { // fromIdx > toIdx here + moveOneDown(accessor, id, branchID, oldVersion, newVersion, toIdx, fromIdx - 1); + } + + // create the item + addEntry(accessor, id, branchID, 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, branchID, oldVersion, newVersion, startIndex, endIndex); + } + + // create the item + addEntry(accessor, id, branchID, 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, branchID, oldVersion, newVersion, startIndex, timestamp); + + // make room for the new item + moveOneUp(accessor, id, branchID, 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, branchID, oldVersion, newVersion, index, timestamp); + + // create the item + addEntry(accessor, id, branchID, 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, branchID, oldVersion, newVersion, lastIndex, timestamp); + 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, branchID, oldVersion, newVersion, lastIndex, timestamp); + 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 branchId, 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, startIndex++, id); + stmt.setInt(column++, branchId); + stmt.setInt(column++, newVersion); + stmt.setInt(column++, index); + + int result = DBUtil.update(stmt, false); + switch (result) + { + case 1: + // entry for current revision was already present. + // index update succeeded. + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp updated: {0} -> {1}", index, index - 1); //$NON-NLS-1$ + } + + break; + case 0: + Object value = getValue(accessor, id, branchId, index, false); + + if (value != null) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp remove: {0}", index); //$NON-NLS-1$ + } + + removeEntry(accessor, id, branchId, oldVersion, newVersion, index, timestamp); + } + else + { + value = getValueFromBase(accessor, id, branchId, index); + { + TRACER.format("moveOneUp add historic entry at: {0}", index); //$NON-NLS-1$ + } + + addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value, timestamp); + } + + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp add: {0}", index - 1); //$NON-NLS-1$ + } + + addEntry(accessor, id, branchId, newVersion, index - 1, value, timestamp); + 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 branchId, 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++, branchId); + stmt.setInt(column++, newVersion); + stmt.setInt(column++, index); + + int result = DBUtil.update(stmt, false); + switch (result) + { + case 1: + // entry for current revision was already present. + // index update succeeded. + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown updated: {0} -> {1}", index, index + 1); //$NON-NLS-1$ + } + + break; + case 0: + Object value = getValue(accessor, id, branchId, index, false); + if (value != null) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown remove: {0}", index); //$NON-NLS-1$ + } + + removeEntry(accessor, id, branchId, oldVersion, newVersion, index, timestamp); + } + else + { + value = getValueFromBase(accessor, id, branchId, index); + { + TRACER.format("moveOneDown add historic entry at: {0}", index); //$NON-NLS-1$ + } + + addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value, timestamp); + } + + addEntry(accessor, id, branchId, newVersion, index + 1, value, timestamp); + 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 branchId, 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++, branchId); + 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 addHistoricEntry(IDBStoreAccessor accessor, CDOID id, int branchId, int versionAdded, + int versionRemoved, int index, Object value, long timestamp) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + if (TRACER.isEnabled()) + { + TRACER.format( + "Adding historic value for feature {0}.{1} index {2} of {3}:{4}v{5}-v{6} : {7}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, branchId, versionAdded, versionRemoved, + 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++, branchId); + stmt.setInt(column++, versionAdded); + stmt.setNull(column++, 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 branchId, int oldVersion, int newVersion, + int index, long timestamp) + { + 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++, branchId); + 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++, branchId); + stmt.setInt(column++, index); + result = DBUtil.update(stmt, false); + + if (result == 0) + { + // no entry removed -> this means that we are in a branch and + // the entry has not been modified since the branch fork. + // therefore, we have to copy the base value and mark it as removed + Object value = getValueFromBase(accessor, id, branchId, index); + addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value, timestamp); + } + } + } + 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 branchId, int index, boolean getFromBase) + { + 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++, branchId); + stmt.setInt(column++, index); + + ResultSet resultSet = stmt.executeQuery(); + if (resultSet.next()) + { + CDOID tag = idHandler.getCDOID(resultSet, 1); + Object value = getTypeMapping(tag).readValue(resultSet); + result = CDORevisionUtil.createFeatureMapEntry(getFeatureByTag(tag), value); + } + else + { + // value is not in this branch. + // -> read from base revision + if (getFromBase) + { + result = getValueFromBase(accessor, id, branchId, index); + } // else: result remains null + } + 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; + } + + /** + * Read a single value (at a given index) from the base revision + * + * @param accessor + * the DBStoreAccessor + * @param id + * the ID of the revision + * @param branchID + * the ID of the current (child) branch + * @param index + * the index to read the value from + * @return the value which is at index <code>index</code> in revision with ID <code>id</code> in the parent branch at + * the base of this branch (indicated by <code>branchID</code>). + */ + private FeatureMap.Entry getValueFromBase(IDBStoreAccessor accessor, CDOID id, int branchID, int index) + { + IStoreChunkReader chunkReader = createBaseChunkReader(accessor, id, branchID); + chunkReader.addSimpleChunk(index); + List<Chunk> chunks = chunkReader.executeRead(); + return (FeatureMap.Entry)chunks.get(0).get(0); + } + + private IStoreChunkReader createBaseChunkReader(IDBStoreAccessor accessor, CDOID id, int branchID) + { + InternalRepository repository = (InternalRepository)accessor.getStore().getRepository(); + + CDOBranchManager branchManager = repository.getBranchManager(); + CDOBranch branch = branchManager.getBranch(branchID); + CDOBranchPoint base = branch.getBase(); + if (base.getBranch() == null) + { + // Branch is main branch! + throw new IllegalArgumentException("Base of main branch is null"); + } + + InternalCDORevisionManager revisionManager = repository.getRevisionManager(); + InternalCDORevision baseRevision = revisionManager.getRevision(id, base, 0, CDORevision.DEPTH_NONE, true); + + return accessor.createChunkReader(baseRevision, getFeature()); + } + + 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/BranchingListTableMappingWithRanges.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/BranchingListTableMappingWithRanges.java index 96079b3298..f9a584affd 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 @@ -1,1447 +1,1458 @@ -/*
- * 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:
- * Stefan Winkler - initial API and implementation taken from AuditListTableMappingWithRanges
- * 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.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;
-import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk;
-import org.eclipse.emf.cdo.server.ITransaction;
-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.Iterator;
-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 BranchingListTableMappingWithRanges extends BasicAbstractListTableMapping implements
- IListMappingDeltaSupport
-{
- private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, BranchingListTableMappingWithRanges.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;
-
- public BranchingListTableMappingWithRanges(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[5];
-
- dbFields[0] = table.addField(CDODBSchema.LIST_REVISION_ID, store.getIDHandler().getDBType());
- dbFields[1] = table.addField(CDODBSchema.LIST_REVISION_BRANCH, DBType.INTEGER);
- dbFields[2] = table.addField(CDODBSchema.LIST_REVISION_VERSION_ADDED, DBType.INTEGER);
- dbFields[3] = table.addField(CDODBSchema.LIST_REVISION_VERSION_REMOVED, DBType.INTEGER);
- dbFields[4] = table.addField(CDODBSchema.LIST_IDX, DBType.INTEGER);
-
- // add field for value
- typeMapping = getMappingStrategy().createValueMapping(getFeature());
- typeMapping.createDBField(table, CDODBSchema.LIST_VALUE);
-
- // add table indexes
- for (IDBField dbField : dbFields)
- {
- table.addIndex(Type.NON_UNIQUE, dbField);
- }
- }
-
- 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_IDX);
- builder.append(", "); //$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_BRANCH);
- 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_BRANCH);
- 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 (?, ?, ?, ?, ?, ?)"); //$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_REVISION_BRANCH);
- 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_REVISION_BRANCH);
- 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_BRANCH);
- 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_REVISION_BRANCH);
- 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_BRANCH);
- builder.append("=? AND "); //$NON-NLS-1$
- builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED);
- builder.append(" IS NULL"); //$NON-NLS-1$
- sqlClearList = builder.toString();
- }
-
- protected final IDBTable getTable()
- {
- return table;
- }
-
- protected final ITypeMapping getTypeMapping()
- {
- return typeMapping;
- }
-
- public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, final int listChunk)
- {
- MoveableList<Object> list = revision.getList(getFeature());
- int valuesToRead = list.size();
- if (listChunk != CDORevision.UNCHUNKED && listChunk < valuesToRead)
- {
- valuesToRead = listChunk;
- }
-
- if (valuesToRead == 0)
- {
- // nothing to read take shortcut
- return;
- }
-
- CDOID id = revision.getID();
- int branchID = revision.getBranch().getID();
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading list values for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$
- getFeature().getName(), revision);
- }
-
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
- ResultSet resultSet = null;
-
- IStoreChunkReader baseReader = null;
- try
- {
- String sql = sqlSelectChunksPrefix + sqlOrderByIndex;
- stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH);
- idHandler.setCDOID(stmt, 1, id);
- stmt.setInt(2, branchID);
- stmt.setInt(3, revision.getVersion());
- stmt.setInt(4, revision.getVersion());
- stmt.setMaxRows(valuesToRead); // optimization - don't read unneeded rows.
-
- resultSet = stmt.executeQuery();
-
- int currentIndex = 0;
-
- while (valuesToRead > 0 && resultSet.next())
- {
- int index = resultSet.getInt(1);
- if (index > currentIndex)
- {
- if (baseReader == null)
- {
- baseReader = createBaseChunkReader(accessor, id, branchID);
- }
-
- baseReader.addRangedChunk(currentIndex, index);
- if (TRACER.isEnabled())
- {
- TRACER.format("Scheduling range {0}-{1} to be read from base revision", currentIndex, index); //$NON-NLS-1$
- }
-
- valuesToRead -= index - currentIndex;
- currentIndex = index;
- }
-
- Object value = typeMapping.readValue(resultSet);
- if (TRACER.isEnabled())
- {
- TRACER.format("Read value for index {0} from result set: {1}", currentIndex, value); //$NON-NLS-1$
- }
-
- list.set(currentIndex++, value);
- valuesToRead--;
- }
-
- if (valuesToRead > 0)
- {
- if (baseReader == null)
- {
- baseReader = createBaseChunkReader(accessor, id, branchID);
- }
-
- baseReader.addRangedChunk(currentIndex, currentIndex + valuesToRead);
- if (TRACER.isEnabled())
- {
- TRACER.format(
- "Scheduling range {0}-{1} to be read from base revision", currentIndex, currentIndex + valuesToRead); //$NON-NLS-1$
- }
- }
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- DBUtil.close(resultSet);
- statementCache.releasePreparedStatement(stmt);
- }
-
- if (baseReader != null)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading base revision chunks for feature {0}.{1} of {2} from base revision {3}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), revision, baseReader.getRevision());
- }
-
- List<Chunk> baseChunks = baseReader.executeRead();
- for (Chunk chunk : baseChunks)
- {
- int startIndex = chunk.getStartIndex();
- for (int i = 0; i < chunk.size(); i++)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Copying value {0} at chunk index {1}+{2} to index {3}", //$NON-NLS-1$
- chunk.get(i), startIndex, i, startIndex + i);
- }
-
- list.set(startIndex + i, chunk.get(i));
- }
- }
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading {3} list values done for feature {0}.{1} of {2}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), revision, 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}", getContainingClass().getName(), //$NON-NLS-1$
- getFeature().getName(), chunkReader.getRevision());
- }
-
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache();
- PreparedStatement stmt = null;
- ResultSet resultSet = null;
-
- IStoreChunkReader baseReader = 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().getBranch().getID());
- stmt.setInt(3, chunkReader.getRevision().getVersion());
- stmt.setInt(4, chunkReader.getRevision().getVersion());
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Readung Chunks: {0}", stmt); //$NON-NLS-1$
- }
-
- resultSet = stmt.executeQuery();
-
- int nextDBIndex = Integer.MAX_VALUE; // next available DB index
- if (resultSet.next())
- {
- nextDBIndex = resultSet.getInt(1);
- }
-
- for (Chunk chunk : chunks)
- {
- int startIndex = chunk.getStartIndex();
- int missingValueStartIndex = -1;
-
- for (int i = 0; i < chunk.size(); i++)
- {
- int nextListIndex = startIndex + i; // next expected list index
-
- if (nextDBIndex == nextListIndex)
- {
- // DB value is available. check first if missing indexes were present before.
- if (missingValueStartIndex != -1)
- {
- // read missing indexes from missingValueStartIndex to currentIndex
- if (baseReader == null)
- {
- baseReader = createBaseChunkReader(chunkReader.getAccessor(), chunkReader.getRevision().getID(),
- chunkReader.getRevision().getBranch().getID());
- }
- if (TRACER.isEnabled())
- {
- TRACER.format(
- "Scheduling range {0}-{1} to be read from base revision", missingValueStartIndex, nextListIndex); //$NON-NLS-1$
- }
-
- baseReader.addRangedChunk(missingValueStartIndex, nextListIndex);
-
- // reset missingValueStartIndex
- missingValueStartIndex = -1;
- }
-
- // now read value and set to chunk
- Object value = typeMapping.readValue(resultSet);
- if (TRACER.isEnabled())
- {
- TRACER.format("ChunkReader read value for index {0} from result set: {1}", nextDBIndex, value); //$NON-NLS-1$
- }
- chunk.add(i, value);
-
- // advance DB cursor and read next available index
- if (resultSet.next())
- {
- nextDBIndex = resultSet.getInt(1);
- }
- else
- {
- // no more DB indexes available, but we have to continue checking for gaps, therefore set to MAX_VALUE
- nextDBIndex = Integer.MAX_VALUE;
- }
- }
- else
- {
- // gap between next DB index and next list index detected.
- // skip until end of chunk or until DB value becomes available
- if (missingValueStartIndex == -1)
- {
- missingValueStartIndex = nextListIndex;
- }
- }
- }
-
- // chunk complete. check for missing values at the end of the chunk.
- if (missingValueStartIndex != -1)
- {
- // read missing indexes from missingValueStartIndex to last chunk index
- if (baseReader == null)
- {
- baseReader = createBaseChunkReader(chunkReader.getAccessor(), chunkReader.getRevision().getID(),
- chunkReader.getRevision().getBranch().getID());
- }
-
- if (TRACER.isEnabled())
- {
- TRACER
- .format(
- "Scheduling range {0}-{1} to be read from base revision", missingValueStartIndex, chunk.getStartIndex() + chunk.size()); //$NON-NLS-1$
- }
- baseReader.addRangedChunk(missingValueStartIndex, chunk.getStartIndex() + chunk.size());
- }
- }
- }
- catch (SQLException ex)
- {
- throw new DBException(ex);
- }
- finally
- {
- DBUtil.close(resultSet);
- statementCache.releasePreparedStatement(stmt);
- }
-
- // now read missing values from base revision.
- if (baseReader != null)
- {
- List<Chunk> baseChunks = baseReader.executeRead();
-
- Iterator<Chunk> thisIterator = chunks.iterator();
- Chunk thisChunk = thisIterator.next();
-
- for (Chunk baseChunk : baseChunks)
- {
- int baseStartIndex = baseChunk.getStartIndex();
-
- while (baseStartIndex > thisChunk.getStartIndex() + thisChunk.size())
- {
- // advance thisChunk, because it does not match baseChunk
- thisChunk = thisIterator.next();
- }
-
- // baseChunk now corresponds to thisChunk, but startIndex of baseChunk may be higher.
- // therefore calculate offset
- int offset = baseStartIndex - thisChunk.getStartIndex();
-
- // and copy values.
- for (int i = 0; i < baseChunk.size(); i++)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Copying base chunk reader value {0} at index {1} to current chunk reader at index {2}.",
- baseChunk.get(i), baseChunk.getStartIndex() + i, thisChunk.getStartIndex() + i + offset);
- }
-
- thisChunk.add(i + offset, baseChunk.get(i));
- } // finally, continue with the next baseChunk
- }
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Reading list chunk values done for feature {0}.{1} of {2}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), chunkReader.getRevision());
- }
- }
-
- 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} : {4}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), index, revision, value);
- }
-
- addEntry(accessor, revision.getID(), revision.getBranch().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
- * @param lastIndex
- */
- public void clearList(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion, int lastIndex)
- {
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- try
- {
- // check for each index if the value exists in the current branch
- for (int i = 0; i <= lastIndex; i++)
- {
- if (getValue(accessor, id, branchId, i, false) == null)
- {
- // if not, add a historic entry for missing ones.
- addHistoricEntry(accessor, id, branchId, 0, newVersion, i, getValueFromBase(accessor, id, branchId, i));
- }
- }
-
- // clear rest of the list
- stmt = statementCache.getPreparedStatement(sqlClearList, ReuseProbability.HIGH);
- stmt.setInt(1, newVersion);
- getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 2, id);
- stmt.setInt(3, branchId);
-
- int result = DBUtil.update(stmt, false);
- if (TRACER.isEnabled())
- {
- TRACER.format("ClearList result: {0}", result); //$NON-NLS-1$
- }
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised)
- {
- ITransaction transaction = accessor.getTransaction();
- InternalCDORevision revision = (InternalCDORevision)transaction.getRevision(id);
- int branchID = transaction.getBranch().getID();
-
- if (TRACER.isEnabled())
- {
- TRACER.format("objectDetached {1}", revision); //$NON-NLS-1$
- }
-
- clearList(accessor, id, branchID, revision.getVersion(), FINAL_VERSION, revision.getList(getFeature()).size() - 1);
- }
-
- public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, final int oldVersion,
- final int newVersion, long created, CDOListFeatureDelta delta)
- {
- List<CDOFeatureDelta> listChanges = delta.getListChanges();
- if (listChanges.size() == 0)
- {
- // nothing to do.
- return;
- }
-
- InternalCDORevision originalRevision = (InternalCDORevision)accessor.getTransaction().getRevision(id);
- 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, branchId, oldVersion, newVersion);
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Processing deltas..."); //$NON-NLS-1$
- }
-
- // optimization: it's only necessary to process deltas
- // starting with the last feature delta which clears the list
- // (any operation before the clear is cascaded by it anyway)
- int index = listChanges.size() - 1;
- while (index > 0)
- {
- CDOFeatureDelta listDelta = listChanges.get(index);
- if (listDelta instanceof CDOClearFeatureDelta || listDelta instanceof CDOUnsetFeatureDelta)
- {
- break;
- }
- index--;
- }
- while (index < listChanges.size())
- {
- listChanges.get(index++).accept(visitor);
- }
-
- visitor.finishPendingRemove();
- }
-
- /**
- * @author Stefan Winkler
- * @author Andras Peteri
- */
- private class ListDeltaVisitor implements CDOFeatureDeltaVisitor
- {
- private IDBStoreAccessor accessor;
-
- private CDOID id;
-
- private int branchID;
-
- private int oldVersion;
-
- private int newVersion;
-
- private int lastIndex;
-
- private int lastRemovedIndex;
-
- public ListDeltaVisitor(IDBStoreAccessor accessor, InternalCDORevision originalRevision, int targetBranchID,
- int oldVersion, int newVersion)
- {
- this.accessor = accessor;
- id = originalRevision.getID();
- branchID = targetBranchID;
- this.oldVersion = oldVersion;
- this.newVersion = newVersion;
- lastIndex = originalRevision.getList(getFeature()).size() - 1;
- lastRemovedIndex = -1;
- }
-
- 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, branchID, oldVersion, newVersion, startIndex, endIndex);
- }
-
- // create the item
- addEntry(accessor, id, branchID, 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, branchID, 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, branchID, oldVersion, newVersion, index);
-
- // create the item
- addEntry(accessor, id, branchID, 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, branchID, oldVersion, newVersion, lastIndex);
- lastIndex = -1;
- lastRemovedIndex = -1;
- }
-
- public void visit(CDOClearFeatureDelta delta)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Delta Clearing"); //$NON-NLS-1$
- }
-
- clearList(accessor, id, branchID, oldVersion, newVersion, lastIndex);
- lastIndex = -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, branchID, fromIdx, true);
-
- // remove the item
- removeEntry(accessor, id, branchID, oldVersion, newVersion, fromIdx);
-
- // adjust indexes and shift either up or down for regular moves
- if (!optimizeMove)
- {
- if (fromIdx < toIdx)
- {
- moveOneUp(accessor, id, branchID, oldVersion, newVersion, fromIdx + 1, toIdx);
- }
- else
- { // fromIdx > toIdx here
- moveOneDown(accessor, id, branchID, oldVersion, newVersion, toIdx, fromIdx - 1);
- }
- }
-
- // create the item
- addEntry(accessor, id, branchID, newVersion, toIdx, value);
- }
-
- public void visit(CDOListFeatureDelta delta)
- {
- throw new ImplementationError("Should not be called"); //$NON-NLS-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, branchID, oldVersion, newVersion, startIndex + 1, endIndex);
-
- --lastIndex;
- lastRemovedIndex = -1;
- }
- }
-
- private void moveOneUp(IDBStoreAccessor accessor, CDOID id, int branchId, 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++, branchId);
- stmt.setInt(column++, newVersion);
- stmt.setInt(column++, index);
-
- int result = DBUtil.update(stmt, false);
- switch (result)
- {
- case 1:
- // entry for current revision was already present.
- // index update succeeded.
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneUp updated: {0} -> {1}", index, index - 1); //$NON-NLS-1$
- }
-
- break;
- // no entry for current revision there.
- case 0:
- Object value = getValue(accessor, id, branchId, index, false);
-
- if (value != null)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneUp remove: {0}", index); //$NON-NLS-1$
- }
-
- removeEntry(accessor, id, branchId, oldVersion, newVersion, index);
- }
- else
- {
- value = getValueFromBase(accessor, id, branchId, index);
- {
- TRACER.format("moveOneUp add historic entry at: {0}", index); //$NON-NLS-1$
- }
-
- addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value);
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneUp add: {0}", index - 1); //$NON-NLS-1$
- }
-
- addEntry(accessor, id, branchId, newVersion, index - 1, value);
- 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 branchId, 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++, branchId);
- stmt.setInt(column++, newVersion);
- stmt.setInt(column++, index);
-
- int result = DBUtil.update(stmt, false);
- switch (result)
- {
- case 1:
- // entry for current revision was already present.
- // index update succeeded.
-
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneDown updated: {0} -> {1}", index, index + 1); //$NON-NLS-1$
- }
-
- break;
- case 0:
- Object value = getValue(accessor, id, branchId, index, false);
-
- if (value != null)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneDown remove: {0}", index); //$NON-NLS-1$
- }
-
- removeEntry(accessor, id, branchId, oldVersion, newVersion, index);
- }
- else
- {
- value = getValueFromBase(accessor, id, branchId, index);
- {
- TRACER.format("moveOneDown add historic entry at: {0}", index); //$NON-NLS-1$
- }
-
- addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value);
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("moveOneDown add: {0}", index + 1); //$NON-NLS-1$
- }
-
- addEntry(accessor, id, branchId, newVersion, index + 1, value);
- 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 branchId, 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}:{4}v{5} : {6}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), index, id, branchId, version, value);
- }
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlInsertEntry, ReuseProbability.HIGH);
-
- int column = 1;
- idHandler.setCDOID(stmt, column++, id);
- stmt.setInt(column++, branchId);
- stmt.setInt(column++, version); // versionAdded
- stmt.setNull(column++, DBType.INTEGER.getCode()); // versionRemoved
- 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 addHistoricEntry(IDBStoreAccessor accessor, CDOID id, int branchId, int versionAdded,
- int versionRemoved, int index, Object value)
- {
- IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
- IPreparedStatementCache statementCache = accessor.getStatementCache();
- PreparedStatement stmt = null;
-
- if (TRACER.isEnabled())
- {
- TRACER.format(
- "Adding historic value for feature {0}.{1} index {2} of {3}:{4}v{5}-v{6} : {7}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), index, id, branchId, versionAdded, versionRemoved,
- value);
- }
-
- try
- {
- stmt = statementCache.getPreparedStatement(sqlInsertEntry, ReuseProbability.HIGH);
-
- int column = 1;
- idHandler.setCDOID(stmt, column++, id);
- stmt.setInt(column++, branchId);
- stmt.setInt(column++, versionAdded); // versionAdded
- stmt.setInt(column++, versionRemoved); // versionRemoved
- 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 branchId, 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}:{4}v{5}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), index, id, branchId, 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++, branchId);
- 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++, branchId);
- stmt.setInt(column++, index);
-
- result = DBUtil.update(stmt, false);
-
- if (result == 0)
- {
- // no entry removed -> this means that we are in a branch and
- // the entry has not been modified since the branch fork.
- // therefore, we have to copy the base value and mark it as removed
- Object value = getValueFromBase(accessor, id, branchId, index);
- addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value);
- }
- }
- }
- catch (SQLException e)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Removing value for feature {0}.{1} index {2} of {3}:{4}v{5} FAILED {6}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), index, id, branchId, 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}:{4}v{5} FAILED {6}", //$NON-NLS-1$
- getContainingClass().getName(), getFeature().getName(), index, id, branchId, newVersion, e.getMessage());
- }
-
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
- }
-
- /**
- * Read a single value from the current revision's list.
- *
- * @param accessor
- * the store accessor
- * @param id
- * the revision's ID
- * @param branchId
- * the revision's branch ID
- * @param index
- * the index from which to get the value
- * @param getFromBase
- * if <code>true</code>, the value is recursively loaded from the base revision of a branch, if it is not
- * present in the current branch (because it has not been changed since the branch fork). If
- * <code>false</code>, <code>null</code> is returned in the former case.
- */
- private Object getValue(IDBStoreAccessor accessor, CDOID id, int branchId, int index, boolean getFromBase)
- {
- 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++, branchId);
- stmt.setInt(column++, index);
-
- ResultSet resultSet = stmt.executeQuery();
- if (resultSet.next())
- {
- result = typeMapping.readValue(resultSet);
- if (TRACER.isEnabled())
- {
- TRACER.format("Read value (index {0}) from result set: {1}", index, result); //$NON-NLS-1$
- }
- }
- else
- {
- // value is not in this branch.
- // -> read from base revision
- if (getFromBase)
- {
- result = getValueFromBase(accessor, id, branchId, index);
- } // else: result remains null
- }
- }
- catch (SQLException e)
- {
- throw new DBException(e);
- }
- finally
- {
- statementCache.releasePreparedStatement(stmt);
- }
-
- return result;
- }
-
- /**
- * Read a single value (at a given index) from the base revision
- *
- * @param accessor
- * the DBStoreAccessor
- * @param id
- * the ID of the revision
- * @param branchID
- * the ID of the current (child) branch
- * @param index
- * the index to read the value from
- * @return the value which is at index <code>index</code> in revision with ID <code>id</code> in the parent branch at
- * the base of this branch (indicated by <code>branchID</code>).
- */
- private Object getValueFromBase(IDBStoreAccessor accessor, CDOID id, int branchID, int index)
- {
- IStoreChunkReader chunkReader = createBaseChunkReader(accessor, id, branchID);
- chunkReader.addSimpleChunk(index);
- List<Chunk> chunks = chunkReader.executeRead();
- return chunks.get(0).get(0);
- }
-
- private IStoreChunkReader createBaseChunkReader(IDBStoreAccessor accessor, CDOID id, int branchID)
- {
- IRepository repository = accessor.getStore().getRepository();
- CDOBranchPoint base = repository.getBranchManager().getBranch(branchID).getBase();
- InternalCDORevision baseRevision = (InternalCDORevision)repository.getRevisionManager().getRevision(id, base, /*
- * referenceChunk
- * =
- */0, /*
- * prefetchDepth
- * =
- */
- CDORevision.DEPTH_NONE, true);
- IStoreChunkReader chunkReader = accessor.createChunkReader(baseRevision, getFeature());
- return chunkReader;
- }
-
- 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: + * Stefan Winkler - initial API and implementation taken from AuditListTableMappingWithRanges + * 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.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.IStoreAccessor.QueryXRefsContext; +import org.eclipse.emf.cdo.server.IStoreChunkReader; +import org.eclipse.emf.cdo.server.IStoreChunkReader.Chunk; +import org.eclipse.emf.cdo.server.ITransaction; +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.emf.cdo.spi.common.revision.InternalCDORevisionManager; +import org.eclipse.emf.cdo.spi.server.InternalRepository; + +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.Iterator; +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 BranchingListTableMappingWithRanges extends BasicAbstractListTableMapping implements + IListMappingDeltaSupport +{ + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, BranchingListTableMappingWithRanges.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; + + public BranchingListTableMappingWithRanges(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[5]; + + dbFields[0] = table.addField(CDODBSchema.LIST_REVISION_ID, store.getIDHandler().getDBType()); + dbFields[1] = table.addField(CDODBSchema.LIST_REVISION_BRANCH, DBType.INTEGER); + dbFields[2] = table.addField(CDODBSchema.LIST_REVISION_VERSION_ADDED, DBType.INTEGER); + dbFields[3] = table.addField(CDODBSchema.LIST_REVISION_VERSION_REMOVED, DBType.INTEGER); + dbFields[4] = table.addField(CDODBSchema.LIST_IDX, DBType.INTEGER); + + // add field for value + typeMapping = getMappingStrategy().createValueMapping(getFeature()); + typeMapping.createDBField(table, CDODBSchema.LIST_VALUE); + + // add table indexes + for (IDBField dbField : dbFields) + { + table.addIndex(Type.NON_UNIQUE, dbField); + } + } + + 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_IDX); + builder.append(", "); //$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_BRANCH); + 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_BRANCH); + 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 (?, ?, ?, ?, ?, ?)"); //$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_REVISION_BRANCH); + 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_REVISION_BRANCH); + 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_BRANCH); + 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_REVISION_BRANCH); + 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_BRANCH); + builder.append("=? AND "); //$NON-NLS-1$ + builder.append(CDODBSchema.LIST_REVISION_VERSION_REMOVED); + builder.append(" IS NULL"); //$NON-NLS-1$ + sqlClearList = builder.toString(); + } + + protected final IDBTable getTable() + { + return table; + } + + protected final ITypeMapping getTypeMapping() + { + return typeMapping; + } + + public void readValues(IDBStoreAccessor accessor, InternalCDORevision revision, final int listChunk) + { + MoveableList<Object> list = revision.getList(getFeature()); + int valuesToRead = list.size(); + if (listChunk != CDORevision.UNCHUNKED && listChunk < valuesToRead) + { + valuesToRead = listChunk; + } + + if (valuesToRead == 0) + { + // nothing to read take shortcut + return; + } + + CDOID id = revision.getID(); + int branchID = revision.getBranch().getID(); + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list values for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), revision); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + IStoreChunkReader baseReader = null; + try + { + String sql = sqlSelectChunksPrefix + sqlOrderByIndex; + stmt = statementCache.getPreparedStatement(sql, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, id); + stmt.setInt(2, branchID); + stmt.setInt(3, revision.getVersion()); + stmt.setInt(4, revision.getVersion()); + stmt.setMaxRows(valuesToRead); // optimization - don't read unneeded rows. + + resultSet = stmt.executeQuery(); + + int currentIndex = 0; + + while (valuesToRead > 0 && resultSet.next()) + { + int index = resultSet.getInt(1); + if (index > currentIndex) + { + if (baseReader == null) + { + baseReader = createBaseChunkReader(accessor, id, branchID); + } + + baseReader.addRangedChunk(currentIndex, index); + if (TRACER.isEnabled()) + { + TRACER.format("Scheduling range {0}-{1} to be read from base revision", currentIndex, index); //$NON-NLS-1$ + } + + valuesToRead -= index - currentIndex; + currentIndex = index; + } + + Object value = typeMapping.readValue(resultSet); + if (TRACER.isEnabled()) + { + TRACER.format("Read value for index {0} from result set: {1}", currentIndex, value); //$NON-NLS-1$ + } + + list.set(currentIndex++, value); + valuesToRead--; + } + + if (valuesToRead > 0) + { + if (baseReader == null) + { + baseReader = createBaseChunkReader(accessor, id, branchID); + } + + baseReader.addRangedChunk(currentIndex, currentIndex + valuesToRead); + if (TRACER.isEnabled()) + { + TRACER.format( + "Scheduling range {0}-{1} to be read from base revision", currentIndex, currentIndex + valuesToRead); //$NON-NLS-1$ + } + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + + if (baseReader != null) + { + if (TRACER.isEnabled()) + { + TRACER.format("Reading base revision chunks for feature {0}.{1} of {2} from base revision {3}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), revision, baseReader.getRevision()); + } + + List<Chunk> baseChunks = baseReader.executeRead(); + for (Chunk chunk : baseChunks) + { + int startIndex = chunk.getStartIndex(); + for (int i = 0; i < chunk.size(); i++) + { + if (TRACER.isEnabled()) + { + TRACER.format("Copying value {0} at chunk index {1}+{2} to index {3}", //$NON-NLS-1$ + chunk.get(i), startIndex, i, startIndex + i); + } + + list.set(startIndex + i, chunk.get(i)); + } + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading {3} list values done for feature {0}.{1} of {2}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), revision, list.size()); + } + } + + public final void readChunks(IDBStoreChunkReader chunkReader, List<Chunk> chunks, String where) + { + CDORevision revision = chunkReader.getRevision(); + CDOID id = revision.getID(); + int branchID = revision.getBranch().getID(); + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values for feature {0}.{1} of {2}", getContainingClass().getName(), //$NON-NLS-1$ + getFeature().getName(), revision); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = chunkReader.getAccessor().getStatementCache(); + PreparedStatement stmt = null; + ResultSet resultSet = null; + + IStoreChunkReader baseReader = 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, id); + stmt.setInt(2, branchID); + stmt.setInt(3, revision.getVersion()); + stmt.setInt(4, revision.getVersion()); + + if (TRACER.isEnabled()) + { + TRACER.format("Readung Chunks: {0}", stmt); //$NON-NLS-1$ + } + + resultSet = stmt.executeQuery(); + + int nextDBIndex = Integer.MAX_VALUE; // next available DB index + if (resultSet.next()) + { + nextDBIndex = resultSet.getInt(1); + } + + for (Chunk chunk : chunks) + { + int startIndex = chunk.getStartIndex(); + int missingValueStartIndex = -1; + + for (int i = 0; i < chunk.size(); i++) + { + int nextListIndex = startIndex + i; // next expected list index + + if (nextDBIndex == nextListIndex) + { + // DB value is available. check first if missing indexes were present before. + if (missingValueStartIndex != -1) + { + // read missing indexes from missingValueStartIndex to currentIndex + if (baseReader == null) + { + baseReader = createBaseChunkReader(chunkReader.getAccessor(), id, branchID); + } + + if (TRACER.isEnabled()) + { + TRACER.format( + "Scheduling range {0}-{1} to be read from base revision", missingValueStartIndex, nextListIndex); //$NON-NLS-1$ + } + + baseReader.addRangedChunk(missingValueStartIndex, nextListIndex); + + // reset missingValueStartIndex + missingValueStartIndex = -1; + } + + // now read value and set to chunk + Object value = typeMapping.readValue(resultSet); + if (TRACER.isEnabled()) + { + TRACER.format("ChunkReader read value for index {0} from result set: {1}", nextDBIndex, value); //$NON-NLS-1$ + } + + chunk.add(i, value); + + // advance DB cursor and read next available index + if (resultSet.next()) + { + nextDBIndex = resultSet.getInt(1); + } + else + { + // no more DB indexes available, but we have to continue checking for gaps, therefore set to MAX_VALUE + nextDBIndex = Integer.MAX_VALUE; + } + } + else + { + // gap between next DB index and next list index detected. + // skip until end of chunk or until DB value becomes available + if (missingValueStartIndex == -1) + { + missingValueStartIndex = nextListIndex; + } + } + } + + // chunk complete. check for missing values at the end of the chunk. + if (missingValueStartIndex != -1) + { + // read missing indexes from missingValueStartIndex to last chunk index + if (baseReader == null) + { + baseReader = createBaseChunkReader(chunkReader.getAccessor(), id, branchID); + } + + if (TRACER.isEnabled()) + { + TRACER + .format( + "Scheduling range {0}-{1} to be read from base revision", missingValueStartIndex, chunk.getStartIndex() + chunk.size()); //$NON-NLS-1$ + } + + baseReader.addRangedChunk(missingValueStartIndex, chunk.getStartIndex() + chunk.size()); + } + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + statementCache.releasePreparedStatement(stmt); + } + + // now read missing values from base revision. + if (baseReader != null) + { + List<Chunk> baseChunks = baseReader.executeRead(); + + Iterator<Chunk> thisIterator = chunks.iterator(); + Chunk thisChunk = thisIterator.next(); + + for (Chunk baseChunk : baseChunks) + { + int baseStartIndex = baseChunk.getStartIndex(); + + while (baseStartIndex > thisChunk.getStartIndex() + thisChunk.size()) + { + // advance thisChunk, because it does not match baseChunk + thisChunk = thisIterator.next(); + } + + // baseChunk now corresponds to thisChunk, but startIndex of baseChunk may be higher. + // therefore calculate offset + int offset = baseStartIndex - thisChunk.getStartIndex(); + + // and copy values. + for (int i = 0; i < baseChunk.size(); i++) + { + if (TRACER.isEnabled()) + { + TRACER.format("Copying base chunk reader value {0} at index {1} to current chunk reader at index {2}.", + baseChunk.get(i), baseChunk.getStartIndex() + i, thisChunk.getStartIndex() + i + offset); + } + + thisChunk.add(i + offset, baseChunk.get(i)); + } // finally, continue with the next baseChunk + } + } + + if (TRACER.isEnabled()) + { + TRACER.format("Reading list chunk values done for feature {0}.{1} of {2}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), revision); + } + } + + 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} : {4}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, revision, value); + } + + addEntry(accessor, revision.getID(), revision.getBranch().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 + * @param lastIndex + */ + public void clearList(IDBStoreAccessor accessor, CDOID id, int branchId, int oldVersion, int newVersion, int lastIndex) + { + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + try + { + // check for each index if the value exists in the current branch + for (int i = 0; i <= lastIndex; i++) + { + if (getValue(accessor, id, branchId, i, false) == null) + { + // if not, add a historic entry for missing ones. + addHistoricEntry(accessor, id, branchId, 0, newVersion, i, getValueFromBase(accessor, id, branchId, i)); + } + } + + // clear rest of the list + stmt = statementCache.getPreparedStatement(sqlClearList, ReuseProbability.HIGH); + stmt.setInt(1, newVersion); + getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 2, id); + stmt.setInt(3, branchId); + + int result = DBUtil.update(stmt, false); + if (TRACER.isEnabled()) + { + TRACER.format("ClearList result: {0}", result); //$NON-NLS-1$ + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised) + { + ITransaction transaction = accessor.getTransaction(); + InternalCDORevision revision = (InternalCDORevision)transaction.getRevision(id); + int branchID = transaction.getBranch().getID(); + + if (TRACER.isEnabled()) + { + TRACER.format("objectDetached {1}", revision); //$NON-NLS-1$ + } + + clearList(accessor, id, branchID, revision.getVersion(), FINAL_VERSION, revision.getList(getFeature()).size() - 1); + } + + public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, final int oldVersion, + final int newVersion, long created, CDOListFeatureDelta delta) + { + List<CDOFeatureDelta> listChanges = delta.getListChanges(); + if (listChanges.size() == 0) + { + // nothing to do. + return; + } + + InternalCDORevision originalRevision = (InternalCDORevision)accessor.getTransaction().getRevision(id); + 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, branchId, oldVersion, newVersion); + + if (TRACER.isEnabled()) + { + TRACER.format("Processing deltas..."); //$NON-NLS-1$ + } + + // optimization: it's only necessary to process deltas + // starting with the last feature delta which clears the list + // (any operation before the clear is cascaded by it anyway) + int index = listChanges.size() - 1; + while (index > 0) + { + CDOFeatureDelta listDelta = listChanges.get(index); + if (listDelta instanceof CDOClearFeatureDelta || listDelta instanceof CDOUnsetFeatureDelta) + { + break; + } + index--; + } + while (index < listChanges.size()) + { + listChanges.get(index++).accept(visitor); + } + + visitor.finishPendingRemove(); + } + + /** + * @author Stefan Winkler + * @author Andras Peteri + */ + private class ListDeltaVisitor implements CDOFeatureDeltaVisitor + { + private IDBStoreAccessor accessor; + + private CDOID id; + + private int branchID; + + private int oldVersion; + + private int newVersion; + + private int lastIndex; + + private int lastRemovedIndex; + + public ListDeltaVisitor(IDBStoreAccessor accessor, InternalCDORevision originalRevision, int targetBranchID, + int oldVersion, int newVersion) + { + this.accessor = accessor; + id = originalRevision.getID(); + branchID = targetBranchID; + this.oldVersion = oldVersion; + this.newVersion = newVersion; + lastIndex = originalRevision.getList(getFeature()).size() - 1; + lastRemovedIndex = -1; + } + + 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, branchID, oldVersion, newVersion, startIndex, endIndex); + } + + // create the item + addEntry(accessor, id, branchID, 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, branchID, 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, branchID, oldVersion, newVersion, index); + + // create the item + addEntry(accessor, id, branchID, 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, branchID, oldVersion, newVersion, lastIndex); + lastIndex = -1; + lastRemovedIndex = -1; + } + + public void visit(CDOClearFeatureDelta delta) + { + if (TRACER.isEnabled()) + { + TRACER.format("Delta Clearing"); //$NON-NLS-1$ + } + + clearList(accessor, id, branchID, oldVersion, newVersion, lastIndex); + lastIndex = -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, branchID, fromIdx, true); + + // remove the item + removeEntry(accessor, id, branchID, oldVersion, newVersion, fromIdx); + + // adjust indexes and shift either up or down for regular moves + if (!optimizeMove) + { + if (fromIdx < toIdx) + { + moveOneUp(accessor, id, branchID, oldVersion, newVersion, fromIdx + 1, toIdx); + } + else + { // fromIdx > toIdx here + moveOneDown(accessor, id, branchID, oldVersion, newVersion, toIdx, fromIdx - 1); + } + } + + // create the item + addEntry(accessor, id, branchID, newVersion, toIdx, value); + } + + public void visit(CDOListFeatureDelta delta) + { + throw new ImplementationError("Should not be called"); //$NON-NLS-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, branchID, oldVersion, newVersion, startIndex + 1, endIndex); + + --lastIndex; + lastRemovedIndex = -1; + } + } + + private void moveOneUp(IDBStoreAccessor accessor, CDOID id, int branchId, 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++, branchId); + stmt.setInt(column++, newVersion); + stmt.setInt(column++, index); + + int result = DBUtil.update(stmt, false); + switch (result) + { + case 1: + // entry for current revision was already present. + // index update succeeded. + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp updated: {0} -> {1}", index, index - 1); //$NON-NLS-1$ + } + + break; + // no entry for current revision there. + case 0: + Object value = getValue(accessor, id, branchId, index, false); + + if (value != null) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp remove: {0}", index); //$NON-NLS-1$ + } + + removeEntry(accessor, id, branchId, oldVersion, newVersion, index); + } + else + { + value = getValueFromBase(accessor, id, branchId, index); + { + TRACER.format("moveOneUp add historic entry at: {0}", index); //$NON-NLS-1$ + } + + addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value); + } + + if (TRACER.isEnabled()) + { + TRACER.format("moveOneUp add: {0}", index - 1); //$NON-NLS-1$ + } + + addEntry(accessor, id, branchId, newVersion, index - 1, value); + 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 branchId, 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++, branchId); + stmt.setInt(column++, newVersion); + stmt.setInt(column++, index); + + int result = DBUtil.update(stmt, false); + switch (result) + { + case 1: + // entry for current revision was already present. + // index update succeeded. + + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown updated: {0} -> {1}", index, index + 1); //$NON-NLS-1$ + } + + break; + case 0: + Object value = getValue(accessor, id, branchId, index, false); + + if (value != null) + { + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown remove: {0}", index); //$NON-NLS-1$ + } + + removeEntry(accessor, id, branchId, oldVersion, newVersion, index); + } + else + { + value = getValueFromBase(accessor, id, branchId, index); + { + TRACER.format("moveOneDown add historic entry at: {0}", index); //$NON-NLS-1$ + } + + addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value); + } + + if (TRACER.isEnabled()) + { + TRACER.format("moveOneDown add: {0}", index + 1); //$NON-NLS-1$ + } + + addEntry(accessor, id, branchId, newVersion, index + 1, value); + 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 branchId, 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}:{4}v{5} : {6}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, branchId, version, value); + } + + try + { + stmt = statementCache.getPreparedStatement(sqlInsertEntry, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, branchId); + stmt.setInt(column++, version); // versionAdded + stmt.setNull(column++, DBType.INTEGER.getCode()); // versionRemoved + 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 addHistoricEntry(IDBStoreAccessor accessor, CDOID id, int branchId, int versionAdded, + int versionRemoved, int index, Object value) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IPreparedStatementCache statementCache = accessor.getStatementCache(); + PreparedStatement stmt = null; + + if (TRACER.isEnabled()) + { + TRACER.format( + "Adding historic value for feature {0}.{1} index {2} of {3}:{4}v{5}-v{6} : {7}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, branchId, versionAdded, versionRemoved, + value); + } + + try + { + stmt = statementCache.getPreparedStatement(sqlInsertEntry, ReuseProbability.HIGH); + + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, branchId); + stmt.setInt(column++, versionAdded); // versionAdded + stmt.setInt(column++, versionRemoved); // versionRemoved + 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 branchId, 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}:{4}v{5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, branchId, 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++, branchId); + 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++, branchId); + stmt.setInt(column++, index); + + result = DBUtil.update(stmt, false); + + if (result == 0) + { + // no entry removed -> this means that we are in a branch and + // the entry has not been modified since the branch fork. + // therefore, we have to copy the base value and mark it as removed + Object value = getValueFromBase(accessor, id, branchId, index); + addHistoricEntry(accessor, id, branchId, 0, newVersion, index, value); + } + } + } + catch (SQLException e) + { + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature {0}.{1} index {2} of {3}:{4}v{5} FAILED {6}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, branchId, 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}:{4}v{5} FAILED {6}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, branchId, newVersion, e.getMessage()); + } + + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + } + + /** + * Read a single value from the current revision's list. + * + * @param accessor + * the store accessor + * @param id + * the revision's ID + * @param branchId + * the revision's branch ID + * @param index + * the index from which to get the value + * @param getFromBase + * if <code>true</code>, the value is recursively loaded from the base revision of a branch, if it is not + * present in the current branch (because it has not been changed since the branch fork). If + * <code>false</code>, <code>null</code> is returned in the former case. + */ + private Object getValue(IDBStoreAccessor accessor, CDOID id, int branchId, int index, boolean getFromBase) + { + 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++, branchId); + stmt.setInt(column++, index); + + ResultSet resultSet = stmt.executeQuery(); + if (resultSet.next()) + { + result = typeMapping.readValue(resultSet); + if (TRACER.isEnabled()) + { + TRACER.format("Read value (index {0}) from result set: {1}", index, result); //$NON-NLS-1$ + } + } + else + { + // value is not in this branch. + // -> read from base revision + if (getFromBase) + { + result = getValueFromBase(accessor, id, branchId, index); + } // else: result remains null + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + statementCache.releasePreparedStatement(stmt); + } + + return result; + } + + /** + * Read a single value (at a given index) from the base revision + * + * @param accessor + * the DBStoreAccessor + * @param id + * the ID of the revision + * @param branchID + * the ID of the current (child) branch + * @param index + * the index to read the value from + * @return the value which is at index <code>index</code> in revision with ID <code>id</code> in the parent branch at + * the base of this branch (indicated by <code>branchID</code>). + */ + private Object getValueFromBase(IDBStoreAccessor accessor, CDOID id, int branchID, int index) + { + IStoreChunkReader chunkReader = createBaseChunkReader(accessor, id, branchID); + chunkReader.addSimpleChunk(index); + List<Chunk> chunks = chunkReader.executeRead(); + return chunks.get(0).get(0); + } + + private IStoreChunkReader createBaseChunkReader(IDBStoreAccessor accessor, CDOID id, int branchID) + { + InternalRepository repository = (InternalRepository)accessor.getStore().getRepository(); + + CDOBranchManager branchManager = repository.getBranchManager(); + CDOBranch branch = branchManager.getBranch(branchID); + CDOBranchPoint base = branch.getBase(); + if (base.getBranch() == null) + { + // Branch is main branch! + throw new IllegalArgumentException("Base of main branch is null"); + } + + InternalCDORevisionManager revisionManager = repository.getRevisionManager(); + InternalCDORevision baseRevision = revisionManager.getRevision(id, base, 0, CDORevision.DEPTH_NONE, true); + + return accessor.createChunkReader(baseRevision, getFeature()); + } + + 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/src/org/eclipse/emf/cdo/internal/server/Repository.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java index 8347f99acc..aec0598956 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Repository.java @@ -368,10 +368,11 @@ public class Repository extends Container<Object> implements InternalRepository throw new IllegalStateException("Branching is not supported by " + this); } + long lastFinishedTimeStamp = timeStampAuthority.getLastFinishedTimeStamp(); long baseTimeStamp = branchInfo.getBaseTimeStamp(); - if (baseTimeStamp == CDOBranchPoint.UNSPECIFIED_DATE) + if (baseTimeStamp == CDOBranchPoint.UNSPECIFIED_DATE || baseTimeStamp > lastFinishedTimeStamp) { - baseTimeStamp = getTimeStamp(); + baseTimeStamp = lastFinishedTimeStamp; branchInfo = new BranchInfo(branchInfo.getName(), branchInfo.getBaseBranchID(), baseTimeStamp); } diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerBrowser.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerBrowser.java index 52d3ab1f3e..965e895a5f 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerBrowser.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/CDOServerBrowser.java @@ -11,6 +11,7 @@ package org.eclipse.emf.cdo.server; import org.eclipse.emf.cdo.common.branch.CDOBranch; +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; @@ -806,7 +807,22 @@ public class CDOServerBrowser extends Worker showKeyValue(pout, true, "version", revision.getVersion()); showKeyValue(pout, true, "created", commitInfo); showKeyValue(pout, true, "revised", CDOCommonUtil.formatTimeStamp(revision.getRevised())); - if (!(revision instanceof SyntheticCDORevision)) + if (revision instanceof SyntheticCDORevision) + { + if (revision instanceof PointerCDORevision) + { + PointerCDORevision pointer = (PointerCDORevision)revision; + CDOBranchVersion target = pointer.getTarget(); + CDOBranch branch = target.getBranch(); + int version = target.getVersion(); + + String label = getVersionLabel("v", version, branch); + CDORevisionKey targetKey = CDORevisionUtil.createRevisionKey(pointer.getID(), branch, version); + String value = CDORevisionUtil.formatRevisionKey(targetKey); + showKeyValue(pout, true, "target", browser.href(label, getName(), "revision", value)); + } + } + else { showKeyValue(pout, true, "resource", getRevisionValue(revision.getResourceID(), browser, ids, revision)); showKeyValue(pout, true, "container", getRevisionValue(revision.getContainerID(), browser, ids, revision)); @@ -841,12 +857,10 @@ public class CDOServerBrowser extends Worker builder.append(" "); for (CDORevision revision : revisions) { - String label = getVersionPrefix(revision) + revision.getVersion(); - String branchName = revision.getBranch().getName(); - if (!CDOBranch.MAIN_BRANCH_NAME.equals(branchName)) - { - label += "[" + branchName + "]"; - } + String versionPrefix = getVersionPrefix(revision); + int version = revision.getVersion(); + CDOBranch branch = revision.getBranch(); + String label = getVersionLabel(versionPrefix, version, branch); builder.append(" "); if (revision == context) @@ -879,6 +893,17 @@ public class CDOServerBrowser extends Worker return value; } + private String getVersionLabel(String versionPrefix, int version, CDOBranch branch) + { + String label = versionPrefix + version; + String branchName = branch.getName(); + if (!CDOBranch.MAIN_BRANCH_NAME.equals(branchName)) + { + label += "[" + branchName + "]"; + } + return label; + } + private String getVersionPrefix(CDORevision revision) { if (revision instanceof PointerCDORevision) diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllConfigs.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllConfigs.java index 69b668b9d1..baf512d12a 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllConfigs.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllConfigs.java @@ -1,105 +1,110 @@ -/*
- * 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.tests.bundle.OM;
-import org.eclipse.emf.cdo.tests.config.IScenario;
-import org.eclipse.emf.cdo.tests.config.impl.ConfigTest;
-import org.eclipse.emf.cdo.tests.config.impl.ConfigTestSuite;
-
-import java.util.List;
-
-/**
- * @author Eike Stepper
- */
-public abstract class AllConfigs extends ConfigTestSuite
-{
- @Override
- protected void initTestClasses(List<Class<? extends ConfigTest>> testClasses, IScenario scenario)
- {
- // General
- testClasses.add(InitialTest.class);
- testClasses.add(CDOIDTest.class);
- testClasses.add(ComplexTest.class);
- testClasses.add(AttributeTest.class);
- testClasses.add(EnumTest.class);
- testClasses.add(EMapTest.class);
- testClasses.add(UnsetTest.class);
- testClasses.add(StateMachineTest.class);
- testClasses.add(SessionTest.class);
- testClasses.add(RevisionManagerTest.class);
- testClasses.add(RevisionManagerClientSideTest.class);
- testClasses.add(BranchingTest.class);
- testClasses.add(BranchingSameSessionTest.class);
- testClasses.add(BranchingWithCacheClearTest.class);
- testClasses.add(MergingTest.class);
- testClasses.add(ViewTest.class);
- testClasses.add(TransactionTest.class);
- testClasses.add(PushTransactionTest.class);
- testClasses.add(PushTransactionWithoutReconstructSavepointsTest.class);
- testClasses.add(CommitInfoTest.class);
- testClasses.add(AuditTest.class);
- testClasses.add(AuditSameSessionTest.class);
- testClasses.add(ResourceTest.class);
- testClasses.add(ContainmentTest.class);
- testClasses.add(InvalidationTest.class);
- testClasses.add(RollbackTest.class);
- testClasses.add(CrossReferenceTest.class);
- testClasses.add(ChunkingTest.class);
- testClasses.add(ChunkingWithMEMTest.class);
- testClasses.add(MEMStoreQueryTest.class);
- testClasses.add(PackageRegistryTest.class);
- testClasses.add(PartialCommitTest.class);
- testClasses.add(MetaTest.class);
- testClasses.add(RevisionDeltaTest.class);
- testClasses.add(RevisionDeltaInBranchTest.class);
- testClasses.add(RevisionDeltaCascadingBranchesTest.class);
- testClasses.add(IndexReconstructionTest.class);
- testClasses.add(AutoAttacherTest.class);
- testClasses.add(SavePointTest.class);
- testClasses.add(ChangeSubscriptionTest.class);
- testClasses.add(DetachTest.class);
- testClasses.add(ExternalReferenceTest.class);
- testClasses.add(XATransactionTest.class);
- testClasses.add(TransactionHandlerTest.class);
- testClasses.add(RepositoryTest.class);
- testClasses.add(LockingManagerTest.class);
- testClasses.add(LockingManagerRestartTransactionTest.class);
- testClasses.add(LockingManagerRestartSessionTest.class);
- testClasses.add(LockingManagerRestartRepositoryTest.class);
- testClasses.add(LockingNotificationsTest.class);
- testClasses.add(MultiValuedOfAttributeTest.class);
- testClasses.add(MapTest.class);
- testClasses.add(FeatureMapTest.class);
- testClasses.add(AdapterManagerTest.class);
- testClasses.add(ConflictResolverTest.class);
- testClasses.add(DynamicXSDTest.class);
- testClasses.add(SetFeatureTest.class);
- testClasses.add(DynamicPackageTest.class);
- testClasses.add(LegacyTest.class);
- testClasses.add(XRefTest.class);
- testClasses.add(StickyViewsTest.class);
- testClasses.add(LobTest.class);
- testClasses.add(OCLQueryTest.class);
- testClasses.add(ViewProviderTest.class);
- testClasses.add(WorkspaceTest.class);
- testClasses.add(BackupTest.class);
- testClasses.add(ResourceModificationTrackingTest.class);
-
- // TODO testClasses.add(RemoteSessionManagerTest.class);
- // TODO testClasses.add(ConflictResolverMergingTest.class);
- // TODO testClasses.add(NonCDOResourceTest.class);
- // TODO testClasses.add(GeneratedEcoreTest.class);
-
- // Bugzilla verifications
- testClasses.addAll(getTestClasses(OM.BUNDLE, "org.eclipse.emf.cdo.tests.bugzilla"));
- }
-}
+/* + * 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.tests.bundle.OM; +import org.eclipse.emf.cdo.tests.config.IScenario; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTestSuite; + +import java.util.List; + +/** + * @author Eike Stepper + */ +public abstract class AllConfigs extends ConfigTestSuite +{ + public List<Class<? extends ConfigTest>> getBugzillaTests() + { + return getTestClasses(OM.BUNDLE, "org.eclipse.emf.cdo.tests.bugzilla"); + } + + @Override + protected void initTestClasses(List<Class<? extends ConfigTest>> testClasses, IScenario scenario) + { + // General + testClasses.add(InitialTest.class); + testClasses.add(CDOIDTest.class); + testClasses.add(ComplexTest.class); + testClasses.add(AttributeTest.class); + testClasses.add(EnumTest.class); + testClasses.add(EMapTest.class); + testClasses.add(UnsetTest.class); + testClasses.add(StateMachineTest.class); + testClasses.add(SessionTest.class); + testClasses.add(RevisionManagerTest.class); + testClasses.add(RevisionManagerClientSideTest.class); + testClasses.add(BranchingTest.class); + testClasses.add(BranchingSameSessionTest.class); + testClasses.add(BranchingWithCacheClearTest.class); + testClasses.add(MergingTest.class); + testClasses.add(ViewTest.class); + testClasses.add(TransactionTest.class); + testClasses.add(PushTransactionTest.class); + testClasses.add(PushTransactionWithoutReconstructSavepointsTest.class); + testClasses.add(CommitInfoTest.class); + testClasses.add(AuditTest.class); + testClasses.add(AuditSameSessionTest.class); + testClasses.add(ResourceTest.class); + testClasses.add(ContainmentTest.class); + testClasses.add(InvalidationTest.class); + testClasses.add(RollbackTest.class); + testClasses.add(CrossReferenceTest.class); + testClasses.add(ChunkingTest.class); + testClasses.add(ChunkingWithMEMTest.class); + testClasses.add(MEMStoreQueryTest.class); + testClasses.add(PackageRegistryTest.class); + testClasses.add(PartialCommitTest.class); + testClasses.add(MetaTest.class); + testClasses.add(RevisionDeltaTest.class); + testClasses.add(RevisionDeltaInBranchTest.class); + testClasses.add(RevisionDeltaCascadingBranchesTest.class); + testClasses.add(IndexReconstructionTest.class); + testClasses.add(AutoAttacherTest.class); + testClasses.add(SavePointTest.class); + testClasses.add(ChangeSubscriptionTest.class); + testClasses.add(DetachTest.class); + testClasses.add(ExternalReferenceTest.class); + testClasses.add(XATransactionTest.class); + testClasses.add(TransactionHandlerTest.class); + testClasses.add(RepositoryTest.class); + testClasses.add(LockingManagerTest.class); + testClasses.add(LockingManagerRestartTransactionTest.class); + testClasses.add(LockingManagerRestartSessionTest.class); + testClasses.add(LockingManagerRestartRepositoryTest.class); + testClasses.add(LockingNotificationsTest.class); + testClasses.add(MultiValuedOfAttributeTest.class); + testClasses.add(MapTest.class); + testClasses.add(FeatureMapTest.class); + testClasses.add(AdapterManagerTest.class); + testClasses.add(ConflictResolverTest.class); + testClasses.add(DynamicXSDTest.class); + testClasses.add(SetFeatureTest.class); + testClasses.add(DynamicPackageTest.class); + testClasses.add(LegacyTest.class); + testClasses.add(XRefTest.class); + testClasses.add(StickyViewsTest.class); + testClasses.add(LobTest.class); + testClasses.add(OCLQueryTest.class); + testClasses.add(ViewProviderTest.class); + testClasses.add(WorkspaceTest.class); + testClasses.add(BackupTest.class); + testClasses.add(ResourceModificationTrackingTest.class); + + // TODO testClasses.add(RemoteSessionManagerTest.class); + // TODO testClasses.add(ConflictResolverMergingTest.class); + // TODO testClasses.add(NonCDOResourceTest.class); + // TODO testClasses.add(GeneratedEcoreTest.class); + + // Bugzilla verifications + testClasses.addAll(getBugzillaTests()); + } +} diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ResourceTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ResourceTest.java index c504f9c3ab..416182c50e 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ResourceTest.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/ResourceTest.java @@ -1,1680 +1,1691 @@ -/*
- * 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
- */
-package org.eclipse.emf.cdo.tests;
-
-import org.eclipse.emf.cdo.CDOObject;
-import org.eclipse.emf.cdo.CDOState;
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.eresource.CDOResource;
-import org.eclipse.emf.cdo.eresource.CDOResourceFolder;
-import org.eclipse.emf.cdo.eresource.CDOResourceNode;
-import org.eclipse.emf.cdo.session.CDOSession;
-import org.eclipse.emf.cdo.tests.model1.Category;
-import org.eclipse.emf.cdo.tests.model1.Company;
-import org.eclipse.emf.cdo.tests.model1.Order;
-import org.eclipse.emf.cdo.tests.model1.OrderDetail;
-import org.eclipse.emf.cdo.tests.model1.Product1;
-import org.eclipse.emf.cdo.tests.model1.VAT;
-import org.eclipse.emf.cdo.transaction.CDOTransaction;
-import org.eclipse.emf.cdo.util.CDOURIUtil;
-import org.eclipse.emf.cdo.util.CDOUtil;
-import org.eclipse.emf.cdo.util.CommitException;
-import org.eclipse.emf.cdo.util.ObjectNotFoundException;
-import org.eclipse.emf.cdo.view.CDOView;
-
-import org.eclipse.emf.common.notify.Notification;
-import org.eclipse.emf.common.notify.Notifier;
-import org.eclipse.emf.common.notify.impl.AdapterImpl;
-import org.eclipse.emf.common.util.URI;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.resource.Resource;
-import org.eclipse.emf.ecore.resource.ResourceSet;
-import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
-import org.eclipse.emf.ecore.util.EcoreUtil;
-import org.eclipse.emf.ecore.xmi.XMIResource;
-import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;
-
-import java.io.ByteArrayOutputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @author Eike Stepper
- */
-public class ResourceTest extends AbstractCDOTest
-{
- /**
- * Test logic not up to date with the new xmi:id format.
- */
- public void _testSaveXMI() throws Exception
- {
- ByteArrayOutputStream cdoOUT = new ByteArrayOutputStream();
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- CDOResource resource = transaction.createResource(getResourcePath("/folder/res1"));
-
- counter = 0;
- Category category = createCategoryTree(3);
- resource.getContents().add(category);
-
- transaction.commit();
- resource.save(cdoOUT, null);
- }
-
- ByteArrayOutputStream xmiOUT = new ByteArrayOutputStream();
- {
- XMIResource resource = new XMIResourceImpl(URI.createFileURI("/folder/res1"));
-
- counter = 0;
- Category category = createCategoryTree(3);
- resource.getContents().add(category);
-
- resource.save(xmiOUT, null);
- }
-
- String xmiString = xmiOUT.toString();
- msg("XMI:\n\n" + xmiString);
-
- String cdoString = cdoOUT.toString();
- msg("CDO:\n\n" + cdoString);
-
- assertEquals(xmiString, cdoString);
- }
-
- public void testSaveXMI_WithXRef() throws Exception
- {
- ByteArrayOutputStream cdoOUT = new ByteArrayOutputStream();
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- CDOResource resource = transaction.createResource(getResourcePath("/folder/res1"));
-
- counter = 0;
- Category category = createCategoryTree(3);
- resource.getContents().add(category);
-
- OrderDetail orderDetail = getModel1Factory().createOrderDetail();
- orderDetail.setPrice(147.111f);
- orderDetail.setProduct(category.getProducts().get(0));
- resource.getContents().add(orderDetail);
-
- transaction.commit();
- resource.save(cdoOUT, null);
- }
-
- ByteArrayOutputStream xmiOUT = new ByteArrayOutputStream();
- {
- XMIResource resource = new XMIResourceImpl(URI.createFileURI("/folder/res1"));
-
- counter = 0;
- Category category = createCategoryTree(3);
- resource.getContents().add(category);
-
- OrderDetail orderDetail = getModel1Factory().createOrderDetail();
- orderDetail.setPrice(147.111f);
- orderDetail.setProduct(category.getProducts().get(0));
- resource.getContents().add(orderDetail);
-
- resource.save(xmiOUT, null);
- }
-
- String xmiString = xmiOUT.toString();
- msg("XMI:\n\n" + xmiString);
-
- String cdoString = cdoOUT.toString();
- msg("CDO:\n\n" + cdoString);
-
- // TODO assertEquals(xmiString, cdoString);
- }
-
- public void testSaveXMI_WithXRef_OtherResource() throws Exception
- {
- ByteArrayOutputStream cdoOUT = new ByteArrayOutputStream();
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- CDOResource resource = transaction.createResource(getResourcePath("/folder/res1"));
- CDOResource resource2 = transaction.createResource(getResourcePath("/folder/res2"));
-
- counter = 0;
- Category category = createCategoryTree(3);
- resource.getContents().add(category);
-
- OrderDetail orderDetail = getModel1Factory().createOrderDetail();
- orderDetail.setPrice(147.111f);
- orderDetail.setProduct(category.getProducts().get(0));
- resource2.getContents().add(orderDetail);
-
- transaction.commit();
- resource.save(cdoOUT, null);
- resource2.save(cdoOUT, null);
- }
-
- ByteArrayOutputStream xmiOUT = new ByteArrayOutputStream();
- {
- XMIResource resource = new XMIResourceImpl(URI.createFileURI("/folder/res1"));
- XMIResource resource2 = new XMIResourceImpl(URI.createFileURI("/folder/res2"));
-
- counter = 0;
- Category category = createCategoryTree(3);
- resource.getContents().add(category);
-
- OrderDetail orderDetail = getModel1Factory().createOrderDetail();
- orderDetail.setPrice(147.111f);
- orderDetail.setProduct(category.getProducts().get(0));
- resource2.getContents().add(orderDetail);
-
- resource.save(xmiOUT, null);
- resource2.save(xmiOUT, null);
- }
-
- String xmiString = xmiOUT.toString();
- msg("XMI:\n\n" + xmiString);
-
- String cdoString = cdoOUT.toString();
- msg("CDO:\n\n" + cdoString);
-
- // TODO assertEquals(xmiString, cdoString);
- }
-
- @CleanRepositoriesBefore
- public void testAttachDetachResourceDepth1_Delete() throws Exception
- {
- attachDetachResourceDepth1(1, true, 0);
- }
-
- @CleanRepositoriesBefore
- public void testAttachDetachResourceDepth1_Remove() throws Exception
- {
- attachDetachResourceDepth1(1, false, 0);
- }
-
- @CleanRepositoriesBefore
- public void testAttachDetachResourceDepth2_Delete() throws Exception
- {
- attachDetachResourceDepth1(2, true, 1);
- }
-
- @CleanRepositoriesBefore
- public void testAttachDetachResourceDepth2_Remove() throws Exception
- {
- attachDetachResourceDepth1(2, false, 1);
- }
-
- @CleanRepositoriesBefore
- public void testAttachDetachResourceDepth3_Delete() throws Exception
- {
- attachDetachResourceDepth1(3, true, 2);
- }
-
- @CleanRepositoriesBefore
- public void testAttachDetachResourceDepth3_Remove() throws Exception
- {
- attachDetachResourceDepth1(3, false, 2);
- }
-
- @CleanRepositoriesBefore
- public void testAttachDetachResourceDepth3_Remove_Tree() throws Exception
- {
- attachDetachResourceDepth1(3, false, 1);
- }
-
- public void testRootResourceFromURI() throws Exception
- {
- URI rootResourceURI = null;
- URI resourceURI = null;
- String resourcePath = getResourcePath("/test1");
- {
- CDOSession session = openSession();
- ResourceSet resourceSet = new ResourceSetImpl();
- CDOTransaction transaction = session.openTransaction(resourceSet);
-
- resourceURI = URI.createURI("cdo:" + resourcePath);
- Resource res1 = resourceSet.createResource(resourceURI);
-
- transaction.commit();
- rootResourceURI = EcoreUtil.getURI(transaction.getRootResource());
- resourceURI = EcoreUtil.getURI((EObject)res1);
- }
-
- CDOSession session = openSession();
- ResourceSet resourceSet = new ResourceSetImpl();
- CDOTransaction transaction = session.openTransaction(resourceSet);
- CDOResource rootResource = (CDOResource)resourceSet.getEObject(rootResourceURI, true);
- assertProxy(rootResource);
- assertSame(rootResource, transaction.getRootResource());
-
- CDOResource resource = (CDOResource)resourceSet.getEObject(resourceURI, true);
- assertClean(resource, transaction);
- assertSame(resource, transaction.getResource(resourcePath));
-
- transaction.close();
- session.close();
- }
-
- @CleanRepositoriesBefore
- public void testCreateResource_FromResourceSet() throws Exception
- {
- CDOSession session = openSession();
- ResourceSet resourceSet = new ResourceSetImpl();
- CDOTransaction transaction = session.openTransaction(resourceSet);
-
- final URI uri = URI.createURI("cdo:/test1");
- CDOResource resource = (CDOResource)resourceSet.createResource(uri);
- assertActive(resource);
- assertNew(resource, transaction);
- assertEquals(transaction.getResourceSet(), resource.getResourceSet());
- assertEquals(CDOURIUtil.createResourceURI(session, "test1"), resource.getURI());
- assertEquals("test1", resource.getName());
- assertEquals(null, resource.getFolder());
-
- transaction.getRootResource().getContents().contains(resource);
- transaction.commit();
-
- CDOObject cdoResource = resource;
- CDOObject cdoRootResource = transaction.getRootResource();
- assertClean(cdoResource, transaction);
- assertClean(cdoRootResource, transaction);
- assertEquals(CDOID.NULL, cdoResource.cdoRevision().data().getContainerID());
- assertEquals(cdoRootResource.cdoID(), cdoResource.cdoRevision().data().getResourceID());
- assertEquals(cdoRootResource.cdoID(), cdoRootResource.cdoRevision().data().getResourceID());
- assertEquals(true, transaction.getResourceSet().getResources().contains(resource));
- assertEquals(false, transaction.getResourceSet().getResources().contains(transaction.getRootResource()));// Bug
- // 346636
-
- transaction.getRootResource().getContents().remove(resource);
- assertEquals(false, transaction.getResourceSet().getResources().contains(resource));
- assertEquals(false, transaction.getResourceSet().getResources().contains(transaction.getRootResource()));// Bug
- // 346636
- }
-
- public void testCreateNestedResource_FromResourceSet() throws Exception
- {
- CDOSession session = openSession();
- ResourceSet resourceSet = new ResourceSetImpl();
- CDOTransaction transaction = session.openTransaction(resourceSet);
-
- final URI uri = URI.createURI("cdo:/folder/test1");
- CDOResource resource = (CDOResource)resourceSet.createResource(uri);
- assertActive(resource);
- assertNew(resource, transaction);
- assertEquals(transaction.getResourceSet(), resource.getResourceSet());
- assertEquals(CDOURIUtil.createResourceURI(session, "folder/test1"), resource.getURI());
- assertEquals("test1", resource.getName());
-
- CDOResourceFolder folder = resource.getFolder();
- assertNotNull(folder);
- assertEquals("folder", folder.getName());
- assertEquals(null, folder.getFolder());
- }
-
- public void testCreateResource_FromTransaction() throws Exception
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
-
- // Test if Resource is well-formed after CDOResourceFactoryImpl.
- // Adapter will be called right after and could be used!
- transaction.getResourceSet().eAdapters().add(new TestAdapter());
-
- CDOResource resource = transaction.createResource(getResourcePath("/test1"));
- assertActive(resource);
-
- CDOResource resourceCopy = transaction.getOrCreateResource(getResourcePath("/test1"));
- assertEquals(resource, resourceCopy);
- assertNew(resource, transaction);
- assertEquals(CDOURIUtil.createResourceURI(session, getResourcePath("test1")), resource.getURI());
- assertEquals(transaction.getResourceSet(), resource.getResourceSet());
- }
-
- public void testCreateResource_WithDeepPath() throws Exception
- {
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- transaction.createResource(getResourcePath("/org/eclipse/net4j/core"));
- transaction.commit();
- session.close();
- }
-
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath("/org/eclipse/net4j/core"));
- assertEquals(CDOURIUtil.createResourceURI(session, getResourcePath("/org/eclipse/net4j/core")), resource.getURI());
- assertEquals(transaction.getResourceSet(), resource.getResourceSet());
- session.close();
- }
-
- public void testLoadAbsentResource_FromResourceSet() throws Exception
- {
- CDOSession session = openSession();
- ResourceSet resourceSet = new ResourceSetImpl();
- CDOTransaction transaction = session.openTransaction(resourceSet);
-
- final URI uri = URI.createURI("cdo:/test1");
- CDOResource resource = (CDOResource)resourceSet.getResource(uri, false);
- assertEquals(null, resource);
-
- try
- {
- resourceSet.getResource(uri, true);
- }
- catch (Exception ignore)
- {
- }
-
- transaction.close();
- }
-
- public void testRemoveResourceWithCloseView() throws Exception
- {
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- ResourceSet rset = transaction.getResourceSet();
- CDOResource resource = transaction.createResource(getResourcePath("/test1"));
- assertActive(resource);
-
- transaction.commit();
- assertEquals(1, rset.getResources().size()); // Bug 346636
- assertEquals(1, CDOUtil.getViewSet(rset).getViews().length);
-
- transaction.close();
- assertEquals(0, CDOUtil.getViewSet(rset).getViews().length);
- assertEquals(0, rset.getResources().size());
- session.close();
- }
-
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- CDOResource resource = (CDOResource)transaction.getResourceSet().getResource(
- CDOURIUtil.createResourceURI(transaction, getResourcePath("/test1")), true);
- assertNotNull(resource);
- assertEquals(transaction.getResourceSet(), resource.getResourceSet());
- assertEquals(1, transaction.getResourceSet().getResources().size());
- assertEquals(CDOState.PROXY, resource.cdoState());
- assertEquals(transaction, resource.cdoView());
- assertNull(resource.cdoRevision());
- }
- }
-
- public void testAttachResource() throws Exception
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
-
- transaction.createResource(getResourcePath("/my/resource1"));
- assertEquals(1, transaction.getResourceSet().getResources().size());// Bug 346636
-
- transaction.commit();
- session.close();
- }
-
- public void testCommitMultipleResources() throws CommitException
- {
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- transaction.createResource(getResourcePath("/level1/level2-A/level3"));
- transaction.createResource(getResourcePath("/level1/level2-B/level3"));
- transaction.commit();
- session.close();
- }
-
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- CDOResource resource1 = transaction.getResource(getResourcePath("/level1/level2-A/level3"));
- CDOResource resource2 = transaction.getResource(getResourcePath("/level1/level2-B/level3"));
- assertEquals(getResourcePath("/level1/level2-A/level3"), resource1.getPath());
- assertEquals(getResourcePath("/level1/level2-B/level3"), resource2.getPath());
- session.close();
- }
-
- public void testLoadMultipleResources() throws CommitException
- {
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- transaction.createResource(getResourcePath("/level1/level2-A/level3"));
- transaction.createResource(getResourcePath("/level1/level2-B/level3"));
- transaction.commit();
- session.close();
- }
-
- clearCache(getRepository().getRevisionManager());
-
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- CDOResource resource1 = transaction.getResource(getResourcePath("/level1/level2-A/level3"));
- CDOResource resource2 = transaction.getResource(getResourcePath("/level1/level2-B/level3"));
- assertEquals(getResourcePath("/level1/level2-A/level3"), resource1.getPath());
- assertEquals(getResourcePath("/level1/level2-B/level3"), resource2.getPath());
- session.close();
- }
-
- public void testDuplicatePath() throws Exception
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- transaction.createResource(getResourcePath("/my/resource"));
- transaction.commit();
-
- transaction.createResource(getResourcePath("/my/resource"));
-
- try
- {
- transaction.commit();
- fail("CommitException expected");
- }
- catch (CommitException expected)
- {
- // Success
- }
- finally
- {
- session.close();
- }
- }
-
- public void testDuplicatePathAfterDetach() throws Exception
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
- transaction.commit();
-
- resource.delete(null);
- transaction.commit();
-
- transaction.createResource(getResourcePath("/my/resource"));
- transaction.commit();
- session.close();
- }
-
- @CleanRepositoriesBefore
- public void testChangePathFromDepth0ToDepth0() throws Exception
- {
- changePath(0, 0);
- }
-
- @CleanRepositoriesBefore
- public void testChangePathFromDepth0ToDepth1() throws Exception
- {
- changePath(0, 1);
- }
-
- @CleanRepositoriesBefore
- public void testChangePathFromDepth0ToDepth2() throws Exception
- {
- changePath(0, 2);
- }
-
- @CleanRepositoriesBefore
- public void testChangePathFromDepth0ToDepth3() throws Exception
- {
- changePath(0, 3);
- }
-
- @CleanRepositoriesBefore
- public void testChangePathFromDepth3ToDepth3() throws Exception
- {
- changePath(3, 3);
- }
-
- @CleanRepositoriesBefore
- public void testChangePathFromDepth3ToDepth2() throws Exception
- {
- changePath(3, 2);
- }
-
- @CleanRepositoriesBefore
- public void testChangePathFromDepth3ToDepth1() throws Exception
- {
- changePath(3, 1);
- }
-
- @CleanRepositoriesBefore
- public void testChangePathFromDepth3ToDepth0() throws Exception
- {
- changePath(3, 0);
- }
-
- public void testChangeResourceURI() throws Exception
- {
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
- transaction.commit();
-
- URI uri = URI.createURI("cdo://repo1/renamed");
- assertEquals(CDOURIUtil.createResourceURI(session, "/renamed"), uri);
- resource.setURI(uri);
-
- transaction.commit();
- session.close();
- }
-
- clearCache(getRepository().getRevisionManager());
-
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
-
- assertEquals(false, transaction.hasResource("/my/resource"));
- assertEquals(true, transaction.hasResource("/renamed"));
- }
-
- @CleanRepositoriesBefore
- public void testChangeResourceFolderURI() throws Exception
- {
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- CDOResource resource = transaction.createResource(getResourcePath("/my/resource"));
- transaction.commit();
-
- URI uri = URI.createURI("cdo://repo1/renamed");
- assertEquals(CDOURIUtil.createResourceURI(session, "/renamed"), uri);
- resource.setURI(uri);
-
- transaction.commit();
- session.close();
- }
-
- clearCache(getRepository().getRevisionManager());
-
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
-
- assertEquals(false, transaction.hasResource("/my/resource"));
- assertEquals(true, transaction.hasResource("/renamed"));
- }
-
- public void testPathNotNull() throws Exception
- {
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- CDOResource resource = transaction.createResource(getResourcePath("/res1"));
- assertEquals(getResourcePath("/res1"), resource.getPath());
- assertEquals(CDOURIUtil.createResourceURI(session, getResourcePath("/res1")), resource.getURI());
-
- transaction.commit();
- assertEquals(getResourcePath("/res1"), resource.getPath());
- assertEquals(CDOURIUtil.createResourceURI(session, getResourcePath("/res1")), resource.getURI());
- session.close();
- }
-
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- CDOResource resource = transaction.getResource(getResourcePath("/res1"));
- assertEquals(getResourcePath("/res1"), resource.getPath());
- assertEquals(CDOURIUtil.createResourceURI(session, getResourcePath("/res1")), resource.getURI());
-
- CDOResource resource2 = transaction.getOrCreateResource(getResourcePath("/res2"));
- assertEquals(getResourcePath("/res2"), resource2.getPath());
- assertEquals(CDOURIUtil.createResourceURI(session, getResourcePath("/res2")), resource2.getURI());
-
- transaction.commit();
- assertEquals(getResourcePath("/res2"), resource2.getPath());
- assertEquals(CDOURIUtil.createResourceURI(session, getResourcePath("/res2")), resource2.getURI());
- session.close();
- }
-
- {
- CDOSession session = openSession();
- CDOView view = session.openView();
- CDOResource resource2 = view.getResource(getResourcePath("/res2"));
- assertEquals(getResourcePath("/res2"), resource2.getPath());
- assertEquals(CDOURIUtil.createResourceURI(session, getResourcePath("/res2")), resource2.getURI());
- session.close();
- }
- }
-
- public void testPrefetchContents() throws Exception
- {
- {
- Company company = getModel1Factory().createCompany();
- company.getCategories().add(createCategoryTree(3));
-
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
-
- CDOResource resource = transaction.createResource(getResourcePath("/res1"));
- resource.getContents().add(company);
-
- transaction.commit();
- session.close();
- }
-
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
-
- CDOResource resource = transaction.getResource(getResourcePath("/res1"));
- resource.cdoPrefetch(CDORevision.DEPTH_INFINITE);
-
- Company company = (Company)resource.getContents().get(0);
- System.out.println(company);
-
- session.close();
- }
-
- private static int counter;
-
- private Category createCategoryTree(int depth)
- {
- if (depth == 0)
- {
- return null;
- }
-
- Category category = getModel1Factory().createCategory();
- for (int i = 0; i < 2; i++)
- {
- Category child = createCategoryTree(depth - 1);
- if (child != null)
- {
- category.getCategories().add(child);
- }
- }
-
- for (int i = 0; i < 3; i++)
- {
- Product1 child = getModel1Factory().createProduct1();
- // generate a unique id
- String id = "test " + depth + "_" + i + "_" + ++counter;
- child.setName(id);
- category.getProducts().add(child);
- }
-
- return category;
- }
-
- /**
- * bug 208689
- */
- public void testQueryResources() throws Exception
- {
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- createResource(transaction, "/aresource");
- createResource(transaction, "/aaresource");
- createResource(transaction, "/abresource");
- createResource(transaction, "/acresource");
- createResource(transaction, "/adresource");
- createResource(transaction, "/aeresource");
- createResource(transaction, "/bresource");
- createResource(transaction, "/baresource");
- createResource(transaction, "/bbresource");
- createResource(transaction, "/bcresource");
- createResource(transaction, "/bdresource");
- createResource(transaction, "/beresource");
- createResource(transaction, "/bearesource");
- createResource(transaction, "/bebresource");
- createResource(transaction, "/cresource");
- createResource(transaction, "/caresource");
- createResource(transaction, "/caresource2");
- createResource(transaction, "/caresource3");
- createResource(transaction, "/cbresource");
- createResource(transaction, "/ccresource");
- createResource(transaction, "/cdresource");
- createResource(transaction, "/ceresource");
- transaction.commit();
- session.close();
- }
-
- CDOSession session = openSession();
- CDOView view = session.openView();
- queryResources(view, "a", 6);
- queryResources(view, "b", 8);
- queryResources(view, "c", 8);
- queryResources(view, "be", 3);
- queryResources(view, "ca", 3);
- session.close();
- }
-
- /**
- * bug 208689
- */
- public void testQueryModifiedResources() throws Exception
- {
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- createResource(transaction, "/aresource");
- createResource(transaction, "/aaresource");
- createResource(transaction, "/abresource");
- createResource(transaction, "/acresource");
- createResource(transaction, "/adresource");
- createResource(transaction, "/aeresource");
- createResource(transaction, "/bresource");
- createResource(transaction, "/baresource");
- createResource(transaction, "/bbresource");
- createResource(transaction, "/bcresource");
- createResource(transaction, "/bdresource");
- createResource(transaction, "/beresource");
- createResource(transaction, "/bearesource");
- createResource(transaction, "/bebresource");
- createResource(transaction, "/cresource");
- createResource(transaction, "/caresource");
- createResource(transaction, "/caresource2");
- createResource(transaction, "/caresource3");
- createResource(transaction, "/cbresource");
- createResource(transaction, "/ccresource");
- createResource(transaction, "/cdresource");
- createResource(transaction, "/ceresource");
- transaction.commit();
- modifyResource(transaction, "/aresource");
- modifyResource(transaction, "/aaresource");
- modifyResource(transaction, "/abresource");
- modifyResource(transaction, "/acresource");
- modifyResource(transaction, "/adresource");
- modifyResource(transaction, "/aeresource");
- modifyResource(transaction, "/bresource");
- modifyResource(transaction, "/baresource");
- modifyResource(transaction, "/bbresource");
- modifyResource(transaction, "/bcresource");
- modifyResource(transaction, "/bdresource");
- modifyResource(transaction, "/beresource");
- modifyResource(transaction, "/bearesource");
- modifyResource(transaction, "/bebresource");
- modifyResource(transaction, "/cresource");
- modifyResource(transaction, "/caresource");
- modifyResource(transaction, "/caresource2");
- modifyResource(transaction, "/caresource3");
- modifyResource(transaction, "/cbresource");
- modifyResource(transaction, "/ccresource");
- modifyResource(transaction, "/cdresource");
- modifyResource(transaction, "/ceresource");
- transaction.commit();
- session.close();
- }
-
- CDOSession session = openSession();
- CDOView view = session.openView();
- queryResources(view, "a", 6);
- queryResources(view, "b", 8);
- queryResources(view, "c", 8);
- queryResources(view, "be", 3);
- queryResources(view, "ca", 3);
- session.close();
- }
-
- public void testDeleteResource() throws Exception
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
-
- CDOResource resource = createResource(transaction, "/resource1");
- CDOID resourceID = resource.cdoID();
-
- CDOObject object = CDOUtil.getCDOObject(resource.getContents().get(0));
- CDOID objectID = object.cdoID();
-
- transaction.commit();
- resource.delete(null);
- transaction.commit();
- transaction.close();
-
- CDOView view = session.openView();
- assertEquals(false, view.hasResource("/resource1"));
-
- try
- {
- view.getResourceNode("/resource1");
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- }
-
- try
- {
- view.getResource(getResourcePath("/resource1"));
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- }
-
- try
- {
- view.getObject(resourceID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- try
- {
- view.getObject(objectID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- session.close();
- }
-
- public void testDeleteResourceFresh() throws Exception
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
-
- CDOResource resource = createResource(transaction, "/resource1");
- CDOID resourceID = resource.cdoID();
-
- CDOObject object = CDOUtil.getCDOObject(resource.getContents().get(0));
- CDOID objectID = object.cdoID();
-
- transaction.commit();
- resource.delete(null);
- transaction.commit();
- transaction.close();
-
- clearCache(getRepository().getRevisionManager());
- CDOView view = session.openView();
- assertEquals(false, view.hasResource("/resource1"));
-
- try
- {
- view.getResourceNode("/resource1");
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- }
-
- try
- {
- view.getResource(getResourcePath("/resource1"));
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- }
-
- try
- {
- view.getObject(resourceID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- try
- {
- view.getObject(objectID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- session.close();
- }
-
- public void testDeleteResourceDifferentSession() throws Exception
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
-
- CDOSession session2 = openSession();
- CDOView view = session2.openView();
-
- CDOResource resource = createResource(transaction, "/resource1");
- CDOID resourceID = resource.cdoID();
-
- CDOObject object = CDOUtil.getCDOObject(resource.getContents().get(0));
- CDOID objectID = object.cdoID();
-
- transaction.commit();
- assertEquals(true, view.hasResource(getResourcePath("/resource1")));
- assertEquals(resource.getURI(), view.getResource(getResourcePath("/resource1")).getURI());
-
- resource.delete(null);
- commitAndSync(transaction, view);
- transaction.close();
-
- assertEquals(false, view.hasResource("/resource1"));
-
- try
- {
- view.getResourceNode("/resource1");
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- }
-
- try
- {
- view.getResource(getResourcePath("/resource1"));
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- }
-
- try
- {
- view.getObject(resourceID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- try
- {
- view.getObject(objectID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- session.close();
- }
-
- public void testDeleteResourceDifferentSessionFresh() throws Exception
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
-
- CDOSession session2 = openSession();
- CDOView view = session2.openView();
-
- CDOResource resource = createResource(transaction, "/resource1");
- CDOID resourceID = resource.cdoID();
-
- CDOObject object = CDOUtil.getCDOObject(resource.getContents().get(0));
- CDOID objectID = object.cdoID();
-
- transaction.commit();
- assertEquals(true, view.hasResource(getResourcePath("/resource1")));
- assertEquals(resource.getURI(), view.getResource(getResourcePath("/resource1")).getURI());
-
- resource.delete(null);
- commitAndSync(transaction, view);
- transaction.close();
-
- clearCache(getRepository().getRevisionManager());
- assertEquals(false, view.hasResource("/resource1"));
-
- try
- {
- view.getResourceNode("/resource1");
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- }
-
- try
- {
- CDOResource resource1 = view.getResource(getResourcePath("/resource1"));
- assertNull(resource1);
- fail("Exception expected");
- // TODO Fails on automated build:
- // junit.framework.AssertionFailedError: Exception expected
- // at org.eclipse.emf.cdo.tests.ResourceTest.testDeleteResourceDifferentSessionFresh(ResourceTest.java:859)
- // at org.eclipse.net4j.tests.AbstractOMTest.runBare(AbstractOMTest.java:86)
- // at org.eclipse.net4j.tests.AbstractOMTest.run(AbstractOMTest.java:108)
- // at org.eclipse.emf.cdo.tests.config.impl.ConfigTestSuite$TestWrapper.runTest(ConfigTestSuite.java:126)
- // at org.eclipse.test.EclipseTestRunner.run(EclipseTestRunner.java:332)
- // at org.eclipse.test.EclipseTestRunner.run(EclipseTestRunner.java:202)
- // at org.eclipse.test.CoreTestApplication.runTests(CoreTestApplication.java:35)
- // at org.eclipse.test.CoreTestApplication.run(CoreTestApplication.java:31)
- // at org.eclipse.equinox.internal.app.EclipseAppContainer.callMethodWithException(EclipseAppContainer.java:574)
- // at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)
- // at org.eclipse.equinox.internal.app.MainApplicationLauncher.run(MainApplicationLauncher.java:32)
- // at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110)
- // at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79)
- // at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:368)
- // at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179)
- // at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:559)
- // at org.eclipse.equinox.launcher.Main.basicRun(Main.java:514)
- // at org.eclipse.equinox.launcher.Main.run(Main.java:1311)
- // at org.eclipse.equinox.launcher.Main.main(Main.java:1287)
- // at org.eclipse.core.launcher.Main.main(Main.java:34)
- }
- catch (Exception expected)
- {
- expected.printStackTrace();
- }
-
- try
- {
- view.getObject(resourceID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- try
- {
- view.getObject(objectID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- session.close();
- }
-
- public void testDeleteResourceFolder() throws Exception
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- CDOResource resource = createResource(transaction, "/folder/resource1");
- CDOObject object = CDOUtil.getCDOObject(resource.getContents().get(0));
- transaction.commit();
-
- CDOResourceFolder folder = resource.getFolder();
- CDOID folderID = folder.cdoID();
- CDOID resourceID = resource.cdoID();
- CDOID objectID = object.cdoID();
-
- folder.delete(null);
- transaction.commit();
- transaction.close();
-
- CDOView view = session.openView();
- assertEquals(false, view.hasResource("/folder/resource1"));
-
- try
- {
- view.getResourceNode("/folder");
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- }
-
- try
- {
- view.getResourceNode("/folder/resource1");
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- }
-
- try
- {
- view.getResource(getResourcePath("/folder/resource1"));
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- }
-
- try
- {
- view.getObject(folderID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- try
- {
- view.getObject(resourceID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- try
- {
- view.getObject(objectID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- session.close();
- }
-
- public void testDeleteResourceFolderFresh() throws Exception
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- CDOResource resource = createResource(transaction, "/folder/resource1");
- CDOObject object = CDOUtil.getCDOObject(resource.getContents().get(0));
- transaction.commit();
-
- CDOResourceFolder folder = resource.getFolder();
- CDOID folderID = folder.cdoID();
- CDOID resourceID = resource.cdoID();
- CDOID objectID = object.cdoID();
-
- folder.delete(null);
- transaction.commit();
- transaction.close();
-
- clearCache(getRepository().getRevisionManager());
- CDOView view = session.openView();
- assertEquals(false, view.hasResource("/folder/resource1"));
-
- try
- {
- view.getResourceNode("/folder");
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- }
-
- try
- {
- view.getResourceNode("/folder/resource1");
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- }
-
- try
- {
- view.getResource(getResourcePath("/folder/resource1"));
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- }
-
- try
- {
- view.getObject(folderID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- try
- {
- view.getObject(resourceID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- try
- {
- view.getObject(objectID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- session.close();
- }
-
- public void testDeleteResourceFolderDifferentSession() throws Exception
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
-
- CDOSession session2 = openSession();
- CDOView view = session2.openView();
-
- CDOResource resource = createResource(transaction, "/folder/resource1");
- CDOResourceFolder folder = resource.getFolder();
- CDOObject object = CDOUtil.getCDOObject(resource.getContents().get(0));
-
- transaction.commit();
- assertEquals(true, view.hasResource(getResourcePath("/folder/resource1")));
- assertEquals(resource.getURI(), view.getResource(getResourcePath("/folder/resource1")).getURI());
-
- CDOID folderID = folder.cdoID();
- CDOID resourceID = resource.cdoID();
- CDOID objectID = object.cdoID();
-
- folder.delete(null);
- commitAndSync(transaction, view);
- transaction.close();
-
- assertEquals(false, view.hasResource("/folder/resource1"));
-
- try
- {
- view.getResourceNode("/folder");
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- }
-
- try
- {
- view.getResourceNode("/folder/resource1");
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- }
-
- try
- {
- view.getResource(getResourcePath("/folder/resource1"));
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- }
-
- try
- {
- view.getObject(folderID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- try
- {
- view.getObject(resourceID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- try
- {
- view.getObject(objectID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- session.close();
- }
-
- public void testDeleteResourceFolderDifferentSessionFresh() throws Exception
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
-
- CDOSession session2 = openSession();
- CDOView view = session2.openView();
-
- CDOResource resource = createResource(transaction, "/folder/resource1");
- CDOResourceFolder folder = resource.getFolder();
- CDOObject object = CDOUtil.getCDOObject(resource.getContents().get(0));
-
- transaction.commit();
- assertEquals(true, view.hasResource(getResourcePath("/folder/resource1")));
- assertEquals(resource.getURI(), view.getResource(getResourcePath("/folder/resource1")).getURI());
-
- CDOID folderID = folder.cdoID();
- CDOID resourceID = resource.cdoID();
- CDOID objectID = object.cdoID();
-
- folder.delete(null);
- commitAndSync(transaction, view);
- transaction.close();
-
- clearCache(getRepository().getRevisionManager());
- assertEquals(false, view.hasResource("/folder/resource1"));
-
- try
- {
- view.getResourceNode("/folder");
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- }
-
- try
- {
- view.getResourceNode("/folder/resource1");
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- }
-
- try
- {
- view.getResource(getResourcePath("/folder/resource1"));
- // TODO Fails on automated build
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- }
-
- try
- {
- view.getObject(folderID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- try
- {
- view.getObject(resourceID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- try
- {
- view.getObject(objectID);
- fail("ObjectNotFoundException expected");
- }
- catch (ObjectNotFoundException expected)
- {
- }
-
- session.close();
- }
-
- /**
- * Create resource with the following pattern /test1/test2/test3 for a depth 3. <br>
- * After it will remove the resource with the following rule:<br>
- * if calldelete is true <code>resource.delete(null)</code> <br>
- * if calldelete is false it will use the depthtoRemove to call <code>object.remove(resource)</code><br>
- * deptToRemove = /0/1/2/...<br>
- * It will remove it from parent folder (depthtoRemove - 1);
- */
- private void attachDetachResourceDepth1(int depth, boolean callDelete, int depthtoRemove) throws Exception
- {
- CDOSession session = openSession();
- ResourceSet resourceSet = new ResourceSetImpl();
- CDOTransaction transaction = session.openTransaction(resourceSet);
- CDOResource rootResource = transaction.getRootResource();
- assertSame(rootResource, rootResource.eResource());
- String path = "";
- List<String> names = new ArrayList<String>();
- for (int i = 0; i < depth; i++)
- {
- String name = "test" + String.valueOf(i + 1);
- names.add(name);
- path += "/" + name;
- }
-
- final URI uri = URI.createURI("cdo:" + path);
- CDOResource resource = (CDOResource)resourceSet.createResource(uri);
- assertEquals(names.get(names.size() - 1), resource.getName());
-
- transaction.commit();
- List<CDOResourceNode> nodesList = new ArrayList<CDOResourceNode>();
- CDOResource resourceByLookup = null;
- CDOResourceNode next = null;
- for (int i = 0; i < depth; i++)
- {
- if (i == 0)
- {
- next = (CDOResourceNode)rootResource.getContents().get(0);
- }
- else
- {
- next = ((CDOResourceFolder)next).getNodes().get(0);
- }
-
- nodesList.add(next);
- }
-
- resourceByLookup = (CDOResource)next;
- assertSame(resource, resourceByLookup);
- assertClean(resourceByLookup, transaction);
- assertEquals(true, resourceSet.getResources().contains(resourceByLookup));
-
- CDOObject cdoParent = null;
- CDOObject cdoRootResource = CDOUtil.getCDOObject(rootResource);
- for (int i = 0; i < depth; i++)
- {
- CDOResourceNode resourceNode = nodesList.get(i);
- CDOObject cdoResourceNode = CDOUtil.getCDOObject(resourceNode);
-
- if (i == 0)
- {
- assertEquals(cdoRootResource.cdoID(), cdoResourceNode.cdoRevision().data().getResourceID());
- assertEquals(CDOID.NULL, cdoResourceNode.cdoRevision().data().getContainerID());
- }
- else
- {
- assertEquals(CDOID.NULL, cdoResourceNode.cdoRevision().data().getResourceID());
- assertEquals(cdoParent.cdoID(), cdoResourceNode.cdoRevision().data().getContainerID());
- }
-
- cdoParent = cdoResourceNode;
- }
-
- if (callDelete)
- {
- resource.delete(null);
- depthtoRemove = depth;
- }
- else
- {
- CDOResourceNode node = nodesList.get(depthtoRemove);
- if (depthtoRemove == 0)
- {
- rootResource.getContents().remove(node);
- }
- else
- {
- CDOResourceFolder parentFolder = (CDOResourceFolder)nodesList.get(depthtoRemove - 1);
- assertEquals(parentFolder, node.getFolder());
- parentFolder.getNodes().remove(node);
- }
- }
-
- for (int i = depthtoRemove; i < depth; i++)
- {
- CDOResourceNode transientNode = nodesList.get(i);
- assertTransient(transientNode);
- if (transientNode instanceof CDOResource)
- {
- assertEquals(false, resourceSet.getResources().contains(transientNode));
- }
-
- assertEquals(null, transientNode.eResource());
- if (i == depthtoRemove)
- {
- assertEquals(null, transientNode.eContainer());
- }
- else
- {
- assertEquals(cdoParent, transientNode.eContainer());
- }
-
- cdoParent = transientNode;
- }
-
- transaction.commit();
- }
-
- private void changePath(int depthFrom, int depthTo) throws Exception
- {
- String prefixA = "testA";
- String prefixB = "testB";
-
- String oldPath = createPath(prefixA, depthFrom, "test");
- String newPath = createPath(prefixB, depthTo, "test2");
- {
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
- CDOResource resource = transaction.createResource(oldPath);
- Order order = getModel1Factory().createPurchaseOrder();
- resource.getContents().add(order);
-
- String path = CDOURIUtil.extractResourcePath(resource.getURI());
- assertEquals(oldPath, path);
- assertEquals(depthFrom, CDOURIUtil.analyzePath(resource.getURI()).size() - 1);
-
- transaction.commit();
-
- CDOID idBeforeChangePath = CDOUtil.getCDOObject(resource).cdoID();
- CDOID idBeforeChangePathOrder = CDOUtil.getCDOObject(order).cdoID();
-
- msg("New path");
- resource.setPath(newPath);
- path = CDOURIUtil.extractResourcePath(resource.getURI());
- assertEquals(depthTo, CDOURIUtil.analyzePath(resource.getURI()).size() - 1);
- assertEquals(newPath, path);
- transaction.commit();
-
- CDOID idAfterChangePath = CDOUtil.getCDOObject(resource).cdoID();
- assertEquals(idBeforeChangePath, idAfterChangePath);
-
- CDOID idAfterChangePathOrder = CDOUtil.getCDOObject(order).cdoID();
- assertEquals(idBeforeChangePathOrder, idAfterChangePathOrder);
-
- Resource resourceRenamed = transaction.getResourceSet().getResource(
- CDOURIUtil.createResourceURI(session, newPath), false);
-
- assertEquals(resource, resourceRenamed);
- assertClean(resource, transaction);
- assertClean(order, transaction);
- session.close();
- }
-
- clearCache(getRepository().getRevisionManager());
- CDOSession session = openSession();
- CDOTransaction transaction = session.openTransaction();
-
- try
- {
- URI uri = CDOURIUtil.createResourceURI(session, oldPath);
- transaction.getResourceSet().getResource(uri, true);
- fail("Doesn't exist");
- }
- catch (Exception ex)
- {
- }
-
- Resource resource = transaction.getResourceSet().getResource(CDOURIUtil.createResourceURI(session, newPath), true);
- assertNotNull(resource);
- }
-
- private String createPath(String namePrefix, int depth, String name)
- {
- String path = "";
- for (int i = 0; i < depth; i++)
- {
- String localName = namePrefix + String.valueOf(i + 1);
- path += "/" + localName;
- }
-
- path += "/" + name;
- return path;
- }
-
- private CDOResource createResource(CDOTransaction transaction, String path)
- {
- Product1 p = getModel1Factory().createProduct1();
- p.setName("test-" + path);
- p.setVat(VAT.VAT0);
-
- CDOResource resource = transaction.createResource(getResourcePath(path));
- resource.getContents().add(p);
- return resource;
- }
-
- private CDOResource modifyResource(CDOTransaction transaction, String path)
- {
- Product1 p = getModel1Factory().createProduct1();
- p.setName("test-" + path + "-modified");
- p.setVat(VAT.VAT0);
-
- CDOResource resource = transaction.getResource(getResourcePath(path));
- resource.getContents().add(p);
- return resource;
- }
-
- private void queryResources(CDOView view, String namePrefix, int expected)
- {
- msg("Name prefix: " + namePrefix);
- CDOResourceFolder folder = (CDOResourceFolder)view.getResourceNode(getResourcePath(null));
- List<CDOResourceNode> nodes = view.queryResources(folder, namePrefix, false);
- for (CDOResourceNode node : nodes)
- {
- msg("Result: " + node.getPath());
- }
-
- assertEquals(expected, nodes.size());
- }
-
- /**
- * See bug 353249.
- */
- public void testGetResourceNodeContract()
- {
- CDOView view = openSession().openView();
-
- try
- {
- view.getResourceNode("SomePath/That/DoesntExist");
- fail("Exception expected");
- }
- catch (Exception expected)
- {
- // SUCCCESS
- }
- }
-
- /**
- * @author Eike Stepper
- */
- private static class TestAdapter extends AdapterImpl
- {
- @Override
- public void notifyChanged(Notification msg)
- {
- super.notifyChanged(msg);
- if (msg.getNewValue() instanceof CDOResource)
- {
- ((CDOResource)msg.getNewValue()).getPath();
- }
- }
-
- @Override
- public void setTarget(Notifier newTarget)
- {
- }
-
- @Override
- public boolean isAdapterForType(Object type)
- {
- return super.isAdapterForType(type);
- }
- }
-}
+/* + * 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 + */ +package org.eclipse.emf.cdo.tests; + +import org.eclipse.emf.cdo.CDOObject; +import org.eclipse.emf.cdo.CDOState; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.eresource.CDOResource; +import org.eclipse.emf.cdo.eresource.CDOResourceFolder; +import org.eclipse.emf.cdo.eresource.CDOResourceNode; +import org.eclipse.emf.cdo.session.CDOSession; +import org.eclipse.emf.cdo.tests.model1.Category; +import org.eclipse.emf.cdo.tests.model1.Company; +import org.eclipse.emf.cdo.tests.model1.Order; +import org.eclipse.emf.cdo.tests.model1.OrderDetail; +import org.eclipse.emf.cdo.tests.model1.Product1; +import org.eclipse.emf.cdo.tests.model1.VAT; +import org.eclipse.emf.cdo.transaction.CDOTransaction; +import org.eclipse.emf.cdo.util.CDOURIUtil; +import org.eclipse.emf.cdo.util.CDOUtil; +import org.eclipse.emf.cdo.util.CommitException; +import org.eclipse.emf.cdo.util.ObjectNotFoundException; +import org.eclipse.emf.cdo.view.CDOView; + +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.emf.common.notify.impl.AdapterImpl; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.ecore.xmi.XMIResource; +import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl; + +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Eike Stepper + */ +public class ResourceTest extends AbstractCDOTest +{ + /** + * Test logic not up to date with the new xmi:id format. + */ + public void _testSaveXMI() throws Exception + { + ByteArrayOutputStream cdoOUT = new ByteArrayOutputStream(); + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.createResource(getResourcePath("/folder/res1")); + + counter = 0; + Category category = createCategoryTree(3); + resource.getContents().add(category); + + transaction.commit(); + resource.save(cdoOUT, null); + } + + ByteArrayOutputStream xmiOUT = new ByteArrayOutputStream(); + { + XMIResource resource = new XMIResourceImpl(URI.createFileURI("/folder/res1")); + + counter = 0; + Category category = createCategoryTree(3); + resource.getContents().add(category); + + resource.save(xmiOUT, null); + } + + String xmiString = xmiOUT.toString(); + msg("XMI:\n\n" + xmiString); + + String cdoString = cdoOUT.toString(); + msg("CDO:\n\n" + cdoString); + + assertEquals(xmiString, cdoString); + } + + public void testSaveXMI_WithXRef() throws Exception + { + ByteArrayOutputStream cdoOUT = new ByteArrayOutputStream(); + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.createResource(getResourcePath("/folder/res1")); + + counter = 0; + Category category = createCategoryTree(3); + resource.getContents().add(category); + + OrderDetail orderDetail = getModel1Factory().createOrderDetail(); + orderDetail.setPrice(147.111f); + orderDetail.setProduct(category.getProducts().get(0)); + resource.getContents().add(orderDetail); + + transaction.commit(); + resource.save(cdoOUT, null); + } + + ByteArrayOutputStream xmiOUT = new ByteArrayOutputStream(); + { + XMIResource resource = new XMIResourceImpl(URI.createFileURI("/folder/res1")); + + counter = 0; + Category category = createCategoryTree(3); + resource.getContents().add(category); + + OrderDetail orderDetail = getModel1Factory().createOrderDetail(); + orderDetail.setPrice(147.111f); + orderDetail.setProduct(category.getProducts().get(0)); + resource.getContents().add(orderDetail); + + resource.save(xmiOUT, null); + } + + String xmiString = xmiOUT.toString(); + msg("XMI:\n\n" + xmiString); + + String cdoString = cdoOUT.toString(); + msg("CDO:\n\n" + cdoString); + + // TODO assertEquals(xmiString, cdoString); + } + + public void testSaveXMI_WithXRef_OtherResource() throws Exception + { + ByteArrayOutputStream cdoOUT = new ByteArrayOutputStream(); + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.createResource(getResourcePath("/folder/res1")); + CDOResource resource2 = transaction.createResource(getResourcePath("/folder/res2")); + + counter = 0; + Category category = createCategoryTree(3); + resource.getContents().add(category); + + OrderDetail orderDetail = getModel1Factory().createOrderDetail(); + orderDetail.setPrice(147.111f); + orderDetail.setProduct(category.getProducts().get(0)); + resource2.getContents().add(orderDetail); + + transaction.commit(); + resource.save(cdoOUT, null); + resource2.save(cdoOUT, null); + } + + ByteArrayOutputStream xmiOUT = new ByteArrayOutputStream(); + { + XMIResource resource = new XMIResourceImpl(URI.createFileURI("/folder/res1")); + XMIResource resource2 = new XMIResourceImpl(URI.createFileURI("/folder/res2")); + + counter = 0; + Category category = createCategoryTree(3); + resource.getContents().add(category); + + OrderDetail orderDetail = getModel1Factory().createOrderDetail(); + orderDetail.setPrice(147.111f); + orderDetail.setProduct(category.getProducts().get(0)); + resource2.getContents().add(orderDetail); + + resource.save(xmiOUT, null); + resource2.save(xmiOUT, null); + } + + String xmiString = xmiOUT.toString(); + msg("XMI:\n\n" + xmiString); + + String cdoString = cdoOUT.toString(); + msg("CDO:\n\n" + cdoString); + + // TODO assertEquals(xmiString, cdoString); + } + + @CleanRepositoriesBefore + public void testAttachDetachResourceDepth1_Delete() throws Exception + { + attachDetachResourceDepth1(1, true, 0); + } + + @CleanRepositoriesBefore + public void testAttachDetachResourceDepth1_Remove() throws Exception + { + attachDetachResourceDepth1(1, false, 0); + } + + @CleanRepositoriesBefore + public void testAttachDetachResourceDepth2_Delete() throws Exception + { + attachDetachResourceDepth1(2, true, 1); + } + + @CleanRepositoriesBefore + public void testAttachDetachResourceDepth2_Remove() throws Exception + { + attachDetachResourceDepth1(2, false, 1); + } + + @CleanRepositoriesBefore + public void testAttachDetachResourceDepth3_Delete() throws Exception + { + attachDetachResourceDepth1(3, true, 2); + } + + @CleanRepositoriesBefore + public void testAttachDetachResourceDepth3_Remove() throws Exception + { + attachDetachResourceDepth1(3, false, 2); + } + + @CleanRepositoriesBefore + public void testAttachDetachResourceDepth3_Remove_Tree() throws Exception + { + attachDetachResourceDepth1(3, false, 1); + } + + public void testRootResourceFromURI() throws Exception + { + URI rootResourceURI = null; + URI resourceURI = null; + String resourcePath = getResourcePath("/test1"); + { + CDOSession session = openSession(); + ResourceSet resourceSet = new ResourceSetImpl(); + CDOTransaction transaction = session.openTransaction(resourceSet); + + resourceURI = URI.createURI("cdo:" + resourcePath); + Resource res1 = resourceSet.createResource(resourceURI); + + transaction.commit(); + rootResourceURI = EcoreUtil.getURI(transaction.getRootResource()); + resourceURI = EcoreUtil.getURI((EObject)res1); + } + + CDOSession session = openSession(); + ResourceSet resourceSet = new ResourceSetImpl(); + CDOTransaction transaction = session.openTransaction(resourceSet); + CDOResource rootResource = (CDOResource)resourceSet.getEObject(rootResourceURI, true); + assertProxy(rootResource); + assertSame(rootResource, transaction.getRootResource()); + + CDOResource resource = (CDOResource)resourceSet.getEObject(resourceURI, true); + assertClean(resource, transaction); + assertSame(resource, transaction.getResource(resourcePath)); + + transaction.close(); + session.close(); + } + + // public void testRootResourceParent() throws Exception + // { + // CDOSession session = openSession(); + // ResourceSet resourceSet = new ResourceSetImpl(); + // CDOTransaction transaction = session.openTransaction(resourceSet); + // CDOResource rootResource = transaction.getRootResource(); + // assertEquals(true, CDOIDUtil.isNull(rootResource.cdoRevision().data().getResourceID())); + // assertEquals(null, rootResource.eResource()); + // assertEquals(false, transaction.getResourceSet().getResources().contains(rootResource)); + // } + + @CleanRepositoriesBefore + public void testCreateResource_FromResourceSet() throws Exception + { + CDOSession session = openSession(); + ResourceSet resourceSet = new ResourceSetImpl(); + CDOTransaction transaction = session.openTransaction(resourceSet); + + final URI uri = URI.createURI("cdo:/test1"); + CDOResource resource = (CDOResource)resourceSet.createResource(uri); + assertActive(resource); + assertNew(resource, transaction); + assertEquals(transaction.getResourceSet(), resource.getResourceSet()); + assertEquals(CDOURIUtil.createResourceURI(session, "test1"), resource.getURI()); + assertEquals("test1", resource.getName()); + assertEquals(null, resource.getFolder()); + + transaction.getRootResource().getContents().contains(resource); + transaction.commit(); + + CDOObject cdoResource = resource; + CDOObject cdoRootResource = transaction.getRootResource(); + assertClean(cdoResource, transaction); + assertClean(cdoRootResource, transaction); + assertEquals(CDOID.NULL, cdoResource.cdoRevision().data().getContainerID()); + assertEquals(cdoRootResource.cdoID(), cdoResource.cdoRevision().data().getResourceID()); + assertEquals(cdoRootResource.cdoID(), cdoRootResource.cdoRevision().data().getResourceID()); + assertEquals(true, transaction.getResourceSet().getResources().contains(resource)); + assertEquals(false, transaction.getResourceSet().getResources().contains(transaction.getRootResource()));// Bug + // 346636 + + transaction.getRootResource().getContents().remove(resource); + assertEquals(false, transaction.getResourceSet().getResources().contains(resource)); + assertEquals(false, transaction.getResourceSet().getResources().contains(transaction.getRootResource()));// Bug + // 346636 + } + + public void testCreateNestedResource_FromResourceSet() throws Exception + { + CDOSession session = openSession(); + ResourceSet resourceSet = new ResourceSetImpl(); + CDOTransaction transaction = session.openTransaction(resourceSet); + + final URI uri = URI.createURI("cdo:/folder/test1"); + CDOResource resource = (CDOResource)resourceSet.createResource(uri); + assertActive(resource); + assertNew(resource, transaction); + assertEquals(transaction.getResourceSet(), resource.getResourceSet()); + assertEquals(CDOURIUtil.createResourceURI(session, "folder/test1"), resource.getURI()); + assertEquals("test1", resource.getName()); + + CDOResourceFolder folder = resource.getFolder(); + assertNotNull(folder); + assertEquals("folder", folder.getName()); + assertEquals(null, folder.getFolder()); + } + + public void testCreateResource_FromTransaction() throws Exception + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + + // Test if Resource is well-formed after CDOResourceFactoryImpl. + // Adapter will be called right after and could be used! + transaction.getResourceSet().eAdapters().add(new TestAdapter()); + + CDOResource resource = transaction.createResource(getResourcePath("/test1")); + assertActive(resource); + + CDOResource resourceCopy = transaction.getOrCreateResource(getResourcePath("/test1")); + assertEquals(resource, resourceCopy); + assertNew(resource, transaction); + assertEquals(CDOURIUtil.createResourceURI(session, getResourcePath("test1")), resource.getURI()); + assertEquals(transaction.getResourceSet(), resource.getResourceSet()); + } + + public void testCreateResource_WithDeepPath() throws Exception + { + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + transaction.createResource(getResourcePath("/org/eclipse/net4j/core")); + transaction.commit(); + session.close(); + } + + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath("/org/eclipse/net4j/core")); + assertEquals(CDOURIUtil.createResourceURI(session, getResourcePath("/org/eclipse/net4j/core")), resource.getURI()); + assertEquals(transaction.getResourceSet(), resource.getResourceSet()); + session.close(); + } + + public void testLoadAbsentResource_FromResourceSet() throws Exception + { + CDOSession session = openSession(); + ResourceSet resourceSet = new ResourceSetImpl(); + CDOTransaction transaction = session.openTransaction(resourceSet); + + final URI uri = URI.createURI("cdo:/test1"); + CDOResource resource = (CDOResource)resourceSet.getResource(uri, false); + assertEquals(null, resource); + + try + { + resourceSet.getResource(uri, true); + } + catch (Exception ignore) + { + } + + transaction.close(); + } + + public void testRemoveResourceWithCloseView() throws Exception + { + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + ResourceSet rset = transaction.getResourceSet(); + CDOResource resource = transaction.createResource(getResourcePath("/test1")); + assertActive(resource); + + transaction.commit(); + assertEquals(1, rset.getResources().size()); // Bug 346636 + assertEquals(1, CDOUtil.getViewSet(rset).getViews().length); + + transaction.close(); + assertEquals(0, CDOUtil.getViewSet(rset).getViews().length); + assertEquals(0, rset.getResources().size()); + session.close(); + } + + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = (CDOResource)transaction.getResourceSet().getResource( + CDOURIUtil.createResourceURI(transaction, getResourcePath("/test1")), true); + assertNotNull(resource); + assertEquals(transaction.getResourceSet(), resource.getResourceSet()); + assertEquals(1, transaction.getResourceSet().getResources().size()); + assertEquals(CDOState.PROXY, resource.cdoState()); + assertEquals(transaction, resource.cdoView()); + assertNull(resource.cdoRevision()); + } + } + + public void testAttachResource() throws Exception + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + + transaction.createResource(getResourcePath("/my/resource1")); + assertEquals(1, transaction.getResourceSet().getResources().size());// Bug 346636 + + transaction.commit(); + session.close(); + } + + public void testCommitMultipleResources() throws CommitException + { + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + transaction.createResource(getResourcePath("/level1/level2-A/level3")); + transaction.createResource(getResourcePath("/level1/level2-B/level3")); + transaction.commit(); + session.close(); + } + + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource1 = transaction.getResource(getResourcePath("/level1/level2-A/level3")); + CDOResource resource2 = transaction.getResource(getResourcePath("/level1/level2-B/level3")); + assertEquals(getResourcePath("/level1/level2-A/level3"), resource1.getPath()); + assertEquals(getResourcePath("/level1/level2-B/level3"), resource2.getPath()); + session.close(); + } + + public void testLoadMultipleResources() throws CommitException + { + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + transaction.createResource(getResourcePath("/level1/level2-A/level3")); + transaction.createResource(getResourcePath("/level1/level2-B/level3")); + transaction.commit(); + session.close(); + } + + clearCache(getRepository().getRevisionManager()); + + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource1 = transaction.getResource(getResourcePath("/level1/level2-A/level3")); + CDOResource resource2 = transaction.getResource(getResourcePath("/level1/level2-B/level3")); + assertEquals(getResourcePath("/level1/level2-A/level3"), resource1.getPath()); + assertEquals(getResourcePath("/level1/level2-B/level3"), resource2.getPath()); + session.close(); + } + + public void testDuplicatePath() throws Exception + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + transaction.createResource(getResourcePath("/my/resource")); + transaction.commit(); + + transaction.createResource(getResourcePath("/my/resource")); + + try + { + transaction.commit(); + fail("CommitException expected"); + } + catch (CommitException expected) + { + // Success + } + finally + { + session.close(); + } + } + + public void testDuplicatePathAfterDetach() throws Exception + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.createResource(getResourcePath("/my/resource")); + transaction.commit(); + + resource.delete(null); + transaction.commit(); + + transaction.createResource(getResourcePath("/my/resource")); + transaction.commit(); + session.close(); + } + + @CleanRepositoriesBefore + public void testChangePathFromDepth0ToDepth0() throws Exception + { + changePath(0, 0); + } + + @CleanRepositoriesBefore + public void testChangePathFromDepth0ToDepth1() throws Exception + { + changePath(0, 1); + } + + @CleanRepositoriesBefore + public void testChangePathFromDepth0ToDepth2() throws Exception + { + changePath(0, 2); + } + + @CleanRepositoriesBefore + public void testChangePathFromDepth0ToDepth3() throws Exception + { + changePath(0, 3); + } + + @CleanRepositoriesBefore + public void testChangePathFromDepth3ToDepth3() throws Exception + { + changePath(3, 3); + } + + @CleanRepositoriesBefore + public void testChangePathFromDepth3ToDepth2() throws Exception + { + changePath(3, 2); + } + + @CleanRepositoriesBefore + public void testChangePathFromDepth3ToDepth1() throws Exception + { + changePath(3, 1); + } + + @CleanRepositoriesBefore + public void testChangePathFromDepth3ToDepth0() throws Exception + { + changePath(3, 0); + } + + public void testChangeResourceURI() throws Exception + { + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.createResource(getResourcePath("/my/resource")); + transaction.commit(); + + URI uri = URI.createURI("cdo://repo1/renamed"); + assertEquals(CDOURIUtil.createResourceURI(session, "/renamed"), uri); + resource.setURI(uri); + + transaction.commit(); + session.close(); + } + + clearCache(getRepository().getRevisionManager()); + + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + + assertEquals(false, transaction.hasResource("/my/resource")); + assertEquals(true, transaction.hasResource("/renamed")); + } + + @CleanRepositoriesBefore + public void testChangeResourceFolderURI() throws Exception + { + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.createResource(getResourcePath("/my/resource")); + transaction.commit(); + + URI uri = URI.createURI("cdo://repo1/renamed"); + assertEquals(CDOURIUtil.createResourceURI(session, "/renamed"), uri); + resource.setURI(uri); + + transaction.commit(); + session.close(); + } + + clearCache(getRepository().getRevisionManager()); + + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + + assertEquals(false, transaction.hasResource("/my/resource")); + assertEquals(true, transaction.hasResource("/renamed")); + } + + public void testPathNotNull() throws Exception + { + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.createResource(getResourcePath("/res1")); + assertEquals(getResourcePath("/res1"), resource.getPath()); + assertEquals(CDOURIUtil.createResourceURI(session, getResourcePath("/res1")), resource.getURI()); + + transaction.commit(); + assertEquals(getResourcePath("/res1"), resource.getPath()); + assertEquals(CDOURIUtil.createResourceURI(session, getResourcePath("/res1")), resource.getURI()); + session.close(); + } + + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath("/res1")); + assertEquals(getResourcePath("/res1"), resource.getPath()); + assertEquals(CDOURIUtil.createResourceURI(session, getResourcePath("/res1")), resource.getURI()); + + CDOResource resource2 = transaction.getOrCreateResource(getResourcePath("/res2")); + assertEquals(getResourcePath("/res2"), resource2.getPath()); + assertEquals(CDOURIUtil.createResourceURI(session, getResourcePath("/res2")), resource2.getURI()); + + transaction.commit(); + assertEquals(getResourcePath("/res2"), resource2.getPath()); + assertEquals(CDOURIUtil.createResourceURI(session, getResourcePath("/res2")), resource2.getURI()); + session.close(); + } + + { + CDOSession session = openSession(); + CDOView view = session.openView(); + CDOResource resource2 = view.getResource(getResourcePath("/res2")); + assertEquals(getResourcePath("/res2"), resource2.getPath()); + assertEquals(CDOURIUtil.createResourceURI(session, getResourcePath("/res2")), resource2.getURI()); + session.close(); + } + } + + public void testPrefetchContents() throws Exception + { + { + Company company = getModel1Factory().createCompany(); + company.getCategories().add(createCategoryTree(3)); + + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + + CDOResource resource = transaction.createResource(getResourcePath("/res1")); + resource.getContents().add(company); + + transaction.commit(); + session.close(); + } + + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + + CDOResource resource = transaction.getResource(getResourcePath("/res1")); + resource.cdoPrefetch(CDORevision.DEPTH_INFINITE); + + Company company = (Company)resource.getContents().get(0); + System.out.println(company); + + session.close(); + } + + private static int counter; + + private Category createCategoryTree(int depth) + { + if (depth == 0) + { + return null; + } + + Category category = getModel1Factory().createCategory(); + for (int i = 0; i < 2; i++) + { + Category child = createCategoryTree(depth - 1); + if (child != null) + { + category.getCategories().add(child); + } + } + + for (int i = 0; i < 3; i++) + { + Product1 child = getModel1Factory().createProduct1(); + // generate a unique id + String id = "test " + depth + "_" + i + "_" + ++counter; + child.setName(id); + category.getProducts().add(child); + } + + return category; + } + + /** + * bug 208689 + */ + public void testQueryResources() throws Exception + { + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + createResource(transaction, "/aresource"); + createResource(transaction, "/aaresource"); + createResource(transaction, "/abresource"); + createResource(transaction, "/acresource"); + createResource(transaction, "/adresource"); + createResource(transaction, "/aeresource"); + createResource(transaction, "/bresource"); + createResource(transaction, "/baresource"); + createResource(transaction, "/bbresource"); + createResource(transaction, "/bcresource"); + createResource(transaction, "/bdresource"); + createResource(transaction, "/beresource"); + createResource(transaction, "/bearesource"); + createResource(transaction, "/bebresource"); + createResource(transaction, "/cresource"); + createResource(transaction, "/caresource"); + createResource(transaction, "/caresource2"); + createResource(transaction, "/caresource3"); + createResource(transaction, "/cbresource"); + createResource(transaction, "/ccresource"); + createResource(transaction, "/cdresource"); + createResource(transaction, "/ceresource"); + transaction.commit(); + session.close(); + } + + CDOSession session = openSession(); + CDOView view = session.openView(); + queryResources(view, "a", 6); + queryResources(view, "b", 8); + queryResources(view, "c", 8); + queryResources(view, "be", 3); + queryResources(view, "ca", 3); + session.close(); + } + + /** + * bug 208689 + */ + public void testQueryModifiedResources() throws Exception + { + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + createResource(transaction, "/aresource"); + createResource(transaction, "/aaresource"); + createResource(transaction, "/abresource"); + createResource(transaction, "/acresource"); + createResource(transaction, "/adresource"); + createResource(transaction, "/aeresource"); + createResource(transaction, "/bresource"); + createResource(transaction, "/baresource"); + createResource(transaction, "/bbresource"); + createResource(transaction, "/bcresource"); + createResource(transaction, "/bdresource"); + createResource(transaction, "/beresource"); + createResource(transaction, "/bearesource"); + createResource(transaction, "/bebresource"); + createResource(transaction, "/cresource"); + createResource(transaction, "/caresource"); + createResource(transaction, "/caresource2"); + createResource(transaction, "/caresource3"); + createResource(transaction, "/cbresource"); + createResource(transaction, "/ccresource"); + createResource(transaction, "/cdresource"); + createResource(transaction, "/ceresource"); + transaction.commit(); + modifyResource(transaction, "/aresource"); + modifyResource(transaction, "/aaresource"); + modifyResource(transaction, "/abresource"); + modifyResource(transaction, "/acresource"); + modifyResource(transaction, "/adresource"); + modifyResource(transaction, "/aeresource"); + modifyResource(transaction, "/bresource"); + modifyResource(transaction, "/baresource"); + modifyResource(transaction, "/bbresource"); + modifyResource(transaction, "/bcresource"); + modifyResource(transaction, "/bdresource"); + modifyResource(transaction, "/beresource"); + modifyResource(transaction, "/bearesource"); + modifyResource(transaction, "/bebresource"); + modifyResource(transaction, "/cresource"); + modifyResource(transaction, "/caresource"); + modifyResource(transaction, "/caresource2"); + modifyResource(transaction, "/caresource3"); + modifyResource(transaction, "/cbresource"); + modifyResource(transaction, "/ccresource"); + modifyResource(transaction, "/cdresource"); + modifyResource(transaction, "/ceresource"); + transaction.commit(); + session.close(); + } + + CDOSession session = openSession(); + CDOView view = session.openView(); + queryResources(view, "a", 6); + queryResources(view, "b", 8); + queryResources(view, "c", 8); + queryResources(view, "be", 3); + queryResources(view, "ca", 3); + session.close(); + } + + public void testDeleteResource() throws Exception + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + + CDOResource resource = createResource(transaction, "/resource1"); + CDOID resourceID = resource.cdoID(); + + CDOObject object = CDOUtil.getCDOObject(resource.getContents().get(0)); + CDOID objectID = object.cdoID(); + + transaction.commit(); + resource.delete(null); + transaction.commit(); + transaction.close(); + + CDOView view = session.openView(); + assertEquals(false, view.hasResource("/resource1")); + + try + { + view.getResourceNode("/resource1"); + fail("Exception expected"); + } + catch (Exception expected) + { + } + + try + { + view.getResource(getResourcePath("/resource1")); + fail("Exception expected"); + } + catch (Exception expected) + { + } + + try + { + view.getObject(resourceID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + try + { + view.getObject(objectID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + session.close(); + } + + public void testDeleteResourceFresh() throws Exception + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + + CDOResource resource = createResource(transaction, "/resource1"); + CDOID resourceID = resource.cdoID(); + + CDOObject object = CDOUtil.getCDOObject(resource.getContents().get(0)); + CDOID objectID = object.cdoID(); + + transaction.commit(); + resource.delete(null); + transaction.commit(); + transaction.close(); + + clearCache(getRepository().getRevisionManager()); + CDOView view = session.openView(); + assertEquals(false, view.hasResource("/resource1")); + + try + { + view.getResourceNode("/resource1"); + fail("Exception expected"); + } + catch (Exception expected) + { + } + + try + { + view.getResource(getResourcePath("/resource1")); + fail("Exception expected"); + } + catch (Exception expected) + { + } + + try + { + view.getObject(resourceID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + try + { + view.getObject(objectID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + session.close(); + } + + public void testDeleteResourceDifferentSession() throws Exception + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + + CDOSession session2 = openSession(); + CDOView view = session2.openView(); + + CDOResource resource = createResource(transaction, "/resource1"); + CDOID resourceID = resource.cdoID(); + + CDOObject object = CDOUtil.getCDOObject(resource.getContents().get(0)); + CDOID objectID = object.cdoID(); + + transaction.commit(); + assertEquals(true, view.hasResource(getResourcePath("/resource1"))); + assertEquals(resource.getURI(), view.getResource(getResourcePath("/resource1")).getURI()); + + resource.delete(null); + commitAndSync(transaction, view); + transaction.close(); + + assertEquals(false, view.hasResource("/resource1")); + + try + { + view.getResourceNode("/resource1"); + fail("Exception expected"); + } + catch (Exception expected) + { + } + + try + { + view.getResource(getResourcePath("/resource1")); + fail("Exception expected"); + } + catch (Exception expected) + { + } + + try + { + view.getObject(resourceID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + try + { + view.getObject(objectID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + session.close(); + } + + public void testDeleteResourceDifferentSessionFresh() throws Exception + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + + CDOSession session2 = openSession(); + CDOView view = session2.openView(); + + CDOResource resource = createResource(transaction, "/resource1"); + CDOID resourceID = resource.cdoID(); + + CDOObject object = CDOUtil.getCDOObject(resource.getContents().get(0)); + CDOID objectID = object.cdoID(); + + transaction.commit(); + assertEquals(true, view.hasResource(getResourcePath("/resource1"))); + assertEquals(resource.getURI(), view.getResource(getResourcePath("/resource1")).getURI()); + + resource.delete(null); + commitAndSync(transaction, view); + transaction.close(); + + clearCache(getRepository().getRevisionManager()); + assertEquals(false, view.hasResource("/resource1")); + + try + { + view.getResourceNode("/resource1"); + fail("Exception expected"); + } + catch (Exception expected) + { + } + + try + { + CDOResource resource1 = view.getResource(getResourcePath("/resource1")); + assertNull(resource1); + fail("Exception expected"); + // TODO Fails on automated build: + // junit.framework.AssertionFailedError: Exception expected + // at org.eclipse.emf.cdo.tests.ResourceTest.testDeleteResourceDifferentSessionFresh(ResourceTest.java:859) + // at org.eclipse.net4j.tests.AbstractOMTest.runBare(AbstractOMTest.java:86) + // at org.eclipse.net4j.tests.AbstractOMTest.run(AbstractOMTest.java:108) + // at org.eclipse.emf.cdo.tests.config.impl.ConfigTestSuite$TestWrapper.runTest(ConfigTestSuite.java:126) + // at org.eclipse.test.EclipseTestRunner.run(EclipseTestRunner.java:332) + // at org.eclipse.test.EclipseTestRunner.run(EclipseTestRunner.java:202) + // at org.eclipse.test.CoreTestApplication.runTests(CoreTestApplication.java:35) + // at org.eclipse.test.CoreTestApplication.run(CoreTestApplication.java:31) + // at org.eclipse.equinox.internal.app.EclipseAppContainer.callMethodWithException(EclipseAppContainer.java:574) + // at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196) + // at org.eclipse.equinox.internal.app.MainApplicationLauncher.run(MainApplicationLauncher.java:32) + // at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110) + // at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79) + // at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:368) + // at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179) + // at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:559) + // at org.eclipse.equinox.launcher.Main.basicRun(Main.java:514) + // at org.eclipse.equinox.launcher.Main.run(Main.java:1311) + // at org.eclipse.equinox.launcher.Main.main(Main.java:1287) + // at org.eclipse.core.launcher.Main.main(Main.java:34) + } + catch (Exception expected) + { + expected.printStackTrace(); + } + + try + { + view.getObject(resourceID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + try + { + view.getObject(objectID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + session.close(); + } + + public void testDeleteResourceFolder() throws Exception + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = createResource(transaction, "/folder/resource1"); + CDOObject object = CDOUtil.getCDOObject(resource.getContents().get(0)); + transaction.commit(); + + CDOResourceFolder folder = resource.getFolder(); + CDOID folderID = folder.cdoID(); + CDOID resourceID = resource.cdoID(); + CDOID objectID = object.cdoID(); + + folder.delete(null); + transaction.commit(); + transaction.close(); + + CDOView view = session.openView(); + assertEquals(false, view.hasResource("/folder/resource1")); + + try + { + view.getResourceNode("/folder"); + fail("Exception expected"); + } + catch (Exception expected) + { + } + + try + { + view.getResourceNode("/folder/resource1"); + fail("Exception expected"); + } + catch (Exception expected) + { + } + + try + { + view.getResource(getResourcePath("/folder/resource1")); + fail("Exception expected"); + } + catch (Exception expected) + { + } + + try + { + view.getObject(folderID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + try + { + view.getObject(resourceID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + try + { + view.getObject(objectID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + session.close(); + } + + public void testDeleteResourceFolderFresh() throws Exception + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = createResource(transaction, "/folder/resource1"); + CDOObject object = CDOUtil.getCDOObject(resource.getContents().get(0)); + transaction.commit(); + + CDOResourceFolder folder = resource.getFolder(); + CDOID folderID = folder.cdoID(); + CDOID resourceID = resource.cdoID(); + CDOID objectID = object.cdoID(); + + folder.delete(null); + transaction.commit(); + transaction.close(); + + clearCache(getRepository().getRevisionManager()); + CDOView view = session.openView(); + assertEquals(false, view.hasResource("/folder/resource1")); + + try + { + view.getResourceNode("/folder"); + fail("Exception expected"); + } + catch (Exception expected) + { + } + + try + { + view.getResourceNode("/folder/resource1"); + fail("Exception expected"); + } + catch (Exception expected) + { + } + + try + { + view.getResource(getResourcePath("/folder/resource1")); + fail("Exception expected"); + } + catch (Exception expected) + { + } + + try + { + view.getObject(folderID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + try + { + view.getObject(resourceID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + try + { + view.getObject(objectID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + session.close(); + } + + public void testDeleteResourceFolderDifferentSession() throws Exception + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + + CDOSession session2 = openSession(); + CDOView view = session2.openView(); + + CDOResource resource = createResource(transaction, "/folder/resource1"); + CDOResourceFolder folder = resource.getFolder(); + CDOObject object = CDOUtil.getCDOObject(resource.getContents().get(0)); + + transaction.commit(); + assertEquals(true, view.hasResource(getResourcePath("/folder/resource1"))); + assertEquals(resource.getURI(), view.getResource(getResourcePath("/folder/resource1")).getURI()); + + CDOID folderID = folder.cdoID(); + CDOID resourceID = resource.cdoID(); + CDOID objectID = object.cdoID(); + + folder.delete(null); + commitAndSync(transaction, view); + transaction.close(); + + assertEquals(false, view.hasResource("/folder/resource1")); + + try + { + view.getResourceNode("/folder"); + fail("Exception expected"); + } + catch (Exception expected) + { + } + + try + { + view.getResourceNode("/folder/resource1"); + fail("Exception expected"); + } + catch (Exception expected) + { + } + + try + { + view.getResource(getResourcePath("/folder/resource1")); + fail("Exception expected"); + } + catch (Exception expected) + { + } + + try + { + view.getObject(folderID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + try + { + view.getObject(resourceID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + try + { + view.getObject(objectID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + session.close(); + } + + public void testDeleteResourceFolderDifferentSessionFresh() throws Exception + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + + CDOSession session2 = openSession(); + CDOView view = session2.openView(); + + CDOResource resource = createResource(transaction, "/folder/resource1"); + CDOResourceFolder folder = resource.getFolder(); + CDOObject object = CDOUtil.getCDOObject(resource.getContents().get(0)); + + transaction.commit(); + assertEquals(true, view.hasResource(getResourcePath("/folder/resource1"))); + assertEquals(resource.getURI(), view.getResource(getResourcePath("/folder/resource1")).getURI()); + + CDOID folderID = folder.cdoID(); + CDOID resourceID = resource.cdoID(); + CDOID objectID = object.cdoID(); + + folder.delete(null); + commitAndSync(transaction, view); + transaction.close(); + + clearCache(getRepository().getRevisionManager()); + assertEquals(false, view.hasResource("/folder/resource1")); + + try + { + view.getResourceNode("/folder"); + fail("Exception expected"); + } + catch (Exception expected) + { + } + + try + { + view.getResourceNode("/folder/resource1"); + fail("Exception expected"); + } + catch (Exception expected) + { + } + + try + { + view.getResource(getResourcePath("/folder/resource1")); + // TODO Fails on automated build + fail("Exception expected"); + } + catch (Exception expected) + { + } + + try + { + view.getObject(folderID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + try + { + view.getObject(resourceID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + try + { + view.getObject(objectID); + fail("ObjectNotFoundException expected"); + } + catch (ObjectNotFoundException expected) + { + } + + session.close(); + } + + /** + * Create resource with the following pattern /test1/test2/test3 for a depth 3. <br> + * After it will remove the resource with the following rule:<br> + * if calldelete is true <code>resource.delete(null)</code> <br> + * if calldelete is false it will use the depthtoRemove to call <code>object.remove(resource)</code><br> + * deptToRemove = /0/1/2/...<br> + * It will remove it from parent folder (depthtoRemove - 1); + */ + private void attachDetachResourceDepth1(int depth, boolean callDelete, int depthtoRemove) throws Exception + { + CDOSession session = openSession(); + ResourceSet resourceSet = new ResourceSetImpl(); + CDOTransaction transaction = session.openTransaction(resourceSet); + CDOResource rootResource = transaction.getRootResource(); + assertSame(rootResource, rootResource.eResource()); + String path = ""; + List<String> names = new ArrayList<String>(); + for (int i = 0; i < depth; i++) + { + String name = "test" + String.valueOf(i + 1); + names.add(name); + path += "/" + name; + } + + final URI uri = URI.createURI("cdo:" + path); + CDOResource resource = (CDOResource)resourceSet.createResource(uri); + assertEquals(names.get(names.size() - 1), resource.getName()); + + transaction.commit(); + List<CDOResourceNode> nodesList = new ArrayList<CDOResourceNode>(); + CDOResource resourceByLookup = null; + CDOResourceNode next = null; + for (int i = 0; i < depth; i++) + { + if (i == 0) + { + next = (CDOResourceNode)rootResource.getContents().get(0); + } + else + { + next = ((CDOResourceFolder)next).getNodes().get(0); + } + + nodesList.add(next); + } + + resourceByLookup = (CDOResource)next; + assertSame(resource, resourceByLookup); + assertClean(resourceByLookup, transaction); + assertEquals(true, resourceSet.getResources().contains(resourceByLookup)); + + CDOObject cdoParent = null; + CDOObject cdoRootResource = CDOUtil.getCDOObject(rootResource); + for (int i = 0; i < depth; i++) + { + CDOResourceNode resourceNode = nodesList.get(i); + CDOObject cdoResourceNode = CDOUtil.getCDOObject(resourceNode); + + if (i == 0) + { + assertEquals(cdoRootResource.cdoID(), cdoResourceNode.cdoRevision().data().getResourceID()); + assertEquals(CDOID.NULL, cdoResourceNode.cdoRevision().data().getContainerID()); + } + else + { + assertEquals(CDOID.NULL, cdoResourceNode.cdoRevision().data().getResourceID()); + assertEquals(cdoParent.cdoID(), cdoResourceNode.cdoRevision().data().getContainerID()); + } + + cdoParent = cdoResourceNode; + } + + if (callDelete) + { + resource.delete(null); + depthtoRemove = depth; + } + else + { + CDOResourceNode node = nodesList.get(depthtoRemove); + if (depthtoRemove == 0) + { + rootResource.getContents().remove(node); + } + else + { + CDOResourceFolder parentFolder = (CDOResourceFolder)nodesList.get(depthtoRemove - 1); + assertEquals(parentFolder, node.getFolder()); + parentFolder.getNodes().remove(node); + } + } + + for (int i = depthtoRemove; i < depth; i++) + { + CDOResourceNode transientNode = nodesList.get(i); + assertTransient(transientNode); + if (transientNode instanceof CDOResource) + { + assertEquals(false, resourceSet.getResources().contains(transientNode)); + } + + assertEquals(null, transientNode.eResource()); + if (i == depthtoRemove) + { + assertEquals(null, transientNode.eContainer()); + } + else + { + assertEquals(cdoParent, transientNode.eContainer()); + } + + cdoParent = transientNode; + } + + transaction.commit(); + } + + private void changePath(int depthFrom, int depthTo) throws Exception + { + String prefixA = "testA"; + String prefixB = "testB"; + + String oldPath = createPath(prefixA, depthFrom, "test"); + String newPath = createPath(prefixB, depthTo, "test2"); + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.createResource(oldPath); + Order order = getModel1Factory().createPurchaseOrder(); + resource.getContents().add(order); + + String path = CDOURIUtil.extractResourcePath(resource.getURI()); + assertEquals(oldPath, path); + assertEquals(depthFrom, CDOURIUtil.analyzePath(resource.getURI()).size() - 1); + + transaction.commit(); + + CDOID idBeforeChangePath = CDOUtil.getCDOObject(resource).cdoID(); + CDOID idBeforeChangePathOrder = CDOUtil.getCDOObject(order).cdoID(); + + msg("New path"); + resource.setPath(newPath); + path = CDOURIUtil.extractResourcePath(resource.getURI()); + assertEquals(depthTo, CDOURIUtil.analyzePath(resource.getURI()).size() - 1); + assertEquals(newPath, path); + transaction.commit(); + + CDOID idAfterChangePath = CDOUtil.getCDOObject(resource).cdoID(); + assertEquals(idBeforeChangePath, idAfterChangePath); + + CDOID idAfterChangePathOrder = CDOUtil.getCDOObject(order).cdoID(); + assertEquals(idBeforeChangePathOrder, idAfterChangePathOrder); + + Resource resourceRenamed = transaction.getResourceSet().getResource( + CDOURIUtil.createResourceURI(session, newPath), false); + + assertEquals(resource, resourceRenamed); + assertClean(resource, transaction); + assertClean(order, transaction); + session.close(); + } + + clearCache(getRepository().getRevisionManager()); + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + + try + { + URI uri = CDOURIUtil.createResourceURI(session, oldPath); + transaction.getResourceSet().getResource(uri, true); + fail("Doesn't exist"); + } + catch (Exception ex) + { + } + + Resource resource = transaction.getResourceSet().getResource(CDOURIUtil.createResourceURI(session, newPath), true); + assertNotNull(resource); + } + + private String createPath(String namePrefix, int depth, String name) + { + String path = ""; + for (int i = 0; i < depth; i++) + { + String localName = namePrefix + String.valueOf(i + 1); + path += "/" + localName; + } + + path += "/" + name; + return path; + } + + private CDOResource createResource(CDOTransaction transaction, String path) + { + Product1 p = getModel1Factory().createProduct1(); + p.setName("test-" + path); + p.setVat(VAT.VAT0); + + CDOResource resource = transaction.createResource(getResourcePath(path)); + resource.getContents().add(p); + return resource; + } + + private CDOResource modifyResource(CDOTransaction transaction, String path) + { + Product1 p = getModel1Factory().createProduct1(); + p.setName("test-" + path + "-modified"); + p.setVat(VAT.VAT0); + + CDOResource resource = transaction.getResource(getResourcePath(path)); + resource.getContents().add(p); + return resource; + } + + private void queryResources(CDOView view, String namePrefix, int expected) + { + msg("Name prefix: " + namePrefix); + CDOResourceFolder folder = (CDOResourceFolder)view.getResourceNode(getResourcePath(null)); + List<CDOResourceNode> nodes = view.queryResources(folder, namePrefix, false); + for (CDOResourceNode node : nodes) + { + msg("Result: " + node.getPath()); + } + + assertEquals(expected, nodes.size()); + } + + /** + * See bug 353249. + */ + public void testGetResourceNodeContract() + { + CDOView view = openSession().openView(); + + try + { + view.getResourceNode("SomePath/That/DoesntExist"); + fail("Exception expected"); + } + catch (Exception expected) + { + // SUCCCESS + } + } + + /** + * @author Eike Stepper + */ + private static class TestAdapter extends AdapterImpl + { + @Override + public void notifyChanged(Notification msg) + { + super.notifyChanged(msg); + if (msg.getNewValue() instanceof CDOResource) + { + ((CDOResource)msg.getNewValue()).getPath(); + } + } + + @Override + public void setTarget(Notifier newTarget) + { + } + + @Override + public boolean isAdapterForType(Object type) + { + return super.isAdapterForType(type); + } + } +} diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_314264_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_314264_Test.java index 91c50e36bf..40ae949137 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_314264_Test.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_314264_Test.java @@ -1,147 +1,171 @@ -/*
- * 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.bugzilla;
-
-import org.eclipse.emf.cdo.common.branch.CDOBranch;
-import org.eclipse.emf.cdo.eresource.CDOResource;
-import org.eclipse.emf.cdo.session.CDOSession;
-import org.eclipse.emf.cdo.tests.AbstractCDOTest;
-import org.eclipse.emf.cdo.tests.config.IRepositoryConfig;
-import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.CleanRepositoriesBefore;
-import org.eclipse.emf.cdo.tests.model2.TaskContainer;
-import org.eclipse.emf.cdo.tests.util.TestAdapter;
-import org.eclipse.emf.cdo.transaction.CDOTransaction;
-import org.eclipse.emf.cdo.view.CDOAdapterPolicy;
-import org.eclipse.emf.cdo.view.CDOView;
-
-import org.eclipse.emf.common.notify.Notification;
-import org.eclipse.emf.spi.cdo.DefaultCDOMerger;
-
-/**
- * IndexOutOfBoundsException during merge.
- * <p>
- * See bug 314264
- */
-@CleanRepositoriesBefore
-public class Bugzilla_314264_Test extends AbstractCDOTest
-{
- @Requires(IRepositoryConfig.CAPABILITY_BRANCHING)
- @CleanRepositoriesBefore
- public void testMergeTest() throws Exception
- {
- // setup transaction.
- final CDOSession session = openSession();
- final CDOTransaction tr1 = session.openTransaction();
- tr1.options().addChangeSubscriptionPolicy(CDOAdapterPolicy.ALL);
-
- final CDOResource resource = tr1.createResource(getResourcePath("/test1"));
- TaskContainer container = getModel2Factory().createTaskContainer();
- resource.getContents().add(container);
-
- // add at least 2 elements to avoid getting a clear when removing one.
- container.getTasks().add(getModel2Factory().createTask());
- container.getTasks().add(getModel2Factory().createTask());
- tr1.commit();
-
- // sleep(1000);
-
- final CDOBranch otherBranch = tr1.getBranch().createBranch("other");
- final CDOTransaction tr2 = session.openTransaction(otherBranch);
-
- TaskContainer otherContainer = tr2.getObject(container);
- assertNotNull(otherContainer);
-
- // add a new element on other branch at index 0.
- otherContainer.getTasks().add(0, getModel2Factory().createTask());
-
- // remove an element on main branch at index 0.
- container.getTasks().remove(0);
-
- commitAndSync(tr1, tr2);
- commitAndSync(tr2, tr1);
-
- // merge the other branch to main.
- tr1.merge(tr2.getBranch().getHead(), new DefaultCDOMerger.PerFeature.ManyValued());
-
- tr1.commit();
- assertEquals(false, tr1.isDirty());
- }
-
- public void testNotificationBuilderTest() throws Exception
- {
- // setup transaction.
- final CDOSession session = openSession();
- final CDOTransaction tr1 = session.openTransaction();
- tr1.options().addChangeSubscriptionPolicy(CDOAdapterPolicy.ALL);
-
- final CDOResource resource = tr1.createResource(getResourcePath("/test1"));
- TaskContainer container = getModel2Factory().createTaskContainer();
- resource.getContents().add(container);
- tr1.commit();
-
- final BlockingResultContainer result = new BlockingResultContainer();
-
- // setup additional view.
- CDOView view = session.openView();
- view.options().addChangeSubscriptionPolicy(CDOAdapterPolicy.ALL);
-
- TestAdapter adapter = new TestAdapter()
- {
- private int counter;
-
- @Override
- public void notifyChanged(Notification notification)
- {
- if (counter != 0)
- {
- result.setResult(new Boolean(notification.getPosition() == 0));
- }
-
- counter++;
- }
- };
-
- TaskContainer containerObject = view.getObject(container);
- containerObject.eAdapters().add(adapter);
-
- // add elements at index 0 causing NotificationBuilder to patch indices beyond 0.
- container.getTasks().add(0, getModel2Factory().createTask());
- container.getTasks().add(0, getModel2Factory().createTask());
- tr1.commit();
-
- Boolean indexWasCorrect = (Boolean)result.getResult();
- assertEquals(true, indexWasCorrect != null && indexWasCorrect.booleanValue());
- }
-
- /**
- * @author Eike Stepper
- */
- private static class BlockingResultContainer
- {
- private Object result;
-
- public synchronized Object getResult() throws Exception
- {
- while (result == null)
- {
- wait(5000);
- }
-
- return result;
- }
-
- public synchronized void setResult(Object result)
- {
- this.result = result;
- notifyAll();
- }
- }
-}
+/* + * 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.bugzilla; + +import org.eclipse.emf.cdo.common.branch.CDOBranch; +import org.eclipse.emf.cdo.eresource.CDOResource; +import org.eclipse.emf.cdo.session.CDOSession; +import org.eclipse.emf.cdo.tests.AbstractCDOTest; +import org.eclipse.emf.cdo.tests.config.IRepositoryConfig; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.CleanRepositoriesBefore; +import org.eclipse.emf.cdo.tests.model2.TaskContainer; +import org.eclipse.emf.cdo.tests.util.TestAdapter; +import org.eclipse.emf.cdo.transaction.CDOTransaction; +import org.eclipse.emf.cdo.view.CDOAdapterPolicy; +import org.eclipse.emf.cdo.view.CDOView; + +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.spi.cdo.DefaultCDOMerger; + +/** + * IndexOutOfBoundsException during merge. + * <p> + * See bug 314264 + */ +@CleanRepositoriesBefore +public class Bugzilla_314264_Test extends AbstractCDOTest +{ + @Requires(IRepositoryConfig.CAPABILITY_BRANCHING) + public void testMerge() throws Exception + { + // Setup transaction. + CDOSession session = openSession(); + CDOTransaction tr1 = session.openTransaction(); + tr1.options().addChangeSubscriptionPolicy(CDOAdapterPolicy.ALL); + + CDOResource resource = tr1.createResource(getResourcePath("/test")); + TaskContainer container1 = getModel2Factory().createTaskContainer(); + resource.getContents().add(container1); + + // Add at least 2 elements to avoid getting a clear when removing one. + container1.getTasks().add(getModel2Factory().createTask()); + container1.getTasks().add(getModel2Factory().createTask()); + tr1.commit(); + + CDOBranch branch2 = tr1.getBranch().createBranch("branch2"); + CDOTransaction tr2 = session.openTransaction(branch2); + + TaskContainer container2 = tr2.getObject(container1); + assertNotNull(container2); + + // Add a new element on other branch at index 0. + container2.getTasks().add(0, getModel2Factory().createTask()); + + // Remove an element on main branch at index 0. + container1.getTasks().remove(0); + commitAndSync(tr1, tr2); + commitAndSync(tr2, tr1); + + // Merge the other branch to main. + tr1.merge(tr2.getBranch().getHead(), new DefaultCDOMerger.PerFeature.ManyValued()); + + tr1.commit(); + assertEquals(false, tr1.isDirty()); + } + + @Requires(IRepositoryConfig.CAPABILITY_BRANCHING) + public void testMerge1() throws Exception + { + // Try again after some warm up. See bug 383602. + testMerge(); + } + + @Requires(IRepositoryConfig.CAPABILITY_BRANCHING) + public void testMerge2() throws Exception + { + // Try again after some warm up. See bug 383602. + testMerge(); + } + + @Requires(IRepositoryConfig.CAPABILITY_BRANCHING) + public void testMerge3() throws Exception + { + // Try again after some warm up. See bug 383602. + testMerge(); + } + + @Requires(IRepositoryConfig.CAPABILITY_BRANCHING) + public void testMerge4() throws Exception + { + // Try again after some warm up. See bug 383602. + testMerge(); + } + + public void testNotificationBuilderTest() throws Exception + { + // Setup transaction. + final CDOSession session = openSession(); + final CDOTransaction tr1 = session.openTransaction(); + tr1.options().addChangeSubscriptionPolicy(CDOAdapterPolicy.ALL); + + final CDOResource resource = tr1.createResource(getResourcePath("/test1")); + TaskContainer container = getModel2Factory().createTaskContainer(); + resource.getContents().add(container); + tr1.commit(); + + final BlockingResultContainer result = new BlockingResultContainer(); + + // Setup additional view. + CDOView view = session.openView(); + view.options().addChangeSubscriptionPolicy(CDOAdapterPolicy.ALL); + + TestAdapter adapter = new TestAdapter() + { + private int counter; + + @Override + public void notifyChanged(Notification notification) + { + if (counter != 0) + { + result.setResult(new Boolean(notification.getPosition() == 0)); + } + + counter++; + } + }; + + TaskContainer containerObject = view.getObject(container); + containerObject.eAdapters().add(adapter); + + // Add elements at index 0 causing NotificationBuilder to patch indices beyond 0. + container.getTasks().add(0, getModel2Factory().createTask()); + container.getTasks().add(0, getModel2Factory().createTask()); + tr1.commit(); + + Boolean indexWasCorrect = (Boolean)result.getResult(); + assertEquals(true, indexWasCorrect != null && indexWasCorrect.booleanValue()); + } + + /** + * @author Eike Stepper + */ + private static class BlockingResultContainer + { + private Object result; + + public synchronized Object getResult() throws Exception + { + while (result == null) + { + wait(5000); + } + + return result; + } + + public synchronized void setResult(Object result) + { + this.result = result; + notifyAll(); + } + } +} diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/AbstractCDOView.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/AbstractCDOView.java index a78d061204..ba36fe8284 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/AbstractCDOView.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/AbstractCDOView.java @@ -58,6 +58,7 @@ import org.eclipse.emf.internal.cdo.messages.Messages; import org.eclipse.emf.internal.cdo.object.CDOLegacyAdapter; import org.eclipse.emf.internal.cdo.query.CDOQueryImpl; +import org.eclipse.net4j.util.CheckUtil; import org.eclipse.net4j.util.ImplementationError; import org.eclipse.net4j.util.ReflectUtil.ExcludeFromDump; import org.eclipse.net4j.util.StringUtil; @@ -214,8 +215,8 @@ public abstract class AbstractCDOView extends Lifecycle implements InternalCDOVi throw new IllegalStateException("RootResourceID is null; is the repository not yet initialized?"); } - CDOResourceImpl resource = (CDOResourceImpl)getObject(rootResourceID); - setRootResource(resource); + getObject(rootResourceID); + CheckUtil.checkState(rootResource, "rootResource"); } return rootResource; |