[562246] Lock auto-release is not propagated to other views

https://bugs.eclipse.org/bugs/show_bug.cgi?id=562246
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/lock/CDOLockStateImpl.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/lock/CDOLockStateImpl.java
index b23ff41..201d44e 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/lock/CDOLockStateImpl.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/internal/common/lock/CDOLockStateImpl.java
@@ -11,12 +11,13 @@
 package org.eclipse.emf.cdo.internal.common.lock;
 
 import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
 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.revision.CDOIDAndBranch;
 import org.eclipse.emf.cdo.spi.common.lock.InternalCDOLockState;
 
-import org.eclipse.net4j.util.CheckUtil;
 import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
 
 import java.util.Collections;
@@ -40,8 +41,8 @@
 
   public CDOLockStateImpl(Object lockedObject)
   {
-    CheckUtil.checkArg(lockedObject, "lockedObject");
-    CheckUtil.checkState(lockedObject instanceof CDOID || lockedObject instanceof CDOIDAndBranch, "lockedObject is of wrong type");
+    assert lockedObject instanceof CDOID || lockedObject instanceof CDOIDAndBranch : "lockedObject is of wrong type";
+    assert !CDOIDUtil.isNull(CDOLockUtil.getLockedObjectID(lockedObject)) : "lockedObject is null";
     this.lockedObject = lockedObject;
   }
 
