diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/LockSampleModificationsInterceptor.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/LockSampleModificationsInterceptor.java
new file mode 100644
index 0000000000000000000000000000000000000000..4cb799c780bc376ccb7e1aa5c03bb239d26298cb
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/LockSampleModificationsInterceptor.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2009 ETH Zuerich, CISD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.systemsx.cisd.openbis.generic.server.dataaccess.db;
+
+import java.io.Serializable;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.hibernate.EmptyInterceptor;
+import org.hibernate.Interceptor;
+import org.hibernate.Transaction;
+import org.hibernate.type.Type;
+
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
+import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
+
+/**
+ * {@link Interceptor} implementation that obtains a (reentrant) Java lock before Sample save/update
+ * and releases it on transaction completion. It is more safe to implement such a lock this way
+ * rather than invoking a method obtaining the lock inside DAO methods because update of Sample
+ * could happen automatically upon flush to DB and it would be easy to introduce such an update
+ * without noticing it. On the other hand we need a lock that will be obtained for every Sample
+ * modification because we have a complex unique code check in a before save/update trigger and we
+ * don't want any race condition or deadlock (if lock is gathered in the trigger). See [LMS-814] for
+ * details.<br>
+ * <br>
+ * NOTE: Explicit exclusive lock on 'samples' table cannot be used because H2 database does not
+ * support it.
+ * 
+ * @author Piotr Buczek
+ */
+public class LockSampleModificationsInterceptor extends EmptyInterceptor
+{
+
+    private static final long serialVersionUID = ServiceVersionHolder.VERSION;
+
+    //
+    // Interceptor
+    //
+
+    @Override
+    public boolean onSave(Object entity, java.io.Serializable id, Object[] state,
+            String[] propertyNames, org.hibernate.type.Type[] types)
+    {
+        obtainLockForSampleModifications(entity);
+        return false;
+    }
+
+    @Override
+    public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState,
+            Object[] previousState, String[] propertyNames, Type[] types)
+    {
+        obtainLockForSampleModifications(entity);
+        return false;
+    }
+
+    @Override
+    public void afterTransactionCompletion(Transaction tx)
+    {
+        releaseLockForSampleModifications();
+    }
+
+    //
+    // implementation using ReentrantLock
+    //
+
+    private final ReentrantLock sampleTableLock = new ReentrantLock();
+
+    private void obtainLockForSampleModifications(Object entity)
+    {
+        if (entity instanceof SamplePE)
+        {
+            sampleTableLock.lock();
+        }
+    }
+
+    private void releaseLockForSampleModifications()
+    {
+        while (sampleTableLock.isHeldByCurrentThread())
+        {
+            sampleTableLock.unlock();
+        }
+    }
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/SampleDAO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/SampleDAO.java
index ff9c1b131f2e605edfb93268bd8f32c0d7134d7c..47e4276f2a1ba3107aff3453f15cedf528b99e83 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/SampleDAO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/SampleDAO.java
@@ -18,9 +18,6 @@ package ch.systemsx.cisd.openbis.generic.server.dataaccess.db;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.locks.ReentrantLock;
-
-import javax.transaction.Synchronization;
 
 import org.apache.log4j.Logger;
 import org.hibernate.Criteria;
@@ -67,8 +64,6 @@ public class SampleDAO extends AbstractGenericEntityDAO<SamplePE> implements ISa
     private static final Logger operationLog =
             LogFactory.getLogger(LogCategory.OPERATION, SampleDAO.class);
 
-    private final ReentrantLock sampleTableLock = new ReentrantLock();
-
     SampleDAO(final SessionFactory sessionFactory, final DatabaseInstancePE databaseInstance)
     {
         super(sessionFactory, databaseInstance, SamplePE.class);
@@ -165,58 +160,7 @@ public class SampleDAO extends AbstractGenericEntityDAO<SamplePE> implements ISa
         return list;
     }
 
-    /**
-     * Obtains a (reentrant) lock and releases it on current transaction completion. This function
-     * should always be executed before saving/updating a sample because we have a complex unique
-     * code check in a trigger and we don't want any race condition or deadlock (if lock is gathered
-     * in the trigger). See [LMS-814] for details.<br>
-     * <br>
-     * NOTE: Explicit exclusive lock on 'samples' table cannot be used because H2 database does not
-     * support it.
-     */
-    private final void lockUntilTransactionCompletion()
-    {
-        final HibernateTemplate hibernateTemplate = getHibernateTemplate();
-        sampleTableLock.lock();
-        try
-        {
-            hibernateTemplate.getSessionFactory().getCurrentSession().getTransaction()
-                    .registerSynchronization(new Synchronization()
-                        {
-                            public void afterCompletion(int arg0)
-                            {
-                                if (sampleTableLock.isHeldByCurrentThread())
-                                {
-                                    sampleTableLock.unlock();
-                                }
-                            }
-
-                            public void beforeCompletion()
-                            {
-                            }
-                        });
-        } catch (Throwable th)
-        {
-            if (sampleTableLock.isHeldByCurrentThread())
-            {
-                sampleTableLock.unlock();
-            }
-            if (th instanceof RuntimeException)
-            {
-                throw (RuntimeException) th;
-            } else
-            {
-                throw (Error) th;
-            }
-        }
-    }
-
-    /**
-     * <b>IMPORTANT</b> - every method which executes this method should first obtain lock using
-     * {@link SampleDAO#lockUntilTransactionCompletion()}. The obtained lock is reentrant so this
-     * method could as well obtain it itself with a small additional cost if there are many saves in
-     * one transaction.
-     */
+    // LockSampleModificationsInterceptor automatically obtains lock
     private final void internalCreateSample(final SamplePE sample,
             final HibernateTemplate hibernateTemplate,
             final ClassValidator<SamplePE> classValidator, final boolean doLog)
@@ -241,8 +185,6 @@ public class SampleDAO extends AbstractGenericEntityDAO<SamplePE> implements ISa
 
         final HibernateTemplate hibernateTemplate = getHibernateTemplate();
 
-        lockUntilTransactionCompletion();
-
         internalCreateSample(sample, hibernateTemplate,
                 new ClassValidator<SamplePE>(SamplePE.class), true);
         hibernateTemplate.flush();
@@ -451,8 +393,6 @@ public class SampleDAO extends AbstractGenericEntityDAO<SamplePE> implements ISa
 
         final HibernateTemplate hibernateTemplate = getHibernateTemplate();
 
-        lockUntilTransactionCompletion();
-
         final ClassValidator<SamplePE> classValidator =
                 new ClassValidator<SamplePE>(SamplePE.class);
         for (final SamplePE samplePE : samples)
@@ -473,8 +413,6 @@ public class SampleDAO extends AbstractGenericEntityDAO<SamplePE> implements ISa
 
         final HibernateTemplate hibernateTemplate = getHibernateTemplate();
 
-        lockUntilTransactionCompletion();
-
         hibernateTemplate.flush();
         if (operationLog.isInfoEnabled())
         {