diff options
89 files changed, 5244 insertions, 551 deletions
diff --git a/features/org.eclipse.net4j.db.mysql-feature/feature.xml b/features/org.eclipse.net4j.db.mysql-feature/feature.xml index a76aadd927..41fbf937ee 100644 --- a/features/org.eclipse.net4j.db.mysql-feature/feature.xml +++ b/features/org.eclipse.net4j.db.mysql-feature/feature.xml @@ -12,7 +12,7 @@ <feature id="org.eclipse.net4j.db.mysql" label="%featureName" - version="4.2.200.qualifier" + version="4.2.300.qualifier" provider-name="%providerName" license-feature="org.eclipse.emf.cdo.license" license-feature-version="0.0.0"> diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/id/CDOIDUtil.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/id/CDOIDUtil.java index e4a303e7bb..c869398125 100644 --- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/id/CDOIDUtil.java +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/id/CDOIDUtil.java @@ -69,6 +69,14 @@ public final class CDOIDUtil } /** + * @since 4.5 + */ + public static <V> Map<CDOID, V> createMap(Map<? extends CDOID, ? extends V> map) + { + return new HashMap<CDOID, V>(map); + } + + /** * @since 4.0 */ public static CDOIDAndVersion createIDAndVersion(CDOID id, int version) 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.common/src/org/eclipse/emf/cdo/common/revision/CDORevisionUtil.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/revision/CDORevisionUtil.java index 5cccda0ea2..88b674503b 100644 --- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/revision/CDORevisionUtil.java +++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/common/revision/CDORevisionUtil.java @@ -323,20 +323,40 @@ public final class CDORevisionUtil return true; } - InternalCDORevision parent = getParentRevision(child, provider); + CDORevision parent = getParentRevision(child, provider); if (parent != null) { - return isContained(parent, container, provider); + return isContained((InternalCDORevision)parent, container, provider); } return false; } - private static InternalCDORevision getParentRevision(InternalCDORevision revision, CDORevisionProvider provider) + /** + * @since 4.5 + */ + public static void handleParentRevisions(CDORevision revision, CDORevisionProvider provider, + CDORevisionHandler handler) + { + CDORevision parentRevision = getParentRevision(revision, provider); + if (parentRevision != null) + { + if (handler.handleRevision(parentRevision)) + { + handleParentRevisions(parentRevision, provider, handler); + } + } + } + + /** + * @since 4.5 + */ + public static CDORevision getParentRevision(CDORevision revision, CDORevisionProvider provider) { CDOID parentID; + CDORevisionData data = revision.data(); - Object containerID = revision.getContainerID(); + Object containerID = data.getContainerID(); if (containerID instanceof CDOWithID) { parentID = ((CDOWithID)containerID).cdoID(); @@ -348,7 +368,7 @@ public final class CDORevisionUtil if (CDOIDUtil.isNull(parentID)) { - parentID = revision.getResourceID(); + parentID = data.getResourceID(); if (CDOIDUtil.isNull(parentID)) { return null; @@ -361,10 +381,13 @@ public final class CDORevisionUtil } } - return (InternalCDORevision)provider.getRevision(parentID); + return provider.getRevision(parentID); } /** + * @since 4.5 + */ + /** * @since 4.4 */ public static List<CDORevision> getChildRevisions(CDOID container, CDORevisionProvider provider) @@ -485,7 +508,7 @@ public final class CDORevisionUtil } else { - container = getParentRevision(revision, provider); + container = (InternalCDORevision)getParentRevision(revision, provider); } if (container != null) diff --git a/plugins/org.eclipse.emf.cdo.examples.installer/examples/org.eclipse.emf.cdo.examples.master/META-INF/MANIFEST.MF b/plugins/org.eclipse.emf.cdo.examples.installer/examples/org.eclipse.emf.cdo.examples.master/META-INF/MANIFEST.MF index 3ad1ff962e..b4cb289830 100644 --- a/plugins/org.eclipse.emf.cdo.examples.installer/examples/org.eclipse.emf.cdo.examples.master/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.emf.cdo.examples.installer/examples/org.eclipse.emf.cdo.examples.master/META-INF/MANIFEST.MF @@ -1,7 +1,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-SymbolicName: org.eclipse.emf.cdo.examples.master;singleton:=true -Bundle-Version: 4.1.200.qualifier +Bundle-Version: 4.1.300.qualifier Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-ActivationPolicy: lazy diff --git a/plugins/org.eclipse.emf.cdo.examples.installer/examples/org.eclipse.emf.cdo.examples.master/config/cdo-server.xml b/plugins/org.eclipse.emf.cdo.examples.installer/examples/org.eclipse.emf.cdo.examples.master/config/cdo-server.xml index 0e00773d0c..a5fe276c16 100644 --- a/plugins/org.eclipse.emf.cdo.examples.installer/examples/org.eclipse.emf.cdo.examples.master/config/cdo-server.xml +++ b/plugins/org.eclipse.emf.cdo.examples.installer/examples/org.eclipse.emf.cdo.examples.master/config/cdo-server.xml @@ -17,6 +17,7 @@ <property name="overrideUUID" value=""/> <property name="supportingAudits" value="true"/> <property name="supportingBranches" value="true"/> + <property name="supportingUnits" value="false"/> <property name="ensureReferentialIntegrity" value="false"/> <property name="allowInterruptRunningQueries" value="true"/> <property name="idGenerationLocation" value="CLIENT"/> <!-- Possible values: STORE | CLIENT --> @@ -52,6 +53,7 @@ <mappingStrategy type="horizontal"> <!-- callout --> <property name="qualifiedNames" value="true"/> + <property name="withRanges" value="false"/> <!-- Per default, the objectTypeCache is in-memory and contains @@ -59,17 +61,15 @@ uncomment the following line and set the desired size. The cache can be disabled by setting a size of 0. --> - <!-- Optional: <property name="objectTypeCacheSize" value="100000"/> --> - </mappingStrategy> <dbAdapter name="h2"/> <dataSource class="org.h2.jdbcx.JdbcDataSource" - URL="jdbc:h2:database/repo1"/> - + URL="jdbc:h2:/develop/cdo-master/h2/big"/> + <!-- Example: <dbAdapter name="derby-embedded"/> <dataSource class="org.apache.derby.jdbc.EmbeddedDataSource" @@ -87,8 +87,8 @@ <!-- Example: <dbAdapter name="mysql"/> <dataSource class="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" - url="jdbc:mysql://localhost/repo1" - user="sa"/> + url="jdbc:mysql://localhost/big" + user="test" password="0000"/> --> <!-- Example: diff --git a/plugins/org.eclipse.emf.cdo.examples.master/META-INF/MANIFEST.MF b/plugins/org.eclipse.emf.cdo.examples.master/META-INF/MANIFEST.MF index 3ad1ff962e..b4cb289830 100644 --- a/plugins/org.eclipse.emf.cdo.examples.master/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.emf.cdo.examples.master/META-INF/MANIFEST.MF @@ -1,7 +1,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-SymbolicName: org.eclipse.emf.cdo.examples.master;singleton:=true -Bundle-Version: 4.1.200.qualifier +Bundle-Version: 4.1.300.qualifier Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-ActivationPolicy: lazy diff --git a/plugins/org.eclipse.emf.cdo.examples.master/config/cdo-server.xml b/plugins/org.eclipse.emf.cdo.examples.master/config/cdo-server.xml index 0e00773d0c..a5fe276c16 100644 --- a/plugins/org.eclipse.emf.cdo.examples.master/config/cdo-server.xml +++ b/plugins/org.eclipse.emf.cdo.examples.master/config/cdo-server.xml @@ -17,6 +17,7 @@ <property name="overrideUUID" value=""/> <property name="supportingAudits" value="true"/> <property name="supportingBranches" value="true"/> + <property name="supportingUnits" value="false"/> <property name="ensureReferentialIntegrity" value="false"/> <property name="allowInterruptRunningQueries" value="true"/> <property name="idGenerationLocation" value="CLIENT"/> <!-- Possible values: STORE | CLIENT --> @@ -52,6 +53,7 @@ <mappingStrategy type="horizontal"> <!-- callout --> <property name="qualifiedNames" value="true"/> + <property name="withRanges" value="false"/> <!-- Per default, the objectTypeCache is in-memory and contains @@ -59,17 +61,15 @@ uncomment the following line and set the desired size. The cache can be disabled by setting a size of 0. --> - <!-- Optional: <property name="objectTypeCacheSize" value="100000"/> --> - </mappingStrategy> <dbAdapter name="h2"/> <dataSource class="org.h2.jdbcx.JdbcDataSource" - URL="jdbc:h2:database/repo1"/> - + URL="jdbc:h2:/develop/cdo-master/h2/big"/> + <!-- Example: <dbAdapter name="derby-embedded"/> <dataSource class="org.apache.derby.jdbc.EmbeddedDataSource" @@ -87,8 +87,8 @@ <!-- Example: <dbAdapter name="mysql"/> <dataSource class="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" - url="jdbc:mysql://localhost/repo1" - user="sa"/> + url="jdbc:mysql://localhost/big" + user="test" password="0000"/> --> <!-- Example: 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..c97e0f363b --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/db/mapping/IListMappingUnitSupport.java @@ -0,0 +1,34 @@ +/* + * 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, IIDHandler idHandler, CDOID id, MoveableList<Object> list) + throws SQLException; +} diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java index 6b42da68b6..efc86e0dc7 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/CDODBSchema.java @@ -35,13 +35,13 @@ public class CDODBSchema public static final IDBTable PROPERTIES = INSTANCE.addTable("cdo_properties"); //$NON-NLS-1$ public static final IDBField PROPERTIES_NAME = // - PROPERTIES.addField("name", DBType.VARCHAR, 255, true); //$NON-NLS-1$ + PROPERTIES.addField("name", DBType.VARCHAR, 255, true); //$NON-NLS-1$ public static final IDBField PROPERTIES_VALUE = // - PROPERTIES.addField("value", DBType.LONGVARCHAR); //$NON-NLS-1$ + PROPERTIES.addField("value", DBType.LONGVARCHAR); //$NON-NLS-1$ public static final IDBIndex INDEX_PROPERTIES_PK = // - PROPERTIES.addIndex(IDBIndex.Type.PRIMARY_KEY, PROPERTIES_NAME); + PROPERTIES.addIndex(IDBIndex.Type.PRIMARY_KEY, PROPERTIES_NAME); public static final String SQL_DELETE_PROPERTIES = "DELETE FROM " + PROPERTIES + " WHERE " + PROPERTIES_NAME + "=?"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ @@ -60,19 +60,19 @@ public class CDODBSchema public static final IDBTable PACKAGE_UNITS = INSTANCE.addTable("cdo_package_units"); //$NON-NLS-1$ public static final IDBField PACKAGE_UNITS_ID = // - PACKAGE_UNITS.addField("id", DBType.VARCHAR, 255, true); //$NON-NLS-1$ + PACKAGE_UNITS.addField("id", DBType.VARCHAR, 255, true); //$NON-NLS-1$ public static final IDBField PACKAGE_UNITS_ORIGINAL_TYPE = // - PACKAGE_UNITS.addField("original_type", DBType.INTEGER); //$NON-NLS-1$ + PACKAGE_UNITS.addField("original_type", DBType.INTEGER); //$NON-NLS-1$ public static final IDBField PACKAGE_UNITS_TIME_STAMP = // - PACKAGE_UNITS.addField("time_stamp", DBType.BIGINT); //$NON-NLS-1$ + PACKAGE_UNITS.addField("time_stamp", DBType.BIGINT); //$NON-NLS-1$ public static final IDBField PACKAGE_UNITS_PACKAGE_DATA = // - PACKAGE_UNITS.addField("package_data", DBType.BLOB); //$NON-NLS-1$ + PACKAGE_UNITS.addField("package_data", DBType.BLOB); //$NON-NLS-1$ public static final IDBIndex INDEX_PACKAGE_UNITS_PK = // - PACKAGE_UNITS.addIndex(IDBIndex.Type.PRIMARY_KEY, PACKAGE_UNITS_ID); + PACKAGE_UNITS.addIndex(IDBIndex.Type.PRIMARY_KEY, PACKAGE_UNITS_ID); /** * DBTable cdo_packages @@ -80,22 +80,22 @@ public class CDODBSchema public static final IDBTable PACKAGE_INFOS = INSTANCE.addTable("cdo_package_infos"); //$NON-NLS-1$ public static final IDBField PACKAGE_INFOS_URI = // - PACKAGE_INFOS.addField("uri", DBType.VARCHAR, 255, true); //$NON-NLS-1$ + PACKAGE_INFOS.addField("uri", DBType.VARCHAR, 255, true); //$NON-NLS-1$ public static final IDBField PACKAGE_INFOS_PARENT = // - PACKAGE_INFOS.addField("parent", DBType.VARCHAR, 255); //$NON-NLS-1$ + PACKAGE_INFOS.addField("parent", DBType.VARCHAR, 255); //$NON-NLS-1$ public static final IDBField PACKAGE_INFOS_UNIT = // - PACKAGE_INFOS.addField("unit", DBType.VARCHAR, 255); //$NON-NLS-1$ + PACKAGE_INFOS.addField("unit", DBType.VARCHAR, 255); //$NON-NLS-1$ public static final IDBIndex INDEX_PACKAGE_INFOS_PK = // - PACKAGE_INFOS.addIndex(IDBIndex.Type.PRIMARY_KEY, PACKAGE_INFOS_URI); + PACKAGE_INFOS.addIndex(IDBIndex.Type.PRIMARY_KEY, PACKAGE_INFOS_URI); public static final IDBIndex INDEX_PACKAGE_INFOS_PARENT = // - PACKAGE_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, PACKAGE_INFOS_PARENT); + PACKAGE_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, PACKAGE_INFOS_PARENT); public static final IDBIndex INDEX_PACKAGE_INFOS_UNIT = // - PACKAGE_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, PACKAGE_INFOS_UNIT); + PACKAGE_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, PACKAGE_INFOS_UNIT); /** * DBTable cdo_branches @@ -103,19 +103,19 @@ public class CDODBSchema public static final IDBTable BRANCHES = INSTANCE.addTable("cdo_branches"); //$NON-NLS-1$ public static final IDBField BRANCHES_ID = // - BRANCHES.addField("id", DBType.INTEGER, true); //$NON-NLS-1$ + BRANCHES.addField("id", DBType.INTEGER, true); //$NON-NLS-1$ public static final IDBField BRANCHES_NAME = // - BRANCHES.addField("name", DBType.VARCHAR); //$NON-NLS-1$ + BRANCHES.addField("name", DBType.VARCHAR); //$NON-NLS-1$ public static final IDBField BRANCHES_BASE_BRANCH_ID = // - BRANCHES.addField("base_id", DBType.INTEGER); //$NON-NLS-1$ + BRANCHES.addField("base_id", DBType.INTEGER); //$NON-NLS-1$ public static final IDBField BRANCHES_BASE_TIMESTAMP = // - BRANCHES.addField("base_time", DBType.BIGINT); //$NON-NLS-1$ + BRANCHES.addField("base_time", DBType.BIGINT); //$NON-NLS-1$ public static final IDBIndex INDEX_BRANCHES_ID = // - BRANCHES.addIndex(IDBIndex.Type.PRIMARY_KEY, BRANCHES_ID); + BRANCHES.addIndex(IDBIndex.Type.PRIMARY_KEY, BRANCHES_ID); public static final String SQL_CREATE_BRANCH = "INSERT INTO " + BRANCHES + " (" + BRANCHES_ID + ", " + BRANCHES_NAME //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + ", " + BRANCHES_BASE_BRANCH_ID + ", " + BRANCHES_BASE_TIMESTAMP + ") VALUES (?, ?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ @@ -139,33 +139,32 @@ public class CDODBSchema public static final IDBTable COMMIT_INFOS = INSTANCE.addTable("cdo_commit_infos"); //$NON-NLS-1$ public static final IDBField COMMIT_INFOS_TIMESTAMP = // - COMMIT_INFOS.addField("commit_time", DBType.BIGINT, true); //$NON-NLS-1$ + COMMIT_INFOS.addField("commit_time", DBType.BIGINT, true); //$NON-NLS-1$ public static final IDBField COMMIT_INFOS_PREVIOUS_TIMESTAMP = // - COMMIT_INFOS.addField("previous_time", DBType.BIGINT); //$NON-NLS-1$ + COMMIT_INFOS.addField("previous_time", DBType.BIGINT); //$NON-NLS-1$ public static final IDBField COMMIT_INFOS_BRANCH = // - COMMIT_INFOS.addField("branch_id", DBType.INTEGER); //$NON-NLS-1$ + COMMIT_INFOS.addField("branch_id", DBType.INTEGER); //$NON-NLS-1$ public static final IDBField COMMIT_INFOS_USER = // - COMMIT_INFOS.addField("user_id", DBType.VARCHAR); //$NON-NLS-1$ + COMMIT_INFOS.addField("user_id", DBType.VARCHAR); //$NON-NLS-1$ public static final IDBField COMMIT_INFOS_COMMENT = // - COMMIT_INFOS.addField("commit_comment", DBType.VARCHAR); //$NON-NLS-1$ + COMMIT_INFOS.addField("commit_comment", DBType.VARCHAR); //$NON-NLS-1$ public static final IDBIndex INDEX_COMMIT_INFOS_PK = // - COMMIT_INFOS.addIndex(IDBIndex.Type.PRIMARY_KEY, COMMIT_INFOS_TIMESTAMP); + COMMIT_INFOS.addIndex(IDBIndex.Type.PRIMARY_KEY, COMMIT_INFOS_TIMESTAMP); public static final IDBIndex INDEX_COMMIT_INFOS_BRANCH = // - COMMIT_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, COMMIT_INFOS_BRANCH); + COMMIT_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, COMMIT_INFOS_BRANCH); public static final IDBIndex INDEX_COMMIT_INFOS_USER = // - COMMIT_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, COMMIT_INFOS_USER); + COMMIT_INFOS.addIndex(IDBIndex.Type.NON_UNIQUE, COMMIT_INFOS_USER); public static final String SQL_CREATE_COMMIT_INFO = "INSERT INTO " + COMMIT_INFOS + "(" + COMMIT_INFOS_TIMESTAMP //$NON-NLS-1$ //$NON-NLS-2$ + ", " + COMMIT_INFOS_PREVIOUS_TIMESTAMP + ", " + COMMIT_INFOS_BRANCH + ", " + COMMIT_INFOS_USER + ", " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ - + COMMIT_INFOS_COMMENT + ") " - + "VALUES (?, ?, ?, ?, ?)"; //$NON-NLS-1$ + + COMMIT_INFOS_COMMENT + ") " + "VALUES (?, ?, ?, ?, ?)"; //$NON-NLS-2$ /** * DBTable cdo_lobs @@ -173,19 +172,19 @@ public class CDODBSchema public static final IDBTable LOBS = INSTANCE.addTable("cdo_lobs"); //$NON-NLS-1$ public static final IDBField LOBS_ID = // - LOBS.addField("id", DBType.VARCHAR, 64, true); //$NON-NLS-1$ + LOBS.addField("id", DBType.VARCHAR, 64, true); //$NON-NLS-1$ public static final IDBField LOBS_SIZE = // - LOBS.addField("lsize", DBType.BIGINT); //$NON-NLS-1$ + LOBS.addField("lsize", DBType.BIGINT); //$NON-NLS-1$ public static final IDBField LOBS_BDATA = // - LOBS.addField("bdata", DBType.BLOB); //$NON-NLS-1$ + LOBS.addField("bdata", DBType.BLOB); //$NON-NLS-1$ public static final IDBField LOBS_CDATA = // - LOBS.addField("cdata", DBType.CLOB); //$NON-NLS-1$ + LOBS.addField("cdata", DBType.CLOB); //$NON-NLS-1$ public static final IDBIndex INDEX_LOBS_ID = // - LOBS.addIndex(IDBIndex.Type.PRIMARY_KEY, LOBS_ID); + LOBS.addIndex(IDBIndex.Type.PRIMARY_KEY, LOBS_ID); public static final String SQL_QUERY_LOBS = "SELECT 1 FROM " + CDODBSchema.LOBS + " WHERE " + CDODBSchema.LOBS_ID //$NON-NLS-1$ //$NON-NLS-2$ + "=?"; //$NON-NLS-1$ 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..90514b8911 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; @@ -63,8 +65,11 @@ import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionManager; import org.eclipse.emf.cdo.spi.server.InternalCommitContext; import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalUnitManager; +import org.eclipse.emf.cdo.spi.server.InternalUnitManager.InternalObjectAttacher; import org.eclipse.emf.cdo.spi.server.StoreAccessor; +import org.eclipse.net4j.db.BatchedStatement; import org.eclipse.net4j.db.DBException; import org.eclipse.net4j.db.DBUtil; import org.eclipse.net4j.db.IDBConnection; @@ -118,10 +123,10 @@ public class DBStoreAccessor extends StoreAccessor implements IDBStoreAccessor, private ConnectionKeepAliveTask connectionKeepAliveTask; - private Set<CDOID> newObjects = new HashSet<CDOID>(); - private CDOID maxID = CDOID.NULL; + private InternalObjectAttacher objectAttacher; + public DBStoreAccessor(DBStore store, ISession session) throws DBException { super(store, session); @@ -133,17 +138,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,17 +485,17 @@ 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()) { CDOID id = revision.getID(); - newObjects.add(id); if (adjustMaxID && idHandler.compare(id, maxID) > 0) { @@ -560,24 +565,52 @@ public class DBStoreAccessor extends StoreAccessor implements IDBStoreAccessor, } @Override - protected void writeRevisions(InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor) + protected void writeNewObjectRevisions(InternalCommitContext context, InternalCDORevision[] newObjects, + CDOBranch branch, OMMonitor monitor) + { + writeRevisions(context, true, newObjects, branch, monitor); + } + + @Override + protected void writeDirtyObjectRevisions(InternalCommitContext context, InternalCDORevision[] dirtyObjects, + CDOBranch branch, OMMonitor monitor) + { + writeRevisions(context, false, dirtyObjects, branch, monitor); + } + + protected void writeRevisions(InternalCommitContext context, boolean attachNewObjects, + InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor) { try { monitor.begin(revisions.length); for (InternalCDORevision revision : revisions) { - boolean mapType = newObjects.contains(revision.getID()); - writeRevision(revision, mapType, true, monitor.fork()); + writeRevision(revision, attachNewObjects, true, monitor.fork()); + } + + if (attachNewObjects) + { + InternalRepository repository = getStore().getRepository(); + if (repository.isSupportingUnits()) + { + InternalUnitManager unitManager = repository.getUnitManager(); + objectAttacher = unitManager.attachObjects(context); + } } } finally { - newObjects.clear(); monitor.done(); } } + @Override + protected void writeRevisions(InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor) + { + throw new UnsupportedOperationException(); + } + protected void writeRevision(InternalCDORevision revision, boolean mapType, boolean revise, OMMonitor monitor) { if (TRACER.isEnabled()) @@ -708,6 +741,12 @@ public class DBStoreAccessor extends StoreAccessor implements IDBStoreAccessor, getStore().getIDHandler().adjustLastObjectID(maxID); maxID = CDOID.NULL; } + + if (objectAttacher != null) + { + objectAttacher.finishedCommit(true); + objectAttacher = null; + } } finally { @@ -730,6 +769,12 @@ public class DBStoreAccessor extends StoreAccessor implements IDBStoreAccessor, @Override protected final void doRollback(IStoreAccessor.CommitContext commitContext) { + if (objectAttacher != null) + { + objectAttacher.finishedCommit(false); + objectAttacher = null; + } + getStore().getMetaDataManager().clearMetaIDMappings(); if (TRACER.isEnabled()) @@ -758,6 +803,7 @@ public class DBStoreAccessor extends StoreAccessor implements IDBStoreAccessor, DBStore store = getStore(); connection = store.getDatabase().getConnection(); connectionKeepAliveTask = new ConnectionKeepAliveTask(this); + objectAttacher = null; long keepAlivePeriod = ConnectionKeepAliveTask.EXECUTION_PERIOD; Map<String, String> storeProps = store.getProperties(); @@ -1443,6 +1489,38 @@ 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 readUnit(IView view, CDOID rootID, CDORevisionHandler revisionHandler) + { + UnitMappingTable unitMappingTable = getStore().getUnitMappingTable(); + unitMappingTable.readUnitRevisions(this, view, rootID, revisionHandler); + } + + public Object initUnit(IView view, CDOID rootID, CDORevisionHandler revisionHandler, Set<CDOID> initializedIDs, + long timeStamp) + { + UnitMappingTable unitMappingTable = getStore().getUnitMappingTable(); + return unitMappingTable.initUnit(this, timeStamp, view, rootID, revisionHandler, initializedIDs); + } + + public void finishUnit(IView view, CDOID rootID, CDORevisionHandler revisionHandler, long timeStamp, + Object initResult, List<CDOID> ids) + { + UnitMappingTable unitMappingTable = getStore().getUnitMappingTable(); + unitMappingTable.finishUnit((BatchedStatement)initResult, rootID, ids, timeStamp); + } + + public void writeUnits(Map<CDOID, CDOID> unitMappings, long timeStamp) + { + UnitMappingTable unitMappingTable = getStore().getUnitMappingTable(); + unitMappingTable.writeUnitMappings(this, unitMappings, timeStamp); + } + /** * @author Stefan Winkler */ diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/MetaDataManager.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/MetaDataManager.java index 71ad7546e0..77abb2bd05 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/MetaDataManager.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/MetaDataManager.java @@ -238,8 +238,11 @@ public class MetaDataManager extends Lifecycle implements IMetaDataManager try { - String sql = "INSERT INTO " + CDODBSchema.PACKAGE_UNITS + " VALUES (?, ?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$ + String sql = "INSERT INTO " + CDODBSchema.PACKAGE_UNITS + " (" + CDODBSchema.PACKAGE_UNITS_ID + ", " //$NON-NLS-1$ //$NON-NLS-2$ + + CDODBSchema.PACKAGE_UNITS_ORIGINAL_TYPE + ", " + CDODBSchema.PACKAGE_UNITS_TIME_STAMP + ", " + + CDODBSchema.PACKAGE_UNITS_PACKAGE_DATA + ") VALUES (?, ?, ?, ?)"; DBUtil.trace(sql); + IDBPreparedStatement stmt = connection.prepareStatement(sql, ReuseProbability.MEDIUM); try @@ -311,7 +314,8 @@ public class MetaDataManager extends Lifecycle implements IMetaDataManager String parentURI = packageInfo.getParentURI(); String unitID = packageInfo.getPackageUnit().getID(); - String sql = "INSERT INTO " + CDODBSchema.PACKAGE_INFOS + " VALUES (?, ?, ?)"; //$NON-NLS-1$ //$NON-NLS-2$ + String sql = "INSERT INTO " + CDODBSchema.PACKAGE_INFOS + " (" + CDODBSchema.PACKAGE_INFOS_URI + ", " //$NON-NLS-1$ //$NON-NLS-2$ + + CDODBSchema.PACKAGE_INFOS_PARENT + ", " + CDODBSchema.PACKAGE_INFOS_UNIT + ") VALUES (?, ?, ?)"; DBUtil.trace(sql); IDBPreparedStatement stmt = connection.prepareStatement(sql, ReuseProbability.MEDIUM); diff --git a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/UUIDHandler.java b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/UUIDHandler.java index 9c2b20d146..040285c0db 100644 --- a/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/UUIDHandler.java +++ b/plugins/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/UUIDHandler.java @@ -63,6 +63,7 @@ public class UUIDHandler extends Lifecycle implements IIDHandler public int compare(CDOID id1, CDOID id2) { + // UUIDs are not generated in strictly ordered form. throw new UnsupportedOperationException(); } 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..31581f12f7 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,7 @@ public abstract class AbstractHorizontalClassMapping implements IClassMapping, I primaryKey.addIndexField(branchField); } - table.addIndex(IDBIndex.Type.NON_UNIQUE, ATTRIBUTES_ID, ATTRIBUTES_REVISED); + table.addIndex(IDBIndex.Type.NON_UNIQUE, ATTRIBUTES_REVISED); } } @@ -175,6 +177,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 +301,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 +314,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 +960,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..9e1cdc6240 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,11 @@ 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; + + private static final boolean CHECK_UNIT_ENTRIES = Boolean + .getBoolean("org.eclipse.emf.cdo.server.db.checkUnitEntries"); + /** * The table of this mapping. */ @@ -97,7 +106,7 @@ public class AuditListTableMappingWithRanges extends AbstractBasicListTableMappi // --------- SQL strings - see initSQLStrings() ----------------- private String sqlSelectChunksPrefix; - private String sqlOrderByIndex; + private String sqlSelectUnitEntries; private String sqlInsertEntry; @@ -172,8 +181,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 +278,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 " + (CHECK_UNIT_ENTRIES ? ATTRIBUTES_ID + ", " : "") + "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 +328,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 +392,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 +563,259 @@ 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, IIDHandler idHandler, CDOID id, MoveableList<Object> list) + throws SQLException + { + int size = list.size(); + for (int i = 0; i < size; i++) + { + resultSet.next(); + + if (CHECK_UNIT_ENTRIES) + { + CDOID checkID = idHandler.getCDOID(resultSet, 1); + if (checkID != id) + { + throw new IllegalStateException("Result set does not deliver expected result"); + } + } + + 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 +1170,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..504c82dbe0 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,29 @@ 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.WrappedException; +import org.eclipse.net4j.util.collection.MoveableList; +import org.eclipse.net4j.util.concurrent.ConcurrencyUtil; +import org.eclipse.net4j.util.concurrent.TimeoutRuntimeException; 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 +66,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 +120,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(); + String[] strings = buildSQLSelects(false); + String sqlSelectAttributesPrefix = strings[0]; + sqlSelectAttributesCurrent = strings[1]; + sqlSelectAttributesByTime = strings[2]; - 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(); - - 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 +200,92 @@ 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$ + + if (forUnits) + { + builder.append(" ORDER BY "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + } + + 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$ + + if (forUnits) + { + builder.append(" ORDER BY "); //$NON-NLS-1$ + builder.append(ATTRIBUTES_ID); + } + + strings[2] = builder.toString(); + + return strings; + } + public boolean readRevision(IDBStoreAccessor accessor, InternalCDORevision revision, int listChunk) { IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler(); @@ -217,7 +303,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 +679,210 @@ 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(); + + 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); + } + + listFiller.await(); + } + 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; + + private Throwable exception; + + 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() throws SQLException + { + // Schedule an end marker revision. + schedule(new StubCDORevision(getEClass())); + + try + { + latch.await(); + } + catch (InterruptedException ex) + { + throw new TimeoutRuntimeException(); + } + + if (exception instanceof RuntimeException) + { + throw (RuntimeException)exception; + } + + if (exception instanceof Error) + { + throw (Error)exception; + } + + if (exception instanceof SQLException) + { + throw (SQLException)exception; + } + + if (exception instanceof Exception) + { + throw WrappedException.wrap((Exception)exception); + } + } + + public void run() + { + try + { + while (store.isActive()) + { + InternalCDORevision revision = queue.poll(1, TimeUnit.SECONDS); + if (revision == null) + { + continue; + } + + if (revision instanceof StubCDORevision) + { + return; + } + + readUnitEntries(revision); + } + } + catch (Throwable ex) + { + exception = ex; + } + finally + { + latch.countDown(); + } + } + + private void readUnitEntries(InternalCDORevision revision) throws SQLException + { + CDOID id = revision.getID(); + + 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], idHandler, id, 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..4bdacb754f --- /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,317 @@ +/* + * 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.BatchedStatement; +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.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * @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 + " (" + UNITS_ELEM + ", " + UNITS_UNIT + + ") 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 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); + } + } + + public BatchedStatement initUnit(IDBStoreAccessor accessor, long timeStamp, IView view, CDOID rootID, + CDORevisionHandler revisionHandler, Set<CDOID> initializedIDs) + { + IIDHandler idHandler = mappingStrategy.getStore().getIDHandler(); + IDBConnection connection = accessor.getDBConnection(); + BatchedStatement stmt = DBUtil.batched(connection.prepareStatement(SQL_INSERT_MAPPINGS, ReuseProbability.HIGH), + WRITE_UNIT_MAPPING_BATCH_SIZE); + + try + { + CDORevision revision = view.getRevision(rootID); + + initUnit(stmt, view, rootID, revisionHandler, initializedIDs, timeStamp, idHandler, revision); + return stmt; + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + // Don't close the statement; that's done later in finishUnit(). + } + } + + private void initUnit(BatchedStatement stmt, IView view, CDOID rootID, CDORevisionHandler revisionHandler, + Set<CDOID> initializedIDs, long timeStamp, IIDHandler idHandler, CDORevision revision) throws SQLException + { + revisionHandler.handleRevision(revision); + + CDOID id = revision.getID(); + initializedIDs.add(id); + + writeUnitMapping(stmt, rootID, timeStamp, idHandler, id); + + List<CDORevision> children = CDORevisionUtil.getChildRevisions(revision, view, true); + for (CDORevision child : children) + { + initUnit(stmt, view, rootID, revisionHandler, initializedIDs, timeStamp, idHandler, child); + } + } + + public void finishUnit(BatchedStatement stmt, CDOID rootID, List<CDOID> ids, long timeStamp) + { + IDBStore store = mappingStrategy.getStore(); + IIDHandler idHandler = store.getIDHandler(); + Connection connection = null; + + try + { + connection = stmt.getConnection(); + + for (CDOID id : ids) + { + writeUnitMapping(stmt, rootID, timeStamp, idHandler, id); + } + } + catch (SQLException ex) + { + DBUtil.rollbackSilently(connection); + throw new DBException(ex); + } + finally + { + DBUtil.close(stmt); + } + + try + { + connection.commit(); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } + + public void writeUnitMappings(IDBStoreAccessor accessor, Map<CDOID, CDOID> unitMappings, long timeStamp) + { + IIDHandler idHandler = mappingStrategy.getStore().getIDHandler(); + IDBConnection connection = accessor.getDBConnection(); + BatchedStatement stmt = DBUtil.batched(connection.prepareStatement(SQL_INSERT_MAPPINGS, ReuseProbability.HIGH), + WRITE_UNIT_MAPPING_BATCH_SIZE); + + try + { + for (Entry<CDOID, CDOID> entry : unitMappings.entrySet()) + { + CDOID id = entry.getKey(); + CDOID rootID = entry.getValue(); + writeUnitMapping(stmt, rootID, timeStamp, idHandler, id); + } + } + catch (SQLException ex) + { + throw new DBException(ex); + } + finally + { + DBUtil.close(stmt); + } + } + + private void writeUnitMapping(BatchedStatement stmt, CDOID rootID, long timeStamp, IIDHandler idHandler, CDOID id) + throws SQLException + { + idHandler.setCDOID(stmt, 1, id); + idHandler.setCDOID(stmt, 2, rootID); + // stmt.setLong(3, timeStamp); + stmt.executeUpdate(); + } + + @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.hibernate/src/org/eclipse/emf/cdo/server/internal/hibernate/HibernateRawCommitContext.java b/plugins/org.eclipse.emf.cdo.server.hibernate/src/org/eclipse/emf/cdo/server/internal/hibernate/HibernateRawCommitContext.java index abf8fb61a0..c6ce59833c 100644 --- a/plugins/org.eclipse.emf.cdo.server.hibernate/src/org/eclipse/emf/cdo/server/internal/hibernate/HibernateRawCommitContext.java +++ b/plugins/org.eclipse.emf.cdo.server.hibernate/src/org/eclipse/emf/cdo/server/internal/hibernate/HibernateRawCommitContext.java @@ -20,6 +20,7 @@ import org.eclipse.emf.cdo.common.id.CDOIDUtil; import org.eclipse.emf.cdo.common.lock.CDOLockState; import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants; import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.server.IStoreAccessor; import org.eclipse.emf.cdo.server.IView; import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; @@ -47,7 +48,6 @@ import java.util.Set; */ public class HibernateRawCommitContext implements InternalCommitContext { - private Map<CDOID, CDOID> idMappings = CDOIDUtil.createMap(); private List<InternalCDORevision> dirtyObjects = new ArrayList<InternalCDORevision>(); @@ -245,6 +245,11 @@ public class HibernateRawCommitContext implements InternalCommitContext this.newObjects = newObjects; } + public IStoreAccessor getAccessor() + { + return null; + } + public void preWrite() { } 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..7277f935fa --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server.net4j/src/org/eclipse/emf/cdo/server/internal/net4j/protocol/UnitIndication.java @@ -0,0 +1,128 @@ +/* + * 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.IUnit; +import org.eclipse.emf.cdo.server.IUnitManager; +import org.eclipse.emf.cdo.spi.server.InternalView; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; +import org.eclipse.net4j.util.om.monitor.OMMonitor.Async; + +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 + { + final InternalView view = getView(viewID); + IUnitManager unitManager = getRepository().getUnitManager(); + + if (opcode == CDOProtocolConstants.UNIT_CHECK) + { + out.writeBoolean(unitManager.isUnit(rootID)); + return; + } + + if (opcode == CDOProtocolConstants.UNIT_CLOSE) + { + IUnit unit = unitManager.getUnit(rootID); + if (unit != null) + { + unit.close(view); + out.writeBoolean(true); + return; + } + + out.writeBoolean(false); + return; + } + + final IOException[] ioException = { null }; + final RuntimeException[] runtimeException = { null }; + + monitor.begin(); + Async async = monitor.forkAsync(); + + try + { + boolean success = view.openUnit(rootID, opcode == CDOProtocolConstants.UNIT_CREATE, new CDORevisionHandler() + { + public boolean handleRevision(CDORevision revision) + { + try + { + view.unsubscribe(revision.getID()); + 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; + } + }); + + 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); + } + finally + { + async.stop(); + monitor.done(); + } + } +} diff --git a/plugins/org.eclipse.emf.cdo.server.product/CDOServer.launch b/plugins/org.eclipse.emf.cdo.server.product/CDOServer.launch index 27049b215f..9ef9177045 100644 --- a/plugins/org.eclipse.emf.cdo.server.product/CDOServer.launch +++ b/plugins/org.eclipse.emf.cdo.server.product/CDOServer.launch @@ -26,7 +26,7 @@ <stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/> <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -debug -console"/> <stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/> -<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms40m -Xmx1g -Ddebug=true -Dorg.eclipse.net4j.tcp.ssl.passphrase=ab987c -Dorg.eclipse.net4j.tcp.ssl.trust=file:///${workspace_loc:org.eclipse.emf.cdo.examples}/sslKey/testTrust -Dorg.eclipse.net4j.tcp.ssl.key=file:///${workspace_loc:org.eclipse.emf.cdo.examples}/sslKey/testKeys -Dorg.eclipse.emf.cdo.server.browser.port=7777 -Dorg.osgi.service.http.port=8080 -Dosgi.checkConfiguration=false"/> +<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms1G -Xmx1G -Ddebug=true -Dorg.eclipse.net4j.tcp.ssl.passphrase=ab987c -Dorg.eclipse.net4j.tcp.ssl.trust=file:///${workspace_loc:org.eclipse.emf.cdo.examples}/sslKey/testTrust -Dorg.eclipse.net4j.tcp.ssl.key=file:///${workspace_loc:org.eclipse.emf.cdo.examples}/sslKey/testKeys -Dorg.eclipse.emf.cdo.server.browser.port=7777 -Dorg.osgi.service.http.port=8080 -Dosgi.checkConfiguration=false"/> <stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc:org.eclipse.emf.cdo.server.product}"/> <stringAttribute key="pde.version" value="3.3"/> <stringAttribute key="product" value="org.eclipse.platform.ide"/> diff --git a/plugins/org.eclipse.emf.cdo.server.product/META-INF/MANIFEST.MF b/plugins/org.eclipse.emf.cdo.server.product/META-INF/MANIFEST.MF index a0623e833d..dd63b31d06 100644 --- a/plugins/org.eclipse.emf.cdo.server.product/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.emf.cdo.server.product/META-INF/MANIFEST.MF @@ -1,7 +1,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-SymbolicName: org.eclipse.emf.cdo.server.product;singleton:=true -Bundle-Version: 4.1.300.qualifier +Bundle-Version: 4.1.400.qualifier Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/plugins/org.eclipse.emf.cdo.server.product/config/cdo-server.xml b/plugins/org.eclipse.emf.cdo.server.product/config/cdo-server.xml index 3bf0040baf..a5e09ecb46 100644 --- a/plugins/org.eclipse.emf.cdo.server.product/config/cdo-server.xml +++ b/plugins/org.eclipse.emf.cdo.server.product/config/cdo-server.xml @@ -17,6 +17,7 @@ <property name="overrideUUID" value=""/> <property name="supportingAudits" value="true"/> <property name="supportingBranches" value="true"/> + <property name="supportingUnits" value="false"/> <property name="ensureReferentialIntegrity" value="false"/> <property name="allowInterruptRunningQueries" value="true"/> <property name="idGenerationLocation" value="STORE"/> <!-- Possible values: STORE | CLIENT --> @@ -52,6 +53,7 @@ <mappingStrategy type="horizontal"> <!-- callout --> <property name="qualifiedNames" value="true"/> + <property name="withRanges" value="false"/> <!-- Per default, the objectTypeCache is in-memory and contains @@ -59,17 +61,15 @@ uncomment the following line and set the desired size. The cache can be disabled by setting a size of 0. --> - <!-- Optional: <property name="objectTypeCacheSize" value="100000"/> --> - </mappingStrategy> <dbAdapter name="h2"/> <dataSource class="org.h2.jdbcx.JdbcDataSource" - URL="jdbc:h2:database/repo1"/> - + URL="jdbc:h2:/develop/cdo-master/h2/big"/> + <!-- Example: <dbAdapter name="derby-embedded"/> <dataSource class="org.apache.derby.jdbc.EmbeddedDataSource" @@ -87,8 +87,8 @@ <!-- Example: <dbAdapter name="mysql"/> <dataSource class="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" - url="jdbc:mysql://localhost/repo1" - user="sa"/> + url="jdbc:mysql://localhost/big" + user="test" password="0000"/> --> <!-- Example: 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..bb485055dd 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 @@ -92,6 +92,7 @@ import org.eclipse.emf.cdo.spi.server.InternalSession; import org.eclipse.emf.cdo.spi.server.InternalSessionManager; import org.eclipse.emf.cdo.spi.server.InternalStore; import org.eclipse.emf.cdo.spi.server.InternalTransaction; +import org.eclipse.emf.cdo.spi.server.InternalUnitManager; import org.eclipse.emf.cdo.spi.server.InternalView; import org.eclipse.emf.internal.cdo.object.CDOFactoryImpl; @@ -147,7 +148,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 +170,8 @@ public class Repository extends Container<Object>implements InternalRepository, private boolean supportingBranches; + private boolean supportingUnits; + private boolean serializingCommits; private boolean ensuringReferentialIntegrity; @@ -180,7 +183,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; @@ -198,31 +201,33 @@ public class Repository extends Container<Object>implements InternalRepository, private InternalLockManager lockingManager; + private InternalUnitManager unitManager; + private IQueryHandlerProvider queryHandlerProvider; private IManagedContainer container; - private List<ReadAccessHandler> readAccessHandlers = new ArrayList<ReadAccessHandler>(); + private final List<ReadAccessHandler> readAccessHandlers = new ArrayList<ReadAccessHandler>(); - private List<WriteAccessHandler> writeAccessHandlers = new ArrayList<WriteAccessHandler>(); - - 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 +360,11 @@ public class Repository extends Container<Object>implements InternalRepository, return supportingBranches; } + public boolean isSupportingUnits() + { + return supportingUnits; + } + @Deprecated public boolean isSupportingEcore() { @@ -972,6 +982,17 @@ public class Repository extends Container<Object>implements InternalRepository, this.sessionManager = sessionManager; } + public InternalUnitManager getUnitManager() + { + return unitManager; + } + + public void setUnitManager(InternalUnitManager unitManager) + { + checkInactive(); + this.unitManager = unitManager; + } + public InternalCDOBranchManager getBranchManager() { return branchManager; @@ -1918,6 +1939,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 +2114,7 @@ public class Repository extends Container<Object>implements InternalRepository, newPackageUnitsForRootResource.add(packageUnit); } } + return newPackageUnitsForRootResource.toArray(new InternalCDOPackageUnit[0]); } @@ -2152,7 +2181,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 +2196,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); } @@ -2199,6 +2228,11 @@ public class Repository extends Container<Object>implements InternalRepository, LifecycleUtil.activate(commitManager); LifecycleUtil.activate(queryHandlerProvider); + if (supportingUnits) + { + LifecycleUtil.activate(unitManager); + } + if (!skipInitialization) { long creationTime = store.getCreationTime(); @@ -2227,7 +2261,8 @@ public class Repository extends Container<Object>implements InternalRepository, @Override protected void doDeactivate() throws Exception { - LifecycleUtil.deactivate(getLockingManager()); + LifecycleUtil.deactivate(unitManager); + LifecycleUtil.deactivate(lockingManager); LifecycleUtil.deactivate(queryHandlerProvider); LifecycleUtil.deactivate(commitManager); LifecycleUtil.deactivate(commitInfoManager); @@ -2293,6 +2328,11 @@ public class Repository extends Container<Object>implements InternalRepository, setLockingManager(createLockManager()); } + if (getUnitManager() == null) + { + setUnitManager(createUnitManager()); + } + super.doBeforeActivate(); } @@ -2331,6 +2371,11 @@ public class Repository extends Container<Object>implements InternalRepository, return new CommitManager(); } + protected InternalUnitManager createUnitManager() + { + return new UnitManager(this); + } + @Deprecated protected InternalLockManager createLockManager() { 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/Session.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java index 776eae1ed0..f0016a2b45 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/Session.java @@ -74,7 +74,7 @@ import java.util.concurrent.atomic.AtomicInteger; /** * @author Eike Stepper */ -public class Session extends Container<IView>implements InternalSession +public class Session extends Container<IView> implements InternalSession { private InternalSessionManager manager; @@ -402,7 +402,7 @@ public class Session extends Container<IView>implements InternalSession public void collectContainedRevisions(InternalCDORevision revision, CDOBranchPoint branchPoint, int referenceChunk, Set<CDOID> revisions, List<CDORevision> additionalRevisions) { - InternalCDORevisionManager revisionManager = getManager().getRepository().getRevisionManager(); + InternalCDORevisionManager revisionManager = manager.getRepository().getRevisionManager(); for (EStructuralFeature feature : revision.getClassInfo().getAllPersistentFeatures()) { // TODO Clarify feature maps @@ -577,8 +577,10 @@ public class Session extends Container<IView>implements InternalSession } } - private boolean hasSubscription(CDOID id, InternalView[] views) + private boolean isDeltaNeeded(CDOID id, InternalView[] views) { + boolean supportingUnits = manager.getRepository().isSupportingUnits(); + for (InternalView view : views) { try @@ -587,6 +589,11 @@ public class Session extends Container<IView>implements InternalSession { return true; } + + if (supportingUnits && view.isInOpenUnit(id)) + { + return true; + } } catch (Exception ex) { @@ -788,7 +795,7 @@ public class Session extends Container<IView>implements InternalSession CDORevisionDelta revisionDelta = (CDORevisionDelta)changedObjects.get(index); CDOID id = revisionDelta.getID(); - if (changes || hasSubscription(id, views)) + if (changes || isDeltaNeeded(id, views)) { if (permissionManager == null) { 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..f2edc097f3 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 @@ -1322,7 +1322,7 @@ public class TransactionCommitContext implements InternalCommitContext } } - protected IStoreAccessor getAccessor() + public IStoreAccessor getAccessor() { return accessor; } 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..d70f14681c --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/internal/server/UnitManager.java @@ -0,0 +1,734 @@ +/* + * 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.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.CDORevisionProvider; +import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; +import org.eclipse.emf.cdo.common.util.CDOException; +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.common.revision.InternalCDORevision; +import org.eclipse.emf.cdo.spi.server.InternalCommitContext; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalUnitManager; +import org.eclipse.emf.cdo.spi.server.InternalView; + +import org.eclipse.net4j.util.container.Container; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; + +/** + * @author Eike Stepper + */ +public class UnitManager extends Container<IUnit> implements InternalUnitManager +{ + private final InternalRepository repository; + + private final Map<CDOID, IUnit> units = CDOIDUtil.createMap(); + + private final Map<CDOID, UnitInitializer> unitInitializers = CDOIDUtil.createMap(); + + private final Set<ObjectAttacher> objectAttachers = new HashSet<ObjectAttacher>(); + + private final ReentrantReadWriteLock managerLock = new ReentrantReadWriteLock(); + + public UnitManager(InternalRepository repository) + { + this.repository = repository; + } + + public final InternalRepository getRepository() + { + return repository; + } + + public boolean isUnit(CDOID rootID) + { + checkActive(); + + ReadLock readLock = managerLock.readLock(); + readLock.lock(); + + try + { + // No need to synchronize on units because all other modifiers hold the manager write lock. + return units.containsKey(rootID); + } + finally + { + readLock.unlock(); + } + } + + public IUnit createUnit(CDOID rootID, IView view, CDORevisionHandler revisionHandler) + { + checkActive(); + + WriteLock writeLock = managerLock.writeLock(); + UnitInitializer unitInitializer; + boolean hook = false; + + //////////////////////////////////// + // Phase 1: Register (short, locked) + //////////////////////////////////// + + writeLock.lock(); + + try + { + createUnitHook1(); + + // No need to synchronize on units because all other access holds the manager lock. + if (units.containsKey(rootID)) + { + return null; + } + + // No need to synchronize on unitInitializers because all other access holds the manager lock. + unitInitializer = unitInitializers.get(rootID); + if (unitInitializer != null) + { + hook = true; + } + else + { + checkNotNested(rootID, view, units.keySet()); + checkNotNested(rootID, view, unitInitializers.keySet()); + + unitInitializer = createUnitInitializer(rootID, view, revisionHandler); + + // No need to synchronize on unitInitializers because all other access holds the manager lock. + unitInitializers.put(rootID, unitInitializer); + + // Synchronize on objectAttachers because objectAttacherFinishedCommit() doesn't acquire the manager lock! + synchronized (objectAttachers) + { + for (ObjectAttacher objectAttacher : objectAttachers) + { + List<CDOID> ids = objectAttacher.removeUnmappedRevisionsFor(unitInitializer); + if (!ids.isEmpty()) + { + unitInitializer.addObjectAttacher(objectAttacher, ids); + } + } + } + } + } + finally + { + writeLock.unlock(); + } + + if (hook) + { + return unitInitializer.hook(rootID, view, revisionHandler); + } + + IUnit unit = null; + + try + { + ///////////////////////////////////////////////////// + // Phase 2: Initialize (potentially long, not locked) + ///////////////////////////////////////////////////// + + unit = unitInitializer.initialize(); + } + finally + { + /////////////////////////////////// + // Phase 3: Publish (short, locked) + /////////////////////////////////// + + try + { + writeLock.lock(); + + try + { + // No need to synchronize on unitInitializers because all other access holds the manager lock. + unitInitializers.remove(rootID); + + if (unit != null) + { + // No need to synchronize on units because all other access holds the manager lock. + units.put(rootID, unit); + } + } + finally + { + writeLock.unlock(); + } + } + finally + { + unitInitializer.notifyHookedInitializers(); + } + } + + fireElementAddedEvent(unit); + return unit; + } + + private void checkNotNested(CDOID rootID, IView view, Set<CDOID> unitIDs) + { + InternalCDORevision rootRevision = (InternalCDORevision)view.getRevision(rootID); + CDOID unitID = getUnit(rootRevision, view, unitIDs); + if (unitID != null) + { + throw new CDOException("Attempt to nest the new unit " + rootID + " in the existing unit " + unitID); + } + + Set<CDOID> set = Collections.singleton(rootID); + for (CDOID id : unitIDs) + { + InternalCDORevision revision = (InternalCDORevision)view.getRevision(id); + if (getUnit(revision, view, set) != null) + { + throw new CDOException("Attempt to nest the existing unit " + id + " in the new unit " + rootID); + } + } + } + + public IUnit getUnit(CDOID rootID) + { + checkActive(); + + ReadLock readLock = managerLock.readLock(); + readLock.lock(); + + try + { + // No need to synchronize on units because all other modifiers hold the manager write lock. + return units.get(rootID); + } + finally + { + readLock.unlock(); + } + } + + public IUnit[] getUnits() + { + checkActive(); + return getElements(); + } + + public IUnit[] getElements() + { + ReadLock readLock = managerLock.readLock(); + readLock.lock(); + + try + { + // No need to synchronize on units because all other modifiers hold the manager write lock. + return units.values().toArray(new IUnit[units.size()]); + } + finally + { + readLock.unlock(); + } + } + + public InternalObjectAttacher attachObjects(InternalCommitContext commitContext) + { + checkActive(); + + long timeStamp = commitContext.getTimeStamp(); + + ObjectAttacher objectAttacher = null; + Map<CDOID, CDOID> unitMappings = CDOIDUtil.createMap(); + + /////////////////////////////////////////////// + // Phase 1: Analyze new objects (short, locked) + /////////////////////////////////////////////// + + ReadLock readLock = managerLock.readLock(); + readLock.lock(); + + try + { + attachObjectsHook1(); + + Set<CDOID> rootIDs = new HashSet<CDOID>(); + + // No need to synchronize on units because all other modifiers hold the manager write lock. + rootIDs.addAll(units.keySet()); + + // No need to synchronize on unitInitializers because all other modifiers hold the manager write lock. + rootIDs.addAll(unitInitializers.keySet()); + + List<InternalCDORevision> unmappedRevisions = new ArrayList<InternalCDORevision>(); + boolean checkUnits = !rootIDs.isEmpty(); + + for (InternalCDORevision revision : commitContext.getNewObjects()) + { + if (checkUnits) + { + CDOID rootID = getUnit(revision, commitContext, rootIDs); + if (rootID != null) + { + unitMappings.put(revision.getID(), rootID); + continue; + } + } + + unmappedRevisions.add(revision); + } + + if (!unmappedRevisions.isEmpty()) + { + objectAttacher = createObjectAttacher(commitContext, unmappedRevisions); + + // Read lock holders must synchronize modifications of the private collections. + synchronized (objectAttachers) + { + objectAttachers.add(objectAttacher); + } + } + } + finally + { + readLock.unlock(); + } + + ////////////////////////////////////////////////////////// + // Phase 2: Map objects to existing units (long, unlocked) + ////////////////////////////////////////////////////////// + + if (!unitMappings.isEmpty()) + { + mapAttachedObjectsToUnits(commitContext, timeStamp, unitMappings); + } + + return objectAttacher; + } + + /** + * Does not hold any manager lock when called. + */ + public void objectAttacherFinishedCommit(ObjectAttacher objectAttacher) + { + checkActive(); + + synchronized (objectAttachers) + { + objectAttachers.remove(objectAttacher); + } + } + + @Override + protected void doActivate() throws Exception + { + super.doActivate(); + + UnitSupport storeAccessor = (UnitSupport)repository.getStore().getReader(null); + + try + { + List<CDOID> roots = storeAccessor.readUnitRoots(); + for (CDOID root : roots) + { + IUnit unit = createUnit(root); + + // No need to synchronize on units because all other access call checkActive() + units.put(root, unit); + } + } + finally + { + storeAccessor.release(); + } + } + + @Override + protected void doDeactivate() throws Exception + { + // No need to synchronize on units because all other access call checkActive() + units.clear(); + + super.doDeactivate(); + } + + protected Unit createUnit(CDOID root) + { + return new Unit(root); + } + + protected UnitInitializer createUnitInitializer(CDOID rootID, IView view, CDORevisionHandler revisionHandler) + { + return new UnitInitializer(rootID, view, revisionHandler); + } + + protected ObjectAttacher createObjectAttacher(InternalCommitContext commitContext, + List<InternalCDORevision> unmappedRevisions) + { + return new ObjectAttacher(commitContext, unmappedRevisions); + } + + protected void mapAttachedObjectsToUnits(InternalCommitContext commitContext, long timeStamp, + Map<CDOID, CDOID> unitMappings) + { + UnitSupport storeAccessor = (UnitSupport)commitContext.getAccessor(); + storeAccessor.writeUnits(unitMappings, timeStamp); + } + + protected void createUnitHook1() + { + } + + protected void attachObjectsHook1() + { + } + + private static CDOID getUnit(InternalCDORevision revision, CDORevisionProvider revisionProvider, Set<CDOID> rootIDs) + { + if (rootIDs.isEmpty()) + { + return null; + } + + CDOID id = revision.getID(); + if (rootIDs.contains(id)) + { + return id; + } + + CDORevision parentRevision = CDORevisionUtil.getParentRevision(revision, revisionProvider); + if (parentRevision != null) + { + return getUnit((InternalCDORevision)parentRevision, revisionProvider, rootIDs); + } + + return null; + } + + /** + * @author Eike Stepper + */ + protected class Unit implements IUnit + { + private final CDOID rootID; + + private final Set<IView> views = new HashSet<IView>(); + + public Unit(CDOID rootID) + { + this.rootID = rootID; + } + + public IUnitManager getManager() + { + return UnitManager.this; + } + + public CDOID getRootID() + { + return rootID; + } + + public boolean isOpen() + { + synchronized (views) + { + return !views.isEmpty(); + } + } + + public void open(IView view, final CDORevisionHandler revisionHandler) + { + synchronized (views) + { + views.add(view); + } + + UnitSupport storeAccessor = (UnitSupport)repository.getStore().getReader(null); + + try + { + storeAccessor.readUnit(view, rootID, revisionHandler); + } + finally + { + storeAccessor.release(); + } + } + + public void close(IView view) + { + synchronized (views) + { + views.remove(view); + } + + ((InternalView)view).closeUnit(rootID); + } + + @Override + public String toString() + { + return "Unit[" + rootID + "]"; + } + + /** + * Does not hold any manager lock when called. + */ + public void initialize(IView view, long timeStamp, CDORevisionHandler revisionHandler, + Map<ObjectAttacher, List<CDOID>> objectAttachers) + { + UnitSupport storeAccessor = (UnitSupport)repository.getStore().getWriter(null); + + try + { + Set<CDOID> initializedIDs = new HashSet<CDOID>(); + Object initResult = storeAccessor.initUnit(view, rootID, revisionHandler, initializedIDs, timeStamp); + + List<CDOID> ids = new ArrayList<CDOID>(); + for (Entry<ObjectAttacher, List<CDOID>> entry : objectAttachers.entrySet()) + { + ObjectAttacher objectAttacher = entry.getKey(); + if (objectAttacher.awaitFinishedCommit()) + { + for (CDOID id : entry.getValue()) + { + if (!initializedIDs.contains(id)) + { + ids.add(id); + } + } + } + } + + storeAccessor.finishUnit(view, rootID, revisionHandler, timeStamp, initResult, ids); + } + finally + { + storeAccessor.release(); + } + } + } + + /** + * @author Eike Stepper + */ + protected class UnitInitializer implements CDORevisionHandler + { + private final long timeStamp = repository.getTimeStamp(); + + private final Map<ObjectAttacher, List<CDOID>> concurrentObjectAttachers = new HashMap<ObjectAttacher, List<CDOID>>(); + + private final CountDownLatch unitInitialized = new CountDownLatch(1); + + private final CDOID rootID; + + private final IView view; + + private final CDORevisionHandler revisionHandler; + + private final List<CDORevisionHandler> hookedRevisionHandlers = new CopyOnWriteArrayList<CDORevisionHandler>(); + + private volatile boolean hasHookedRevisionHandlers; + + private Unit unit; + + public UnitInitializer(CDOID rootID, IView view, CDORevisionHandler revisionHandler) + { + this.rootID = rootID; + this.view = view; + this.revisionHandler = revisionHandler; + } + + public CDOID getRootID() + { + return rootID; + } + + /** + * Does not hold any manager lock when called. + */ + public IUnit initialize() + { + unit = new Unit(rootID); + unit.initialize(view, timeStamp, revisionHandler, concurrentObjectAttachers); + return unit; + } + + /** + * Does not hold any manager lock when called. + */ + public IUnit hook(CDOID rootID, IView view, final CDORevisionHandler revisionHandler) + { + final Set<CDOID> ids = new HashSet<CDOID>(); + + hookedRevisionHandlers.add(new CDORevisionHandler() + { + public boolean handleRevision(CDORevision revision) + { + ids.add(revision.getID()); + return revisionHandler.handleRevision(revision); + } + }); + + // It's okay to do this unsynchronized. The worst thing that could happen is that the hooked revision handler is + // missed a few times during UnitInitializer.handleRevision(), but that's okay because it probably missed many + // revisions already and therefore performs an openUnit() subsequently, anyways. After all, hooked revision + // handlers, + // i.e., concurrent createUnit() calls for the same unit, are extremely rare. + hasHookedRevisionHandlers = true; + + try + { + // Now wait for the main revision handler to finish. + unitInitialized.await(); + } + catch (InterruptedException ex) + { + return null; + } + + // Now send the missed revisions. + unit.open(view, new CDORevisionHandler() + { + public boolean handleRevision(CDORevision revision) + { + if (ids.contains(revision.getID())) + { + // This revision has already been sent. Skip to the next one. + return true; + } + + return revisionHandler.handleRevision(revision); + } + }); + + return unit; + } + + /** + * Does not hold any manager lock when called. + */ + public void notifyHookedInitializers() + { + unitInitialized.countDown(); + } + + public boolean handleRevision(CDORevision revision) + { + if (revisionHandler.handleRevision(revision)) + { + if (hasHookedRevisionHandlers) + { + for (CDORevisionHandler hookedRevisionHandler : hookedRevisionHandlers) + { + hookedRevisionHandler.handleRevision(revision); + } + } + + return true; + } + + return false; + } + + /** + * Holds the manager write lock when called. + */ + public void addObjectAttacher(ObjectAttacher objectAttacher, List<CDOID> ids) + { + concurrentObjectAttachers.put(objectAttacher, ids); + } + } + + /** + * @author Eike Stepper + */ + protected class ObjectAttacher implements InternalObjectAttacher + { + private final InternalCommitContext commitContext; + + private final List<InternalCDORevision> unmappedRevisions; + + private final CountDownLatch commitFinished = new CountDownLatch(1); + + private boolean commitSucceeded; + + public ObjectAttacher(InternalCommitContext commitContext, List<InternalCDORevision> unmappedRevisions) + { + this.commitContext = commitContext; + this.unmappedRevisions = unmappedRevisions; + } + + /** + * Does not hold any manager lock when called. + */ + public void finishedCommit(boolean success) + { + objectAttacherFinishedCommit(this); + + commitSucceeded = success; + commitFinished.countDown(); + } + + /** + * Holds the manager write lock when called. + */ + public List<CDOID> removeUnmappedRevisionsFor(UnitInitializer unitInitializer) + { + List<CDOID> ids = new ArrayList<CDOID>(); + + Set<CDOID> rootIDs = Collections.singleton(unitInitializer.getRootID()); + for (Iterator<InternalCDORevision> it = unmappedRevisions.iterator(); it.hasNext();) + { + InternalCDORevision revision = it.next(); + if (getUnit(revision, commitContext, rootIDs) != null) + { + ids.add(revision.getID()); + it.remove(); + } + } + + return ids; + } + + public boolean awaitFinishedCommit() + { + try + { + commitFinished.await(); + } + catch (InterruptedException ex) + { + return false; + } + + return commitSucceeded; + } + } +} 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..3ff8d9c950 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; @@ -57,9 +61,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 +161,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 +175,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 +205,66 @@ public class View extends Lifecycle implements InternalView, CDOCommonView.Optio this.durableLockingID = durableLockingID; } + public boolean openUnit(CDOID rootID, boolean create, CDORevisionHandler revisionHandler) + { + 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; + } + + unit.open(this, revisionHandler); + } + + openUnitRoots.add(rootID); + return true; + } + + public void closeUnit(CDOID rootID) + { + openUnitRoots.remove(rootID); + } + + public boolean isInOpenUnit(CDOID id) + { + if (openUnitRoots.isEmpty()) + { + return false; + } + + if (openUnitRoots.contains(id)) + { + return true; + } + + 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 +293,12 @@ public class View extends Lifecycle implements InternalView, CDOCommonView.Optio return false; } - return changeSubscriptionIDs.contains(id); + if (changeSubscriptionIDs.contains(id)) + { + return true; + } + + return false; } /** @@ -288,6 +359,7 @@ public class View extends Lifecycle implements InternalView, CDOCommonView.Optio public void doClose() { clearChangeSubscription(); + openUnitRoots.clear(); closed = true; } 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..a2cc5da856 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,26 @@ 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 readUnit(IView view, CDOID rootID, CDORevisionHandler revisionHandler); + + public Object initUnit(IView view, CDOID rootID, CDORevisionHandler revisionHandler, Set<CDOID> initializedIDs, + long timeStamp); + + public void finishUnit(IView view, CDOID rootID, CDORevisionHandler revisionHandler, long timeStamp, + Object initResult, List<CDOID> ids); + + public void writeUnits(Map<CDOID, CDOID> unitMappings, long timeStamp); + } } 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..be18cfa165 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IUnit.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; + +/** + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @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..15523d76e9 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/server/IUnitManager.java @@ -0,0 +1,35 @@ +/* + * 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; + +/** + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @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/InternalCommitContext.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitContext.java index d304a713df..f911de3243 100644 --- a/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitContext.java +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalCommitContext.java @@ -67,6 +67,11 @@ public interface InternalCommitContext extends IStoreAccessor.CommitContext, CDO public InternalTransaction getTransaction(); /** + * @since 4.5 + */ + public IStoreAccessor getAccessor(); + + /** * @since 4.2 */ public long getTimeStamp(); 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..7c0a55c9bd 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 @@ -116,6 +116,16 @@ public interface InternalRepository extends IRepository, PackageProcessor, Packa */ public InternalLockManager getLockingManager(); + /** + * @since 4.5 + */ + public InternalUnitManager getUnitManager(); + + /** + * @since 4.5 + */ + public void setUnitManager(InternalUnitManager unitManager); + public InternalQueryManager getQueryManager(); public void setQueryHandlerProvider(IQueryHandlerProvider queryHandlerProvider); @@ -262,6 +272,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/InternalUnitManager.java b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalUnitManager.java new file mode 100644 index 0000000000..731ffbfc01 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.server/src/org/eclipse/emf/cdo/spi/server/InternalUnitManager.java @@ -0,0 +1,34 @@ +/* + * 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.spi.server; + +import org.eclipse.emf.cdo.server.IUnitManager; + +/** + * @author Eike Stepper + * @since 4.5 + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. +*/ +public interface InternalUnitManager extends IUnitManager +{ + public InternalRepository getRepository(); + + public InternalObjectAttacher attachObjects(InternalCommitContext commitContext); + + /** + * @author Eike Stepper + */ + public interface InternalObjectAttacher + { + public void finishedCommit(boolean success); + } +} 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..d140229147 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,6 +12,7 @@ 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; @@ -55,4 +56,19 @@ public interface InternalView extends IView, ILifecycle public void clearChangeSubscription(); public void doClose(); + + /** + * @since 4.5 + */ + public boolean openUnit(CDOID rootID, boolean create, CDORevisionHandler revisionHandler); + + /** + * @since 4.5 + */ + public void closeUnit(CDOID rootID); + + /** + * @since 4.5 + */ + public boolean isInOpenUnit(CDOID id); } 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..1cdbe40908 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 @@ -95,7 +95,7 @@ public abstract class StoreAccessor extends StoreAccessorBase if (newObjects.length != 0) { - writeRevisions(newObjects, branch, monitor.fork(newObjects.length)); + writeNewObjectRevisions(context, newObjects, branch, monitor.fork(newObjects.length)); } if (dirtyCount != 0) @@ -106,7 +106,7 @@ public abstract class StoreAccessor extends StoreAccessorBase } else { - writeRevisions(dirtyObjects, branch, monitor.fork(dirtyCount)); + writeDirtyObjectRevisions(context, dirtyObjects, branch, monitor.fork(dirtyCount)); } } @@ -167,6 +167,24 @@ public abstract class StoreAccessor extends StoreAccessorBase String comment, OMMonitor monitor); /** + * @since 4.5 + */ + protected void writeNewObjectRevisions(InternalCommitContext context, InternalCDORevision[] newObjects, + CDOBranch branch, OMMonitor monitor) + { + writeRevisions(newObjects, branch, monitor); + } + + /** + * @since 4.5 + */ + protected void writeDirtyObjectRevisions(InternalCommitContext context, InternalCDORevision[] dirtyObjects, + CDOBranch branch, OMMonitor monitor) + { + writeRevisions(dirtyObjects, branch, monitor); + } + + /** * @since 3.0 */ protected abstract void writeRevisions(InternalCDORevision[] revisions, CDOBranch branch, OMMonitor monitor); 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..419531a15d 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,7 @@ 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); // 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/SessionTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/SessionTest.java index 352c77c0c6..6f9f3f209e 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/SessionTest.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/SessionTest.java @@ -50,6 +50,7 @@ import org.eclipse.emf.spi.cdo.InternalCDOSession; import java.util.Collection; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * @author Eike Stepper @@ -234,7 +235,7 @@ public class SessionTest extends AbstractCDOTest { try { - startLatch.await(); + startLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); msg("Committing NOW!"); commitTime[0] = transaction.commit().getTimeStamp(); @@ -251,7 +252,7 @@ public class SessionTest extends AbstractCDOTest CDOSession session2 = openSession(); startLatch.countDown(); - stopLatch.await(); + stopLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); assertEquals(true, session2.waitForUpdate(commitTime[0], DEFAULT_TIMEOUT)); transaction.getSession().close(); 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/bugzilla/Bugzilla_329254_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_329254_Test.java index 558d16f004..2fe86adfd0 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_329254_Test.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_329254_Test.java @@ -31,6 +31,7 @@ import org.eclipse.net4j.util.io.IOUtil; import java.util.Map; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * LastCommitTimeStamp updated even when a serverSide Error occurred. @@ -82,9 +83,10 @@ public class Bugzilla_329254_Test extends AbstractCDOTest // grant the other session access to enter and // block until it has left again. enterLatch.countDown(); + try { - leaveLatch.await(); + leaveLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); } catch (InterruptedException ex) { @@ -244,7 +246,7 @@ public class Bugzilla_329254_Test extends AbstractCDOTest try { // wait until session 2 has entered write. - enterLatch.await(); + enterLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); } catch (InterruptedException ex) { diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_340709_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_340709_Test.java index 98f48c2f1e..094c48b482 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_340709_Test.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_340709_Test.java @@ -21,6 +21,7 @@ import org.eclipse.net4j.util.event.IEvent; import org.eclipse.net4j.util.event.IListener; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * @author Martin Fluegge @@ -70,7 +71,7 @@ public class Bugzilla_340709_Test extends AbstractCDOTest try { - latch.await(); + latch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); } catch (InterruptedException ex) { diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_349793_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_349793_Test.java index d974bc46c7..3d2694de88 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_349793_Test.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_349793_Test.java @@ -24,6 +24,7 @@ import org.eclipse.net4j.util.event.IEvent; import org.eclipse.net4j.util.event.IListener; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * @author Egidijus Vaisnora @@ -55,7 +56,7 @@ public class Bugzilla_349793_Test extends AbstractCDOTest try { testExecutionLatch.countDown(); - invalidationLatch.await(); + invalidationLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); } catch (InterruptedException ex) { @@ -66,7 +67,7 @@ public class Bugzilla_349793_Test extends AbstractCDOTest }); long timestamp = doSecondSessionSync(); - testExecutionLatch.await(); + testExecutionLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); CDOTransaction freshTransaction = session.openTransaction(); invalidationLatch.countDown(); diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_423699_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_423699_Test.java index f2408663bf..4fd143d666 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_423699_Test.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_423699_Test.java @@ -4,7 +4,7 @@ * 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 */ @@ -38,7 +38,6 @@ import java.util.LinkedHashSet; */ public class Bugzilla_423699_Test extends AbstractLockingTest { - private CDOSession session; private InternalCDOTransaction tx; @@ -112,6 +111,7 @@ public class Bugzilla_423699_Test extends AbstractLockingTest { tx.enableDurableLocking(); } + CDOID containerID = container.cdoID(); CDOID childID = child.cdoID(); @@ -119,6 +119,7 @@ public class Bugzilla_423699_Test extends AbstractLockingTest Collection<CDOObject> objectsToLock = new LinkedHashSet<CDOObject>(); objectsToLock.add(container); objectsToLock.add(child); + tx.lockObjects(objectsToLock, LockType.WRITE, 10000); assertIsLocked(durableLocking, true, containerID); assertIsLocked(durableLocking, true, childID); @@ -128,8 +129,10 @@ public class Bugzilla_423699_Test extends AbstractLockingTest // Step 4: commit tx.commit(new NullProgressMonitor()); + // Lock should be deleted on detached object assertIsLocked(durableLocking, false, childID); + // Lock should be deleted only if lock autorelease is enabled assertIsLocked(durableLocking, !autoReleaseLocksEnabled, containerID); @@ -142,6 +145,7 @@ public class Bugzilla_423699_Test extends AbstractLockingTest session = openSession(); tx = (InternalCDOTransaction)session.openTransaction(durableLockingID); tx.options().setAutoReleaseLocksEnabled(autoReleaseLocksEnabled); + // Lock states should not have changed assertIsLocked(durableLocking, false, childID); assertIsLocked(durableLocking, !autoReleaseLocksEnabled, containerID); @@ -165,6 +169,7 @@ public class Bugzilla_423699_Test extends AbstractLockingTest if (durably) { LockGrade durableLock = null; + try { InternalSession session = getRepository().getSessionManager().getSession(tx.getSessionID()); @@ -177,12 +182,14 @@ public class Bugzilla_423699_Test extends AbstractLockingTest { StoreThreadLocal.release(); } + assertEquals(elementID + " has not the expected durable lock status", shouldBeLocked, durableLock != null); } // Step 2: check lock ArrayList<CDOID> elementIDs = new ArrayList<CDOID>(); elementIDs.add(elementID); + CDOLockState cdoLockState = tx.getLockStates(elementIDs)[0]; assertEquals(elementID + " has wrong lock status", shouldBeLocked, cdoLockState.getWriteLockOwner() != null); } diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_427773_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_427773_Test.java index 33ca285381..1408bb950d 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_427773_Test.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_427773_Test.java @@ -32,8 +32,6 @@ public class Bugzilla_427773_Test extends AbstractCDOTest @Override public void setUp() throws Exception { - SUPPRESS_OUTPUT = true; - UserManager userManager = new UserManager(); userManager.activate(); userManager.addUser(USER_1_CREDENTIALS.getUserID(), USER_1_CREDENTIALS.getPassword()); 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.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_486458_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_486458_Test.java new file mode 100644 index 0000000000..015d974929 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_486458_Test.java @@ -0,0 +1,410 @@ +/* + * 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.bugzilla; + +import org.eclipse.emf.cdo.CDOState; +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevision; +import org.eclipse.emf.cdo.common.util.CDOException; +import org.eclipse.emf.cdo.eresource.CDOResource; +import org.eclipse.emf.cdo.server.IRepository.Props; +import org.eclipse.emf.cdo.session.CDOSession; +import org.eclipse.emf.cdo.spi.server.InternalSession; +import org.eclipse.emf.cdo.spi.server.InternalView; +import org.eclipse.emf.cdo.tests.AbstractCDOTest; +import org.eclipse.emf.cdo.tests.config.IRepositoryConfig; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.CleanRepositoriesAfter; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.CleanRepositoriesBefore; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.Requires; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.Skips; +import org.eclipse.emf.cdo.tests.model1.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.tests.util.TestAdapter; +import org.eclipse.emf.cdo.transaction.CDOTransaction; +import org.eclipse.emf.cdo.util.CDOUtil; +import org.eclipse.emf.cdo.util.CommitException; +import org.eclipse.emf.cdo.util.ConcurrentAccessException; +import org.eclipse.emf.cdo.view.CDOAdapterPolicy; +import org.eclipse.emf.cdo.view.CDOUnit; + +import org.eclipse.emf.common.notify.impl.AdapterImpl; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.InternalEList; +import org.eclipse.emf.spi.cdo.InternalCDOView; + +import java.util.Iterator; +import java.util.Map; + +/** + * Bug 486458 - Provide support for optimized loading and notifying of object units + * + * @author Eike Stepper + */ +@Requires({ IRepositoryConfig.CAPABILITY_AUDITING, "DB.ranges" }) +@Skips(IRepositoryConfig.CAPABILITY_BRANCHING) +@CleanRepositoriesBefore(reason = "Instrumented repository") +@CleanRepositoriesAfter(reason = "Instrumented repository") +public class Bugzilla_486458_Test extends AbstractCDOTest +{ + @Override + protected void doSetUp() throws Exception + { + Map<String, Object> map = getTestProperties(); + map.put(Props.SUPPORTING_UNITS, Boolean.toString(true)); + + super.doSetUp(); + } + + public void testPrefetchBigModel() throws Exception + { + fillRepository(); + clearCache(getRepository().getRevisionManager()); + + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath("test")); + + long start = System.currentTimeMillis(); + resource.cdoPrefetch(CDORevision.DEPTH_INFINITE); + long stop = System.currentTimeMillis(); + System.out.println("Prefetched: " + (stop - start)); + + int count = iterateResource(resource); + assertEquals(7714, count); + } + + public void testCreateUnit() throws Exception + { + fillRepository(); + + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath("test")); + + assertEquals(false, transaction.getUnitManager().isUnit(resource)); + assertEquals(0, transaction.getUnitManager().getOpenUnits().length); + + InternalSession serverSession = getRepository().getSessionManager().getSession(session.getSessionID()); + InternalView serverView = serverSession.getView(transaction.getViewID()); + CDOID childID = CDOUtil.getCDOObject(resource.getContents().get(0)).cdoID(); + assertEquals(false, serverView.isInOpenUnit(childID)); + + long start = System.currentTimeMillis(); + CDOUnit unit = transaction.getUnitManager().createUnit(resource); + long stop = System.currentTimeMillis(); + System.out.println("Created Unit: " + (stop - start)); + + assertEquals(true, transaction.getUnitManager().isUnit(resource)); + assertEquals(1, transaction.getUnitManager().getOpenUnits().length); + assertSame(unit, transaction.getUnitManager().getOpenUnits()[0]); + assertSame(unit, transaction.getUnitManager().getOpenUnit(resource)); + assertEquals(true, serverView.isInOpenUnit(childID)); + + int count = iterateResource(resource); + assertEquals(7714, count); + + unit.close(); + assertEquals(true, transaction.getUnitManager().isUnit(resource)); + assertEquals(0, transaction.getUnitManager().getOpenUnits().length); + assertEquals(null, transaction.getUnitManager().getOpenUnit(resource)); + assertEquals(false, serverView.isInOpenUnit(childID)); + } + + public void testDetectNestedUnit() throws Exception + { + fillRepository(); + + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath("test")); + + CDOUnit unit = transaction.getUnitManager().createUnit(resource); + EObject parent = resource.getFolder(); + EObject child = resource.getContents().get(0); + + try + { + transaction.getUnitManager().createUnit(parent); + fail("CDOException expected"); + } + catch (CDOException expected) + { + // SUCCESS + } + + try + { + transaction.getUnitManager().createUnit(child); + fail("CDOException expected"); + } + catch (CDOException expected) + { + // SUCCESS + } + + assertEquals(true, transaction.getUnitManager().isUnit(resource)); + assertEquals(false, transaction.getUnitManager().isUnit(parent)); + assertEquals(false, transaction.getUnitManager().isUnit(child)); + + assertEquals(1, transaction.getUnitManager().getOpenUnits().length); + assertSame(unit, transaction.getUnitManager().getOpenUnits()[0]); + } + + public void testOpenUnit() throws Exception + { + fillRepository(); + + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath("test")); + CDOUnit createdUnit = transaction.getUnitManager().createUnit(resource); + assertEquals(7714, createdUnit.getElements()); + + session.close(); + } + + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath("test")); + + assertEquals(true, transaction.getUnitManager().isUnit(resource)); + assertEquals(0, transaction.getUnitManager().getOpenUnits().length); + + long start = System.currentTimeMillis(); + CDOUnit openedUnit = transaction.getUnitManager().openUnit(resource); + assertEquals(7714, openedUnit.getElements()); + long stop = System.currentTimeMillis(); + System.out.println("Opened Unit: " + (stop - start)); + + assertEquals(true, transaction.getUnitManager().isUnit(resource)); + assertEquals(1, transaction.getUnitManager().getOpenUnits().length); + assertSame(openedUnit, transaction.getUnitManager().getOpenUnits()[0]); + + int count = iterateResource(resource); + assertEquals(7714, count); + } + } + + public void testUpdateUnit() throws Exception + { + fillRepository(); + + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath("test")); + + CDOUnit unit = transaction.getUnitManager().createUnit(resource); + int elements = unit.getElements(); + + Company company = (Company)resource.getContents().get(0); + Category category = getModel1Factory().createCategory(); + company.getCategories().add(category); + assertEquals(elements + 1, unit.getElements()); + + transaction.commit(); + transaction.options().addChangeSubscriptionPolicy(CDOAdapterPolicy.ALL); + + category.eAdapters().add(new AdapterImpl()); + assertEquals(false, ((InternalCDOView)transaction).hasSubscription(CDOUtil.getCDOObject(category).cdoID())); + + company.getCategories().remove(category); + assertEquals(elements, unit.getElements()); + } + + public void testNotificationsAfterOpenUnit() throws Exception + { + fillRepository(); + + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + transaction.options().addChangeSubscriptionPolicy(CDOAdapterPolicy.ALL); + + CDOResource resource = transaction.getResource(getResourcePath("test")); + Company root = (Company)resource.getContents().get(0); + Category child = (Category)root.eContents().get(0); + Company sibling = (Company)resource.getContents().get(1); + + CDOUnit unit = transaction.getUnitManager().createUnit(root); + + TestAdapter adapter = new TestAdapter(); + root.eAdapters().add(adapter); + child.eAdapters().add(adapter); + sibling.eAdapters().add(adapter); + + assertEquals(false, ((InternalCDOView)transaction).hasSubscription(CDOUtil.getCDOObject(root).cdoID())); + assertEquals(false, ((InternalCDOView)transaction).hasSubscription(CDOUtil.getCDOObject(child).cdoID())); + assertEquals(true, ((InternalCDOView)transaction).hasSubscription(CDOUtil.getCDOObject(sibling).cdoID())); + + InternalSession serverSession = getRepository().getSessionManager().getSession(session.getSessionID()); + InternalView serverView = serverSession.getView(transaction.getViewID()); + assertEquals(false, serverView.hasSubscription(CDOUtil.getCDOObject(root).cdoID())); + assertEquals(false, serverView.hasSubscription(CDOUtil.getCDOObject(child).cdoID())); + assertEquals(true, serverView.hasSubscription(CDOUtil.getCDOObject(sibling).cdoID())); + + CDOSession session2 = openSession(); + CDOTransaction transaction2 = session2.openTransaction(); + CDOResource resource2 = transaction2.getResource(getResourcePath("test")); + + Company root2 = (Company)resource2.getContents().get(0); + root2.setName("Name"); + commitAndSync(transaction2, transaction); + assertEquals(CDOState.CLEAN, CDOUtil.getCDOObject(root).cdoState()); + assertEquals("Name", root.getName()); + + Category child2 = (Category)root2.eContents().get(0); + child2.setName("Name"); + commitAndSync(transaction2, transaction); + assertEquals(CDOState.CLEAN, CDOUtil.getCDOObject(child2).cdoState()); + assertEquals("Name", child.getName()); + + Company sibling2 = (Company)resource2.getContents().get(1); + sibling2.setName("Name"); + commitAndSync(transaction2, transaction); + assertEquals(CDOState.CLEAN, CDOUtil.getCDOObject(sibling2).cdoState()); + assertEquals("Name", sibling.getName()); + + unit.close(); + + root2.setName("Name2"); + commitAndSync(transaction2, transaction); + assertEquals(CDOState.CLEAN, CDOUtil.getCDOObject(root).cdoState()); + assertEquals("Name2", root.getName()); + + child2.setName("Name2"); + commitAndSync(transaction2, transaction); + assertEquals(CDOState.CLEAN, CDOUtil.getCDOObject(child2).cdoState()); + assertEquals("Name2", child.getName()); + + sibling2.setName("Name2"); + commitAndSync(transaction2, transaction); + assertEquals(CDOState.CLEAN, CDOUtil.getCDOObject(sibling2).cdoState()); + assertEquals("Name2", sibling.getName()); + } + + private void fillRepository() throws ConcurrentAccessException, CommitException + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.createResource(getResourcePath("test")); + + for (int i = 0; i < 3; i++) + { + Company company = getModel1Factory().createCompany(); + addUnique(resource.getContents(), company); + fillCompany(company); + + long start = System.currentTimeMillis(); + transaction.commit(); + long stop = System.currentTimeMillis(); + System.out.println("Committed: " + (stop - start)); + } + + session.close(); + } + + private void fillCompany(Company company) + { + for (int i = 0; i < 5; i++) + { + Category category = getModel1Factory().createCategory(); + addUnique(company.getCategories(), category); + fillCategory(category, 3); + } + + for (int i = 0; i < 10; i++) + { + Supplier supplier = getModel1Factory().createSupplier(); + addUnique(company.getSuppliers(), supplier); + } + + for (int i = 0; i < 10; i++) + { + Customer customer = getModel1Factory().createCustomer(); + addUnique(company.getCustomers(), customer); + } + + for (int i = 0; i < 10; i++) + { + PurchaseOrder order = getModel1Factory().createPurchaseOrder(); + order.setSupplier(company.getSuppliers().get(i)); + addUnique(company.getPurchaseOrders(), order); + + for (int j = 0; j < 10; j++) + { + OrderDetail orderDetail = getModel1Factory().createOrderDetail(); + addUnique(order.getOrderDetails(), orderDetail); + } + } + + for (int i = 0; i < 10; i++) + { + SalesOrder order = getModel1Factory().createSalesOrder(); + order.setCustomer(company.getCustomers().get(i)); + addUnique(company.getSalesOrders(), order); + + for (int j = 0; j < 10; 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 < 10; 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); + } + + private static int iterateResource(CDOResource resource) + { + int count = 1; + long start = System.currentTimeMillis(); + + for (Iterator<EObject> it = resource.eAllContents(); it.hasNext();) + { + it.next(); + ++count; + } + + long stop = System.currentTimeMillis(); + System.out.println("Iterated: " + (stop - start)); + + return count; + } +} diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_486458a_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_486458a_Test.java new file mode 100644 index 0000000000..a4c7b06814 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_486458a_Test.java @@ -0,0 +1,348 @@ +/* + * 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.bugzilla; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.eresource.CDOResource; +import org.eclipse.emf.cdo.internal.server.Repository; +import org.eclipse.emf.cdo.internal.server.TransactionCommitContext; +import org.eclipse.emf.cdo.internal.server.UnitManager; +import org.eclipse.emf.cdo.server.IRepository.Props; +import org.eclipse.emf.cdo.server.IUnit; +import org.eclipse.emf.cdo.server.IView; +import org.eclipse.emf.cdo.session.CDOSession; +import org.eclipse.emf.cdo.spi.server.InternalCommitContext; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalTransaction; +import org.eclipse.emf.cdo.spi.server.InternalUnitManager; +import org.eclipse.emf.cdo.tests.AbstractCDOTest; +import org.eclipse.emf.cdo.tests.config.IRepositoryConfig; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.CleanRepositoriesAfter; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.CleanRepositoriesBefore; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.Requires; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.Skips; +import org.eclipse.emf.cdo.tests.config.impl.RepositoryConfig; +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.cdo.util.CommitException; +import org.eclipse.emf.cdo.util.ConcurrentAccessException; + +import org.eclipse.net4j.util.om.monitor.OMMonitor; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.InternalEList; + +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Bug 486458 - Provide support for optimized loading and notifying of object units + * <p> + * Tests that a createUnit() that starts slightly before a commit() considers the newly attached objects. + * + * @author Eike Stepper + */ +@Requires({ IRepositoryConfig.CAPABILITY_AUDITING, "DB.ranges" }) +@Skips(IRepositoryConfig.CAPABILITY_BRANCHING) +@CleanRepositoriesBefore(reason = "Instrumented repository") +@CleanRepositoriesAfter(reason = "Instrumented repository") +public class Bugzilla_486458a_Test extends AbstractCDOTest +{ + private CountDownLatch unitRegistered; + + private CountDownLatch startInitializeUnit; + + public void testCreateUnitWithParallelCommit() throws Exception + { + fillRepository(); + + unitRegistered = new CountDownLatch(1); + startInitializeUnit = new CountDownLatch(1); + + final int[] committed = { 0 }; + + Thread committer = new Thread("Committer") + { + @Override + public void run() + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath("test")); + + Company company = (Company)resource.getContents().get(0); + Category category = company.getCategories().get(0); + + committed[0] = fillCategory(category, 3); + + try + { + unitRegistered.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); + } + catch (InterruptedException ex) + { + return; + } + + try + { + transaction.commit(); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + + session.close(); + } + }; + + committer.setDaemon(true); + committer.start(); + + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath("test")); + + transaction.getUnitManager().createUnit(resource); + committer.join(DEFAULT_TIMEOUT); + + session = openSession(); + transaction = session.openTransaction(); + resource = transaction.getResource(getResourcePath("test")); + + int count = iterateResource(resource); + int expected = 7714 + committed[0]; + assertEquals(expected, count); + + session = openSession(); + transaction = session.openTransaction(); + resource = transaction.getResource(getResourcePath("test")); + + transaction.getUnitManager().openUnit(resource); + + session = openSession(); + transaction = session.openTransaction(); + resource = transaction.getResource(getResourcePath("test")); + + count = iterateResource(resource); + assertEquals(expected, count); + } + + @Override + public synchronized Map<String, Object> getTestProperties() + { + Map<String, Object> map = super.getTestProperties(); + map.put(Props.SUPPORTING_UNITS, Boolean.toString(true)); + return map; + } + + @Override + protected void doSetUp() throws Exception + { + createRepository(); + super.doSetUp(); + } + + private void createRepository() + { + Repository repository = new Repository.Default() + { + @Override + public InternalCommitContext createCommitContext(InternalTransaction transaction) + { + return new TransactionCommitContext(transaction) + { + @Override + public void write(OMMonitor monitor) + { + if (startInitializeUnit != null) + { + startInitializeUnit.countDown(); + } + + super.write(monitor); + } + }; + } + + @Override + protected InternalUnitManager createUnitManager() + { + return new UnitManager(this) + { + @Override + protected UnitInitializer createUnitInitializer(CDOID rootID, IView view, CDORevisionHandler revisionHandler) + { + return new UnitInitializer(rootID, view, revisionHandler) + { + @Override + public IUnit initialize() + { + if (unitRegistered != null) + { + unitRegistered.countDown(); + } + + try + { + startInitializeUnit.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); + } + catch (InterruptedException ex) + { + return null; + } + + return super.initialize(); + } + }; + } + }; + } + }; + + Map<String, String> props = getRepositoryProperties(); + ((InternalRepository)repository).setProperties(props); + + repository.setName(IRepositoryConfig.REPOSITORY_NAME); + + Map<String, Object> map = getTestProperties(); + map.put(RepositoryConfig.PROP_TEST_REPOSITORY, repository); + } + + private void fillRepository() throws ConcurrentAccessException, CommitException + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.createResource(getResourcePath("test")); + + for (int i = 0; i < 3; i++) + { + Company company = getModel1Factory().createCompany(); + addUnique(resource.getContents(), company); + fillCompany(company); + + long start = System.currentTimeMillis(); + transaction.commit(); + long stop = System.currentTimeMillis(); + System.out.println("Committed: " + (stop - start)); + } + + session.close(); + } + + private void fillCompany(Company company) + { + for (int i = 0; i < 5; i++) + { + Category category = getModel1Factory().createCategory(); + addUnique(company.getCategories(), category); + fillCategory(category, 3); + } + + for (int i = 0; i < 10; i++) + { + Supplier supplier = getModel1Factory().createSupplier(); + addUnique(company.getSuppliers(), supplier); + } + + for (int i = 0; i < 10; i++) + { + Customer customer = getModel1Factory().createCustomer(); + addUnique(company.getCustomers(), customer); + } + + for (int i = 0; i < 10; i++) + { + PurchaseOrder order = getModel1Factory().createPurchaseOrder(); + order.setSupplier(company.getSuppliers().get(i)); + addUnique(company.getPurchaseOrders(), order); + + for (int j = 0; j < 10; j++) + { + OrderDetail orderDetail = getModel1Factory().createOrderDetail(); + addUnique(order.getOrderDetails(), orderDetail); + } + } + + for (int i = 0; i < 10; i++) + { + SalesOrder order = getModel1Factory().createSalesOrder(); + order.setCustomer(company.getCustomers().get(i)); + addUnique(company.getSalesOrders(), order); + + for (int j = 0; j < 10; j++) + { + OrderDetail orderDetail = getModel1Factory().createOrderDetail(); + addUnique(order.getOrderDetails(), orderDetail); + } + } + } + + private int fillCategory(Category category, int depth) + { + int count = 0; + + for (int i = 0; i < 5; i++) + { + Category child = getModel1Factory().createCategory(); + addUnique(category.getCategories(), child); + ++count; + + if (depth > 1) + { + count += fillCategory(child, depth - 1); + } + } + + for (int i = 0; i < 10; i++) + { + Product1 product = getModel1Factory().createProduct1(); + addUnique(category.getProducts(), product); + ++count; + } + + return count; + } + + private static <T extends EObject> void addUnique(EList<T> list, T object) + { + ((InternalEList<T>)list).addUnique(object); + } + + private static int iterateResource(CDOResource resource) + { + int count = 1; + long start = System.currentTimeMillis(); + + for (Iterator<EObject> it = resource.eAllContents(); it.hasNext();) + { + it.next(); + ++count; + } + + long stop = System.currentTimeMillis(); + System.out.println("Iterated: " + (stop - start)); + + return count; + } +} diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_486458b_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_486458b_Test.java new file mode 100644 index 0000000000..e6bfa290ed --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_486458b_Test.java @@ -0,0 +1,381 @@ +/* + * 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.bugzilla; + +import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.revision.CDORevisionHandler; +import org.eclipse.emf.cdo.eresource.CDOResource; +import org.eclipse.emf.cdo.internal.server.Repository; +import org.eclipse.emf.cdo.internal.server.UnitManager; +import org.eclipse.emf.cdo.server.IRepository.Props; +import org.eclipse.emf.cdo.server.IUnit; +import org.eclipse.emf.cdo.server.IView; +import org.eclipse.emf.cdo.session.CDOSession; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalUnitManager; +import org.eclipse.emf.cdo.tests.AbstractCDOTest; +import org.eclipse.emf.cdo.tests.config.IRepositoryConfig; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.CleanRepositoriesAfter; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.CleanRepositoriesBefore; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.Requires; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.Skips; +import org.eclipse.emf.cdo.tests.config.impl.RepositoryConfig; +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.cdo.util.CommitException; +import org.eclipse.emf.cdo.util.ConcurrentAccessException; +import org.eclipse.emf.cdo.view.CDOUnit; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.InternalEList; + +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Bug 486458 - Provide support for optimized loading and notifying of object units + * <p> + * Tests that a commit() that starts slightly before a createUnit() contributes to the new unit. + * + * @author Eike Stepper + */ +@Requires({ IRepositoryConfig.CAPABILITY_AUDITING, "DB.ranges" }) +@Skips(IRepositoryConfig.CAPABILITY_BRANCHING) +@CleanRepositoriesBefore(reason = "Instrumented repository") +@CleanRepositoriesAfter(reason = "Instrumented repository") +public class Bugzilla_486458b_Test extends AbstractCDOTest +{ + private CountDownLatch analyzeStarted; + + private CountDownLatch initializeFinished; + + private boolean longCommit; + + public void testShortCommitWithParallelCreateUnit() throws Exception + { + fillRepository(); + + analyzeStarted = new CountDownLatch(1); + + final int[] committed = { 0 }; + + Thread committer = new Thread("Committer") + { + @Override + public void run() + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath("test")); + + Company company = (Company)resource.getContents().get(0); + Category category = company.getCategories().get(0); + + committed[0] = fillCategory(category, 3); + + try + { + transaction.commit(); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + + session.close(); + } + }; + + committer.setDaemon(true); + committer.start(); + + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath("test")); + + transaction.getUnitManager().createUnit(resource); + committer.join(DEFAULT_TIMEOUT); + + session = openSession(); + transaction = session.openTransaction(); + resource = transaction.getResource(getResourcePath("test")); + + int count = iterateResource(resource); + int expected = 7714 + committed[0]; + assertEquals(expected, count); + + session = openSession(); + transaction = session.openTransaction(); + resource = transaction.getResource(getResourcePath("test")); + + CDOUnit unit = transaction.getUnitManager().openUnit(resource); + assertEquals(expected, unit.getElements()); + + session = openSession(); + transaction = session.openTransaction(); + resource = transaction.getResource(getResourcePath("test")); + + count = iterateResource(resource); + assertEquals(expected, count); + } + + public void testLongCommitWithParallelCreateUnit() throws Exception + { + longCommit = true; + testShortCommitWithParallelCreateUnit(); + } + + @Override + public synchronized Map<String, Object> getTestProperties() + { + Map<String, Object> map = super.getTestProperties(); + map.put(Props.SUPPORTING_UNITS, Boolean.toString(true)); + return map; + } + + @Override + protected void doSetUp() throws Exception + { + createRepository(); + super.doSetUp(); + } + + private void createRepository() + { + Repository repository = new Repository.Default() + { + @Override + protected InternalUnitManager createUnitManager() + { + return new UnitManager(this) + { + @Override + protected void attachObjectsHook1() + { + try + { + Thread.sleep(2000); + } + catch (InterruptedException ex) + { + return; + } + + if (analyzeStarted != null) + { + analyzeStarted.countDown(); + } + } + + @Override + public IUnit createUnit(CDOID rootID, IView view, CDORevisionHandler revisionHandler) + { + if (analyzeStarted != null) + { + try + { + analyzeStarted.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); + } + catch (InterruptedException ex) + { + return null; + } + } + + return super.createUnit(rootID, view, revisionHandler); + } + + @Override + protected UnitInitializer createUnitInitializer(CDOID rootID, IView view, CDORevisionHandler revisionHandler) + { + return new UnitInitializer(rootID, view, revisionHandler) + { + @Override + public IUnit initialize() + { + if (longCommit) + { + initializeFinished = new CountDownLatch(1); + } + + try + { + return super.initialize(); + } + finally + { + if (initializeFinished != null) + { + initializeFinished.countDown(); + } + } + } + }; + } + + @Override + public void objectAttacherFinishedCommit(ObjectAttacher objectAttacher) + { + if (initializeFinished != null) + { + try + { + initializeFinished.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); + Thread.sleep(2000); + } + catch (InterruptedException ex) + { + return; + } + } + + super.objectAttacherFinishedCommit(objectAttacher); + } + }; + } + }; + + Map<String, String> props = getRepositoryProperties(); + ((InternalRepository)repository).setProperties(props); + + repository.setName(IRepositoryConfig.REPOSITORY_NAME); + + Map<String, Object> map = getTestProperties(); + map.put(RepositoryConfig.PROP_TEST_REPOSITORY, repository); + } + + private void fillRepository() throws ConcurrentAccessException, CommitException + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.createResource(getResourcePath("test")); + + for (int i = 0; i < 3; i++) + { + Company company = getModel1Factory().createCompany(); + addUnique(resource.getContents(), company); + fillCompany(company); + + long start = System.currentTimeMillis(); + transaction.commit(); + long stop = System.currentTimeMillis(); + System.out.println("Committed: " + (stop - start)); + } + + session.close(); + } + + private void fillCompany(Company company) + { + for (int i = 0; i < 5; i++) + { + Category category = getModel1Factory().createCategory(); + addUnique(company.getCategories(), category); + fillCategory(category, 3); + } + + for (int i = 0; i < 10; i++) + { + Supplier supplier = getModel1Factory().createSupplier(); + addUnique(company.getSuppliers(), supplier); + } + + for (int i = 0; i < 10; i++) + { + Customer customer = getModel1Factory().createCustomer(); + addUnique(company.getCustomers(), customer); + } + + for (int i = 0; i < 10; i++) + { + PurchaseOrder order = getModel1Factory().createPurchaseOrder(); + order.setSupplier(company.getSuppliers().get(i)); + addUnique(company.getPurchaseOrders(), order); + + for (int j = 0; j < 10; j++) + { + OrderDetail orderDetail = getModel1Factory().createOrderDetail(); + addUnique(order.getOrderDetails(), orderDetail); + } + } + + for (int i = 0; i < 10; i++) + { + SalesOrder order = getModel1Factory().createSalesOrder(); + order.setCustomer(company.getCustomers().get(i)); + addUnique(company.getSalesOrders(), order); + + for (int j = 0; j < 10; j++) + { + OrderDetail orderDetail = getModel1Factory().createOrderDetail(); + addUnique(order.getOrderDetails(), orderDetail); + } + } + } + + private int fillCategory(Category category, int depth) + { + int count = 0; + + for (int i = 0; i < 5; i++) + { + Category child = getModel1Factory().createCategory(); + addUnique(category.getCategories(), child); + ++count; + + if (depth > 1) + { + count += fillCategory(child, depth - 1); + } + } + + for (int i = 0; i < 10; i++) + { + Product1 product = getModel1Factory().createProduct1(); + addUnique(category.getProducts(), product); + ++count; + } + + return count; + } + + private static <T extends EObject> void addUnique(EList<T> list, T object) + { + ((InternalEList<T>)list).addUnique(object); + } + + private static int iterateResource(CDOResource resource) + { + int count = 1; + long start = System.currentTimeMillis(); + + for (Iterator<EObject> it = resource.eAllContents(); it.hasNext();) + { + it.next(); + ++count; + } + + long stop = System.currentTimeMillis(); + System.out.println("Iterated: " + (stop - start)); + + return count; + } +} diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_486458c_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_486458c_Test.java new file mode 100644 index 0000000000..e37086b265 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_486458c_Test.java @@ -0,0 +1,256 @@ +/* + * 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.bugzilla; + +import org.eclipse.emf.cdo.eresource.CDOResource; +import org.eclipse.emf.cdo.internal.server.Repository; +import org.eclipse.emf.cdo.internal.server.UnitManager; +import org.eclipse.emf.cdo.server.IRepository.Props; +import org.eclipse.emf.cdo.session.CDOSession; +import org.eclipse.emf.cdo.spi.server.InternalRepository; +import org.eclipse.emf.cdo.spi.server.InternalUnitManager; +import org.eclipse.emf.cdo.tests.AbstractCDOTest; +import org.eclipse.emf.cdo.tests.config.IRepositoryConfig; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.CleanRepositoriesAfter; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.CleanRepositoriesBefore; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.Requires; +import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.Skips; +import org.eclipse.emf.cdo.tests.config.impl.RepositoryConfig; +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.cdo.util.CommitException; +import org.eclipse.emf.cdo.util.ConcurrentAccessException; +import org.eclipse.emf.cdo.view.CDOUnit; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.InternalEList; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Bug 486458 - Provide support for optimized loading and notifying of object units + * <p> + * Tests that a createUnit() that starts slightly before another createUnit() creates/opens the same new unit. + * + * @author Eike Stepper + */ +@Requires({ IRepositoryConfig.CAPABILITY_AUDITING, "DB.ranges" }) +@Skips(IRepositoryConfig.CAPABILITY_BRANCHING) +@CleanRepositoriesBefore(reason = "Instrumented repository") +@CleanRepositoriesAfter(reason = "Instrumented repository") +public class Bugzilla_486458c_Test extends AbstractCDOTest +{ + private CountDownLatch createStarted; + + public void testParallelCreateUnits() throws Exception + { + fillRepository(); + + createStarted = new CountDownLatch(1); + + final int[] secondCreated = { 0 }; + + Thread secondCreator = new Thread("Committer") + { + @Override + public void run() + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath("test")); + + try + { + createStarted.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); + } + catch (InterruptedException ex) + { + ex.printStackTrace(); + return; + } + + CDOUnit unit = transaction.getUnitManager().createUnit(resource); + secondCreated[0] = unit.getElements(); + + session.close(); + } + }; + + secondCreator.setDaemon(true); + secondCreator.start(); + + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.getResource(getResourcePath("test")); + + CDOUnit unit = transaction.getUnitManager().createUnit(resource); + int created = unit.getElements(); + assertEquals(7714, created); + + secondCreator.join(DEFAULT_TIMEOUT); + assertEquals(7714, secondCreated[0]); + } + + @Override + public synchronized Map<String, Object> getTestProperties() + { + Map<String, Object> map = super.getTestProperties(); + map.put(Props.SUPPORTING_UNITS, Boolean.toString(true)); + return map; + } + + @Override + protected void doSetUp() throws Exception + { + createRepository(); + super.doSetUp(); + } + + private void createRepository() + { + Repository repository = new Repository.Default() + { + @Override + protected InternalUnitManager createUnitManager() + { + return new UnitManager(this) + { + @Override + protected void createUnitHook1() + { + if (createStarted != null) + { + createStarted.countDown(); + } + } + }; + } + }; + + Map<String, String> props = getRepositoryProperties(); + ((InternalRepository)repository).setProperties(props); + + repository.setName(IRepositoryConfig.REPOSITORY_NAME); + + Map<String, Object> map = getTestProperties(); + map.put(RepositoryConfig.PROP_TEST_REPOSITORY, repository); + } + + private void fillRepository() throws ConcurrentAccessException, CommitException + { + CDOSession session = openSession(); + CDOTransaction transaction = session.openTransaction(); + CDOResource resource = transaction.createResource(getResourcePath("test")); + + for (int i = 0; i < 3; i++) + { + Company company = getModel1Factory().createCompany(); + addUnique(resource.getContents(), company); + fillCompany(company); + + long start = System.currentTimeMillis(); + transaction.commit(); + long stop = System.currentTimeMillis(); + System.out.println("Committed: " + (stop - start)); + } + + session.close(); + } + + private void fillCompany(Company company) + { + for (int i = 0; i < 5; i++) + { + Category category = getModel1Factory().createCategory(); + addUnique(company.getCategories(), category); + fillCategory(category, 3); + } + + for (int i = 0; i < 10; i++) + { + Supplier supplier = getModel1Factory().createSupplier(); + addUnique(company.getSuppliers(), supplier); + } + + for (int i = 0; i < 10; i++) + { + Customer customer = getModel1Factory().createCustomer(); + addUnique(company.getCustomers(), customer); + } + + for (int i = 0; i < 10; i++) + { + PurchaseOrder order = getModel1Factory().createPurchaseOrder(); + order.setSupplier(company.getSuppliers().get(i)); + addUnique(company.getPurchaseOrders(), order); + + for (int j = 0; j < 10; j++) + { + OrderDetail orderDetail = getModel1Factory().createOrderDetail(); + addUnique(order.getOrderDetails(), orderDetail); + } + } + + for (int i = 0; i < 10; i++) + { + SalesOrder order = getModel1Factory().createSalesOrder(); + order.setCustomer(company.getCustomers().get(i)); + addUnique(company.getSalesOrders(), order); + + for (int j = 0; j < 10; j++) + { + OrderDetail orderDetail = getModel1Factory().createOrderDetail(); + addUnique(order.getOrderDetails(), orderDetail); + } + } + } + + private int fillCategory(Category category, int depth) + { + int count = 0; + + for (int i = 0; i < 5; i++) + { + Category child = getModel1Factory().createCategory(); + addUnique(category.getCategories(), child); + ++count; + + if (depth > 1) + { + count += fillCategory(child, depth - 1); + } + } + + for (int i = 0; i < 10; i++) + { + Product1 product = getModel1Factory().createProduct1(); + addUnique(category.getProducts(), product); + ++count; + } + + return count; + } + + private static <T extends EObject> void addUnique(EList<T> list, T object) + { + ((InternalEList<T>)list).addUnique(object); + } +} diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/RepositoryConfig.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/RepositoryConfig.java index d0f55e331f..2d657e0222 100644 --- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/RepositoryConfig.java +++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/RepositoryConfig.java @@ -51,6 +51,7 @@ import org.eclipse.emf.cdo.spi.server.InternalRepositorySynchronizer; import org.eclipse.emf.cdo.spi.server.InternalSessionManager; import org.eclipse.emf.cdo.spi.server.InternalStore; import org.eclipse.emf.cdo.spi.server.InternalSynchronizableRepository; +import org.eclipse.emf.cdo.spi.server.InternalUnitManager; import org.eclipse.emf.cdo.tests.config.IRepositoryConfig; import org.eclipse.emf.cdo.tests.config.IScenario; import org.eclipse.emf.cdo.tests.config.impl.ConfigTest.CleanRepositoriesAfter; @@ -106,6 +107,8 @@ public abstract class RepositoryConfig extends Config implements IRepositoryConf public static final String PROP_TEST_REVISION_MANAGER = "test.repository.RevisionManager"; + public static final String PROP_TEST_UNIT_MANAGER = "test.repository.UnitManager"; + public static final String PROP_TEST_SESSION_MANAGER = "test.repository.SessionManager"; public static final String PROP_TEST_AUTHENTICATOR = "test.repository.Authenticator"; @@ -604,6 +607,12 @@ public abstract class RepositoryConfig extends Config implements IRepositoryConf repository.setRevisionManager(revisionManager); + InternalUnitManager unitManager = getTestUnitManager(); + if (unitManager != null) + { + repository.setUnitManager(unitManager); + } + InternalSessionManager sessionManager = getTestSessionManager(); if (sessionManager == null) { @@ -652,6 +661,11 @@ public abstract class RepositoryConfig extends Config implements IRepositoryConf return (InternalCDORevisionManager)getTestProperty(PROP_TEST_REVISION_MANAGER); } + protected InternalUnitManager getTestUnitManager() + { + return (InternalUnitManager)getTestProperty(PROP_TEST_UNIT_MANAGER); + } + protected InternalSessionManager getTestSessionManager() { return (InternalSessionManager)getTestProperty(PROP_TEST_SESSION_MANAGER); diff --git a/plugins/org.eclipse.emf.cdo.ui.compare/src/org/eclipse/emf/cdo/ui/compare/CDOCompareEditorUtil.java b/plugins/org.eclipse.emf.cdo.ui.compare/src/org/eclipse/emf/cdo/ui/compare/CDOCompareEditorUtil.java index 672d70a51f..a2e6641fce 100644 --- a/plugins/org.eclipse.emf.cdo.ui.compare/src/org/eclipse/emf/cdo/ui/compare/CDOCompareEditorUtil.java +++ b/plugins/org.eclipse.emf.cdo.ui.compare/src/org/eclipse/emf/cdo/ui/compare/CDOCompareEditorUtil.java @@ -15,6 +15,7 @@ import org.eclipse.emf.cdo.CDOState; import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; import org.eclipse.emf.cdo.common.id.CDOID; +import org.eclipse.emf.cdo.common.id.CDOIDUtil; import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; import org.eclipse.emf.cdo.compare.CDOCompare; import org.eclipse.emf.cdo.compare.CDOCompareUtil; @@ -80,7 +81,6 @@ import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -814,7 +814,7 @@ public class CDOCompareEditorUtil Collection<CDOObject> values = transaction.getNewObjects().values(); if (!values.isEmpty()) { - Map<CDOID, CDOID> idMappings = new HashMap<CDOID, CDOID>(); + Map<CDOID, CDOID> idMappings = CDOIDUtil.createMap(); CDOObject[] rightObjects = values.toArray(new CDOObject[values.size()]); for (CDOObject rightObject : rightObjects) 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..4411af376a --- /dev/null +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOUnit.java @@ -0,0 +1,39 @@ +/* + * 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; + +/** + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @author Eike Stepper + * @since 4.5 + */ +public interface CDOUnit +{ + public CDOUnitManager getManager(); + + public EObject getRoot(); + + public int getElements(); + + /** + * Same as calling {@link #close() close(true}. + */ + public void close(); + + /** + * Closes this unit and optionally {@link CDOView.Options#addChangeSubscriptionPolicy(CDOAdapterPolicy) resubscribes} + * all contained objects for change notifications. + */ + public void close(boolean resubscribe); +} 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..fd94f255b7 --- /dev/null +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/cdo/view/CDOUnitManager.java @@ -0,0 +1,54 @@ +/* + * 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; + +/** + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @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/transaction/CDOTransactionImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java index 925ae9391f..a2616b7ed1 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/transaction/CDOTransactionImpl.java @@ -1993,6 +1993,8 @@ public class CDOTransactionImpl extends CDOViewImpl implements InternalCDOTransa // here be removed from the collection of reattached objects lastSavepoint.getReattachedObjects().remove(id); } + + getUnitManager().removeObject(object); } finally { diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/AbstractCDOView.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/AbstractCDOView.java index d5772c8206..37b00d69bd 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/AbstractCDOView.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/view/AbstractCDOView.java @@ -1777,7 +1777,13 @@ public abstract class AbstractCDOView extends CDOCommitHistoryProviderImpl<CDOOb lastLookupObject = null; } - return objects.remove(id); + InternalCDOObject object = objects.remove(id); + if (object != null) + { + objectDeregistered(object); + } + + return object; } finally { @@ -1786,6 +1792,11 @@ public abstract class AbstractCDOView extends CDOCommitHistoryProviderImpl<CDOOb } } + protected void objectDeregistered(InternalCDOObject object) + { + // Subclasses may override. + } + /** * @return Never <code>null</code> */ @@ -2247,6 +2258,8 @@ public abstract class AbstractCDOView extends CDOCommitHistoryProviderImpl<CDOOb TRACER.format(Messages.getString("CDOViewImpl.20"), old); //$NON-NLS-1$ } } + + objectRegistered(object); } finally { @@ -2255,6 +2268,11 @@ public abstract class AbstractCDOView extends CDOCommitHistoryProviderImpl<CDOOb } } + protected void objectRegistered(InternalCDOObject object) + { + // Subclasses may override. + } + public void deregisterObject(InternalCDOObject object) { synchronized (getViewMonitor()) 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..4087ffe837 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,9 +119,11 @@ 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; +import java.util.Map.Entry; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.CountDownLatch; @@ -135,6 +143,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(); @@ -926,23 +936,15 @@ public class CDOViewImpl extends AbstractCDOView implements IExecutorServiceProv } @Override - public InternalCDOObject removeObject(CDOID id) + protected void objectRegistered(InternalCDOObject object) { - synchronized (getViewMonitor()) - { - lockView(); + unitManager.addObject(object); + } - try - { - InternalCDOObject removedObject = super.removeObject(id); - removeLockState(removedObject); - return removedObject; - } - finally - { - unlockView(); - } - } + @Override + protected void objectDeregistered(InternalCDOObject object) + { + removeLockState(object); } public CDOLockState[] getLockStates(Collection<CDOID> ids) @@ -1551,6 +1553,8 @@ public class CDOViewImpl extends AbstractCDOView implements IExecutorServiceProv { new SyncTester().start(); } + + unitManager.activate(); } @Override @@ -1591,6 +1595,8 @@ public class CDOViewImpl extends AbstractCDOView implements IExecutorServiceProv @Override protected void doDeactivate() throws Exception { + unitManager.deactivate(); + CDOViewRegistryImpl.INSTANCE.deregister(this); LifecycleUtil.deactivate(invalidationRunner, OMLogger.Level.WARN); @@ -1805,6 +1811,346 @@ public class CDOViewImpl extends AbstractCDOView implements IExecutorServiceProv return id; } + public final CDOUnitManagerImpl getUnitManager() + { + return unitManager; + } + + /** + * @author Eike Stepper + */ + public 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[] getElements() + { + synchronized (getViewMonitor()) + { + lockView(); + + try + { + return unitPerRoot.values().toArray(new CDOUnit[unitPerRoot.size()]); + } + finally + { + unlockView(); + } + } + } + + public CDOUnit[] getOpenUnits() + { + return getElements(); + } + + public CDOUnit getOpenUnit(EObject object) + { + synchronized (getViewMonitor()) + { + lockView(); + + try + { + return getOpenUnitUnsynced(object); + } + finally + { + unlockView(); + } + } + } + + public CDOUnit getOpenUnitUnsynced(EObject object) + { + return unitPerObject.get(object); + } + + public void addObject(InternalCDOObject object) + { + if (!unitPerRoot.isEmpty()) + { + CDOUnit unit = getOpenUnitUnsynced(object); + if (unit == null) + { + EObject parent = getParent(object); + EObject rootResource = getRootResource(); + + while (parent != null && parent != rootResource) + { + unit = getOpenUnitUnsynced(parent); + if (unit != null) + { + unitPerObject.put(object, unit); + ++((CDOUnitImpl)unit).elements; + break; + } + + parent = getParent(parent); + } + } + } + } + + public void removeObject(InternalCDOObject object) + { + if (!unitPerRoot.isEmpty()) + { + CDOUnit unit = unitPerObject.remove(object); + if (unit != null) + { + if (unit.getRoot() == object) + { + unitPerRoot.remove(object); + } + + --((CDOUnitImpl)unit).elements; + } + } + } + + @Override + protected void doDeactivate() throws Exception + { + unitPerRoot.clear(); + unitPerObject.clear(); + super.doDeactivate(); + } + + private EObject getParent(EObject object) + { + EObject parent = object.eContainer(); + if (parent == null) + { + parent = (EObject)((InternalEObject)object).eDirectResource(); + } + + return parent; + } + + 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 + { + if (opcode == CDOProtocolConstants.UNIT_CREATE) + { + CDOUnit containingUnit = getOpenUnit(root); + if (containingUnit != null) + { + throw new CDOException( + "Attempt to nest the new unit " + root + " in the existing unit " + containingUnit); + } + + for (CDOUnit existingUnit : unitPerRoot.values()) + { + if (EcoreUtil.isAncestor(root, existingUnit.getRoot())) + { + throw new CDOException( + "Attempt to nest the existing unit " + existingUnit + " in the new unit " + root); + } + } + } + + final InternalCDORevisionManager revisionManager = session.getRevisionManager(); + final CDOUnitImpl unit = new CDOUnitImpl(root); + + int viewID = getViewID(); + CDOID rootID = getCDORoot(root).cdoID(); + + CDORevisionHandler revisionHandler = opcode == CDOProtocolConstants.UNIT_CREATE + || opcode == CDOProtocolConstants.UNIT_OPEN ? new CDORevisionHandler() + { + public boolean handleRevision(CDORevision revision) + { + ++unit.elements; + revisionManager.addRevision(revision); + + CDOID id = revision.getID(); + changeSubscriptionManager.removeEntry(id); + + InternalCDOObject object = getObject(id); + unitPerObject.put(object, unit); + return true; + } + } : null; + + CDOSessionProtocol sessionProtocol = session.getSessionProtocol(); + boolean success = sessionProtocol.requestUnit(viewID, rootID, opcode, revisionHandler); + + if (success) + { + if (revisionHandler != null) + { + unitPerRoot.put(root, unit); + unitPerObject.put(root, unit); + } + + return unit; + } + + return null; + } + finally + { + unlockView(); + } + } + } + + private void closeUnit(CDOUnit unit, boolean resubscribe) + { + synchronized (getViewMonitor()) + { + lockView(); + + try + { + requestUnit(unit.getRoot(), CDOProtocolConstants.UNIT_CLOSE); + + if (resubscribe && !options.hasChangeSubscriptionPolicies()) + { + resubscribe = false; + } + + for (Iterator<Entry<EObject, CDOUnit>> it = unitPerObject.entrySet().iterator(); it.hasNext();) + { + Entry<EObject, CDOUnit> entry = it.next(); + if (entry.getValue() == unit) + { + it.remove(); // Remove the object from its unit first, so that shouldSubscribe() can return true. + + if (resubscribe) + { + EObject object = entry.getKey(); + for (Adapter adapter : object.eAdapters()) + { + changeSubscriptionManager.subscribe(object, adapter); + } + } + } + } + + unitPerRoot.remove(unit.getRoot()); + } + finally + { + unlockView(); + } + } + + fireElementRemovedEvent(unit); + } + + /** + * @author Eike Stepper + */ + public final class CDOUnitImpl implements CDOUnit + { + private final EObject root; + + private int elements; + + public CDOUnitImpl(EObject root) + { + this.root = root; + } + + public CDOUnitManagerImpl getManager() + { + return CDOUnitManagerImpl.this; + } + + public EObject getRoot() + { + return root; + } + + public int getElements() + { + return elements; + } + + public void close() + { + close(true); + } + + public void close(boolean resubscribe) + { + closeUnit(this, resubscribe); + } + + @Override + public String toString() + { + return "CDOUnit[" + root + "]"; + } + } + } + /** * @author Simon McDuff * @since 2.0 @@ -1926,7 +2272,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(); @@ -1964,13 +2310,13 @@ public class CDOViewImpl extends AbstractCDOView implements IExecutorServiceProv { for (CDOObject object : newObjects) { - InternalCDOObject cdoDetachedObject = (InternalCDOObject)object; - if (cdoDetachedObject != null) + InternalCDOObject internalObject = (InternalCDOObject)object; + if (internalObject != null) { - int count = getNumberOfValidAdapters(cdoDetachedObject); + int count = getNumberOfValidAdapters(internalObject); if (count > 0) { - subscribe(cdoDetachedObject.cdoID(), cdoDetachedObject, count); + subscribe(internalObject.cdoID(), internalObject, count); } } } @@ -2031,6 +2377,11 @@ public class CDOViewImpl extends AbstractCDOView implements IExecutorServiceProv private boolean shouldSubscribe(EObject eObject, Adapter adapter) { + if (unitManager.getOpenUnitUnsynced(eObject) != null) + { + return false; + } + for (CDOAdapterPolicy policy : options().getChangeSubscriptionPolicies()) { if (policy.isValid(eObject, adapter)) @@ -2072,7 +2423,7 @@ public class CDOViewImpl extends AbstractCDOView implements IExecutorServiceProv // Look if objects need to be unsubscribe if (count <= 0) { - subscriptions.remove(id); + removeEntry(id); // Notification need to be enable to send correct value to the server if (policiesPresent) @@ -2098,9 +2449,14 @@ public class CDOViewImpl extends AbstractCDOView implements IExecutorServiceProv subscribe(id, null, Integer.MIN_VALUE); } - private void addEntry(CDOID key, InternalCDOObject object, int count) + private void addEntry(CDOID id, InternalCDOObject object, int count) + { + subscriptions.put(id, new SubscribeEntry(object, count)); + } + + private void removeEntry(CDOID id) { - subscriptions.put(key, new SubscribeEntry(object, count)); + subscriptions.remove(id); } } @@ -2136,7 +2492,8 @@ public class CDOViewImpl extends AbstractCDOView implements IExecutorServiceProv } /** - * A {@link IListener} to prefetch {@link CDOLockState lockstates} when {@link CDORevision revisions} are loaded, according to {@link Options#setLockStatePrefetchEnabled(boolean)} option. + * A {@link IListener} to prefetch {@link CDOLockState lock states} when {@link CDORevision revisions} are loaded, + * according to {@link Options#setLockStatePrefetchEnabled(boolean)} option. * * @author Esteban Dugueperoux */ @@ -2814,7 +3171,7 @@ public class CDOViewImpl extends AbstractCDOView implements IExecutorServiceProv { if (changeSubscriptionPolicies.add(policy)) { - changeSubscriptionManager.notifyChangeSubcriptionPolicy(); + changeSubscriptionManager.handleChangeSubcriptionPoliciesChanged(); event = new ChangeSubscriptionPoliciesEventImpl(); } } @@ -2840,7 +3197,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/CDOMergingConflictResolver.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOMergingConflictResolver.java index 59020e5dc5..847a3bbf51 100644 --- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOMergingConflictResolver.java +++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/spi/cdo/CDOMergingConflictResolver.java @@ -45,7 +45,6 @@ import org.eclipse.net4j.util.om.trace.ContextTracer; import org.eclipse.emf.ecore.EStructuralFeature; -import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -402,7 +401,7 @@ public class CDOMergingConflictResolver extends AbstractChangeSetsConflictResolv public ObjectsMapUpdater(Map<CDOID, CDOObject> map) { - mapCopy = new HashMap<CDOID, CDOObject>(map); + mapCopy = CDOIDUtil.createMap(map); map.clear(); this.map = map; 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> diff --git a/plugins/org.eclipse.net4j.db.mysql/META-INF/MANIFEST.MF b/plugins/org.eclipse.net4j.db.mysql/META-INF/MANIFEST.MF index 282416a59f..bb9bcb9331 100644 --- a/plugins/org.eclipse.net4j.db.mysql/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.net4j.db.mysql/META-INF/MANIFEST.MF @@ -1,7 +1,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-SymbolicName: org.eclipse.net4j.db.mysql;singleton:=true -Bundle-Version: 4.2.200.qualifier +Bundle-Version: 4.2.300.qualifier Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-Localization: plugin @@ -13,6 +13,6 @@ Require-Bundle: org.eclipse.net4j.db;bundle-version="[4.0.0,5.0.0)";visibility:= Import-Package: com.mysql.jdbc;version="[5.0.0,6.0.0)";resolution:=optional;x-installation:=greedy, com.mysql.jdbc.jdbc2.optional;version="[5.0.0,6.0.0)";resolution:=optional;x-installation:=greedy, org.osgi.framework;version="[1.0.0,2.0.0)" -Export-Package: org.eclipse.net4j.db.internal.mysql.bundle;version="4.2.200";x-internal:=true, - org.eclipse.net4j.db.mysql;version="4.2.200" +Export-Package: org.eclipse.net4j.db.internal.mysql.bundle;version="4.2.300";x-internal:=true, + org.eclipse.net4j.db.mysql;version="4.2.300" Eclipse-RegisterBuddy: org.eclipse.net4j.db diff --git a/plugins/org.eclipse.net4j.db.mysql/src/org/eclipse/net4j/db/mysql/MYSQLAdapter.java b/plugins/org.eclipse.net4j.db.mysql/src/org/eclipse/net4j/db/mysql/MYSQLAdapter.java index 87469593c4..87f9d0cf15 100644 --- a/plugins/org.eclipse.net4j.db.mysql/src/org/eclipse/net4j/db/mysql/MYSQLAdapter.java +++ b/plugins/org.eclipse.net4j.db.mysql/src/org/eclipse/net4j/db/mysql/MYSQLAdapter.java @@ -16,6 +16,9 @@ import org.eclipse.net4j.db.ddl.IDBField; import org.eclipse.net4j.db.ddl.IDBIndex; import org.eclipse.net4j.spi.db.DBAdapter; +import com.mysql.jdbc.ConnectionProperties; + +import java.sql.Connection; import java.sql.SQLException; /** @@ -32,39 +35,39 @@ public class MYSQLAdapter extends DBAdapter private static final String[] RESERVED_WORDS = { "ACCESSIBLE", "ACTION", "ADD", "ALL", "ALTER", "ANALYZE", "AND", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ "AS", //$NON-NLS-1$ - "ASC", "ASENSITIVE", "BEFORE", "BETWEEN", "BIGINT", "BINARY", "BIT", "BLOB", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "BOTH", "BY", "CALL", "CASCADE", "CASE", "CHANGE", "CHAR", "CHARACTER", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "CHECK", "COLLATE", "COLUMN", "COLUMNS", "CONDITION", "CONNECTION", "CONSTRAINT", "CONTINUE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "CONVERT", "CREATE", "CROSS", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "DATABASE", "DATABASES", "DATE", "DAY_HOUR", "DAY_MICROSECOND", "DAY_MINUTE", "DAY_SECOND", "DEC", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "DECIMAL", "DECLARE", "DEFAULT", "DELAYED", "DELETE", "DESC", "DESCRIBE", "DETERMINISTIC", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "DISTINCT", "DISTINCTROW", "DIV", "DOUBLE", "DROP", "DUAL", "EACH", "ELSE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "ELSEIF", "ENCLOSED", "ENUM", "ESCAPED", "EXISTS", "EXIT", "EXPLAIN", "FALSE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "FETCH", "FIELDS", "FLOAT", "FLOAT4", "FLOAT8", "FOR", "FORCE", "FOREIGN", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "FROM", "FULLTEXT", "GENERAL", "GOTO", "GRANT", "GROUP", "HAVING", "HIGH_PRIORITY", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "HOUR_MICROSECOND", "HOUR_MINUTE", "HOUR_SECOND", "IF", "IGNORE", "IGNORE_SERVER_IDS", "IN", "INDEX", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "INFILE", "INNER", "INOUT", "INSENSITIVE", "INSERT", "INT", "INT1", "INT2", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "INT3", "INT4", "INT8", "INTEGER", "INTERVAL", "INTO", "IS", "ITERATE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "JOIN", "KEY", "KEYS", "KILL", "LABEL", "LEADING", "LEAVE", "LEFT", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "LIKE", "LIMIT", "LINEAR", "LINES", "LOAD", "LOCALTIME", "LOCALTIMESTAMP", "LOCK", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "LONG", "LONGBLOB", "LONGTEXT", "LOOP", "LOW_PRIORITY", "MASTER_HEARTBEAT_PERIOD", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ + "ASC", "ASENSITIVE", "BEFORE", "BETWEEN", "BIGINT", "BINARY", "BIT", "BLOB", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "BOTH", "BY", "CALL", "CASCADE", "CASE", "CHANGE", "CHAR", "CHARACTER", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "CHECK", "COLLATE", "COLUMN", "COLUMNS", "CONDITION", "CONNECTION", "CONSTRAINT", "CONTINUE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "CONVERT", "CREATE", "CROSS", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "DATABASE", "DATABASES", "DATE", "DAY_HOUR", "DAY_MICROSECOND", "DAY_MINUTE", "DAY_SECOND", "DEC", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "DECIMAL", "DECLARE", "DEFAULT", "DELAYED", "DELETE", "DESC", "DESCRIBE", "DETERMINISTIC", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "DISTINCT", "DISTINCTROW", "DIV", "DOUBLE", "DROP", "DUAL", "EACH", "ELSE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "ELSEIF", "ENCLOSED", "ENUM", "ESCAPED", "EXISTS", "EXIT", "EXPLAIN", "FALSE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "FETCH", "FIELDS", "FLOAT", "FLOAT4", "FLOAT8", "FOR", "FORCE", "FOREIGN", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "FROM", "FULLTEXT", "GENERAL", "GOTO", "GRANT", "GROUP", "HAVING", "HIGH_PRIORITY", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "HOUR_MICROSECOND", "HOUR_MINUTE", "HOUR_SECOND", "IF", "IGNORE", "IGNORE_SERVER_IDS", "IN", "INDEX", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "INFILE", "INNER", "INOUT", "INSENSITIVE", "INSERT", "INT", "INT1", "INT2", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "INT3", "INT4", "INT8", "INTEGER", "INTERVAL", "INTO", "IS", "ITERATE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "JOIN", "KEY", "KEYS", "KILL", "LABEL", "LEADING", "LEAVE", "LEFT", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "LIKE", "LIMIT", "LINEAR", "LINES", "LOAD", "LOCALTIME", "LOCALTIMESTAMP", "LOCK", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "LONG", "LONGBLOB", "LONGTEXT", "LOOP", "LOW_PRIORITY", "MASTER_HEARTBEAT_PERIOD", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ "MASTER_SSL_VERIFY_SERVER_CERT", "MATCH", //$NON-NLS-1$ //$NON-NLS-2$ - "MAXVALUE", "MEDIUMBLOB", "MEDIUMINT", "MEDIUMTEXT", "MIDDLEINT", "MINUTE_MICROSECOND", "MINUTE_SECOND", "MOD", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "MODIFIES", "NATURAL", "NO", "NOT", "NO_WRITE_TO_BINLOG", "NULL", "NUMERIC", "ON", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "OPTIMIZE", "OPTION", "OPTIONALLY", "OR", "ORDER", "OUT", "OUTER", "OUTFILE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "PRECISION", "PRIMARY", "PRIVILEGES", "PROCEDURE", "PURGE", "RAID0", "RANGE", "READ", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "READS", "READ_WRITE", "REAL", "REFERENCES", "REGEXP", "RELEASE", "RENAME", "REPEAT", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "REPLACE", "REQUIRE", "RESIGNAL", "RESTRICT", "RETURN", "REVOKE", "RIGHT", "RLIKE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "SCHEMA", "SCHEMAS", "SECOND_MICROSECOND", "SELECT", "SENSITIVE", "SEPARATOR", "SET", "SHOW", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "SIGNAL", "SLOW", "SMALLINT", "SONAME", "SPATIAL", "SPECIFIC", "SQL", "SQL_BIG_RESULT", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "SQL_CALC_FOUND_ROWS", "SQLEXCEPTION", "SQL_SMALL_RESULT", "SQLSTATE", "SQLWARNING", "SSL", "STARTING", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ + "MAXVALUE", "MEDIUMBLOB", "MEDIUMINT", "MEDIUMTEXT", "MIDDLEINT", "MINUTE_MICROSECOND", "MINUTE_SECOND", "MOD", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "MODIFIES", "NATURAL", "NO", "NOT", "NO_WRITE_TO_BINLOG", "NULL", "NUMERIC", "ON", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "OPTIMIZE", "OPTION", "OPTIONALLY", "OR", "ORDER", "OUT", "OUTER", "OUTFILE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "PRECISION", "PRIMARY", "PRIVILEGES", "PROCEDURE", "PURGE", "RAID0", "RANGE", "READ", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "READS", "READ_WRITE", "REAL", "REFERENCES", "REGEXP", "RELEASE", "RENAME", "REPEAT", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "REPLACE", "REQUIRE", "RESIGNAL", "RESTRICT", "RETURN", "REVOKE", "RIGHT", "RLIKE", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "SCHEMA", "SCHEMAS", "SECOND_MICROSECOND", "SELECT", "SENSITIVE", "SEPARATOR", "SET", "SHOW", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "SIGNAL", "SLOW", "SMALLINT", "SONAME", "SPATIAL", "SPECIFIC", "SQL", "SQL_BIG_RESULT", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "SQL_CALC_FOUND_ROWS", "SQLEXCEPTION", "SQL_SMALL_RESULT", "SQLSTATE", "SQLWARNING", "SSL", "STARTING", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ "STRAIGHT_JOIN", //$NON-NLS-1$ - "TABLE", "TABLES", "TERMINATED", "TEXT", "THEN", "TIME", "TIMESTAMP", "TINYBLOB", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "TINYINT", "TINYTEXT", "TO", "TRAILING", "TRIGGER", "TRUE", "UNDO", "UNION", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "UNIQUE", "UNLOCK", "UNSIGNED", "UPDATE", "UPGRADE", "USAGE", "USE", "USING", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "UTC_DATE", "UTC_TIME", "UTC_TIMESTAMP", "VALUES", "VARBINARY", "VARCHAR", "VARCHARACTER", "VARYING", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "WHEN", "WHERE", "WHILE", "WITH", "WRITE", "X509", "XOR", "YEAR_MONTH", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ - "ZEROFILL" }; //$NON-NLS-1$ + "TABLE", "TABLES", "TERMINATED", "TEXT", "THEN", "TIME", "TIMESTAMP", "TINYBLOB", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "TINYINT", "TINYTEXT", "TO", "TRAILING", "TRIGGER", "TRUE", "UNDO", "UNION", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "UNIQUE", "UNLOCK", "UNSIGNED", "UPDATE", "UPGRADE", "USAGE", "USE", "USING", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "UTC_DATE", "UTC_TIME", "UTC_TIMESTAMP", "VALUES", "VARBINARY", "VARCHAR", "VARCHARACTER", "VARYING", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "WHEN", "WHERE", "WHILE", "WITH", "WRITE", "X509", "XOR", "YEAR_MONTH", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ + "ZEROFILL" }; //$NON-NLS-1$ public MYSQLAdapter() { @@ -161,4 +164,16 @@ public class MYSQLAdapter extends DBAdapter { return "ALTER TABLE " + tableName + " MODIFY " + fieldName + " " + definition; } + + @Override + public Connection modifyConnection(Connection connection) + { + if (connection instanceof ConnectionProperties) + { + ConnectionProperties connectionProperties = (ConnectionProperties)connection; + connectionProperties.setUseLocalSessionState(true); + } + + return super.modifyConnection(connection); + } } diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/BatchedStatement.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/BatchedStatement.java new file mode 100644 index 0000000000..624d6e6a09 --- /dev/null +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/BatchedStatement.java @@ -0,0 +1,28 @@ +/* + * 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.net4j.db; + +import java.sql.PreparedStatement; + +/** + * @since 4.5 + * @author Eike Stepper + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface BatchedStatement extends PreparedStatement +{ + public int getBatchSize(); + + public int getBatchCount(); + + public int getTotalResult(); +} diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBUtil.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBUtil.java index 647f1efe56..f3c1a4b9eb 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBUtil.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/DBUtil.java @@ -14,6 +14,7 @@ import org.eclipse.net4j.db.ddl.IDBField; import org.eclipse.net4j.db.ddl.IDBNamedElement; import org.eclipse.net4j.db.ddl.IDBSchema; import org.eclipse.net4j.db.ddl.IDBTable; +import org.eclipse.net4j.internal.db.BatchedStatementImpl; import org.eclipse.net4j.internal.db.DBConnection; import org.eclipse.net4j.internal.db.DBDatabase; import org.eclipse.net4j.internal.db.DataSourceConnectionProvider; @@ -376,6 +377,14 @@ public final class DBUtil return IDBAdapter.REGISTRY.get(adapterName); } + /** + * @since 4.5 + */ + public static BatchedStatement batched(PreparedStatement delegate, int batchSize) throws DBException + { + return new BatchedStatementImpl(delegate, batchSize); + } + public static Exception close(ResultSet resultSet) { if (resultSet != null) diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBAdapter.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBAdapter.java index a4fe9b7dfa..571f676f1c 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBAdapter.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/db/IDBAdapter.java @@ -60,6 +60,11 @@ public interface IDBAdapter public IDBConnectionProvider createConnectionProvider(DataSource dataSource); /** + * @since 4.5 + */ + public Connection modifyConnection(Connection connection); + + /** * @since 4.2 */ public IDBSchema readSchema(Connection connection, String name); diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/BatchedStatementImpl.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/BatchedStatementImpl.java new file mode 100644 index 0000000000..c022c64634 --- /dev/null +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/BatchedStatementImpl.java @@ -0,0 +1,134 @@ +/* + * 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.net4j.internal.db; + +import org.eclipse.net4j.db.BatchedStatement; +import org.eclipse.net4j.db.DBException; +import org.eclipse.net4j.db.jdbc.DelegatingPreparedStatement; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * @author Eike Stepper + * @since 4.5 + */ +public final class BatchedStatementImpl extends DelegatingPreparedStatement implements BatchedStatement +{ + private final int batchSize; + + private int batchCount; + + private int totalResult; + + public BatchedStatementImpl(PreparedStatement delegate, int batchSize) throws DBException + { + super(delegate, getConnection(delegate)); + this.batchSize = batchSize; + } + + public int getBatchSize() + { + return batchSize; + } + + public int getBatchCount() + { + return batchCount; + } + + public int getTotalResult() + { + return totalResult; + } + + @Override + public int executeUpdate() throws SQLException + { + PreparedStatement delegate = getDelegate(); + delegate.addBatch(); + + if (++batchCount >= batchSize) + { + return doExecuteBatch(); + } + + return 0; + } + + @Override + public void close() throws SQLException + { + if (batchCount != 0) + { + doExecuteBatch(); + } + + super.close(); + } + + @Override + public ResultSet getResultSet() throws SQLException + { + throw new UnsupportedOperationException("Only updates are supported"); + } + + @Override + public ResultSet executeQuery() throws SQLException + { + throw new UnsupportedOperationException("Only updates are supported"); + } + + @Deprecated + @Override + public ResultSet executeQuery(String sql) throws SQLException + { + throw new UnsupportedOperationException("Only updates are supported"); + } + + private int doExecuteBatch() throws SQLException + { + int sum = 0; + + int[] results = getDelegate().executeBatch(); + for (int i = 0; i < results.length; i++) + { + int result = results[i]; + if (result != Statement.SUCCESS_NO_INFO) + { + if (result < 0) + { + throw new DBException("Result " + i + " is not successful: " + result); + } + + sum += result; + } + } + + totalResult += sum; + return sum; + } + + private static Connection getConnection(PreparedStatement delegate) throws DBException + { + try + { + return delegate.getConnection(); + } + catch (SQLException ex) + { + throw new DBException(ex); + } + } +} diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBDatabase.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBDatabase.java index da9434e860..72665bb84c 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBDatabase.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBDatabase.java @@ -33,7 +33,7 @@ import java.util.LinkedList; /** * @author Eike Stepper */ -public final class DBDatabase extends SetContainer<IDBConnection>implements IDBDatabase +public final class DBDatabase extends SetContainer<IDBConnection> implements IDBDatabase { private DBAdapter adapter; @@ -142,6 +142,8 @@ public final class DBDatabase extends SetContainer<IDBConnection>implements IDBD throw new DBException("No connection from connection provider: " + connectionProvider); } + delegate = adapter.modifyConnection(delegate); + DBConnection connection = new DBConnection(this, delegate); addElement(connection); return connection; diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBPreparedStatement.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBPreparedStatement.java index 295fb361da..b5af459ed8 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBPreparedStatement.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/internal/db/DBPreparedStatement.java @@ -22,7 +22,7 @@ import java.sql.SQLException; /** * @author Eike Stepper */ -public final class DBPreparedStatement extends DelegatingPreparedStatement implements IDBPreparedStatement +public class DBPreparedStatement extends DelegatingPreparedStatement implements IDBPreparedStatement { private final String sql; diff --git a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/DBAdapter.java b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/DBAdapter.java index fbd52b6c73..c50be818e5 100644 --- a/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/DBAdapter.java +++ b/plugins/org.eclipse.net4j.db/src/org/eclipse/net4j/spi/db/DBAdapter.java @@ -144,6 +144,14 @@ public abstract class DBAdapter implements IDBAdapter } /** + * @since 4.5 + */ + public Connection modifyConnection(Connection connection) + { + return connection; + } + + /** * @since 4.2 */ public IDBSchema readSchema(Connection connection, String name) diff --git a/plugins/org.eclipse.net4j/src/org/eclipse/net4j/buffer/BufferInputStream.java b/plugins/org.eclipse.net4j/src/org/eclipse/net4j/buffer/BufferInputStream.java index 52ab739f85..ca3e4b6f74 100644 --- a/plugins/org.eclipse.net4j/src/org/eclipse/net4j/buffer/BufferInputStream.java +++ b/plugins/org.eclipse.net4j/src/org/eclipse/net4j/buffer/BufferInputStream.java @@ -40,6 +40,9 @@ public class BufferInputStream extends InputStream implements IBufferHandler public static final long DEFAULT_MILLIS_INTERRUPT_CHECK = 100; + private static final boolean DISABLE_TIMEOUT = Boolean + .getBoolean("org.eclipse.net4j.buffer.BufferInputStream.disableTimeout"); + private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_BUFFER_STREAM, BufferInputStream.class); private final boolean tracerEnabled; @@ -205,7 +208,7 @@ public class BufferInputStream extends InputStream implements IBufferHandler } long timeout; - if (noTimeout) + if (noTimeout || DISABLE_TIMEOUT) { timeout = check; } @@ -240,6 +243,11 @@ public class BufferInputStream extends InputStream implements IBufferHandler private long computeTimeout(final long check) throws IOTimeoutException { + if (DISABLE_TIMEOUT) + { + return Integer.MAX_VALUE; + } + long remaining; synchronized (this) { |