@@ -52,7 +53,6 @@
 
   public CDOLockStateImpl copy(Object lockedObject)
   {
-    checkNotDisposed();
     CDOLockStateImpl newLockState = new CDOLockStateImpl(lockedObject);
 
     if (readLockOwners != null)
@@ -178,8 +178,6 @@
   @Override
   public void addReadLockOwner(CDOLockOwner lockOwner)
   {
-    checkNotDisposed();
-
     if (readLockOwners == null)
     {
       readLockOwners = new HashSet<>();
@@ -191,8 +189,6 @@
   @Override
   public boolean removeReadLockOwner(CDOLockOwner lockOwner)
   {
-    checkNotDisposed();
-
     if (readLockOwners == null)
     {
       return false;
@@ -216,7 +212,6 @@
   @Override
   public void setWriteLockOwner(CDOLockOwner lockOwner)
   {
-    checkNotDisposed();
     writeLockOwner = lockOwner;
   }
 
@@ -229,7 +224,6 @@
   @Override
   public void setWriteOptionOwner(CDOLockOwner lockOwner)
   {
-    checkNotDisposed();
     writeOptionOwner = lockOwner;
   }
 
@@ -356,6 +350,7 @@
     builder.append(", writeOptionOwner=");
     builder.append(writeOptionOwner != null ? writeOptionOwner : "NONE");
 
+    builder.append("]");
     return builder.toString();
   }
 
@@ -366,12 +361,4 @@
     writeLockOwner = null;
     writeOptionOwner = null;
   }
-
-  private void checkNotDisposed()
-  {
-    if (lockedObject == null)
-    {
-      throw new IllegalStateException("Lock state is disposed");
-    }
-  }
 }
diff --git a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/lock/InternalCDOLockState.java b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/lock/InternalCDOLockState.java
index 1efbd6b..bec95e2 100644
--- a/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/lock/InternalCDOLockState.java
+++ b/plugins/org.eclipse.emf.cdo.common/src/org/eclipse/emf/cdo/spi/common/lock/InternalCDOLockState.java
@@ -10,10 +10,8 @@
  */
 package org.eclipse.emf.cdo.spi.common.lock;
 
-import org.eclipse.emf.cdo.common.id.CDOID;
 import org.eclipse.emf.cdo.common.lock.CDOLockOwner;
 import org.eclipse.emf.cdo.common.lock.CDOLockState;
-import org.eclipse.emf.cdo.internal.common.lock.CDOLockStateImpl;
 
 /**
  * If the meaning of this type isn't clear, there really should be more of a description here...
@@ -27,8 +25,10 @@
 {
   /**
    * @since 4.6
+   * @deprecated As of 4.12 no longer supported.
    */
-  public static final CDOLockState UNLOCKED = new CDOLockStateImpl(CDOID.NULL);
+  @Deprecated
+  public static final CDOLockState UNLOCKED = null;
 
   public void addReadLockOwner(CDOLockOwner lockOwner);
 
diff --git a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitNotificationIndication.java b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitNotificationIndication.java
index 21a9a18..ccdaf43 100644
--- a/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitNotificationIndication.java
+++ b/plugins/org.eclipse.emf.cdo.net4j/src/org/eclipse/emf/cdo/internal/net4j/protocol/CommitNotificationIndication.java
@@ -12,7 +12,6 @@
 package org.eclipse.emf.cdo.internal.net4j.protocol;
 
 import org.eclipse.emf.cdo.common.protocol.CDODataInput;
-import org.eclipse.emf.cdo.common.protocol.CDOProtocol;
 import org.eclipse.emf.cdo.common.protocol.CDOProtocol.CommitNotificationInfo;
 import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
 
@@ -33,7 +32,7 @@
   @Override
   protected void indicating(CDODataInput in) throws IOException
   {
-    CommitNotificationInfo info = new CDOProtocol.CommitNotificationInfo(in);
+    CommitNotificationInfo info = new CommitNotificationInfo(in);
 
     InternalCDOSession session = getSession();
     session.handleCommitNotification(info);
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 8809871..0d1a9c2 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
@@ -1419,7 +1419,7 @@
   {
     CDOCommitInfo commitInfo = info.getCommitInfo();
     boolean isFailureCommitInfo = commitInfo.getBranch() == null;
-    if (isFailureCommitInfo || !commitInfo.isEmpty())
+    if (isFailureCommitInfo || !commitInfo.isEmpty() || info.getLockChangeInfo() != null)
     {
       sessionManager.sendCommitNotification(info);
       commitInfoManager.notifyCommitInfoHandlers(commitInfo);
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 a719f2c..9098dd3 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
@@ -954,6 +954,10 @@
     return timeStamp;
   }
 
+  /**
+   * @deprecated Does not seem to be used.
+   */
+  @Deprecated
   protected void setTimeStamp(long timeStamp)
   {
     repository.forceCommitTimeStamp(timeStamp, new Monitor());
@@ -984,7 +988,7 @@
     try
     {
       // Send notifications (in particular FailureCommitInfos) only if timeStamp had been allocated
-      if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE)
+      if (timeStamp != CDOBranchPoint.UNSPECIFIED_DATE || lockChangeInfo != null)
       {
         sendCommitNotifications(success);
       }
diff --git a/plugins/org.eclipse.emf.cdo.tests/launches/CDO AllTests.launch b/plugins/org.eclipse.emf.cdo.tests/launches/CDO AllTests.launch
index 6c3bede..1fed6c8 100644
--- a/plugins/org.eclipse.emf.cdo.tests/launches/CDO AllTests.launch
+++ b/plugins/org.eclipse.emf.cdo.tests/launches/CDO AllTests.launch
@@ -1,41 +1,41 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
-<booleanAttribute key="com.mountainminds.eclemma.core.INPLACE_INSTRUMENTATION" value="true"/>
-<listAttribute key="com.mountainminds.eclemma.core.INSTRUMENTATION_PATHS">
-<listEntry value="/org.eclipse.net4j.db.h2/bin"/>
-<listEntry value="/org.eclipse.net4j.http.server/bin"/>
-<listEntry value="/org.eclipse.emf.cdo.common/bin"/>
-<listEntry value="/org.eclipse.net4j.http/bin"/>
-<listEntry value="/org.eclipse.emf.cdo.net4j/bin"/>
-<listEntry value="/org.eclipse.emf.cdo/bin"/>
-<listEntry value="/org.eclipse.emf.cdo.common.db/bin"/>
-<listEntry value="/org.eclipse.net4j.tcp/bin"/>
-<listEntry value="/org.eclipse.net4j/bin"/>
-<listEntry value="/org.eclipse.net4j.util/bin"/>
-<listEntry value="/org.eclipse.net4j.jvm/bin"/>
-<listEntry value="/org.eclipse.net4j.db/bin"/>
-<listEntry value="/org.eclipse.emf.cdo.server/bin"/>
-<listEntry value="/org.eclipse.net4j.http.common/bin"/>
-<listEntry value="/org.eclipse.net4j.db.derby/bin"/>
-<listEntry value="/org.eclipse.emf.cdo.server.net4j/bin"/>
-</listAttribute>
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
-<listEntry value="/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTests.java"/>
-</listAttribute>
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
-<listEntry value="1"/>
-</listAttribute>
-<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
-<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
-<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
-</listAttribute>
-<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value=""/>
-<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
-<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
-<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit3"/>
-<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/>
-<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.emf.cdo.tests.AllTests"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.emf.cdo.tests"/>
-<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms40m&#13;&#10;-Xmx4g&#13;&#10;-Dorg.eclipse.net4j.util.om.trace.disable=true&#13;&#10;-Dorg.eclipse.emf.cdo.tests.config.impl.RepositoryConfig.enableServerBrowser=false&#13;&#10;-Dorg.eclipse.net4j.db.close.noisy=true"/>
-<stringAttribute key="yk-options" value="&#13;&#10;additional-options=&#13;&#10;snapshots-dir=&#13;&#10;"/>
+    <booleanAttribute key="com.mountainminds.eclemma.core.INPLACE_INSTRUMENTATION" value="true"/>
+    <listAttribute key="com.mountainminds.eclemma.core.INSTRUMENTATION_PATHS">
+        <listEntry value="/org.eclipse.net4j.db.h2/bin"/>
+        <listEntry value="/org.eclipse.net4j.http.server/bin"/>
+        <listEntry value="/org.eclipse.emf.cdo.common/bin"/>
+        <listEntry value="/org.eclipse.net4j.http/bin"/>
+        <listEntry value="/org.eclipse.emf.cdo.net4j/bin"/>
+        <listEntry value="/org.eclipse.emf.cdo/bin"/>
+        <listEntry value="/org.eclipse.emf.cdo.common.db/bin"/>
+        <listEntry value="/org.eclipse.net4j.tcp/bin"/>
+        <listEntry value="/org.eclipse.net4j/bin"/>
+        <listEntry value="/org.eclipse.net4j.util/bin"/>
+        <listEntry value="/org.eclipse.net4j.jvm/bin"/>
+        <listEntry value="/org.eclipse.net4j.db/bin"/>
+        <listEntry value="/org.eclipse.emf.cdo.server/bin"/>
+        <listEntry value="/org.eclipse.net4j.http.common/bin"/>
+        <listEntry value="/org.eclipse.net4j.db.derby/bin"/>
+        <listEntry value="/org.eclipse.emf.cdo.server.net4j/bin"/>
+    </listAttribute>
+    <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+        <listEntry value="/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/AllTests.java"/>
+    </listAttribute>
+    <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+        <listEntry value="1"/>
+    </listAttribute>
+    <listAttribute key="org.eclipse.debug.ui.favoriteGroups">
+        <listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
+        <listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
+    </listAttribute>
+    <stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value=""/>
+    <booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
+    <stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
+    <stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit3"/>
+    <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/>
+    <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.emf.cdo.tests.AllTests"/>
+    <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.emf.cdo.tests"/>
+    <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea&#13;&#10;-Xms40m&#13;&#10;-Xmx4g&#13;&#10;-Dorg.eclipse.net4j.util.om.trace.disable=true&#13;&#10;-Dorg.eclipse.emf.cdo.tests.config.impl.RepositoryConfig.enableServerBrowser=false&#13;&#10;-Dorg.eclipse.net4j.db.close.noisy=true"/>
+    <stringAttribute key="yk-options" value="&#13;&#10;additional-options=&#13;&#10;snapshots-dir=&#13;&#10;"/>
 </launchConfiguration>
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_562246_Test.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_562246_Test.java
new file mode 100644
index 0000000..9fc057c
--- /dev/null
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/bugzilla/Bugzilla_562246_Test.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2020 Eike Stepper (Loehne, 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.CDOLock;
+import org.eclipse.emf.cdo.common.commit.CDOCommitInfo;
+import org.eclipse.emf.cdo.eresource.CDOResource;
+import org.eclipse.emf.cdo.session.CDOSession;
+import org.eclipse.emf.cdo.tests.AbstractLockingTest;
+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.CDOView;
+
+/**
+ * @author Eike Stepper
+ */
+public class Bugzilla_562246_Test extends AbstractLockingTest
+{
+  public void _testLockAutoReleasePropagation() throws Exception
+  {
+    for (int i = 0; i < 10000; i++)
+    {
+      System.out.println("Different session: " + i);
+      testLockAutoReleasePropagation_DifferentSession();
+      closeAllSessions();
+
+      System.out.println("Same session: " + i);
+      testLockAutoReleasePropagation_SameSession();
+      closeAllSessions();
+    }
+  }
+
+  public void testLockAutoReleasePropagation_DifferentSession() throws Exception
+  {
+    run(false);
+  }
+
+  public void testLockAutoReleasePropagation_SameSession() throws Exception
+  {
+    run(true);
+  }
+
+  private void run(boolean sameSession) throws ConcurrentAccessException, CommitException, Exception
+  {
+    CDOSession session = openSession();
+
+    CDOTransaction trans = session.openTransaction();
+    trans.getOrCreateResource(getResourcePath(sameSession));
+    trans.commit();
+    trans.close();
+
+    CDOView view = session.openView();
+    view.options().setLockNotificationEnabled(true);
+    CDOResource resource = view.getResource(getResourcePath(sameSession));
+    CDOLock writeLock = resource.cdoWriteLock();
+
+    lockAndUnlock(view, sameSession);
+    assertFalse("Lock auto-release was not successful", writeLock.isLockedByOthers());
+
+    // Important for this bug: test it a second time
+    lockAndUnlock(view, sameSession);
+    assertFalse("Lock auto-release was not successful", writeLock.isLockedByOthers());
+  }
+
+  private void lockAndUnlock(CDOView view, boolean sameSession) throws Exception
+  {
+    CDOSession session = sameSession ? view.getSession() : openSession();
+    CDOTransaction transaction = session.openTransaction();
+    CDOCommitInfo commitInfo;
+
+    try
+    {
+      transaction.options().setAutoReleaseLocksEnabled(true);
+      CDOResource resource = transaction.getResource(getResourcePath(sameSession));
+      resource.cdoWriteLock().lock(500);
+
+      commitInfo = transaction.commit();
+      sleep(200);
+    }
+    finally
+    {
+      transaction.close();
+
+      if (!sameSession)
+      {
+        session.close();
+      }
+    }
+
+    view.waitForUpdate(commitInfo.getTimeStamp());
+  }
+
+  private String getResourcePath(boolean sameSession)
+  {
+    return getResourcePath("/autoReleaseLockTest" + (sameSession ? "SameSesssion" : "DifferentSession"));
+  }
+}
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/ISessionConfig.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/ISessionConfig.java
index d725df4..4084446 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/ISessionConfig.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/ISessionConfig.java
@@ -51,4 +51,6 @@
   public CDOSession openSession(String repositoryName);
 
   public CDOSession openSession(CDOSessionConfiguration configuration);
+
+  public void closeAllSessions();
 }
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/ConfigTest.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/ConfigTest.java
index f6f7413..57657d9 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/ConfigTest.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/ConfigTest.java
@@ -421,11 +421,24 @@
     return openSession();
   }
 
+  /**
+   * @category Session
+   */
   protected CDONet4jSessionConfiguration createNet4jSessionConfiguration(String repositoryName)
   {
     return new CDONet4jSessionConfigurationImpl();
   }
 
+  /**
+   * @category Session
+   */
+  public void closeAllSessions()
+  {
+    determineCodeLink();
+    ISessionConfig sessionConfig = getSessionConfig();
+    sessionConfig.closeAllSessions();
+  }
+
   // /////////////////////////////////////////////////////////////////////////
   // //////////////////////// Model //////////////////////////////////////////
 
diff --git a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/SessionConfig.java b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/SessionConfig.java
index d4a39f9..f638016 100644
--- a/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/SessionConfig.java
+++ b/plugins/org.eclipse.emf.cdo.tests/src/org/eclipse/emf/cdo/tests/config/impl/SessionConfig.java
@@ -219,6 +219,30 @@
   }
 
   @Override
+  public void closeAllSessions()
+  {
+    if (sessions != null)
+    {
+      CDOSession[] array;
+      synchronized (sessions)
+      {
+        array = sessions.toArray(new CDOSession[sessions.size()]);
+      }
+
+      for (CDOSession session : array)
+      {
+        session.removeListener(sessionListener);
+        LifecycleUtil.deactivate(session);
+      }
+
+      synchronized (sessions)
+      {
+        sessions.clear();
+      }
+    }
+  }
+
+  @Override
   public void setUp() throws Exception
   {
     super.setUp();
@@ -247,26 +271,7 @@
   {
     try
     {
-      if (sessions != null)
-      {
-        CDOSession[] array;
-        synchronized (sessions)
-        {
-          array = sessions.toArray(new CDOSession[sessions.size()]);
-        }
-
-        for (CDOSession session : array)
-        {
-          session.removeListener(sessionListener);
-          LifecycleUtil.deactivate(session);
-        }
-
-        synchronized (sessions)
-        {
-          sessions.clear();
-        }
-      }
-
+      closeAllSessions();
       sessions = null;
       sessionListener = null;
 
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java
index 60ce5f5..7865ff3 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/session/CDOSessionImpl.java
@@ -1040,15 +1040,15 @@
       CDOCommitInfo commitInfo = info.getCommitInfo();
       registerPackageUnits(commitInfo.getNewPackageUnits());
 
-      InvalidationData invalidationData = new InvalidationData();
-      invalidationData.setCommitInfo(commitInfo);
-      invalidationData.setSender(null);
-      invalidationData.setClearResourcePathCache(info.isClearResourcePathCache());
-      invalidationData.setSecurityImpact(info.getSecurityImpact());
-      invalidationData.setNewPermissions(info.getNewPermissions());
-      invalidationData.setLockChangeInfo(info.getLockChangeInfo());
+      InvalidationData sessionInvalidationData = new InvalidationData();
+      sessionInvalidationData.setCommitInfo(commitInfo);
+      sessionInvalidationData.setSender(null);
+      sessionInvalidationData.setClearResourcePathCache(info.isClearResourcePathCache());
+      sessionInvalidationData.setSecurityImpact(info.getSecurityImpact());
+      sessionInvalidationData.setNewPermissions(info.getNewPermissions());
+      sessionInvalidationData.setLockChangeInfo(info.getLockChangeInfo());
 
-      invalidate(invalidationData);
+      invalidate(sessionInvalidationData);
     }
     catch (RuntimeException ex)
     {
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 da11d35..aea2523 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
@@ -4751,7 +4751,7 @@
           ((InternalCDOPackageUnit)newPackageUnit).setState(CDOPackageUnit.State.LOADED);
         }
 
-        CDOLockChangeInfo lockChangeInfo = makeUnlockChangeInfo(result, branchChanged ? branch : null);
+        CDOLockChangeInfo unlockChangeInfo = makeUnlockChangeInfo(result, branchChanged ? branch : null);
         CDOCommitInfo commitInfo = null;
 
         CommitData newCommitData = result.getNewCommitData();
@@ -4776,15 +4776,24 @@
           commitInfo = makeCommitInfo(timeStamp, previousTimeStamp);
           if (!commitInfo.isEmpty())
           {
-            InvalidationData invalidationData = new InvalidationData();
-            invalidationData.setCommitInfo(commitInfo);
-            invalidationData.setSender(transaction);
-            invalidationData.setClearResourcePathCache(clearResourcePathCache);
-            invalidationData.setSecurityImpact(result.getSecurityImpact());
-            invalidationData.setNewPermissions(result.getNewPermissions());
-            invalidationData.setLockChangeInfo(lockChangeInfo);
+            InvalidationData sessionInvalidationData = new InvalidationData();
+            sessionInvalidationData.setCommitInfo(commitInfo);
+            sessionInvalidationData.setSender(transaction);
+            sessionInvalidationData.setClearResourcePathCache(clearResourcePathCache);
+            sessionInvalidationData.setSecurityImpact(result.getSecurityImpact());
+            sessionInvalidationData.setNewPermissions(result.getNewPermissions());
+            sessionInvalidationData.setLockChangeInfo(unlockChangeInfo);
 
-            session.invalidate(invalidationData);
+            session.invalidate(sessionInvalidationData);
+          }
+          else
+          {
+            CDOLockState[] newLockStates = result.getNewLockStates();
+            if (newLockStates != null)
+            {
+              CDOLockChangeInfo lockChangeInfo = makeLockChangeInfo(CDOLockChangeInfo.Operation.UNLOCK, null, result.getTimeStamp(), newLockStates);
+              session.handleLockNotification(lockChangeInfo, transaction, true);
+            }
           }
 
           // Bug 290032 - Sticky views
@@ -4849,9 +4858,10 @@
           fireEvent(new FinishedEvent(idMappings), listeners);
         }
 
-        if (lockChangeInfo != null && isActive())
+        if (unlockChangeInfo != null && isActive())
         {
-          fireLocksChangedEvent(CDOTransactionImpl.this, lockChangeInfo);
+          // session.handleLockNotification(unlockChangeInfo, transaction, true);
+          fireLocksChangedEvent(CDOTransactionImpl.this, unlockChangeInfo);
         }
       }
       catch (RuntimeException ex)
@@ -4934,7 +4944,7 @@
 
         if (options().isEffectiveAutoReleaseLock(object))
         {
-          lockState.updateFrom(InternalCDOLockState.UNLOCKED);
+          lockState.dispose();
           objectsToUnlock.add(lockState);
         }
       }
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 c04e336..6bcecf2 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
@@ -514,9 +514,9 @@
 
       try
       {
-        for (CDOLockState lockState : newLockStates)
+        for (CDOLockState newLockState : newLockStates)
         {
-          Object lockedObject = lockState.getLockedObject();
+          Object lockedObject = newLockState.getLockedObject();
           CDOID id = CDOLockUtil.getLockedObjectID(lockedObject);
 
           if (id == null && lockedObject instanceof EObject)
@@ -533,20 +533,21 @@
           InternalCDOObject object = getObject(id, loadObjectsOnDemand);
           if (object != null)
           {
-            InternalCDOLockState existingLockState = (InternalCDOLockState)lockStates.get(object);
-            if (existingLockState != null)
+            InternalCDOLockState oldLockState = (InternalCDOLockState)lockStates.get(object);
+            if (oldLockState != null)
             {
-              existingLockState.updateFrom(lockState);
-              lockState = existingLockState;
+              oldLockState.updateFrom(newLockState);
+              newLockState = oldLockState;
             }
             else
             {
-              lockStates.put(object, lockState);
+              newLockState = CDOLockUtil.copyLockState(newLockState);
+              lockStates.put(object, newLockState);
             }
 
             if (consumer != null)
             {
-              consumer.accept(lockState);
+              consumer.accept(newLockState);
             }
           }
         }
@@ -2993,6 +2994,7 @@
     {
       updateLockStates(lockStates, loadOnDemand, null);
 
+      // TODO Should this be done via ExecutorServide.submit() ?!
       for (CDOCommonView view : getSession().getViews())
       {
         if (view != CDOViewImpl.this && view.getBranch() == getBranch())
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/HexUtil.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/HexUtil.java
index 9e8b386..f5c27d8 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/HexUtil.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/HexUtil.java
@@ -202,7 +202,7 @@
 
   public static String longToHex(long v)
   {
-    final String hex = Long.toHexString(v);
+    String hex = Long.toHexString(v);
     if (hex.length() < 8)
     {
       return "00000000".substring(hex.length()) + hex; //$NON-NLS-1$
@@ -211,6 +211,28 @@
     return hex;
   }
 
+  /**
+   * @since 3.13
+   */
+  public static String intToHex(int v)
+  {
+    String hex = Integer.toHexString(v);
+    if (hex.length() < 8)
+    {
+      return "00000000".substring(hex.length()) + hex; //$NON-NLS-1$
+    }
+
+    return hex;
+  }
+
+  /**
+   * @since 3.13
+   */
+  public static String identityHashCode(Object object)
+  {
+    return intToHex(System.identityHashCode(object));
+  }
+
   @Deprecated
   public static String formatByte(int b)
   {
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ReflectUtil.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ReflectUtil.java
index 20e0416..f95d876 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ReflectUtil.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/ReflectUtil.java
@@ -648,7 +648,7 @@
     catch (Exception ex)
     {
       // This can really not happen
-      throw new AssertionError();
+      throw new AssertionError(ex);
     }
 
     makeAccessible(method);