diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/EntityDAG.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/EntityDAG.java
new file mode 100644
index 0000000000000000000000000000000000000000..bca1db398042f88b8064fb22c5d87bb68f256dea
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/EntityDAG.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2011 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;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+
+import ch.systemsx.cisd.common.collections.DAG;
+
+/**
+ * Represents a DAG of registration dependencies between entities. The subclasses of this class
+ * should implement getCode(T), and getDependentEntitiesCodes(T).
+ * 
+ * @author Chandrasekhar Ramakrishnan
+ */
+public abstract class EntityDAG<T>
+{
+    private final List<? extends T> dataSetRegistrations;
+
+    // For simplicity, we use the data set codes to construct the graph, so we need a map
+    // from
+    // codes to data sets
+    HashMap<String, T> codeToDataMap = new HashMap<String, T>();
+
+    HashMap<String, Collection<String>> dependencyGraph = new HashMap<String, Collection<String>>();
+
+    /**
+     * Create a DAG from the registrations.
+     * 
+     * @param dataSetRegistrations
+     */
+    public EntityDAG(List<? extends T> dataSetRegistrations)
+    {
+        this.dataSetRegistrations = dataSetRegistrations;
+        constructGraph();
+    }
+
+    protected abstract String getCode(T entity);
+
+    protected abstract Collection<String> getDependentEntitiesCodes(T entity);
+
+    /**
+     * @return The registrations ordered topologically such that each registrations comes after the
+     *         ones it depends on.
+     */
+    public List<T> getOrderedRegistrations()
+    {
+        DAG<String, Collection<String>> dag = new DAG<String, Collection<String>>(dependencyGraph);
+        List<String> sortedCodes = dag.sortTopologically();
+        ArrayList<T> sortedData = new ArrayList<T>();
+        for (String code : sortedCodes)
+        {
+            T data = codeToDataMap.get(code);
+            // Some of the dependencies may be to *existing* data -- we don't care about those here.
+            if (null != data)
+            {
+                sortedData.add(data);
+            }
+        }
+
+        return sortedData;
+    }
+
+    /**
+     * Create a dependency graph that can be used for topological sorting.
+     */
+    private void constructGraph()
+    {
+        for (T dataSet : dataSetRegistrations)
+        {
+            String dataSetCode = getCode(dataSet);
+            codeToDataMap.put(dataSetCode, dataSet);
+
+            assertNoDependantsForThisDataset(dataSetCode);
+
+            dependencyGraph.put(dataSetCode, getDependentEntitiesCodes(dataSet));
+        }
+    }
+
+    private Collection<String> assertNoDependantsForThisDataset(String dataSetCode)
+    {
+
+        Collection<String> dependents = dependencyGraph.get(dataSetCode);
+        if (dependents != null)
+        {
+            throw new IllegalStateException("Forbidden to add dataset twice!");
+        }
+
+        return dependents;
+    }
+
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/NewExternalDataDAG.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/NewExternalDataDAG.java
index 82082591ddebcba1d6cc245e6bb987e2bb98324a..592293db8ab9736bd0a7ab608cd6531885fc5603 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/NewExternalDataDAG.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/NewExternalDataDAG.java
@@ -17,12 +17,9 @@
 package ch.systemsx.cisd.openbis.generic.server;
 
 import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Collection;
 import java.util.List;
 
-import ch.systemsx.cisd.common.collections.DAG;
-import ch.systemsx.cisd.common.conversation.context.ServiceConversationsThreadContext;
-import ch.systemsx.cisd.common.conversation.progress.IServiceConversationProgressListener;
 import ch.systemsx.cisd.openbis.generic.shared.dto.NewContainerDataSet;
 import ch.systemsx.cisd.openbis.generic.shared.dto.NewExternalData;
 
@@ -31,94 +28,35 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.NewExternalData;
  * 
  * @author Chandrasekhar Ramakrishnan
  */
-public class NewExternalDataDAG
+public class NewExternalDataDAG extends EntityDAG<NewExternalData>
 {
-    private final List<? extends NewExternalData> dataSetRegistrations;
-
-    // For simplicity, we use the data set codes to construct the graph, so we need a map from
-    // codes to data sets
-    HashMap<String, NewExternalData> codeToDataMap = new HashMap<String, NewExternalData>();
-
-    HashMap<String, ArrayList<String>> dependencyGraph = new HashMap<String, ArrayList<String>>();
-
-    /**
-     * Create a DAG from the registrations.
-     * 
-     * @param dataSetRegistrations
-     */
     public NewExternalDataDAG(List<? extends NewExternalData> dataSetRegistrations)
     {
-        super();
-        this.dataSetRegistrations = dataSetRegistrations;
-        constructGraph();
+        super(dataSetRegistrations);
     }
 
-    /**
-     * @return The registrations ordered topologically such that each registrations comes after the
-     *         ones it depends on.
-     */
-    public List<? extends NewExternalData> getOrderedRegistrations()
+    @Override
+    protected String getCode(NewExternalData entity)
     {
-        DAG<String, ArrayList<String>> dag = new DAG<String, ArrayList<String>>(dependencyGraph);
-        List<String> sortedCodes = dag.sortTopologically();
-        ArrayList<NewExternalData> sortedData = new ArrayList<NewExternalData>();
-        for (String code : sortedCodes)
-        {
-            NewExternalData data = codeToDataMap.get(code);
-            // Some of the dependencies may be to *existing* data -- we don't care about those here.
-            if (null != data)
-            {
-                sortedData.add(data);
-            }
-        }
-
-        return sortedData;
+        return entity.getCode();
     }
 
-    /**
-     * Create a dependency graph that can be used for topological sorting.
-     */
-    private void constructGraph()
+    @Override
+    protected Collection<String> getDependentEntitiesCodes(NewExternalData dataSet)
     {
-        IServiceConversationProgressListener listener =
-                ServiceConversationsThreadContext.getProgressListener();
+        ArrayList<String> dependents = new ArrayList<String>();
 
-        int index = 0;
+        // All the parents are dependents
+        dependents.addAll(dataSet.getParentDataSetCodes());
 
-        for (NewExternalData dataSet : dataSetRegistrations)
+        if (dataSet instanceof NewContainerDataSet)
         {
-            String dataSetCode = dataSet.getCode();
-            codeToDataMap.put(dataSetCode, dataSet);
-
-            // There may already be dependents for this data set -- get them or initialize the
-            // dependents
-            ArrayList<String> dependents = getDependentsList(dataSetCode);
-
-            // All the parents are dependents
-            dependents.addAll(dataSet.getParentDataSetCodes());
-
-            if (dataSet instanceof NewContainerDataSet)
-            {
-                // All contained data sets are dependents
-                List<String> containedDataSetCodes =
-                        ((NewContainerDataSet) dataSet).getContainedDataSetCodes();
-                dependents.addAll(containedDataSetCodes);
-            }
-
-            dependencyGraph.put(dataSet.getCode(), dependents);
-
-            listener.update("constructGraph", dataSetRegistrations.size(), ++index);
+            // All contained data sets are dependents
+            List<String> containedDataSetCodes =
+                    ((NewContainerDataSet) dataSet).getContainedDataSetCodes();
+            dependents.addAll(containedDataSetCodes);
         }
-    }
 
-    private ArrayList<String> getDependentsList(String dataSetCode)
-    {
-        ArrayList<String> dependents = dependencyGraph.get(dataSetCode);
-        if (null == dependents)
-        {
-            dependents = new ArrayList<String>();
-            dependencyGraph.put(dataSetCode, dependents);
-        }
         return dependents;
     }
 
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/EntityDagTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/EntityDagTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e81f89d781af5ec256f3497875b1122ccdfaa151
--- /dev/null
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/EntityDagTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2012 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;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
+
+/**
+ * @author Jakub Straszewski
+ */
+public class EntityDagTest
+
+{
+    private class DependencyHolder
+    {
+        String code;
+
+        Collection<String> dependants;
+
+        public DependencyHolder(String code, Collection<String> dependants)
+        {
+            super();
+            this.code = code;
+            this.dependants = dependants;
+        }
+
+        @Override
+        public String toString()
+        {
+            return code;
+        }
+    }
+
+    private EntityDAG<DependencyHolder> createDag(List<DependencyHolder> entities)
+    {
+        return new EntityDAG<EntityDagTest.DependencyHolder>(entities)
+            {
+                @Override
+                protected Collection<String> getDependentEntitiesCodes(DependencyHolder entity)
+                {
+                    return entity.dependants;
+                }
+
+                @Override
+                protected String getCode(DependencyHolder entity)
+                {
+                    return entity.code;
+                }
+            };
+    }
+
+    @Test
+    public void testSortedDependencyOrderIsGood()
+    {
+        List<DependencyHolder> entities = new ArrayList<DependencyHolder>();
+
+        List<String> empty = Collections.emptyList();
+        
+        entities.add(new DependencyHolder("A1", empty));
+        entities.add(new DependencyHolder("A2", Collections.singleton("A1")));
+        entities.add(new DependencyHolder("A3", Collections.singleton("A2")));
+
+        EntityDAG<DependencyHolder> dag = createDag(entities);
+
+        List<? extends DependencyHolder> orderedRegistrations = dag.getOrderedRegistrations();
+
+        Assert.assertEquals(entities, orderedRegistrations);
+    }
+    
+    @Test(expectedExceptions = UserFailureException.class, expectedExceptionsMessageRegExp = "Graph cycle detected. Cannot execute topological sort.")
+    public void testCircularDependency()
+    {
+        List<DependencyHolder> entities = new ArrayList<DependencyHolder>();
+
+        entities.add(new DependencyHolder("A1", Collections.singleton("A3")));
+        entities.add(new DependencyHolder("A2", Collections.singleton("A1")));
+        entities.add(new DependencyHolder("A3", Collections.singleton("A2")));
+
+        EntityDAG<DependencyHolder> dag = createDag(entities);
+
+        List<? extends DependencyHolder> orderedRegistrations = dag.getOrderedRegistrations();
+
+        Assert.assertEquals(entities, orderedRegistrations);
+    }
+
+}