diff options
48 files changed, 2739 insertions, 394 deletions
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocolConstants.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocolConstants.java index d9196795c5..72fa700a76 100644 --- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocolConstants.java +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/protocol/CDOProtocolConstants.java @@ -30,8 +30,9 @@ public interface CDOProtocolConstants * @since 4.2 * @noreference This field is not intended to be referenced by clients. */ - public static final int PROTOCOL_VERSION = 28; // SIGNAL_RESET_TRANSACTION + public static final int PROTOCOL_VERSION = 29; // SIGNAL_UNIT + // public static final int PROTOCOL_VERSION = 28; // SIGNAL_RESET_TRANSACTION // public static final int PROTOCOL_VERSION = 27; // SIGNAL_OPENED_SESSION // public static final int PROTOCOL_VERSION = 26; // Add prefetch depth in LockStateRequest/Indication // public static final int PROTOCOL_VERSION = 25; // OpenSessionResponse.repositoryAuthenticating @@ -312,6 +313,11 @@ public interface CDOProtocolConstants */ public static final short SIGNAL_RESET_TRANSACTION = 61; + /** + * @since 4.5 + */ + public static final short SIGNAL_UNIT = 62; + // ////////////////////////////////////////////////////////////////////// // Session Refresh @@ -470,4 +476,27 @@ public interface CDOProtocolConstants * @since 4.3 */ public static final byte REVISION_DOES_NOT_EXIST = -1; + + // ////////////////////////////////////////////////////////////////////// + // Units + + /** + * @since 4.5 + */ + public static final byte UNIT_CHECK = 0; + + /** + * @since 4.5 + */ + public static final byte UNIT_CREATE = 1; + + /** + * @since 4.5 + */ + public static final byte UNIT_OPEN = 2; + + /** + * @since 4.5 + */ + public static final byte UNIT_CLOSE = 3; } diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java index 2e6f609394..f58ae8f414 100644 --- a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java +++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CDOClientProtocol.java @@ -503,6 +503,47 @@ public class CDOClientProtocol extends AuthenticatingSignalProtocol<CDOSessionIm return send(new LoadMergeDataRequest(this, targetInfo, sourceInfo, targetBaseInfo, sourceBaseInfo)); } + @Deprecated + public CDOLockState[] getLockStates(int viewID, Collection<CDOID> ids) + { + return getLockStates(viewID, ids, CDOLockState.DEPTH_NONE); + } + + public CDOLockState[] getLockStates(int viewID, Collection<CDOID> ids, int depth) + { + return send(new LockStateRequest(this, viewID, ids, depth)); + } + + public void enableLockNotifications(int viewID, boolean on) + { + send(new EnableLockNotificationRequest(this, viewID, on)); + } + + public void setLockNotificationMode(LockNotificationMode mode) + { + send(new SetLockNotificationModeRequest(this, mode)); + } + + public Map<CDORevision, CDOPermission> loadPermissions(InternalCDORevision[] revisions) + { + return send(new LoadPermissionsRequest(this, revisions)); + } + + public void requestChangeCredentials() + { + send(new ChangeCredentialsRequest(this, CredentialsUpdateOperation.CHANGE_PASSWORD, null), new Monitor()); + } + + public void requestResetCredentials(String userID) + { + send(new ChangeCredentialsRequest(this, CredentialsUpdateOperation.RESET_PASSWORD, userID), new Monitor()); + } + + public boolean requestUnit(int viewID, CDOID rootID, byte opcode, CDORevisionHandler revisionHandler) + { + return send(new UnitRequest(this, viewID, rootID, opcode, revisionHandler)); + } + @Override protected SignalReactor createSignalReactor(short signalID) { @@ -600,40 +641,4 @@ public class CDOClientProtocol extends AuthenticatingSignalProtocol<CDOSessionIm REVISION_LOADING.stop(request); } } - - @Deprecated - public CDOLockState[] getLockStates(int viewID, Collection<CDOID> ids) - { - return getLockStates(viewID, ids, CDOLockState.DEPTH_NONE); - } - - public CDOLockState[] getLockStates(int viewID, Collection<CDOID> ids, int depth) - { - return send(new LockStateRequest(this, viewID, ids, depth)); - } - - public void enableLockNotifications(int viewID, boolean on) - { - send(new EnableLockNotificationRequest(this, viewID, on)); - } - - public void setLockNotificationMode(LockNotificationMode mode) - { - send(new SetLockNotificationModeRequest(this, mode)); - } - - public Map<CDORevision, CDOPermission> loadPermissions(InternalCDORevision[] revisions) - { - return send(new LoadPermissionsRequest(this, revisions)); - } - - public void requestChangeCredentials() - { - send(new ChangeCredentialsRequest(this, CredentialsUpdateOperation.CHANGE_PASSWORD, null), new Monitor()); - } - - public void requestResetCredentials(String userID) - { - send(new ChangeCredentialsRequest(this, CredentialsUpdateOperation.RESET_PASSWORD, userID), new Monitor()); - } } diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnitRequest.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnitRequest.java new file mode 100644 index 0000000000..01e23d3559 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/UnitRequest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2016 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class UnitRequest extends CDOClientRequestWithMonitoring<Boolean> +{ + private int viewID; + + private CDOID rootID; + + private byte opcode; + + private CDORevisionHandler revisionHandler; + + public UnitRequest(CDOClientProtocol protocol, int viewID, CDOID rootID, byte opcode, + CDORevisionHandler revisionHandler) + { + super(protocol, CDOProtocolConstants.SIGNAL_UNIT); + this.viewID = viewID; + this.rootID = rootID; + this.opcode = opcode; + this.revisionHandler = revisionHandler; + } + + @Override + protected void requesting(CDODataOutput out, OMMonitor monitor) throws IOException + { + out.writeInt(viewID); + out.writeCDOID(rootID); + out.writeByte(opcode); + } + + @Override + protected Boolean confirming(CDODataInput in, OMMonitor monitor) throws IOException + { + if (opcode == CDOProtocolConstants.UNIT_CREATE || opcode == CDOProtocolConstants.UNIT_OPEN) + { + for (;;) + { + CDORevision revision = in.readCDORevision(); + if (revision == null) + { + break; + } + + revisionHandler.handleRevision(revision); + } + } + + return in.readBoolean(); + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreAccessor.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreAccessor.java index 4b80d68f95..78b9747bcd 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreAccessor.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/IDBStoreAccessor.java @@ -11,6 +11,7 @@ package org.eclipse.emf.cdo.server.db; import org.eclipse.emf.cdo.server.IStoreAccessor; +import org.eclipse.emf.cdo.server.IStoreAccessor.UnitSupport; import org.eclipse.net4j.db.IDBConnection; @@ -23,7 +24,7 @@ import java.sql.Connection; * @noextend This interface is not intended to be extended by clients. * @noimplement This interface is not intended to be implemented by clients. */ -public interface IDBStoreAccessor extends IStoreAccessor.Raw +public interface IDBStoreAccessor extends IStoreAccessor.Raw, UnitSupport { public IDBStore getStore(); diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingUnitSupport.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingUnitSupport.java new file mode 100644 index 0000000000..65e8e77d33 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IClassMappingUnitSupport.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server.db.mapping; + +import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; + +import java.sql.SQLException; + +/** + * An extension interface for {@link IClassMapping class mappings} that support <i>units</i>. + * + * @author Eike Stepper + * @since 4.4 + */ +public interface IClassMappingUnitSupport extends IClassMapping +{ + public void readUnitRevisions(IDBStoreAccessor accessor, CDOBranchPoint branchPoint, CDOID rootID, + CDORevisionHandler revisionHandler) throws SQLException; +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping3.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping3.java new file mode 100644 index 0000000000..8fc37c5a4e --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMapping3.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2016 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server.db.mapping; + +/** + * Extension interface to {@link IListMapping2}. + * + * @author Eike Stepper + * @since 4.4 + */ +public interface IListMapping3 extends IListMapping2 +{ + public void setClassMapping(IClassMapping classMapping); +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingUnitSupport.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingUnitSupport.java new file mode 100644 index 0000000000..8c54771c86 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingUnitSupport.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server.db.mapping; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; + +import org.eclipse.net4j.util.collection.MoveableList; + +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Interface to complement {@link IListMapping} in order to provide unit support. + * + * @author Eike Stepper + * @since 4.4 + */ +public interface IListMappingUnitSupport extends IListMapping +{ + public ResultSet queryUnitEntries(IDBStoreAccessor accessor, IIDHandler idHandler, CDOID rootID) throws SQLException; + + public void readUnitEntries(ResultSet resultSet, MoveableList<Object> list) throws SQLException; +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStore.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStore.java index 9efd29ccc8..48df638948 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStore.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStore.java @@ -32,6 +32,7 @@ import org.eclipse.emf.cdo.server.db.IMetaDataManager; import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; import org.eclipse.emf.cdo.server.internal.db.bundle.OM; import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.IMappingConstants; +import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.UnitMappingTable; import org.eclipse.emf.cdo.server.internal.db.messages.Messages; import org.eclipse.emf.cdo.spi.server.InternalRepository; import org.eclipse.emf.cdo.spi.server.InternalSession; @@ -115,6 +116,8 @@ public class DBStore extends Store implements IDBStore, IMappingConstants, CDOAl private DurableLockingManager durableLockingManager = new DurableLockingManager(this); + private UnitMappingTable unitMappingTable; + private IMappingStrategy mappingStrategy; private IDBDatabase database; @@ -237,6 +240,11 @@ public class DBStore extends Store implements IDBStore, IMappingConstants, CDOAl return durableLockingManager; } + public UnitMappingTable getUnitMappingTable() + { + return unitMappingTable; + } + public Timer getConnectionKeepAliveTimer() { return connectionKeepAliveTimer; @@ -640,6 +648,12 @@ public class DBStore extends Store implements IDBStore, IMappingConstants, CDOAl LifecycleUtil.activate(durableLockingManager); LifecycleUtil.activate(mappingStrategy); + if (repository.isSupportingUnits()) + { + unitMappingTable = new UnitMappingTable(mappingStrategy); + unitMappingTable.activate(); + } + setRevisionTemporality(mappingStrategy.hasAuditSupport() ? RevisionTemporality.AUDITING : RevisionTemporality.NONE); setRevisionParallelism( mappingStrategy.hasBranchingSupport() ? RevisionParallelism.BRANCHING : RevisionParallelism.NONE); @@ -659,6 +673,7 @@ public class DBStore extends Store implements IDBStore, IMappingConstants, CDOAl @Override protected void doDeactivate() throws Exception { + LifecycleUtil.deactivate(unitMappingTable); LifecycleUtil.deactivate(mappingStrategy); LifecycleUtil.deactivate(durableLockingManager); LifecycleUtil.deactivate(metaDataManager); @@ -961,7 +976,7 @@ public class DBStore extends Store implements IDBStore, IMappingConstants, CDOAl }; private static final SchemaMigrator[] SCHEMA_MIGRATORS = { NO_MIGRATION_NEEDED, NON_AUDIT_MIGRATION, - LOB_SIZE_MIGRATION, NO_MIGRATION_NEEDED }; + LOB_SIZE_MIGRATION, NO_MIGRATION_NEEDED }; static { diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java index 834cbca7b9..3af8e796b4 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/DBStoreAccessor.java @@ -39,6 +39,7 @@ import org.eclipse.emf.cdo.server.ISession; import org.eclipse.emf.cdo.server.IStoreAccessor; import org.eclipse.emf.cdo.server.IStoreAccessor.DurableLocking2; import org.eclipse.emf.cdo.server.ITransaction; +import org.eclipse.emf.cdo.server.IView; import org.eclipse.emf.cdo.server.db.IDBStore; import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; import org.eclipse.emf.cdo.server.db.IIDHandler; @@ -49,6 +50,7 @@ import org.eclipse.emf.cdo.server.db.mapping.IClassMappingDeltaSupport; import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; import org.eclipse.emf.cdo.server.internal.db.bundle.OM; import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.AbstractHorizontalClassMapping; +import org.eclipse.emf.cdo.server.internal.db.mapping.horizontal.UnitMappingTable; import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch; import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager.BranchLoader3; @@ -133,17 +135,17 @@ public class DBStoreAccessor extends StoreAccessor implements IDBStoreAccessor, } @Override - public DBStore getStore() + public final DBStore getStore() { return (DBStore)super.getStore(); } - public IDBConnection getDBConnection() + public final IDBConnection getDBConnection() { return connection; } - public Connection getConnection() + public final Connection getConnection() { return connection; } @@ -480,11 +482,12 @@ public class DBStoreAccessor extends StoreAccessor implements IDBStoreAccessor, { super.applyIDMappings(context, monitor); + DBStore store = getStore(); + IIDHandler idHandler = store.getIDHandler(); + // Remember maxID because it may have to be adjusted if the repository is BACKUP or CLONE. See bug 325097. boolean adjustMaxID = !context.getBranchPoint().getBranch().isLocal() - && getStore().getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE; - - IIDHandler idHandler = getStore().getIDHandler(); + && store.getRepository().getIDGenerationLocation() == IDGenerationLocation.STORE; // Remember CDOIDs of new objects. They are cleared after writeRevisions() for (InternalCDORevision revision : context.getNewObjects()) @@ -850,6 +853,13 @@ public class DBStoreAccessor extends StoreAccessor implements IDBStoreAccessor, IMappingStrategy mappingStrategy = store.getMappingStrategy(); mappingStrategy.createMapping(connection, packageUnits, monitor.fork()); + + int writePackageUnits; // TODO Extra commit needed for Mysql between DDL and DML. + connection.commit(); + } + catch (SQLException ex) + { + throw new DBException(ex); } finally { @@ -1443,6 +1453,25 @@ public class DBStoreAccessor extends StoreAccessor implements IDBStoreAccessor, manager.unlock(this, durableLockingID); } + public List<CDOID> readUnitRoots() + { + UnitMappingTable unitMappingTable = getStore().getUnitMappingTable(); + return unitMappingTable.readUnitRoots(this); + } + + public void initUnit(IView view, CDOID rootID, CDORevisionHandler revisionHandler) + { + long created = getStore().getRepository().getTimeStamp(); + UnitMappingTable unitMappingTable = getStore().getUnitMappingTable(); + unitMappingTable.initUnit(this, view, rootID, created, revisionHandler); + } + + public void readUnit(IView view, CDOID rootID, CDORevisionHandler revisionHandler) + { + UnitMappingTable unitMappingTable = getStore().getUnitMappingTable(); + unitMappingTable.readUnitRevisions(this, view, rootID, revisionHandler); + } + /** * @author Stefan Winkler */ diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java index 9552980072..78590284f4 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/AbstractMappingStrategy.java @@ -55,6 +55,7 @@ import org.eclipse.net4j.util.StringUtil; import org.eclipse.net4j.util.WrappedException; import org.eclipse.net4j.util.collection.CloseableIterator; import org.eclipse.net4j.util.lifecycle.Lifecycle; +import org.eclipse.net4j.util.lifecycle.LifecycleUtil; import org.eclipse.net4j.util.om.monitor.OMMonitor; import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; @@ -828,6 +829,21 @@ public abstract class AbstractMappingStrategy extends Lifecycle implements IMapp public abstract IListMapping doCreateFeatureMapMapping(EClass containingClass, EStructuralFeature feature); + @Override + protected void doDeactivate() throws Exception + { + deactivateClassMappings(); + super.doDeactivate(); + } + + protected void deactivateClassMappings() + { + for (IClassMapping classMapping : classMappings.values()) + { + LifecycleUtil.deactivate(classMapping); + } + } + private static Set<CDOFeatureType> doGetForceIndexes(IMappingStrategy mappingStrategy) { return CDOFeatureType.readCombination(mappingStrategy.getProperties().get(Props.FORCE_INDEXES)); diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractBasicListTableMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractBasicListTableMapping.java index 695e803227..b68f84dc8c 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractBasicListTableMapping.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractBasicListTableMapping.java @@ -13,7 +13,8 @@ package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; import org.eclipse.emf.cdo.common.branch.CDOBranch; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; -import org.eclipse.emf.cdo.server.db.mapping.IListMapping2; +import org.eclipse.emf.cdo.server.db.mapping.IClassMapping; +import org.eclipse.emf.cdo.server.db.mapping.IListMapping3; import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; import org.eclipse.emf.ecore.EClass; @@ -22,7 +23,7 @@ import org.eclipse.emf.ecore.EStructuralFeature; /** * @author Stefan Winkler */ -public abstract class AbstractBasicListTableMapping implements IListMapping2, IMappingConstants +public abstract class AbstractBasicListTableMapping implements IListMapping3, IMappingConstants { private IMappingStrategy mappingStrategy; @@ -70,5 +71,10 @@ public abstract class AbstractBasicListTableMapping implements IListMapping2, IM builder.append(toIndex - 1); } + public void setClassMapping(IClassMapping classMapping) + { + // Subclasses may override. + } + public abstract void rawDeleted(IDBStoreAccessor accessor, CDOID id, CDOBranch branch, int version); } diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java index 2efd6d6307..21e0d908cf 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AbstractHorizontalClassMapping.java @@ -31,6 +31,7 @@ import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; import org.eclipse.emf.cdo.server.db.IIDHandler; import org.eclipse.emf.cdo.server.db.mapping.IClassMapping; import org.eclipse.emf.cdo.server.db.mapping.IListMapping; +import org.eclipse.emf.cdo.server.db.mapping.IListMapping3; 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.bundle.OM; @@ -51,6 +52,7 @@ import org.eclipse.net4j.db.ddl.IDBIndex; import org.eclipse.net4j.db.ddl.IDBSchema; import org.eclipse.net4j.db.ddl.IDBTable; import org.eclipse.net4j.spi.db.ddl.InternalDBIndex; +import org.eclipse.net4j.util.lifecycle.IDeactivateable; import org.eclipse.net4j.util.om.monitor.OMMonitor; import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; import org.eclipse.net4j.util.om.trace.ContextTracer; @@ -79,7 +81,7 @@ import java.util.Set; * @author Eike Stepper * @since 2.0 */ -public abstract class AbstractHorizontalClassMapping implements IClassMapping, IMappingConstants +public abstract class AbstractHorizontalClassMapping implements IClassMapping, IMappingConstants, IDeactivateable { private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractHorizontalClassMapping.class); @@ -142,7 +144,9 @@ public abstract class AbstractHorizontalClassMapping implements IClassMapping, I primaryKey.addIndexField(branchField); } - table.addIndex(IDBIndex.Type.NON_UNIQUE, ATTRIBUTES_ID, ATTRIBUTES_REVISED); + int xxx; + // table.addIndex(IDBIndex.Type.NON_UNIQUE, ATTRIBUTES_ID, ATTRIBUTES_REVISED); + table.addIndex(IDBIndex.Type.NON_UNIQUE, ATTRIBUTES_REVISED); } } @@ -175,6 +179,11 @@ public abstract class AbstractHorizontalClassMapping implements IClassMapping, I mapping = mappingStrategy.createListMapping(eClass, feature); } + if (mapping instanceof IListMapping3) + { + ((IListMapping3)mapping).setClassMapping(this); + } + listMappings.add(mapping); // Add field for list sizes @@ -294,9 +303,10 @@ public abstract class AbstractHorizontalClassMapping implements IClassMapping, I } stmt.setMaxRows(1); // Optimization: only 1 row - resultSet = stmt.executeQuery(); - if (!resultSet.next()) + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + if (!readValuesFromResultSet(resultSet, idHandler, revision, false)) { if (TRACER.isEnabled()) { @@ -306,69 +316,95 @@ public abstract class AbstractHorizontalClassMapping implements IClassMapping, I return false; } - revision.setVersion(resultSet.getInt(ATTRIBUTES_VERSION)); + return true; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(resultSet); + } + } - long timeStamp = resultSet.getLong(ATTRIBUTES_CREATED); + /** + * Read the revision's values from the DB. + * + * @return <code>true</code> if the revision has been read successfully.<br> + * <code>false</code> if the revision does not exist in the DB. + */ + protected final boolean readValuesFromResultSet(ResultSet resultSet, IIDHandler idHandler, + InternalCDORevision revision, boolean forUnit) + { + try + { + if (resultSet.next()) + { + long timeStamp = resultSet.getLong(ATTRIBUTES_CREATED); + CDOBranchPoint branchPoint = revision.getBranch().getPoint(timeStamp); - IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); - CDOBranchPoint branchPoint = revision.getBranch().getPoint(timeStamp); + if (forUnit) + { + revision.setID(idHandler.getCDOID(resultSet, ATTRIBUTES_ID)); + } - revision.setBranchPoint(branchPoint); - revision.setRevised(resultSet.getLong(ATTRIBUTES_REVISED)); - revision.setResourceID(idHandler.getCDOID(resultSet, ATTRIBUTES_RESOURCE)); - revision.setContainerID(idHandler.getCDOID(resultSet, ATTRIBUTES_CONTAINER)); - revision.setContainingFeatureID(resultSet.getInt(ATTRIBUTES_FEATURE)); + revision.setBranchPoint(branchPoint); + revision.setVersion(resultSet.getInt(ATTRIBUTES_VERSION)); + revision.setRevised(resultSet.getLong(ATTRIBUTES_REVISED)); + revision.setResourceID(idHandler.getCDOID(resultSet, ATTRIBUTES_RESOURCE)); + revision.setContainerID(idHandler.getCDOID(resultSet, ATTRIBUTES_CONTAINER)); + revision.setContainingFeatureID(resultSet.getInt(ATTRIBUTES_FEATURE)); - for (ITypeMapping mapping : valueMappings) - { - EStructuralFeature feature = mapping.getFeature(); - if (feature.isUnsettable()) + for (ITypeMapping mapping : valueMappings) { - IDBField field = unsettableFields.get(feature); - if (!resultSet.getBoolean(field.getName())) + EStructuralFeature feature = mapping.getFeature(); + if (feature.isUnsettable()) { - // isSet==false -- setValue: null - revision.setValue(feature, null); - continue; + IDBField field = unsettableFields.get(feature); + if (!resultSet.getBoolean(field.getName())) + { + // isSet==false -- setValue: null + revision.setValue(feature, null); + continue; + } } - } - mapping.readValueToRevision(resultSet, revision); - } + mapping.readValueToRevision(resultSet, revision); + } - if (listSizeFields != null) - { - for (Map.Entry<EStructuralFeature, IDBField> listSizeEntry : listSizeFields.entrySet()) + if (listSizeFields != null) { - EStructuralFeature feature = listSizeEntry.getKey(); - IDBField field = listSizeEntry.getValue(); - int size = resultSet.getInt(field.getName()); + for (Map.Entry<EStructuralFeature, IDBField> listSizeEntry : listSizeFields.entrySet()) + { + EStructuralFeature feature = listSizeEntry.getKey(); + IDBField field = listSizeEntry.getValue(); + int size = resultSet.getInt(field.getName()); - // ensure the listSize (TODO: remove assertion) - CDOList list = revision.getList(feature, size); + // ensure the listSize (TODO: remove assertion) + CDOList list = revision.getList(feature, size); - for (int i = 0; i < size; i++) - { - list.add(InternalCDOList.UNINITIALIZED); - } + for (int i = 0; i < size; i++) + { + list.add(InternalCDOList.UNINITIALIZED); + } - if (list.size() != size) - { - Assert.isTrue(false); + if (list.size() != size) + { + Assert.isTrue(false); + } } } + + return true; } - return true; + return false; } catch (SQLException ex) { throw new DBException(ex); } - finally - { - DBUtil.close(resultSet); - } } protected final void readLists(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) @@ -926,6 +962,11 @@ public abstract class AbstractHorizontalClassMapping implements IClassMapping, I protected abstract void writeValues(IDBStoreAccessor accessor, InternalCDORevision revision); + public Exception deactivate() + { + return null; + } + protected static void appendTypeMappingNames(StringBuilder builder, Collection<ITypeMapping> typeMappings) { if (typeMappings != null) diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMappingWithRanges.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMappingWithRanges.java index 3583e2e2c5..45446e29e0 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMappingWithRanges.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/AuditListTableMappingWithRanges.java @@ -36,11 +36,14 @@ 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.mapping.IClassMapping; import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingUnitSupport; 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.bundle.OM; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.server.InternalRepository; import org.eclipse.net4j.db.DBException; import org.eclipse.net4j.db.DBType; @@ -75,7 +78,8 @@ import java.util.List; * @author Stefan Winkler * @author Lothar Werzinger */ -public class AuditListTableMappingWithRanges extends AbstractBasicListTableMapping implements IListMappingDeltaSupport +public class AuditListTableMappingWithRanges extends AbstractBasicListTableMapping + implements IListMappingDeltaSupport, IListMappingUnitSupport { private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AuditListTableMappingWithRanges.class); @@ -84,6 +88,8 @@ public class AuditListTableMappingWithRanges extends AbstractBasicListTableMappi */ private static final int FINAL_VERSION = Integer.MAX_VALUE; + private static final String SQL_ORDER_BY_INDEX = " ORDER BY " + LIST_IDX; + /** * The table of this mapping. */ @@ -97,7 +103,7 @@ public class AuditListTableMappingWithRanges extends AbstractBasicListTableMappi // --------- SQL strings - see initSQLStrings() ----------------- private String sqlSelectChunksPrefix; - private String sqlOrderByIndex; + private String sqlSelectUnitEntries; private String sqlInsertEntry; @@ -172,8 +178,6 @@ public class AuditListTableMappingWithRanges extends AbstractBasicListTableMappi builder.append(">?)"); //$NON-NLS-1$ sqlSelectChunksPrefix = builder.toString(); - sqlOrderByIndex = " ORDER BY " + LIST_IDX; //$NON-NLS-1$ - // ----------------- insert entry ----------------- builder = new StringBuilder("INSERT INTO "); //$NON-NLS-1$ builder.append(tableName); @@ -271,6 +275,26 @@ public class AuditListTableMappingWithRanges extends AbstractBasicListTableMappi sqlDeleteList = builder.toString(); } + @Override + public void setClassMapping(IClassMapping classMapping) + { + InternalRepository repository = (InternalRepository)getMappingStrategy().getStore().getRepository(); + if (repository.isSupportingUnits()) + { + String listTableName = getTable().getName(); + String attributesTableName = classMapping.getDBTables().get(0).getName(); + + sqlSelectUnitEntries = "SELECT cdo_list." + LIST_VALUE + // + " FROM " + listTableName + " cdo_list, " + attributesTableName + ", " + UnitMappingTable.UNITS + // + " WHERE " + UnitMappingTable.UNITS_ELEM + "=" + ATTRIBUTES_ID + // + " AND " + ATTRIBUTES_ID + "=cdo_list." + LIST_REVISION_ID + // + " AND " + UnitMappingTable.UNITS_UNIT + "=?" + // + " AND cdo_list." + LIST_REVISION_VERSION_ADDED + "<=" + ATTRIBUTES_VERSION + // + " AND (cdo_list." + LIST_REVISION_VERSION_REMOVED + " IS NULL OR cdo_list." + LIST_REVISION_VERSION_REMOVED + + ">" + ATTRIBUTES_VERSION + ") ORDER BY cdo_list." + LIST_REVISION_ID + ", cdo_list." + LIST_IDX; + } + } + public Collection<IDBTable> getDBTables() { return Collections.singleton(table); @@ -301,7 +325,7 @@ public class AuditListTableMappingWithRanges extends AbstractBasicListTableMappi getFeature().getName(), revision.getID(), revision.getVersion()); } - String sql = sqlSelectChunksPrefix + sqlOrderByIndex; + String sql = sqlSelectChunksPrefix + SQL_ORDER_BY_INDEX; IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sql, ReuseProbability.HIGH); @@ -365,7 +389,7 @@ public class AuditListTableMappingWithRanges extends AbstractBasicListTableMappi builder.append(where); } - builder.append(sqlOrderByIndex); + builder.append(SQL_ORDER_BY_INDEX); String sql = builder.toString(); IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); @@ -536,6 +560,251 @@ public class AuditListTableMappingWithRanges extends AbstractBasicListTableMappi throw new UnsupportedOperationException("Raw deletion does not work in range-based mappings"); } + public ResultSet queryUnitEntries(IDBStoreAccessor accessor, IIDHandler idHandler, CDOID rootID) throws SQLException + { + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlSelectUnitEntries, + ReuseProbability.MEDIUM); + idHandler.setCDOID(stmt, 1, rootID); + return stmt.executeQuery(); + } + + public void readUnitEntries(ResultSet resultSet, MoveableList<Object> list) throws SQLException + { + int size = list.size(); + for (int i = 0; i < size; i++) + { + resultSet.next(); + + int xxx; // TODO Check that this is the correct revision? + + Object value = typeMapping.readValue(resultSet); + list.set(i, value); + } + } + + private void addEntry(IDBStoreAccessor accessor, CDOID id, int version, int index, Object value) + { + 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); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsertEntry, ReuseProbability.HIGH); + + try + { + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, version); + stmt.setInt(column++, index); + typeMapping.setValue(stmt, column++, value); + + DBUtil.update(stmt, true); + } + catch (SQLException e) + { + throw new DBException(e); + } + catch (IllegalStateException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(stmt); + } + } + + private void removeEntry(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int index) + { + 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); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlDeleteEntry, ReuseProbability.HIGH); + + try + { + // try to delete a temporary entry first + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, index); + stmt.setInt(column++, newVersion); + + int result = DBUtil.update(stmt, false); + if (result == 1) + { + if (TRACER.isEnabled()) + { + TRACER.format("removeEntry deleted: {0}", index); //$NON-NLS-1$ + } + } + else if (result > 1) + { + if (TRACER.isEnabled()) + { + TRACER.format("removeEntry Too many results: {0}: {1}", index, result); //$NON-NLS-1$ + } + + throw new DBException("Too many results"); //$NON-NLS-1$ + } + else + { + // no temporary entry found, so mark the entry as removed + DBUtil.close(stmt); + stmt = accessor.getDBConnection().prepareStatement(sqlRemoveEntry, ReuseProbability.HIGH); + + column = 1; + stmt.setInt(column++, newVersion); + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, index); + + DBUtil.update(stmt, true); + } + } + catch (SQLException e) + { + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage()); + } + + throw new DBException(e); + } + catch (IllegalStateException e) + { + if (TRACER.isEnabled()) + { + TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$ + getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage()); + } + + throw new DBException(e); + } + finally + { + DBUtil.close(stmt); + } + } + + private Object getValue(IDBStoreAccessor accessor, CDOID id, int index) + { + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlGetValue, ReuseProbability.HIGH); + Object result = null; + + try + { + int column = 1; + idHandler.setCDOID(stmt, column++, id); + stmt.setInt(column++, index); + + ResultSet resultSet = stmt.executeQuery(); + if (!resultSet.next()) + { + throw new DBException("getValue() expects exactly one result"); + } + + result = typeMapping.readValue(resultSet); + if (TRACER.isEnabled()) + { + TRACER.format("Read value (index {0}) from result set: {1}", index, result); //$NON-NLS-1$ + } + } + catch (SQLException e) + { + throw new DBException(e); + } + finally + { + DBUtil.close(stmt); + } + + return result; + } + + public final boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere, + QueryXRefsContext context, String idString) + { + + String tableName = getTable().getName(); + String listJoin = getMappingStrategy().getListJoin("a_t", "l_t"); + + StringBuilder builder = new StringBuilder(); + builder.append("SELECT l_t."); //$NON-NLS-1$ + builder.append(LIST_REVISION_ID); + builder.append(", l_t."); //$NON-NLS-1$ + builder.append(LIST_VALUE); + builder.append(", l_t."); //$NON-NLS-1$ + builder.append(LIST_IDX); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(tableName); + builder.append(" l_t, ");//$NON-NLS-1$ + builder.append(mainTableName); + builder.append(" a_t WHERE ");//$NON-NLS-1$ + builder.append("a_t.");//$NON-NLS-1$ + builder.append(mainTableWhere); + builder.append(listJoin); + builder.append(" AND "); //$NON-NLS-1$ + builder.append(LIST_VALUE); + builder.append(" IN "); //$NON-NLS-1$ + builder.append(idString); + String sql = builder.toString(); + + if (TRACER.isEnabled()) + { + TRACER.format("Query XRefs (list): {0}", sql); + } + + IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); + IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sql, ReuseProbability.MEDIUM); + ResultSet resultSet = null; + + try + { + resultSet = stmt.executeQuery(); + 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); + } + } + public void processDelta(final IDBStoreAccessor accessor, final CDOID id, final int branchId, int oldVersion, final int newVersion, long created, CDOListFeatureDelta delta) { @@ -890,227 +1159,4 @@ public class AuditListTableMappingWithRanges extends AbstractBasicListTableMappi } } } - - private void addEntry(IDBStoreAccessor accessor, CDOID id, int version, int index, Object value) - { - 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); - } - - IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); - IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlInsertEntry, ReuseProbability.HIGH); - - try - { - int column = 1; - idHandler.setCDOID(stmt, column++, id); - stmt.setInt(column++, version); - stmt.setInt(column++, index); - typeMapping.setValue(stmt, column++, value); - - DBUtil.update(stmt, true); - } - catch (SQLException e) - { - throw new DBException(e); - } - catch (IllegalStateException e) - { - throw new DBException(e); - } - finally - { - DBUtil.close(stmt); - } - } - - private void removeEntry(IDBStoreAccessor accessor, CDOID id, int oldVersion, int newVersion, int index) - { - 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); - } - - IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); - IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlDeleteEntry, ReuseProbability.HIGH); - - try - { - // try to delete a temporary entry first - int column = 1; - idHandler.setCDOID(stmt, column++, id); - stmt.setInt(column++, index); - stmt.setInt(column++, newVersion); - - int result = DBUtil.update(stmt, false); - if (result == 1) - { - if (TRACER.isEnabled()) - { - TRACER.format("removeEntry deleted: {0}", index); //$NON-NLS-1$ - } - } - else if (result > 1) - { - if (TRACER.isEnabled()) - { - TRACER.format("removeEntry Too many results: {0}: {1}", index, result); //$NON-NLS-1$ - } - - throw new DBException("Too many results"); //$NON-NLS-1$ - } - else - { - // no temporary entry found, so mark the entry as removed - DBUtil.close(stmt); - stmt = accessor.getDBConnection().prepareStatement(sqlRemoveEntry, ReuseProbability.HIGH); - - column = 1; - stmt.setInt(column++, newVersion); - idHandler.setCDOID(stmt, column++, id); - stmt.setInt(column++, index); - - DBUtil.update(stmt, true); - } - } - catch (SQLException e) - { - if (TRACER.isEnabled()) - { - TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$ - getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage()); - } - - throw new DBException(e); - } - catch (IllegalStateException e) - { - if (TRACER.isEnabled()) - { - TRACER.format("Removing value for feature() {0}.{1} index {2} of {3}v{4} FAILED {5}", //$NON-NLS-1$ - getContainingClass().getName(), getFeature().getName(), index, id, newVersion, e.getMessage()); - } - - throw new DBException(e); - } - finally - { - DBUtil.close(stmt); - } - } - - private Object getValue(IDBStoreAccessor accessor, CDOID id, int index) - { - IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); - IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sqlGetValue, ReuseProbability.HIGH); - Object result = null; - - try - { - int column = 1; - idHandler.setCDOID(stmt, column++, id); - stmt.setInt(column++, index); - - ResultSet resultSet = stmt.executeQuery(); - if (!resultSet.next()) - { - throw new DBException("getValue() expects exactly one result"); - } - - result = typeMapping.readValue(resultSet); - if (TRACER.isEnabled()) - { - TRACER.format("Read value (index {0}) from result set: {1}", index, result); //$NON-NLS-1$ - } - } - catch (SQLException e) - { - throw new DBException(e); - } - finally - { - DBUtil.close(stmt); - } - - return result; - } - - public final boolean queryXRefs(IDBStoreAccessor accessor, String mainTableName, String mainTableWhere, - QueryXRefsContext context, String idString) - { - - String tableName = getTable().getName(); - String listJoin = getMappingStrategy().getListJoin("a_t", "l_t"); - - StringBuilder builder = new StringBuilder(); - builder.append("SELECT l_t."); //$NON-NLS-1$ - builder.append(LIST_REVISION_ID); - builder.append(", l_t."); //$NON-NLS-1$ - builder.append(LIST_VALUE); - builder.append(", l_t."); //$NON-NLS-1$ - builder.append(LIST_IDX); - builder.append(" FROM "); //$NON-NLS-1$ - builder.append(tableName); - builder.append(" l_t, ");//$NON-NLS-1$ - builder.append(mainTableName); - builder.append(" a_t WHERE ");//$NON-NLS-1$ - builder.append("a_t.");//$NON-NLS-1$ - builder.append(mainTableWhere); - builder.append(listJoin); - builder.append(" AND "); //$NON-NLS-1$ - builder.append(LIST_VALUE); - builder.append(" IN "); //$NON-NLS-1$ - builder.append(idString); - String sql = builder.toString(); - - if (TRACER.isEnabled()) - { - TRACER.format("Query XRefs (list): {0}", sql); - } - - IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); - IDBPreparedStatement stmt = accessor.getDBConnection().prepareStatement(sql, ReuseProbability.MEDIUM); - ResultSet resultSet = null; - - try - { - resultSet = stmt.executeQuery(); - while (resultSet.next()) - { - CDOID sourceID = idHandler.getCDOID(resultSet, 1); - CDOID targetID = idHandler.getCDOID(resultSet, 2); - int idx = resultSet.getInt(3); - - boolean more = context.addXRef(targetID, sourceID, (EReference)getFeature(), idx); - if (TRACER.isEnabled()) - { - TRACER.format(" add XRef to context: src={0}, tgt={1}, idx={2}", sourceID, targetID, idx); - } - - if (!more) - { - if (TRACER.isEnabled()) - { - TRACER.format(" result limit reached. Ignoring further results."); - } - - return false; - } - } - - return true; - } - catch (SQLException ex) - { - throw new DBException(ex); - } - finally - { - DBUtil.close(resultSet); - DBUtil.close(stmt); - } - } } diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditClassMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditClassMapping.java index 63d4e1840c..605d9d2fed 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditClassMapping.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalAuditClassMapping.java @@ -20,6 +20,7 @@ import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.revision.CDOList; import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; 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; @@ -35,18 +36,27 @@ import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; import org.eclipse.emf.cdo.server.db.IIDHandler; import org.eclipse.emf.cdo.server.db.mapping.IClassMappingAuditSupport; import org.eclipse.emf.cdo.server.db.mapping.IClassMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IClassMappingUnitSupport; +import org.eclipse.emf.cdo.server.db.mapping.IListMapping; import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport; +import org.eclipse.emf.cdo.server.db.mapping.IListMappingUnitSupport; import org.eclipse.emf.cdo.server.db.mapping.ITypeMapping; +import org.eclipse.emf.cdo.server.internal.db.DBStore; import org.eclipse.emf.cdo.server.internal.db.bundle.OM; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; +import org.eclipse.emf.cdo.spi.common.revision.StubCDORevision; +import org.eclipse.emf.cdo.spi.server.InternalRepository; import org.eclipse.net4j.db.DBException; import org.eclipse.net4j.db.DBUtil; import org.eclipse.net4j.db.IDBPreparedStatement; import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +import org.eclipse.net4j.db.IDBResultSet; import org.eclipse.net4j.db.ddl.IDBField; import org.eclipse.net4j.util.ImplementationError; +import org.eclipse.net4j.util.collection.MoveableList; +import org.eclipse.net4j.util.concurrent.ConcurrencyUtil; import org.eclipse.net4j.util.om.monitor.OMMonitor; import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; import org.eclipse.net4j.util.om.trace.ContextTracer; @@ -54,28 +64,38 @@ import org.eclipse.net4j.util.om.trace.ContextTracer; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EStructuralFeature; +import java.sql.ResultSet; import java.sql.SQLException; +import java.util.List; import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; /** * @author Eike Stepper * @since 2.0 */ public class HorizontalAuditClassMapping extends AbstractHorizontalClassMapping - implements IClassMappingAuditSupport, IClassMappingDeltaSupport + implements IClassMappingAuditSupport, IClassMappingDeltaSupport, IClassMappingUnitSupport { private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, HorizontalAuditClassMapping.class); private String sqlInsertAttributes; - private String sqlSelectCurrentAttributes; - - private String sqlSelectAllObjectIDs; + private String sqlSelectAttributesCurrent; private String sqlSelectAttributesByTime; private String sqlSelectAttributesByVersion; + private String sqlSelectUnitCurrent; + + private String sqlSelectUnitByTime; + + private String sqlSelectAllObjectIDs; + private String sqlReviseAttributes; private String sqlRawDeleteAttributes; @@ -98,47 +118,25 @@ public class HorizontalAuditClassMapping extends AbstractHorizontalClassMapping private void initSQLStrings() { // ----------- Select Revision --------------------------- - StringBuilder builder = new StringBuilder(); - builder.append("SELECT "); //$NON-NLS-1$ - builder.append(ATTRIBUTES_VERSION); - builder.append(", "); //$NON-NLS-1$ - builder.append(ATTRIBUTES_CREATED); - builder.append(", "); //$NON-NLS-1$ - builder.append(ATTRIBUTES_REVISED); - builder.append(", "); //$NON-NLS-1$ - builder.append(ATTRIBUTES_RESOURCE); - builder.append(", "); //$NON-NLS-1$ - builder.append(ATTRIBUTES_CONTAINER); - builder.append(", "); //$NON-NLS-1$ - builder.append(ATTRIBUTES_FEATURE); - appendTypeMappingNames(builder, getValueMappings()); - appendFieldNames(builder, getUnsettableFields()); - appendFieldNames(builder, getListSizeFields()); - builder.append(" FROM "); //$NON-NLS-1$ - builder.append(getTable()); - builder.append(" WHERE "); //$NON-NLS-1$ - builder.append(ATTRIBUTES_ID); - builder.append("=? AND ("); //$NON-NLS-1$ - String sqlSelectAttributesPrefix = builder.toString(); - builder.append(ATTRIBUTES_REVISED); - builder.append("=0)"); //$NON-NLS-1$ - sqlSelectCurrentAttributes = builder.toString(); - - builder = new StringBuilder(sqlSelectAttributesPrefix); - builder.append(ATTRIBUTES_CREATED); - builder.append("<=? AND ("); //$NON-NLS-1$ - builder.append(ATTRIBUTES_REVISED); - builder.append("=0 OR "); //$NON-NLS-1$ - builder.append(ATTRIBUTES_REVISED); - builder.append(">=?))"); //$NON-NLS-1$ - sqlSelectAttributesByTime = builder.toString(); + String[] strings = buildSQLSelects(false); + String sqlSelectAttributesPrefix = strings[0]; + sqlSelectAttributesCurrent = strings[1]; + sqlSelectAttributesByTime = strings[2]; - builder = new StringBuilder(sqlSelectAttributesPrefix); + StringBuilder builder = new StringBuilder(sqlSelectAttributesPrefix); builder.append("ABS("); builder.append(ATTRIBUTES_VERSION); - builder.append(")=?)"); //$NON-NLS-1$ + builder.append(")=?"); //$NON-NLS-1$ sqlSelectAttributesByVersion = builder.toString(); + InternalRepository repository = (InternalRepository)getMappingStrategy().getStore().getRepository(); + if (repository.isSupportingUnits()) + { + strings = buildSQLSelects(true); + sqlSelectUnitCurrent = strings[1]; + sqlSelectUnitByTime = strings[2]; + } + // ----------- Insert Attributes ------------------------- builder = new StringBuilder(); builder.append("INSERT INTO "); //$NON-NLS-1$ @@ -200,6 +198,78 @@ public class HorizontalAuditClassMapping extends AbstractHorizontalClassMapping sqlRawDeleteAttributes = builder.toString(); } + private String[] buildSQLSelects(boolean forUnits) + { + String[] strings = new String[3]; + + StringBuilder builder = new StringBuilder(); + builder.append("SELECT "); //$NON-NLS-1$ + if (forUnits) + { + builder.append(ATTRIBUTES_ID); + builder.append(", "); //$NON-NLS-1$ + } + + builder.append(ATTRIBUTES_VERSION); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CREATED); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_RESOURCE); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CONTAINER); + builder.append(", "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_FEATURE); + appendTypeMappingNames(builder, getValueMappings()); + appendFieldNames(builder, getUnsettableFields()); + appendFieldNames(builder, getListSizeFields()); + builder.append(" FROM "); //$NON-NLS-1$ + builder.append(getTable()); + + if (forUnits) + { + builder.append(", "); //$NON-NLS-1$ + builder.append(UnitMappingTable.UNITS); + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + builder.append("="); //$NON-NLS-1$ + builder.append(UnitMappingTable.UNITS); + builder.append("."); //$NON-NLS-1$ + builder.append(UnitMappingTable.UNITS_ELEM); + builder.append(" AND "); //$NON-NLS-1$ + builder.append(UnitMappingTable.UNITS); + builder.append("."); //$NON-NLS-1$ + builder.append(UnitMappingTable.UNITS_UNIT); + builder.append("=?"); //$NON-NLS-1$ + builder.append(" AND "); //$NON-NLS-1$ + } + else + { + builder.append(" WHERE "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + builder.append("=? AND "); //$NON-NLS-1$ + } + + strings[0] = builder.toString(); + + builder.append(ATTRIBUTES_REVISED); + builder.append("=0"); //$NON-NLS-1$ + strings[1] = builder.toString(); + + builder = new StringBuilder(strings[0]); + builder.append("("); //$NON-NLS-1$ + builder.append(ATTRIBUTES_CREATED); + builder.append("<=? AND ("); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append("=0 OR "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_REVISED); + builder.append(">=?))"); //$NON-NLS-1$ + strings[2] = builder.toString(); + + return strings; + } + public boolean readRevision(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) { IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); @@ -217,7 +287,7 @@ public class HorizontalAuditClassMapping extends AbstractHorizontalClassMapping } else { - stmt = accessor.getDBConnection().prepareStatement(sqlSelectCurrentAttributes, ReuseProbability.HIGH); + stmt = accessor.getDBConnection().prepareStatement(sqlSelectAttributesCurrent, ReuseProbability.HIGH); idHandler.setCDOID(stmt, 1, revision.getID()); } @@ -593,6 +663,216 @@ public class HorizontalAuditClassMapping extends AbstractHorizontalClassMapping return builder.toString(); } + public void readUnitRevisions(IDBStoreAccessor accessor, CDOBranchPoint branchPoint, CDOID rootID, + CDORevisionHandler revisionHandler) throws SQLException + { + DBStore store = (DBStore)getMappingStrategy().getStore(); + InternalRepository repository = store.getRepository(); + CDOBranchPoint head = repository.getBranchManager().getMainBranch().getHead(); + EClass eClass = getEClass(); + + int xxx; + long start = System.currentTimeMillis(); + System.out.print(eClass.getName() + ":\t"); + + IIDHandler idHandler = store.getIDHandler(); + IDBPreparedStatement stmt = null; + int oldFetchSize = -1; + + try + { + long timeStamp = branchPoint.getTimeStamp(); + if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE) + { + stmt = accessor.getDBConnection().prepareStatement(sqlSelectUnitByTime, ReuseProbability.MEDIUM); + idHandler.setCDOID(stmt, 1, rootID); + stmt.setLong(2, timeStamp); + stmt.setLong(3, timeStamp); + } + else + { + stmt = accessor.getDBConnection().prepareStatement(sqlSelectUnitCurrent, ReuseProbability.HIGH); + idHandler.setCDOID(stmt, 1, rootID); + } + + AsnychronousListFiller listFiller = new AsnychronousListFiller(accessor, rootID, revisionHandler); + ConcurrencyUtil.execute(repository, listFiller); + + oldFetchSize = stmt.getFetchSize(); + stmt.setFetchSize(100000); + IDBResultSet resultSet = stmt.executeQuery(); + + for (;;) + { + InternalCDORevision revision = store.createRevision(eClass, null); + revision.setBranchPoint(head); + + if (!readValuesFromResultSet(resultSet, idHandler, revision, true)) + { + break; + } + + listFiller.schedule(revision); + } + + long stop = System.currentTimeMillis(); + System.out.print(stop - start); + start = stop; + + listFiller.await(); + + stop = System.currentTimeMillis() - start; + if (stop != 0) + { + System.out.print("\t" + stop); + } + + System.out.println(); + } + finally + { + if (oldFetchSize != -1) + { + stmt.setFetchSize(oldFetchSize); + } + + DBUtil.close(stmt); + } + } + + private class AsnychronousListFiller implements Runnable + { + private final BlockingQueue<InternalCDORevision> queue = new LinkedBlockingQueue<InternalCDORevision>(); + + private final CountDownLatch latch = new CountDownLatch(1); + + private final IDBStoreAccessor accessor; + + private final CDOID rootID; + + private final DBStore store; + + private final IIDHandler idHandler; + + private final IListMappingUnitSupport[] listMappings; + + private final ResultSet[] resultSets; + + private final CDORevisionHandler revisionHandler; + + public AsnychronousListFiller(IDBStoreAccessor accessor, CDOID rootID, CDORevisionHandler revisionHandler) + { + this.accessor = accessor; + this.rootID = rootID; + this.revisionHandler = revisionHandler; + + store = (DBStore)accessor.getStore(); + idHandler = store.getIDHandler(); + + List<IListMapping> tmp = getListMappings(); + int size = tmp.size(); + + listMappings = new IListMappingUnitSupport[size]; + resultSets = new ResultSet[size]; + + int i = 0; + for (IListMapping listMapping : tmp) + { + listMappings[i++] = (IListMappingUnitSupport)listMapping; + } + } + + public void schedule(InternalCDORevision revision) + { + queue.offer(revision); + } + + public void await() + { + // Schedule an end marker revision. + schedule(new StubCDORevision(getEClass())); + + try + { + latch.await(); + } + catch (InterruptedException ex) + { + //$FALL-THROUGH$ + } + } + + public void run() + { + try + { + while (store.isActive()) + { + InternalCDORevision revision; + + try + { + revision = queue.poll(1, TimeUnit.SECONDS); + if (revision == null) + { + continue; + } + + if (revision instanceof StubCDORevision) + { + return; + } + } + catch (InterruptedException ex) + { + return; + } + + try + { + readUnitEntries(revision); + } + catch (SQLException ex) + { + int xxx; + ex.printStackTrace(); + return; + } + } + } + finally + { + latch.countDown(); + } + } + + private void readUnitEntries(InternalCDORevision revision) throws SQLException + { + for (int i = 0; i < listMappings.length; i++) + { + IListMappingUnitSupport listMapping = listMappings[i]; + EStructuralFeature feature = listMapping.getFeature(); + + MoveableList<Object> list = revision.getList(feature); + int size = list.size(); + if (size != 0) + { + if (resultSets[i] == null) + { + resultSets[i] = listMapping.queryUnitEntries(accessor, idHandler, rootID); + } + + listMapping.readUnitEntries(resultSets[i], list); + } + } + + synchronized (revisionHandler) + { + revisionHandler.handleRevision(revision); + } + } + } + /** * @author Stefan Winkler */ diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditClassMapping.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditClassMapping.java index 3a3b6795a2..85c1855bf7 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditClassMapping.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/HorizontalNonAuditClassMapping.java @@ -368,6 +368,8 @@ public class HorizontalNonAuditClassMapping extends AbstractHorizontalClassMappi { if (hasLists) { + // Reading all list rows of an object is not atomic. + // After all row reads are done, check the revision version again (see below). stmtVersion = accessor.getDBConnection().prepareStatement(sqlSelectCurrentVersion, ReuseProbability.HIGH); stmtVersion.setMaxRows(1); // Optimization: only 1 row idHandler.setCDOID(stmtVersion, 1, id); diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeTable.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeTable.java index cade3cb771..5787470f50 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeTable.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/ObjectTypeTable.java @@ -21,6 +21,7 @@ import org.eclipse.emf.cdo.server.db.IDBStore; import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; import org.eclipse.emf.cdo.server.db.IIDHandler; import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; +import org.eclipse.emf.cdo.spi.server.InternalRepository; import org.eclipse.net4j.db.DBException; import org.eclipse.net4j.db.DBType; @@ -213,7 +214,7 @@ public class ObjectTypeTable extends AbstractObjectTypeMapper implements IMappin { super.doActivate(); - IDBStore store = getMappingStrategy().getStore(); + final IDBStore store = getMappingStrategy().getStore(); final DBType idType = store.getIDHandler().getDBType(); final int idLength = store.getIDColumnLength(); @@ -230,6 +231,12 @@ public class ObjectTypeTable extends AbstractObjectTypeMapper implements IMappin table.addField(ATTRIBUTES_CLASS, idType, idLength); table.addField(ATTRIBUTES_CREATED, DBType.BIGINT); table.addIndex(IDBIndex.Type.PRIMARY_KEY, ATTRIBUTES_ID); + + InternalRepository repository = (InternalRepository)store.getRepository(); + if (repository.isSupportingUnits()) + { + table.addIndex(IDBIndex.Type.NON_UNIQUE, ATTRIBUTES_CLASS); + } } }); } diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/UnitMappingTable.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/UnitMappingTable.java new file mode 100644 index 0000000000..f44d2456da --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/UnitMappingTable.java @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2010-2014 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + * Stefan Winkler - bug 259402 + * Stefan Winkler - redesign (prepared statements) + * Stefan Winkler - bug 276926 + */ +package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.server.IView; +import org.eclipse.emf.cdo.server.db.IDBStore; +import org.eclipse.emf.cdo.server.db.IDBStoreAccessor; +import org.eclipse.emf.cdo.server.db.IIDHandler; +import org.eclipse.emf.cdo.server.db.IMetaDataManager; +import org.eclipse.emf.cdo.server.db.mapping.IClassMappingUnitSupport; +import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy; +import org.eclipse.emf.cdo.server.internal.db.CDODBSchema; + +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.DBType; +import org.eclipse.net4j.db.DBUtil; +import org.eclipse.net4j.db.IDBConnection; +import org.eclipse.net4j.db.IDBDatabase; +import org.eclipse.net4j.db.IDBDatabase.RunnableWithSchema; +import org.eclipse.net4j.db.IDBPreparedStatement; +import org.eclipse.net4j.db.IDBPreparedStatement.ReuseProbability; +import org.eclipse.net4j.db.ddl.IDBIndex; +import org.eclipse.net4j.db.ddl.IDBSchema; +import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.util.lifecycle.Lifecycle; + +import org.eclipse.emf.ecore.EClass; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Eike Stepper + * @since 4.0 + */ +public class UnitMappingTable extends Lifecycle implements IMappingConstants +{ + public static final String UNITS = "CDO_UNITS"; //$NON-NLS-1$ + + public static final String UNITS_ELEM = "CDO_ELEM"; //$NON-NLS-1$ + + public static final String UNITS_UNIT = "CDO_UNIT"; //$NON-NLS-1$ + + // public static final String UNITS_CREATED = "CDO_CREATED"; //$NON-NLS-1$ + + private static final String SQL_SELECT_ROOTS = "SELECT DISTINCT " + UNITS_UNIT + " FROM " + UNITS; + + private static final String SQL_INSERT_MAPPINGS = "INSERT INTO " + UNITS + " VALUES (?, ?)"; + + private static final String SQL_SELECT_CLASSES = "SELECT DISTINCT " + ATTRIBUTES_CLASS + " FROM " + UNITS + ", " + + CDODBSchema.CDO_OBJECTS + " WHERE " + UNITS_ELEM + "=" + ATTRIBUTES_ID + " AND " + UNITS_UNIT + "=?"; + + private static final int WRITE_UNIT_MAPPING_BATCH_SIZE = 100000; + + private final IMappingStrategy mappingStrategy; + + private IDBTable table; + + public UnitMappingTable(IMappingStrategy mappingStrategy) + { + this.mappingStrategy = mappingStrategy; + } + + public List<CDOID> readUnitRoots(IDBStoreAccessor accessor) + { + List<CDOID> rootIDs = new ArrayList<CDOID>(); + IIDHandler idHandler = mappingStrategy.getStore().getIDHandler(); + Statement stmt = null; + + try + { + stmt = accessor.getDBConnection().createStatement(); + + if (DBUtil.isTracerEnabled()) + { + DBUtil.trace(stmt.toString()); + } + + ResultSet resultSet = stmt.executeQuery(SQL_SELECT_ROOTS); + while (resultSet.next()) + { + CDOID rootID = idHandler.getCDOID(resultSet, 1); + rootIDs.add(rootID); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(stmt); + } + + return rootIDs; + } + + public void initUnit(IDBStoreAccessor accessor, IView view, CDOID rootID, long created, + CDORevisionHandler revisionHandler) + { + IIDHandler idHandler = mappingStrategy.getStore().getIDHandler(); + IDBConnection connection = accessor.getDBConnection(); + IDBPreparedStatement stmt = connection.prepareStatement(SQL_INSERT_MAPPINGS, ReuseProbability.HIGH); + + try + { + CDORevision revision = view.getRevision(rootID); + int batchCount = 0; + + if (initUnit(accessor, idHandler, stmt, view, rootID, created, revisionHandler, revision, batchCount) != 0) + { + stmt.executeBatch(); + } + + connection.commit(); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(stmt); + } + } + + private int initUnit(IDBStoreAccessor accessor, IIDHandler idHandler, IDBPreparedStatement stmt, IView view, + CDOID rootID, long created, CDORevisionHandler revisionHandler, CDORevision revision, int batchCount) + throws SQLException + { + revisionHandler.handleRevision(revision); + + CDOID id = revision.getID(); + idHandler.setCDOID(stmt, 1, id); + idHandler.setCDOID(stmt, 2, rootID); + // stmt.setLong(3, created); + stmt.addBatch(); + + if (++batchCount > WRITE_UNIT_MAPPING_BATCH_SIZE) + { + stmt.executeBatch(); + batchCount = 0; + } + + List<CDORevision> children = CDORevisionUtil.getChildRevisions(revision, view, true); + for (CDORevision child : children) + { + batchCount = initUnit(accessor, idHandler, stmt, view, rootID, created, revisionHandler, child, batchCount); + } + + return batchCount; + } + + public void readUnitRevisions(IDBStoreAccessor accessor, IView view, CDOID rootID, CDORevisionHandler revisionHandler) + { + IDBStore store = mappingStrategy.getStore(); + IIDHandler idHandler = store.getIDHandler(); + IMetaDataManager metaDataManager = store.getMetaDataManager(); + + IDBConnection connection = accessor.getDBConnection(); + IDBPreparedStatement stmt = connection.prepareStatement(SQL_SELECT_CLASSES, ReuseProbability.HIGH); + int oldFetchSize = -1; + + try + { + idHandler.setCDOID(stmt, 1, rootID); + + oldFetchSize = stmt.getFetchSize(); + stmt.setFetchSize(100000); + ResultSet resultSet = stmt.executeQuery(); + + while (resultSet.next()) + { + CDOID classID = idHandler.getCDOID(resultSet, 1); + EClass eClass = (EClass)metaDataManager.getMetaInstance(classID); + + IClassMappingUnitSupport classMapping = (IClassMappingUnitSupport)mappingStrategy.getClassMapping(eClass); + classMapping.readUnitRevisions(accessor, view, rootID, revisionHandler); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + if (oldFetchSize != -1) + { + try + { + stmt.setFetchSize(oldFetchSize); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } + + DBUtil.close(stmt); + } + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + + IDBStore store = mappingStrategy.getStore(); + final DBType idType = store.getIDHandler().getDBType(); + final int idLength = store.getIDColumnLength(); + + IDBDatabase database = store.getDatabase(); + table = database.getSchema().getTable(UNITS); + if (table == null) + { + database.updateSchema(new RunnableWithSchema() + { + public void run(IDBSchema schema) + { + table = schema.addTable(UNITS); + table.addField(UNITS_ELEM, idType, idLength, true); + table.addField(UNITS_UNIT, idType, idLength); + // table.addField(UNITS_CREATED, DBType.BIGINT); + table.addIndex(IDBIndex.Type.PRIMARY_KEY, UNITS_ELEM); + table.addIndex(IDBIndex.Type.NON_UNIQUE, UNITS_UNIT); + } + }); + } + } + + @Override + protected void doDeactivate() throws Exception + { + table = null; + super.doDeactivate(); + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CDOServerProtocol.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CDOServerProtocol.java index 158ec41677..8b87235fcd 100644 --- a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CDOServerProtocol.java +++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/CDOServerProtocol.java @@ -391,6 +391,9 @@ public class CDOServerProtocol extends SignalProtocol<InternalSession> implement case SIGNAL_RESET_TRANSACTION: return new ResetTransactionIndication(this); + case SIGNAL_UNIT: + return new UnitIndication(this); + default: return super.createSignalReactor(signalID); } diff --git a/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/UnitIndication.java b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/UnitIndication.java new file mode 100644 index 0000000000..3ee438696c --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/UnitIndication.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2016 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server.internal.net4j.protocol; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.protocol.CDODataInput; +import org.eclipse.emf.cdo.common.protocol.CDODataOutput; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.server.IUnitManager; +import org.eclipse.emf.cdo.spi.server.InternalView; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import java.io.IOException; + +/** + * @author Eike Stepper + */ +public class UnitIndication extends CDOServerReadIndicationWithMonitoring +{ + private int viewID; + + private CDOID rootID; + + private byte opcode; + + public UnitIndication(CDOServerProtocol protocol) + { + super(protocol, CDOProtocolConstants.SIGNAL_UNIT); + } + + @Override + protected void indicating(CDODataInput in, OMMonitor monitor) throws Exception + { + viewID = in.readInt(); + rootID = in.readCDOID(); + opcode = in.readByte(); + } + + @Override + protected void responding(final CDODataOutput out, OMMonitor monitor) throws Exception + { + if (opcode == CDOProtocolConstants.UNIT_CHECK) + { + IUnitManager unitManager = getRepository().getUnitManager(); + out.writeBoolean(unitManager.isUnit(rootID)); + return; + } + + final IOException[] ioException = { null }; + final RuntimeException[] runtimeException = { null }; + InternalView view = getView(viewID); + + boolean success = view.openUnit(rootID, opcode == CDOProtocolConstants.UNIT_CREATE, new CDORevisionHandler() + { + public boolean handleRevision(CDORevision revision) + { + try + { + out.writeCDORevision(revision, CDORevision.UNCHUNKED); // Exposes revision to client side + return true; + } + catch (IOException ex) + { + ioException[0] = ex; + } + catch (RuntimeException ex) + { + runtimeException[0] = ex; + } + + return false; + } + }, monitor); + + if (ioException[0] != null) + { + throw ioException[0]; + } + + if (runtimeException[0] != null) + { + throw runtimeException[0]; + } + + out.writeCDORevision(null, CDORevision.UNCHUNKED); // No more revisions + out.writeBoolean(success); + + int xxx; + System.out.println("--> DONE"); + } +} 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 25ede606b0..a21da01059 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 @@ -147,7 +147,7 @@ import java.util.concurrent.Semaphore; * @author Eike Stepper * @since 2.0 */ -public class Repository extends Container<Object>implements InternalRepository, IExecutorServiceProvider +public class Repository extends Container<Object> implements InternalRepository, IExecutorServiceProvider { private static final int UNCHUNKED = CDORevision.UNCHUNKED; @@ -169,6 +169,8 @@ public class Repository extends Container<Object>implements InternalRepository, private boolean supportingBranches; + private boolean supportingUnits; + private boolean serializingCommits; private boolean ensuringReferentialIntegrity; @@ -180,7 +182,7 @@ public class Repository extends Container<Object>implements InternalRepository, /** * Must not be thread-bound to support XA commits. */ - private Semaphore packageRegistryCommitLock = new Semaphore(1); + private final Semaphore packageRegistryCommitLock = new Semaphore(1); private InternalCDOPackageRegistry packageRegistry; @@ -202,27 +204,29 @@ public class Repository extends Container<Object>implements InternalRepository, private IManagedContainer container; - private List<ReadAccessHandler> readAccessHandlers = new ArrayList<ReadAccessHandler>(); + private final UnitManager unitManager = new UnitManager(this); - private List<WriteAccessHandler> writeAccessHandlers = new ArrayList<WriteAccessHandler>(); + private final List<ReadAccessHandler> readAccessHandlers = new ArrayList<ReadAccessHandler>(); - private EPackage[] initialPackages; + private final List<WriteAccessHandler> writeAccessHandlers = new ArrayList<WriteAccessHandler>(); // Bug 297940 - private TimeStampAuthority timeStampAuthority = new TimeStampAuthority(this); - - private long lastTreeRestructuringCommit = -1; + private final TimeStampAuthority timeStampAuthority = new TimeStampAuthority(this); @ExcludeFromDump - private transient Object commitTransactionLock = new Object(); + private final transient Object commitTransactionLock = new Object(); @ExcludeFromDump - private transient Object createBranchLock = new Object(); + private final transient Object createBranchLock = new Object(); private boolean skipInitialization; + private EPackage[] initialPackages; + private CDOID rootResourceID; + private long lastTreeRestructuringCommit = -1; + public Repository() { } @@ -355,6 +359,11 @@ public class Repository extends Container<Object>implements InternalRepository, return supportingBranches; } + public boolean isSupportingUnits() + { + return supportingUnits; + } + @Deprecated public boolean isSupportingEcore() { @@ -963,6 +972,11 @@ public class Repository extends Container<Object>implements InternalRepository, return sessionManager; } + public UnitManager getUnitManager() + { + return unitManager; + } + /** * @since 2.0 */ @@ -1918,6 +1932,13 @@ public class Repository extends Container<Object>implements InternalRepository, supportingBranches = store.getRevisionParallelism() == IStore.RevisionParallelism.BRANCHING; } + // SUPPORTING_UNITS + String valueUnits = properties.get(Props.SUPPORTING_UNITS); + if (valueUnits != null) + { + supportingUnits = Boolean.valueOf(valueUnits); + } + // SERIALIZE_COMMITS String valueCommits = properties.get(Props.SERIALIZE_COMMITS); if (valueCommits != null) @@ -2086,6 +2107,7 @@ public class Repository extends Container<Object>implements InternalRepository, newPackageUnitsForRootResource.add(packageUnit); } } + return newPackageUnitsForRootResource.toArray(new InternalCDOPackageUnit[0]); } @@ -2152,7 +2174,7 @@ public class Repository extends Container<Object>implements InternalRepository, checkState(queryManager, "queryManager"); //$NON-NLS-1$ checkState(commitInfoManager, "commitInfoManager"); //$NON-NLS-1$ checkState(commitManager, "commitManager"); //$NON-NLS-1$ - checkState(getLockingManager(), "lockingManager"); //$NON-NLS-1$ + checkState(lockingManager, "lockingManager"); //$NON-NLS-1$ packageRegistry.setReplacingDescriptors(true); packageRegistry.setPackageProcessor(this); @@ -2167,7 +2189,7 @@ public class Repository extends Container<Object>implements InternalRepository, commitInfoManager.setRepository(this); commitInfoManager.setCommitInfoLoader(this); commitManager.setRepository(this); - getLockingManager().setRepository(this); + lockingManager.setRepository(this); store.setRepository(this); } @@ -2221,13 +2243,20 @@ public class Repository extends Container<Object>implements InternalRepository, } LifecycleUtil.activate(lockingManager); // Needs an initialized main branch / branch manager + + if (supportingUnits) + { + LifecycleUtil.activate(unitManager); + } + setPostActivateState(); } @Override protected void doDeactivate() throws Exception { - LifecycleUtil.deactivate(getLockingManager()); + LifecycleUtil.deactivate(unitManager); + LifecycleUtil.deactivate(lockingManager); LifecycleUtil.deactivate(queryHandlerProvider); LifecycleUtil.deactivate(commitManager); LifecycleUtil.deactivate(commitInfoManager); diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java index 2e40085253..fe13338342 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/ServerCDOView.java @@ -51,6 +51,7 @@ import org.eclipse.emf.cdo.view.CDOFetchRuleManager; import org.eclipse.emf.cdo.view.CDOInvalidationPolicy; import org.eclipse.emf.cdo.view.CDORevisionPrefetchingPolicy; import org.eclipse.emf.cdo.view.CDOStaleReferencePolicy; +import org.eclipse.emf.cdo.view.CDOUnitManager; import org.eclipse.emf.cdo.view.CDOView; import org.eclipse.emf.internal.cdo.session.SessionUtil; @@ -464,6 +465,11 @@ public class ServerCDOView extends AbstractCDOView implements org.eclipse.emf.cd throw new UnsupportedOperationException(); } + public CDOUnitManager getUnitManager() + { + throw new UnsupportedOperationException(); + } + /** * @author Eike Stepper */ diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java index e87ac6a685..009f70f55f 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/TransactionCommitContext.java @@ -717,6 +717,9 @@ public class TransactionCommitContext implements InternalCommitContext protected void handleException(Throwable ex) { + int xxx; + ex.printStackTrace(); + try { if (TRACER.isEnabled()) diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/UnitManager.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/UnitManager.java new file mode 100644 index 0000000000..d22194f39b --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/UnitManager.java @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2016 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.internal.server; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.server.IRepository; +import org.eclipse.emf.cdo.server.IStoreAccessor.UnitSupport; +import org.eclipse.emf.cdo.server.IUnit; +import org.eclipse.emf.cdo.server.IUnitManager; +import org.eclipse.emf.cdo.server.IView; +import org.eclipse.emf.cdo.spi.server.InternalRepository; + +import org.eclipse.net4j.util.container.Container; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; + +/** + * @author Eike Stepper + */ +public final class UnitManager extends Container<IUnit> implements IUnitManager +{ + private final InternalRepository repository; + + private final Map<CDOID, IUnit> units = new HashMap<CDOID, IUnit>(); + + private final ReentrantReadWriteLock managerLock = new ReentrantReadWriteLock(); + + public UnitManager(InternalRepository repository) + { + this.repository = repository; + } + + public final IRepository getRepository() + { + return repository; + } + + public boolean isUnit(CDOID rootID) + { + ReadLock readLock = managerLock.readLock(); + readLock.lock(); + + try + { + return units.containsKey(rootID); + } + finally + { + readLock.unlock(); + } + } + + public IUnit createUnit(CDOID rootID, IView view, CDORevisionHandler revisionHandler) + { + WriteLock writeLock = managerLock.writeLock(); + writeLock.lock(); + + try + { + Unit unit; + if (units.containsKey(rootID)) + { + return null; + } + + int xxx; // TODO Check that units are not nested. + + unit = new Unit(rootID); + units.put(rootID, unit); + + // Acquire unit write lock early here, release it in Unit.init() + unit.unitLock.writeLock().lock(); + + writeLock.unlock(); + writeLock = null; + + unit.init(view, revisionHandler); + fireElementAddedEvent(unit); + return unit; + } + finally + { + if (writeLock != null) + { + writeLock.unlock(); + } + } + } + + public IUnit getUnit(CDOID rootID) + { + ReadLock readLock = managerLock.readLock(); + readLock.lock(); + + try + { + return units.get(rootID); + } + finally + { + readLock.unlock(); + } + } + + public IUnit[] getUnits() + { + return getElements(); + } + + public IUnit[] getElements() + { + ReadLock readLock = managerLock.readLock(); + readLock.lock(); + + try + { + return units.values().toArray(new IUnit[units.size()]); + } + finally + { + readLock.unlock(); + } + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + + UnitSupport unitSupport = (UnitSupport)repository.getStore().getReader(null); + + try + { + List<CDOID> roots = unitSupport.readUnitRoots(); + for (CDOID root : roots) + { + IUnit unit = new Unit(root); + units.put(root, unit); + } + } + finally + { + unitSupport.release(); + } + } + + @Override + protected void doDeactivate() throws Exception + { + units.clear(); + super.doDeactivate(); + } + + /** + * @author Eike Stepper + */ + public final class Unit implements IUnit + { + private final CDOID rootID; + + private final Set<IView> views = new HashSet<IView>(); + + private final ReentrantReadWriteLock unitLock = new ReentrantReadWriteLock(); + + public Unit(CDOID rootID) + { + this.rootID = rootID; + } + + public IUnitManager getManager() + { + return UnitManager.this; + } + + public CDOID getRootID() + { + return rootID; + } + + public void init(IView view, CDORevisionHandler revisionHandler) + { + // Write lock has been acquired by UnitManager.createUnit() + + try + { + UnitSupport unitSupport = (UnitSupport)repository.getStore().getWriter(null); + + try + { + unitSupport.initUnit(view, rootID, revisionHandler); + } + finally + { + unitSupport.release(); + } + } + finally + { + // Write lock has been acquired by UnitManager.createUnit() + unitLock.writeLock().unlock(); + } + } + + public boolean isOpen() + { + ReadLock readLock = unitLock.readLock(); + readLock.lock(); + + try + { + return !views.isEmpty(); + } + finally + { + readLock.unlock(); + } + } + + public void open(IView view, final CDORevisionHandler revisionHandler) + { + ReadLock readLock = unitLock.readLock(); + readLock.lock(); + + try + { + views.add(view); + + UnitSupport unitSupport = (UnitSupport)repository.getStore().getReader(null); + + try + { + unitSupport.readUnit(view, rootID, revisionHandler); + } + finally + { + unitSupport.release(); + } + } + finally + { + readLock.unlock(); + } + } + + public void close(IView view) + { + ReadLock readLock = unitLock.readLock(); + readLock.lock(); + + try + { + views.remove(view); + } + finally + { + readLock.unlock(); + } + } + + @Override + public String toString() + { + return "Unit[" + rootID + "]"; + } + } +} diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java index 9b3f19187a..ec3f221e34 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/View.java @@ -15,9 +15,13 @@ import org.eclipse.emf.cdo.common.CDOCommonView; import org.eclipse.emf.cdo.common.branch.CDOBranch; import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; import org.eclipse.emf.cdo.common.revision.CDORevisionManager; import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; +import org.eclipse.emf.cdo.server.IUnit; +import org.eclipse.emf.cdo.server.IUnitManager; import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil; import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranchManager; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; @@ -29,6 +33,7 @@ import org.eclipse.emf.cdo.spi.server.InternalView; import org.eclipse.net4j.util.AdapterUtil; import org.eclipse.net4j.util.ObjectUtil; import org.eclipse.net4j.util.lifecycle.Lifecycle; +import org.eclipse.net4j.util.om.monitor.OMMonitor; import org.eclipse.net4j.util.options.IOptionsContainer; import org.eclipse.net4j.util.registry.HashMapRegistry; import org.eclipse.net4j.util.registry.IRegistry; @@ -57,9 +62,11 @@ public class View extends Lifecycle implements InternalView, CDOCommonView.Optio private String durableLockingID; - private InternalRepository repository; + private final InternalRepository repository; - private Set<CDOID> changeSubscriptionIDs = new HashSet<CDOID>(); + private final Set<CDOID> changeSubscriptionIDs = new HashSet<CDOID>(); + + private final Set<CDOID> openUnitRoots = new HashSet<CDOID>(); private boolean lockNotificationsEnabled; @@ -155,7 +162,7 @@ public class View extends Lifecycle implements InternalView, CDOCommonView.Optio List<CDORevision> oldRevisions = getRevisions(invalidObjects); setBranchPoint(branchPoint); List<CDORevision> newRevisions = getRevisions(invalidObjects); - + Iterator<CDORevision> it = newRevisions.iterator(); for (CDORevision oldRevision : oldRevisions) { @@ -169,7 +176,7 @@ public class View extends Lifecycle implements InternalView, CDOCommonView.Optio // Fix for Bug 369646: ensure that revisions are fully loaded repository.ensureChunks((InternalCDORevision)newRevision, CDORevision.UNCHUNKED); repository.ensureChunks((InternalCDORevision)oldRevision, CDORevision.UNCHUNKED); - + CDORevisionDelta delta = newRevision.compare(oldRevision); allChangedObjects.add(delta); } @@ -199,6 +206,64 @@ public class View extends Lifecycle implements InternalView, CDOCommonView.Optio this.durableLockingID = durableLockingID; } + public boolean openUnit(CDOID rootID, boolean create, CDORevisionHandler revisionHandler, OMMonitor monitor) + { + IUnitManager unitManager = repository.getUnitManager(); + IUnit unit = unitManager.getUnit(rootID); + + if (create) + { + if (unit != null) + { + return false; + } + + unit = unitManager.createUnit(rootID, this, revisionHandler); + } + else + { + if (unit == null) + { + return false; + } + + int xxx; // TODO Use monitor? + unit.open(this, revisionHandler); + } + + openUnitRoots.add(rootID); + return true; + } + + private boolean isInOpenUnit(CDOID id) + { + if (openUnitRoots.isEmpty()) + { + return false; + } + + if (openUnitRoots.contains(id)) + { + return true; + } + + int xxx; // TODO Remember object->unit relationship for modified objects?! + + InternalCDORevision revision = getRevision(id); + if (revision != null) + { + CDOID parentID = revision.getResourceID(); + if (CDOIDUtil.isNull(parentID)) + { + parentID = (CDOID)revision.getContainerID(); + } + + return isInOpenUnit(parentID); + } + + return false; + } + /** * @since 2.0 */ @@ -227,7 +292,17 @@ public class View extends Lifecycle implements InternalView, CDOCommonView.Optio return false; } - return changeSubscriptionIDs.contains(id); + if (changeSubscriptionIDs.contains(id)) + { + return true; + } + + if (repository.isSupportingUnits()) + { + return isInOpenUnit(id); + } + + return false; } /** diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java index 75db50bbc4..8912da605e 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/embedded/EmbeddedClientSessionProtocol.java @@ -653,6 +653,11 @@ public class EmbeddedClientSessionProtocol extends Lifecycle implements CDOSessi throw new UnsupportedOperationException(); } + public boolean requestUnit(int viewID, CDOID rootID, byte opcode, CDORevisionHandler revisionHandler) + { + throw new UnsupportedOperationException(); + } + @Override protected void doActivate() throws Exception { diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java index 738ee52aff..5de431d616 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IRepository.java @@ -86,6 +86,11 @@ public interface IRepository extends CDOCommonRepository, IQueryHandlerProvider, public ISessionManager getSessionManager(); /** + * @since 4.5 + */ + public IUnitManager getUnitManager(); + + /** * @since 4.0 * @deprecated As of 4.1 use {@link #getLockingManager()}. */ @@ -319,6 +324,11 @@ public interface IRepository extends CDOCommonRepository, IQueryHandlerProvider, public static final String SUPPORTING_BRANCHES = "supportingBranches"; //$NON-NLS-1$ /** + * @since 4.5 + */ + public static final String SUPPORTING_UNITS = "supportingUnits"; //$NON-NLS-1$ + + /** * @since 4.0 * @deprecated As of 4.2 instances of Ecore are always supported (on demand). */ diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java index 62059a2667..2255128941 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IStoreAccessor.java @@ -798,4 +798,20 @@ public interface IStoreAccessor extends IQueryHandlerProvider, BranchLoader, Com public void updateLockArea(LockArea lockArea); } + + /** + * An extension interface for {@link IStoreAccessor store accessors} that support <i>units</i>. + * + * @author Eike Stepper + * @since 4.5 + * @apiviz.exclude + */ + public interface UnitSupport extends IStoreAccessor + { + public List<CDOID> readUnitRoots(); + + public void initUnit(IView view, CDOID rootID, CDORevisionHandler revisionHandler); + + public void readUnit(IView view, CDOID rootID, CDORevisionHandler revisionHandler); + } } diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IUnit.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IUnit.java new file mode 100644 index 0000000000..a41352d478 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IUnit.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2016 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; + +/** + * @author Eike Stepper + * @since 4.5 + */ +public interface IUnit +{ + public IUnitManager getManager(); + + public CDOID getRootID(); + + public boolean isOpen(); + + public void open(IView view, CDORevisionHandler revisionHandler); + + public void close(IView view); +} diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IUnitManager.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IUnitManager.java new file mode 100644 index 0000000000..6c373e9383 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IUnitManager.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.server; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; + +import org.eclipse.net4j.util.container.IContainer; + +/** + * @author Eike Stepper + * @since 4.5 + */ +public interface IUnitManager extends IContainer<IUnit> +{ + public IRepository getRepository(); + + public IUnit createUnit(CDOID rootID, IView view, CDORevisionHandler revisionHandler); + + public boolean isUnit(CDOID rootID); + + public IUnit getUnit(CDOID rootID); + + public IUnit[] getUnits(); +} diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java index c86b01b557..6b60df51a4 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalRepository.java @@ -262,6 +262,11 @@ public interface InternalRepository extends IRepository, PackageProcessor, Packa public void setSkipInitialization(boolean skipInitialization); /** + * @since 4.5 + */ + public boolean isSupportingUnits(); + + /** * @since 4.0 * @deprecated As of 4.3 use {@link #initSystemPackages()}. */ diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalView.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalView.java index f6e8857176..1127725369 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalView.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalView.java @@ -12,10 +12,12 @@ package org.eclipse.emf.cdo.spi.server; import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; import org.eclipse.emf.cdo.server.IView; import org.eclipse.net4j.util.lifecycle.ILifecycle; +import org.eclipse.net4j.util.om.monitor.OMMonitor; import java.util.List; @@ -55,4 +57,9 @@ public interface InternalView extends IView, ILifecycle public void clearChangeSubscription(); public void doClose(); + + /** + * @since 4.5 + */ + public boolean openUnit(CDOID rootID, boolean create, CDORevisionHandler revisionHandler, OMMonitor monitor); } diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/Store.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/Store.java index 931a4b75c1..ca55a80ea4 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/Store.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/Store.java @@ -400,7 +400,12 @@ public abstract class Store extends Lifecycle implements InternalStore { CDORevisionFactory factory = repository.getRevisionManager().getFactory(); InternalCDORevision revision = (InternalCDORevision)factory.createRevision(eClass); - revision.setID(id); + + if (id != null) + { + revision.setID(id); + } + return revision; } diff --git a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessor.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessor.java index 0ff8592ca5..c570d5a32c 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessor.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/StoreAccessor.java @@ -77,9 +77,11 @@ public abstract class StoreAccessor extends StoreAccessorBase { monitor.begin(1 + newPackageUnits.length + 2 + newObjects.length + detachedObjects.length + dirtyCount + 1); - writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, commitComment, monitor.fork()); + int writePackageUnits; // TODO Extra commit needed for Mysql between DDL and DML. writePackageUnits(newPackageUnits, monitor.fork(newPackageUnits.length)); + writeCommitInfo(branch, timeStamp, previousTimeStamp, userID, commitComment, monitor.fork()); + IDGenerationLocation idGenerationLocation = store.getRepository().getIDGenerationLocation(); if (idGenerationLocation == IDGenerationLocation.STORE) { diff --git a/plugins/org.eclipse.emf.cdo.tests.db/CDO AllTests (H2 audit).launch b/plugins/org.eclipse.emf.cdo.tests.db/CDO AllTests (H2 audit).launch index d00e13136c..b3a9432737 100644 --- a/plugins/org.eclipse.emf.cdo.tests.db/CDO AllTests (H2 audit).launch +++ b/plugins/org.eclipse.emf.cdo.tests.db/CDO AllTests (H2 audit).launch @@ -12,5 +12,5 @@ <stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit3"/> <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.emf.cdo.tests.db.AllTestsDBH2Audit"/> <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.emf.cdo.tests.db"/> -<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms40m -Xmx1024m -Dorg.eclipse.net4j.util.om.trace.disable=true"/> +<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms1G -Xmx4G -Dorg.eclipse.net4j.util.om.trace.disable=true -Dorg.eclipse.emf.cdo.tests.config.impl.RepositoryConfig.enableServerBrowser=true"/> </launchConfiguration> diff --git a/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/MysqlConfig.java b/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/MysqlConfig.java index 82f69fb120..d4185a5e8b 100644 --- a/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/MysqlConfig.java +++ b/plugins/org.eclipse.emf.cdo.tests.db/src/org/eclipse/emf/cdo/tests/db/MysqlConfig.java @@ -35,9 +35,11 @@ public class MysqlConfig extends AbstractSetupDBConfig */ public static final String HOST = "localhost"; - public static final String USER = "root"; + public static final String SCHEMA = "test"; - public static final String PASS = "root"; + public static final String USER = "test"; + + public static final String PASS = "0000"; private static final long serialVersionUID = 1L; @@ -62,7 +64,7 @@ public class MysqlConfig extends AbstractSetupDBConfig protected DataSource createDataSourceForDB(String dbName) throws SQLException { MysqlDataSource dataSource = new MysqlDataSource(); - dataSource.setUrl("jdbc:mysql://" + HOST); + dataSource.setUrl("jdbc:mysql://" + HOST + "/" + SCHEMA); dataSource.setUser(USER); if (PASS != null) { 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 1598df139e..af1783cc04 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 @@ -116,7 +116,8 @@ public class AllConfigs extends ConfigTestSuite testClasses.add(WorkspaceTest.class); testClasses.add(BackupTest.class); testClasses.add(ResourceModificationTrackingTest.class); - testClasses.add(CDOStaleReferencePolicyTests.class); + testClasses.add(CDOStaleReferencePolicyTest.class); + testClasses.add(UnitManagerTest.class); // TODO testClasses.add(RemoteSessionManagerTest.class); // TODO testClasses.add(NonCDOResourceTest.class); diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/CDOStaleReferencePolicyTests.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/CDOStaleReferencePolicyTest.java index 56500ab5a3..9d4634c2ea 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/CDOStaleReferencePolicyTests.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/CDOStaleReferencePolicyTest.java @@ -29,7 +29,7 @@ import org.eclipse.emf.ecore.util.EcoreUtil; * * @author Esteban Dugueperoux */ -public class CDOStaleReferencePolicyTests extends AbstractCDOTest +public class CDOStaleReferencePolicyTest extends AbstractCDOTest { private static final String RESOURCE_NAME = "test1.model1"; diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/UnitManagerMain.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/UnitManagerMain.java new file mode 100644 index 0000000000..33e8c17d58 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/UnitManagerMain.java @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2016 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.tests; + +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.eresource.CDOResource; +import org.eclipse.emf.cdo.net4j.CDONet4jSessionConfiguration; +import org.eclipse.emf.cdo.net4j.CDONet4jUtil; +import org.eclipse.emf.cdo.server.net4j.CDONet4jServerUtil; +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.Customer; +import org.eclipse.emf.cdo.tests.model1.Model1Factory; +import org.eclipse.emf.cdo.tests.model1.Model1Package; +import org.eclipse.emf.cdo.tests.model1.OrderDetail; +import org.eclipse.emf.cdo.tests.model1.Product1; +import org.eclipse.emf.cdo.tests.model1.PurchaseOrder; +import org.eclipse.emf.cdo.tests.model1.SalesOrder; +import org.eclipse.emf.cdo.tests.model1.Supplier; +import org.eclipse.emf.cdo.transaction.CDOTransaction; + +import org.eclipse.net4j.Net4jUtil; +import org.eclipse.net4j.connector.IConnector; +import org.eclipse.net4j.tcp.TCPUtil; +import org.eclipse.net4j.util.container.IPluginContainer; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.InternalEList; + +import java.util.Iterator; + +/** + * @author Eike Stepper + */ +public class UnitManagerMain +{ + private static final String RESOURCE_NAME = "test"; + + private static final boolean ADD_UNIQUE = true; + + private static long start; + + /** + * @author Eike Stepper + */ + public static final class FillRepository + { + public static void main(String[] args) throws Exception + { + CDOSession session = openSession(); + + try + { + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.createResource(RESOURCE_NAME); + + for (int i = 0; i < 1; i++) + { + start("Fill " + i); + Company company = Model1Factory.eINSTANCE.createCompany(); + add(resource.getContents(), company); + fillCompany(company); + stop(); + + start("Commit " + i); + transaction.commit(); + stop(); + } + } + finally + { + session.close(); + } + } + + private static void fillCompany(Company company) + { + for (int i = 0; i < 5; i++) + { + Category category = Model1Factory.eINSTANCE.createCategory(); + add(company.getCategories(), category); + fillCategory(category, 5); + } + + for (int i = 0; i < 1000; i++) + { + Supplier supplier = Model1Factory.eINSTANCE.createSupplier(); + add(company.getSuppliers(), supplier); + } + + for (int i = 0; i < 1000; i++) + { + Customer customer = Model1Factory.eINSTANCE.createCustomer(); + add(company.getCustomers(), customer); + } + + for (int i = 0; i < 1000; i++) + { + PurchaseOrder order = Model1Factory.eINSTANCE.createPurchaseOrder(); + order.setSupplier(company.getSuppliers().get(i)); + add(company.getPurchaseOrders(), order); + + for (int j = 0; j < 100; j++) + { + OrderDetail orderDetail = Model1Factory.eINSTANCE.createOrderDetail(); + add(order.getOrderDetails(), orderDetail); + } + } + + for (int i = 0; i < 1000; i++) + { + SalesOrder order = Model1Factory.eINSTANCE.createSalesOrder(); + order.setCustomer(company.getCustomers().get(i)); + add(company.getSalesOrders(), order); + + for (int j = 0; j < 100; j++) + { + OrderDetail orderDetail = Model1Factory.eINSTANCE.createOrderDetail(); + add(order.getOrderDetails(), orderDetail); + } + } + } + + private static void fillCategory(Category category, int depth) + { + for (int i = 0; i < 5; i++) + { + Category child = Model1Factory.eINSTANCE.createCategory(); + add(category.getCategories(), child); + if (depth > 1) + { + fillCategory(child, depth - 1); + } + } + + for (int i = 0; i < 20; i++) + { + Product1 product = Model1Factory.eINSTANCE.createProduct1(); + add(category.getProducts(), product); + } + } + + private static <T extends EObject> void add(EList<T> list, T object) + { + if (ADD_UNIQUE) + { + ((InternalEList<T>)list).addUnique(object); + } + else + { + list.add(object); + } + } + } + + /** + * @author Eike Stepper + */ + public static final class PrefetchResource + { + public static void main(String[] args) throws Exception + { + CDOSession session = openSession(); + + try + { + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(RESOURCE_NAME); + + start("Prefetch Resource"); + resource.cdoPrefetch(CDORevision.DEPTH_INFINITE); + stop(); + + iterateResource(resource); + } + finally + { + session.close(); + } + } + } + + /** + * @author Eike Stepper + */ + public static final class CreateUnit + { + public static void main(String[] args) throws Exception + { + CDOSession session = openSession(); + + try + { + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(RESOURCE_NAME); + + start("Create Unit"); + resource.cdoView().getUnitManager().createUnit(resource); + stop(); + + iterateResource(resource); + } + finally + { + session.close(); + } + } + } + + /** + * @author Eike Stepper + */ + public static final class OpenUnit + { + public static void main(String[] args) throws Exception + { + CDOSession session = openSession(); + + try + { + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(RESOURCE_NAME); + + start("Open Unit"); + resource.cdoView().getUnitManager().openUnit(resource); + stop(); + + iterateResource(resource); + } + finally + { + session.close(); + } + } + } + + private static CDOSession openSession() + { + IConnector connector = TCPUtil.getConnector(IPluginContainer.INSTANCE, "localhost"); + + CDONet4jSessionConfiguration configuration = CDONet4jUtil.createNet4jSessionConfiguration(); + configuration.setConnector(connector); + configuration.setRepositoryName("repo1"); + return configuration.openNet4jSession(); + } + + private static void iterateResource(CDOResource resource) + { + start("Iterate"); + for (Iterator<EObject> it = resource.eAllContents(); it.hasNext();) + { + it.next(); + } + + stop(); + } + + private static void start(String msg) + { + start = System.currentTimeMillis(); + System.out.print(msg + ": "); + } + + private static void stop() + { + long stop = System.currentTimeMillis(); + System.out.println(stop - start); + start = stop; + } + + static + { + Model1Package.eINSTANCE.getClass(); + + Net4jUtil.prepareContainer(IPluginContainer.INSTANCE); + TCPUtil.prepareContainer(IPluginContainer.INSTANCE); + CDONet4jServerUtil.prepareContainer(IPluginContainer.INSTANCE); + CDONet4jUtil.prepareContainer(IPluginContainer.INSTANCE); + IPluginContainer.INSTANCE.activate(); + } +} diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/UnitManagerTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/UnitManagerTest.java new file mode 100644 index 0000000000..c68b879593 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/UnitManagerTest.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2016 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.tests; + +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.eresource.CDOResource; +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.Customer; +import org.eclipse.emf.cdo.tests.model1.OrderDetail; +import org.eclipse.emf.cdo.tests.model1.Product1; +import org.eclipse.emf.cdo.tests.model1.PurchaseOrder; +import org.eclipse.emf.cdo.tests.model1.SalesOrder; +import org.eclipse.emf.cdo.tests.model1.Supplier; +import org.eclipse.emf.cdo.transaction.CDOTransaction; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.InternalEList; + +import java.util.Iterator; + +/** + * @author Eike Stepper + */ +public class UnitManagerTest extends AbstractCDOTest +{ + private static final String RESOURCE_NAME = "test"; + + public void testPrefetchBigModel() throws Exception + { + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.createResource(getResourcePath(RESOURCE_NAME)); + + long start = System.currentTimeMillis(); + for (int i = 0; i < 10; i++) + { + Company company = getModel1Factory().createCompany(); + addUnique(resource.getContents(), company); + fillCompany(company); + long stop = System.currentTimeMillis(); + System.out.println("Filled " + i + ": " + (stop - start)); + + start = stop; + transaction.commit(); + stop = System.currentTimeMillis(); + System.out.println("Committed " + i + ": " + (stop - start)); + start = stop; + } + + session.close(); + System.out.println(); + } + + if (true) + { + clearCache(getRepository().getRevisionManager()); + } + + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath(RESOURCE_NAME)); + + if (true) + { + long start = System.currentTimeMillis(); + resource.cdoPrefetch(CDORevision.DEPTH_INFINITE); + long stop = System.currentTimeMillis(); + System.out.println("Prefetched: " + (stop - start)); + } + + long start = System.currentTimeMillis(); + for (Iterator<EObject> it = resource.eAllContents(); it.hasNext();) + { + it.next(); + } + + long stop = System.currentTimeMillis(); + System.out.println("Iterated: " + (stop - start)); + + session.close(); + System.out.println(); + } + } + + private void fillCompany(Company company) + { + for (int i = 0; i < 5; i++) + { + Category category = getModel1Factory().createCategory(); + addUnique(company.getCategories(), category); + fillCategory(category, 5); + } + + for (int i = 0; i < 1000; i++) + { + Supplier supplier = getModel1Factory().createSupplier(); + addUnique(company.getSuppliers(), supplier); + } + + for (int i = 0; i < 1000; i++) + { + Customer customer = getModel1Factory().createCustomer(); + addUnique(company.getCustomers(), customer); + } + + for (int i = 0; i < 1000; i++) + { + PurchaseOrder order = getModel1Factory().createPurchaseOrder(); + order.setSupplier(company.getSuppliers().get(i)); + addUnique(company.getPurchaseOrders(), order); + + for (int j = 0; j < 100; j++) + { + OrderDetail orderDetail = getModel1Factory().createOrderDetail(); + addUnique(order.getOrderDetails(), orderDetail); + } + } + + for (int i = 0; i < 1000; i++) + { + SalesOrder order = getModel1Factory().createSalesOrder(); + order.setCustomer(company.getCustomers().get(i)); + addUnique(company.getSalesOrders(), order); + + for (int j = 0; j < 100; j++) + { + OrderDetail orderDetail = getModel1Factory().createOrderDetail(); + addUnique(order.getOrderDetails(), orderDetail); + } + } + } + + private void fillCategory(Category category, int depth) + { + for (int i = 0; i < 5; i++) + { + Category child = getModel1Factory().createCategory(); + addUnique(category.getCategories(), child); + if (depth > 1) + { + fillCategory(child, depth - 1); + } + } + + for (int i = 0; i < 20; i++) + { + Product1 product = getModel1Factory().createProduct1(); + addUnique(category.getProducts(), product); + } + } + + private static <T extends EObject> void addUnique(EList<T> list, T object) + { + ((InternalEList<T>)list).addUnique(object); + // list.add(object); + } +} diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_485961_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_485961_Test.java index 7abbea6672..92c0c44cce 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_485961_Test.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_485961_Test.java @@ -54,13 +54,12 @@ public class Bugzilla_485961_Test extends AbstractCDOTest InternalRepository repository = getRepository(); CDORevisionProvider revisionProvider = new ManagedRevisionProvider(repository.getRevisionManager(), repository.getBranchManager().getMainBranch().getHead()); - CDOID rootResourceID = repository.getRootResourceID(); StoreThreadLocal.setSession(repository.getSessionManager().getSession(session.getSessionID())); try { - assertEquals(8, traverse(revisionProvider, rootResourceID)); + assertEquals(4, traverse(revisionProvider, res1.cdoID())); } finally { diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOPushTransaction.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOPushTransaction.java index 13ef778aaf..f139e2a2a7 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOPushTransaction.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/transaction/CDOPushTransaction.java @@ -32,6 +32,7 @@ import org.eclipse.emf.cdo.session.CDOSession; import org.eclipse.emf.cdo.util.CommitException; import org.eclipse.emf.cdo.view.CDOObjectHandler; import org.eclipse.emf.cdo.view.CDOQuery; +import org.eclipse.emf.cdo.view.CDOUnitManager; import org.eclipse.emf.cdo.view.CDOView; import org.eclipse.emf.cdo.view.CDOViewProvider; import org.eclipse.emf.cdo.view.CDOViewSet; @@ -954,6 +955,14 @@ public class CDOPushTransaction extends Notifier implements CDOTransaction return delegate.getURIHandler(); } + /** + * @since 4.5 + */ + public CDOUnitManager getUnitManager() + { + return delegate.getUnitManager(); + } + public String getCommitComment() { return delegate.getCommitComment(); diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOUnit.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOUnit.java new file mode 100644 index 0000000000..26acbe17e0 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOUnit.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2016 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.view; + +import org.eclipse.emf.ecore.EObject; + +/** + * @author Eike Stepper + * @since 4.5 + */ +public interface CDOUnit +{ + public CDOUnitManager getManager(); + + public EObject getRoot(); + + public void close(); +} diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOUnitManager.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOUnitManager.java new file mode 100644 index 0000000000..d31139c633 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOUnitManager.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016 Eike Stepper (Berlin, Germany) and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Eike Stepper - initial API and implementation + */ +package org.eclipse.emf.cdo.view; + +import org.eclipse.emf.cdo.common.util.CDOException; + +import org.eclipse.net4j.util.container.IContainer; + +import org.eclipse.emf.ecore.EObject; + +/** + * @author Eike Stepper + * @since 4.5 + */ +public interface CDOUnitManager extends IContainer<CDOUnit> +{ + public CDOView getView(); + + public boolean isUnit(EObject root); + + public CDOUnit createUnit(EObject root) throws UnitExistsException; + + public CDOUnit openUnit(EObject root) throws UnitNotFoundException; + + public CDOUnit getOpenUnit(EObject object); + + public CDOUnit[] getOpenUnits(); + + /** + * @author Eike Stepper + */ + public static final class UnitExistsException extends CDOException + { + private static final long serialVersionUID = 1L; + } + + /** + * @author Eike Stepper + */ + public static final class UnitNotFoundException extends CDOException + { + private static final long serialVersionUID = 1L; + } +} diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOView.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOView.java index 223ced6a12..23bba9b120 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOView.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOView.java @@ -155,6 +155,11 @@ public interface CDOView extends CDOCommonView, CDOUpdatable, CDOCommitHistory.P public URIHandler getURIHandler(); /** + * @since 4.5 + */ + public CDOUnitManager getUnitManager(); + + /** * Sets the {@link CDOBranch branch} and the point in (repository) time this view should refer to. {@link CDOObject * Objects} provided by this view will be {@link CDORevision#isValid(long) valid} at this time. The special value * {@link CDOCommonView#UNSPECIFIED_DATE UNSPECIFIED_DATE} denotes a "floating view" that always shows the latest diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/DelegatingSessionProtocol.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/DelegatingSessionProtocol.java index d616a5ad11..7ce0726286 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/DelegatingSessionProtocol.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/DelegatingSessionProtocol.java @@ -1061,6 +1061,22 @@ public class DelegatingSessionProtocol extends Lifecycle implements CDOSessionPr } } + public boolean requestUnit(int viewID, CDOID rootID, byte opcode, CDORevisionHandler revisionHandler) + { + int attempt = 0; + for (;;) + { + try + { + return delegate.requestUnit(viewID, rootID, opcode, revisionHandler); + } + catch (Exception ex) + { + handleException(++attempt, ex); + } + } + } + @Override protected void doActivate() throws Exception { diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java index 71f3f4ed2b..bfa3444de4 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/CDOViewImpl.java @@ -25,9 +25,11 @@ import org.eclipse.emf.cdo.common.lock.CDOLockChangeInfo.Operation; import org.eclipse.emf.cdo.common.lock.CDOLockOwner; import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.lock.CDOLockUtil; +import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; import org.eclipse.emf.cdo.common.revision.CDOIDAndBranch; import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; import org.eclipse.emf.cdo.common.revision.CDORevisionKey; import org.eclipse.emf.cdo.common.revision.CDORevisionManager; import org.eclipse.emf.cdo.common.revision.CDORevisionsLoadedEvent; @@ -51,6 +53,8 @@ import org.eclipse.emf.cdo.view.CDOFeatureAnalyzer; import org.eclipse.emf.cdo.view.CDOInvalidationPolicy; import org.eclipse.emf.cdo.view.CDORevisionPrefetchingPolicy; import org.eclipse.emf.cdo.view.CDOStaleReferencePolicy; +import org.eclipse.emf.cdo.view.CDOUnit; +import org.eclipse.emf.cdo.view.CDOUnitManager; import org.eclipse.emf.cdo.view.CDOView; import org.eclipse.emf.cdo.view.CDOViewDurabilityChangedEvent; import org.eclipse.emf.cdo.view.CDOViewInvalidationEvent; @@ -74,6 +78,7 @@ import org.eclipse.net4j.util.concurrent.IExecutorServiceProvider; import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType; import org.eclipse.net4j.util.concurrent.IWorkSerializer; import org.eclipse.net4j.util.concurrent.RunnableWithName; +import org.eclipse.net4j.util.container.Container; import org.eclipse.net4j.util.event.IEvent; import org.eclipse.net4j.util.event.IListener; import org.eclipse.net4j.util.event.Notifier; @@ -95,6 +100,7 @@ import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.notify.NotificationChain; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.InternalEObject; +import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.spi.cdo.CDOSessionProtocol; import org.eclipse.emf.spi.cdo.CDOSessionProtocol.LockObjectsResult; import org.eclipse.emf.spi.cdo.CDOSessionProtocol.UnlockObjectsResult; @@ -113,6 +119,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -135,6 +142,8 @@ public class CDOViewImpl extends AbstractCDOView implements IExecutorServiceProv private String durableLockingID; + private final CDOUnitManagerImpl unitManager = new CDOUnitManagerImpl(); + private ChangeSubscriptionManager changeSubscriptionManager = new ChangeSubscriptionManager(); private AdapterManager adapterManager = new AdapterManager(); @@ -1805,6 +1814,247 @@ public class CDOViewImpl extends AbstractCDOView implements IExecutorServiceProv return id; } + public final CDOUnitManagerImpl getUnitManager() + { + return unitManager; + } + + /** + * @author Eike Stepper + */ + protected final class CDOUnitManagerImpl extends Container<CDOUnit> implements CDOUnitManager + { + private final Map<EObject, CDOUnit> unitPerRoot = new HashMap<EObject, CDOUnit>(); + + private final Map<EObject, CDOUnit> unitPerObject = new HashMap<EObject, CDOUnit>(); + + public CDOUnitManagerImpl() + { + } + + public CDOView getView() + { + return CDOViewImpl.this; + } + + public boolean isUnit(EObject root) + { + CDOUnitImpl unit = requestUnit(root, CDOProtocolConstants.UNIT_CHECK); + return unit != null; + } + + public CDOUnit createUnit(EObject root) throws UnitExistsException + { + CDOUnitImpl unit = requestUnit(root, CDOProtocolConstants.UNIT_CREATE); + if (unit == null) + { + throw new UnitExistsException(); + } + + fireElementAddedEvent(unit); + return unit; + } + + public CDOUnit openUnit(EObject root) throws UnitNotFoundException + { + CDOUnitImpl unit = requestUnit(root, CDOProtocolConstants.UNIT_OPEN); + if (unit == null) + { + throw new UnitNotFoundException(); + } + + fireElementAddedEvent(unit); + return unit; + } + + public CDOUnit getOpenUnit(EObject object) + { + synchronized (getViewMonitor()) + { + lockView(); + + try + { + return getOpenUnitUnsynced(object); + } + finally + { + unlockView(); + } + } + } + + public CDOUnit getOpenUnitUnsynced(EObject object) + { + return unitPerObject.get(object); + } + + public CDOUnit[] getOpenUnits() + { + return getElements(); + } + + public CDOUnit[] getElements() + { + synchronized (getViewMonitor()) + { + lockView(); + + try + { + return unitPerRoot.values().toArray(new CDOUnit[unitPerRoot.size()]); + } + finally + { + unlockView(); + } + } + } + + private CDOObject getCDORoot(EObject root) + { + CDOObject cdoRoot = CDOUtil.getCDOObject(root); + if (cdoRoot == null) + { + throw new IllegalArgumentException("Root " + root + " is not managed by CDO"); + } + + CDOView view = cdoRoot.cdoView(); + if (view != CDOViewImpl.this) + { + throw new IllegalArgumentException("Root " + root + " is managed by " + view); + } + + return cdoRoot; + } + + private CDOUnitImpl requestUnit(EObject root, byte opcode) + { + synchronized (getViewMonitor()) + { + lockView(); + + try + { + CDOUnit containingUnit = getOpenUnit(root); + if (containingUnit != null) + { + throw new IllegalArgumentException("Root " + root + " is contained by " + containingUnit); + } + + for (CDOUnit existingUnit : unitPerRoot.values()) + { + if (EcoreUtil.isAncestor(root, existingUnit.getRoot())) + { + throw new IllegalArgumentException("Root " + root + " contains " + existingUnit); + } + } + + final InternalCDORevisionManager revisionManager = session.getRevisionManager(); + final CDOUnitImpl unit = new CDOUnitImpl(root); + + int viewID = getViewID(); + CDOID rootID = getCDORoot(root).cdoID(); + + CDOSessionProtocol sessionProtocol = session.getSessionProtocol(); + boolean success = sessionProtocol.requestUnit(viewID, rootID, opcode, new CDORevisionHandler() + { + public boolean handleRevision(CDORevision revision) + { + revisionManager.addRevision(revision); + + InternalCDOObject object = getObject(revision.getID()); + unitPerObject.put(object, unit); + + int xxx; // TODO Release possible change subscriptions! + return true; + } + }); + + if (success) + { + if (opcode == CDOProtocolConstants.UNIT_CREATE || opcode == CDOProtocolConstants.UNIT_OPEN) + { + unitPerRoot.put(root, unit); + unitPerObject.put(root, unit); + } + + return unit; + } + + return null; + } + finally + { + unlockView(); + } + } + } + + private void closeUnit(CDOUnit unit) + { + synchronized (getViewMonitor()) + { + lockView(); + + try + { + for (Iterator<CDOUnit> it = unitPerObject.values().iterator(); it.hasNext();) + { + if (it.next() == unit) + { + int xxx; // TODO Re-create needed change subscriptions? + + it.remove(); + } + } + + unitPerRoot.remove(unit.getRoot()); + } + finally + { + unlockView(); + } + } + + fireElementRemovedEvent(unit); + } + + /** + * @author Eike Stepper + */ + protected final class CDOUnitImpl implements CDOUnit + { + private final EObject root; + + public CDOUnitImpl(EObject root) + { + this.root = root; + } + + public CDOUnitManagerImpl getManager() + { + return CDOUnitManagerImpl.this; + } + + public EObject getRoot() + { + return root; + } + + public void close() + { + closeUnit(this); + } + + @Override + public String toString() + { + return "CDOUnit[" + root + "]"; + } + } + } + /** * @author Simon McDuff * @since 2.0 @@ -1926,7 +2176,7 @@ public class CDOViewImpl extends AbstractCDOView implements IExecutorServiceProv /** * Register to the server all objects from the active list */ - private void notifyChangeSubcriptionPolicy() + private void handleChangeSubcriptionPoliciesChanged() { boolean policiesPresent = options().hasChangeSubscriptionPolicies(); subscriptions.clear(); @@ -2814,7 +3064,7 @@ public class CDOViewImpl extends AbstractCDOView implements IExecutorServiceProv { if (changeSubscriptionPolicies.add(policy)) { - changeSubscriptionManager.notifyChangeSubcriptionPolicy(); + changeSubscriptionManager.handleChangeSubcriptionPoliciesChanged(); event = new ChangeSubscriptionPoliciesEventImpl(); } } @@ -2840,7 +3090,7 @@ public class CDOViewImpl extends AbstractCDOView implements IExecutorServiceProv { if (changeSubscriptionPolicies.remove(policy) && !changeSubscriptionPolicies.contains(policy)) { - changeSubscriptionManager.notifyChangeSubcriptionPolicy(); + changeSubscriptionManager.handleChangeSubcriptionPoliciesChanged(); event = new ChangeSubscriptionPoliciesEventImpl(); } } diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java index ec58ab8fcd..1b7fe9ab91 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOSessionProtocol.java @@ -35,6 +35,7 @@ import org.eclipse.emf.cdo.common.protocol.CDODataInput; import org.eclipse.emf.cdo.common.protocol.CDOProtocol; import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; import org.eclipse.emf.cdo.common.revision.CDORevisionKey; import org.eclipse.emf.cdo.common.security.CDOPermission; import org.eclipse.emf.cdo.common.util.CDOCommonUtil; @@ -343,6 +344,11 @@ public interface CDOSessionProtocol extends CDOProtocol, PackageLoader, BranchLo public void requestResetCredentials(String userID); /** + * @since 4.5 + */ + public boolean requestUnit(int viewID, CDOID rootID, byte opcode, CDORevisionHandler revisionHandler); + + /** * If the meaning of this type isn't clear, there really should be more of a description here... * * @author Eike Stepper diff --git a/plugins/org.eclipse.net4j.db.h2/H2 Browser.launch b/plugins/org.eclipse.net4j.db.h2/H2 Browser.launch new file mode 100644 index 0000000000..acf966ae38 --- /dev/null +++ b/plugins/org.eclipse.net4j.db.h2/H2 Browser.launch @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication"> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> +<listEntry value="/org.eclipse.net4j.db.h2"/> +</listAttribute> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> +<listEntry value="4"/> +</listAttribute> +<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/> +<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.h2.tools.Server"/> +<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.net4j.db.h2"/> +<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms1G -Xmx1G"/> +</launchConfiguration> |