diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/AbstractJythonDataSetHandlerTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/AbstractJythonDataSetHandlerTest.java
index 443206b71d78b72790085341dfd84b021fa4d36a..3431fa821c05ed933bbc12ca498f50473024cc71 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/AbstractJythonDataSetHandlerTest.java
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/AbstractJythonDataSetHandlerTest.java
@@ -51,6 +51,7 @@ import ch.systemsx.cisd.etlserver.IStorageProcessorTransactional;
 import ch.systemsx.cisd.etlserver.ITypeExtractor;
 import ch.systemsx.cisd.etlserver.ThreadParameters;
 import ch.systemsx.cisd.etlserver.TopLevelDataSetRegistratorGlobalState;
+import ch.systemsx.cisd.etlserver.registrator.api.v2.JavaTopLevelDataSetHandlerV2;
 import ch.systemsx.cisd.etlserver.registrator.monitor.DssRegistrationHealthMonitor;
 import ch.systemsx.cisd.etlserver.registrator.recovery.DataSetStorageRecoveryManager;
 import ch.systemsx.cisd.etlserver.registrator.recovery.IDataSetStorageRecoveryManager;
@@ -223,6 +224,11 @@ public abstract class AbstractJythonDataSetHandlerTest extends AbstractFileSyste
             handler =
                     new TestingDataSetHandlerV2(globalState, registrationShouldFail,
                             shouldReThrowException);
+        } else if (threadProperties.containsKey("TEST_JAVA_V2_API"))
+        {
+            handler =
+                    new TestingDataSetHandlerJavaV2(globalState, registrationShouldFail,
+                            shouldReThrowException);
         } else
         {
             handler =
@@ -299,24 +305,25 @@ public abstract class AbstractJythonDataSetHandlerTest extends AbstractFileSyste
 
     protected Properties createThreadPropertiesRelativeToScriptsFolder(String scriptPath)
     {
-        return createThreadProperties(getRegistrationScriptsFolderPath() + scriptPath, null, null);
+        return createThreadProperties(getRegistrationScriptsFolderPath() + scriptPath, null, null,
+                null);
     }
 
     protected Properties createThreadPropertiesRelativeToScriptsFolder(String scriptPath,
-            HashMap<String, String> override)
+            String javaProgramClass, HashMap<String, String> override)
     {
-        return createThreadProperties(getRegistrationScriptsFolderPath() + scriptPath, null,
-                override);
+        return createThreadProperties(getRegistrationScriptsFolderPath() + scriptPath,
+                javaProgramClass, null, override);
     }
 
     protected Properties createThreadPropertiesRelativeToScriptsFolder(String scriptPath,
             String validationScriptPath)
     {
-        return createThreadProperties(getRegistrationScriptsFolderPath() + scriptPath,
+        return createThreadProperties(getRegistrationScriptsFolderPath() + scriptPath, null,
                 validationScriptPath, null);
     }
 
-    private Properties createThreadProperties(String scriptPath,
+    private Properties createThreadProperties(String scriptPath, String javaProgramClass,
             String validationScriptPropertyOrNull, HashMap<String, String> overrideOrNull)
     {
         Properties threadProperties = new Properties();
@@ -327,7 +334,15 @@ public abstract class AbstractJythonDataSetHandlerTest extends AbstractFileSyste
         threadProperties.setProperty(ThreadParameters.DELETE_UNIDENTIFIED_KEY, "false");
         threadProperties.setProperty(IStorageProcessorTransactional.STORAGE_PROCESSOR_KEY,
                 MockStorageProcessor.class.getName());
-        threadProperties.setProperty(JythonTopLevelDataSetHandler.SCRIPT_PATH_KEY, scriptPath);
+        if (scriptPath != null)
+        {
+            threadProperties.setProperty(JythonTopLevelDataSetHandler.SCRIPT_PATH_KEY, scriptPath);
+        }
+        if (javaProgramClass != null)
+        {
+            threadProperties.setProperty(JavaTopLevelDataSetHandlerV2.PROGRAM_CLASS_KEY,
+                    javaProgramClass);
+        }
         if (null != validationScriptPropertyOrNull)
         {
             threadProperties.setProperty(
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaAllHooks.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaAllHooks.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b447ce3001db5417967f073e1334f35362cfde9
--- /dev/null
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaAllHooks.java
@@ -0,0 +1,78 @@
+/*
+ * 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.etlserver.registrator;
+
+import java.io.IOException;
+
+import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel;
+import ch.systemsx.cisd.etlserver.registrator.api.v2.IJavaDataSetRegistrationDropboxV2;
+
+/**
+ * @author Pawel Glyzewski
+ */
+public abstract class JavaAllHooks implements IJavaDataSetRegistrationDropboxV2
+{
+    protected JythonHookTestTool jythonHookTestTool = JythonHookTestTool.createInTest();
+
+    @Override
+    public void postStorage(DataSetRegistrationContext context)
+    {
+        try
+        {
+            jythonHookTestTool.log("post_storage");
+        } catch (IOException ex)
+        {
+            throw CheckedExceptionTunnel.wrapIfNecessary(ex);
+        }
+    }
+
+    @Override
+    public void preMetadataRegistration(DataSetRegistrationContext context)
+    {
+        try
+        {
+            jythonHookTestTool.log("pre_metadata_registration");
+        } catch (IOException ex)
+        {
+            throw CheckedExceptionTunnel.wrapIfNecessary(ex);
+        }
+    }
+
+    @Override
+    public void postMetadataRegistration(DataSetRegistrationContext context)
+    {
+        try
+        {
+            jythonHookTestTool.log("post_metadata_registration");
+        } catch (IOException ex)
+        {
+            throw CheckedExceptionTunnel.wrapIfNecessary(ex);
+        }
+    }
+
+    @Override
+    public void rollbackPreRegistration(DataSetRegistrationContext context, Throwable throwable)
+    {
+        try
+        {
+            jythonHookTestTool.log("rollback_pre_registration");
+        } catch (IOException ex)
+        {
+            throw CheckedExceptionTunnel.wrapIfNecessary(ex);
+        }
+    }
+}
\ No newline at end of file
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2DyingProgram.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2DyingProgram.java
new file mode 100644
index 0000000000000000000000000000000000000000..9240a9d59dcac3b617652b9e7e8c8ff97ecd5993
--- /dev/null
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2DyingProgram.java
@@ -0,0 +1,54 @@
+/*
+ * 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.etlserver.registrator;
+
+import ch.systemsx.cisd.common.exceptions.NotImplementedException;
+import ch.systemsx.cisd.etlserver.registrator.api.v2.IDataSetRegistrationTransactionV2;
+import ch.systemsx.cisd.etlserver.registrator.api.v2.IJavaDataSetRegistrationDropboxV2;
+import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifier;
+import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifierFactory;
+
+/**
+ * @author Pawel Glyzewski
+ */
+public class JavaV2DyingProgram extends JavaAllHooks implements IJavaDataSetRegistrationDropboxV2
+{
+    @Override
+    public void process(IDataSetRegistrationTransactionV2 transaction)
+    {
+        // Create the Experiment Identifier
+        @SuppressWarnings("unused")
+        ExperimentIdentifier identifier =
+                new ExperimentIdentifierFactory("/SPACE/PROJECT/EXP-CODE").createIdentifier();
+
+        throw new NullPointerException();
+    }
+
+    @Override
+    public boolean isRetryFunctionDefined()
+    {
+        return false;
+    }
+
+    @Override
+    public boolean shouldRetryProcessing(DataSetRegistrationContext context, Exception problem)
+            throws NotImplementedException
+    {
+        throw new NotImplementedException();
+    }
+
+}
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2FileNotFound.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2FileNotFound.java
new file mode 100644
index 0000000000000000000000000000000000000000..8d36c5b07a7fbf0c19e0749eb8ca38e867048a53
--- /dev/null
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2FileNotFound.java
@@ -0,0 +1,50 @@
+/*
+ * 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.etlserver.registrator;
+
+import ch.systemsx.cisd.common.exceptions.NotImplementedException;
+import ch.systemsx.cisd.etlserver.registrator.api.v1.IDataSet;
+import ch.systemsx.cisd.etlserver.registrator.api.v2.IDataSetRegistrationTransactionV2;
+import ch.systemsx.cisd.etlserver.registrator.api.v2.IJavaDataSetRegistrationDropboxV2;
+
+/**
+ * @author Pawel Glyzewski
+ */
+public class JavaV2FileNotFound extends JavaAllHooks implements IJavaDataSetRegistrationDropboxV2
+{
+    @Override
+    public void process(IDataSetRegistrationTransactionV2 transaction)
+    {
+        IDataSet dataSet = transaction.createNewDataSet();
+        transaction.moveFile("/non/existent/path", dataSet);
+        dataSet.setDataSetType("O1");
+        dataSet.setExperiment(transaction.getExperiment("/SPACE/PROJECT/EXP"));
+    }
+
+    @Override
+    public boolean isRetryFunctionDefined()
+    {
+        return false;
+    }
+
+    @Override
+    public boolean shouldRetryProcessing(DataSetRegistrationContext context, Exception problem)
+            throws NotImplementedException
+    {
+        throw new NotImplementedException();
+    }
+}
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2SimpleTestcase.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2SimpleTestcase.java
new file mode 100644
index 0000000000000000000000000000000000000000..d4c50d5dbb6dd4a49ed03ffe32f7dbeb5c956724
--- /dev/null
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2SimpleTestcase.java
@@ -0,0 +1,54 @@
+/*
+ * 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.etlserver.registrator;
+
+import java.io.File;
+
+import ch.systemsx.cisd.common.exceptions.NotImplementedException;
+import ch.systemsx.cisd.etlserver.registrator.api.v1.IDataSet;
+import ch.systemsx.cisd.etlserver.registrator.api.v2.IDataSetRegistrationTransactionV2;
+import ch.systemsx.cisd.etlserver.registrator.api.v2.IJavaDataSetRegistrationDropboxV2;
+
+/**
+ * @author Pawel Glyzewski
+ */
+public class JavaV2SimpleTestcase extends JavaAllHooks implements IJavaDataSetRegistrationDropboxV2
+{
+    @Override
+    public void process(IDataSetRegistrationTransactionV2 transaction)
+    {
+        IDataSet dataSet = transaction.createNewDataSet();
+        File incoming = transaction.getIncoming();
+        transaction.moveFile(incoming.getPath() + "/sub_data_set_1", dataSet);
+        dataSet.setDataSetType("O1");
+        dataSet.setExperiment(transaction.getExperiment("/SPACE/PROJECT/EXP"));
+    }
+
+    @Override
+    public boolean isRetryFunctionDefined()
+    {
+        return false;
+    }
+
+    @Override
+    public boolean shouldRetryProcessing(DataSetRegistrationContext context, Exception problem)
+            throws NotImplementedException
+    {
+        throw new NotImplementedException();
+    }
+
+}
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2TestcasePostregistrationHookFailed.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2TestcasePostregistrationHookFailed.java
new file mode 100644
index 0000000000000000000000000000000000000000..38fabec0a43ef048ffb425e29506465e37f1dc57
--- /dev/null
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2TestcasePostregistrationHookFailed.java
@@ -0,0 +1,66 @@
+/*
+ * 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.etlserver.registrator;
+
+import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel;
+import ch.systemsx.cisd.common.exceptions.NotImplementedException;
+import ch.systemsx.cisd.etlserver.registrator.api.v1.IDataSet;
+import ch.systemsx.cisd.etlserver.registrator.api.v2.IDataSetRegistrationTransactionV2;
+import ch.systemsx.cisd.etlserver.registrator.api.v2.IJavaDataSetRegistrationDropboxV2;
+
+/**
+ * @author Pawel Glyzewski
+ */
+public class JavaV2TestcasePostregistrationHookFailed extends JavaAllHooks implements
+        IJavaDataSetRegistrationDropboxV2
+{
+    @Override
+    public void postMetadataRegistration(DataSetRegistrationContext context)
+    {
+        try
+        {
+            jythonHookTestTool.log("post_metadata_registration");
+        } catch (Exception ex)
+        {
+            throw CheckedExceptionTunnel.wrapIfNecessary(ex);
+        }
+        throw new IllegalArgumentException(
+                "Fail at post_metadata_registration to cancel registration");
+    }
+
+    @Override
+    public void process(IDataSetRegistrationTransactionV2 transaction)
+    {
+        IDataSet dataSet = transaction.createNewDataSet();
+        transaction.moveFile(transaction.getIncoming().getPath() + "/sub_data_set_1", dataSet);
+        dataSet.setDataSetType("O1");
+        dataSet.setExperiment(transaction.getExperiment("/SPACE/PROJECT/EXP"));
+    }
+
+    @Override
+    public boolean isRetryFunctionDefined()
+    {
+        return false;
+    }
+
+    @Override
+    public boolean shouldRetryProcessing(DataSetRegistrationContext context, Exception problem)
+            throws NotImplementedException
+    {
+        throw new NotImplementedException();
+    }
+}
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2TestcasePreregistrationHookFailed.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2TestcasePreregistrationHookFailed.java
new file mode 100644
index 0000000000000000000000000000000000000000..1312e721fce0a5736cad2b24a0bced8ef173045d
--- /dev/null
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2TestcasePreregistrationHookFailed.java
@@ -0,0 +1,66 @@
+/*
+ * 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.etlserver.registrator;
+
+import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel;
+import ch.systemsx.cisd.common.exceptions.NotImplementedException;
+import ch.systemsx.cisd.etlserver.registrator.api.v1.IDataSet;
+import ch.systemsx.cisd.etlserver.registrator.api.v2.IDataSetRegistrationTransactionV2;
+import ch.systemsx.cisd.etlserver.registrator.api.v2.IJavaDataSetRegistrationDropboxV2;
+
+/**
+ * @author Pawel Glyzewski
+ */
+public class JavaV2TestcasePreregistrationHookFailed extends JavaAllHooks implements
+        IJavaDataSetRegistrationDropboxV2
+{
+    @Override
+    public void preMetadataRegistration(DataSetRegistrationContext context)
+    {
+        try
+        {
+            jythonHookTestTool.log("pre_metadata_registration");
+        } catch (Exception ex)
+        {
+            throw CheckedExceptionTunnel.wrapIfNecessary(ex);
+        }
+        throw new IllegalArgumentException(
+                "Fail at pre_metadata_registration to cancel registration");
+    }
+
+    @Override
+    public void process(IDataSetRegistrationTransactionV2 transaction)
+    {
+        IDataSet dataSet = transaction.createNewDataSet();
+        transaction.moveFile(transaction.getIncoming().getPath() + "/sub_data_set_1", dataSet);
+        dataSet.setDataSetType("O1");
+        dataSet.setExperiment(transaction.getExperiment("/SPACE/PROJECT/EXP"));
+    }
+
+    @Override
+    public boolean isRetryFunctionDefined()
+    {
+        return false;
+    }
+
+    @Override
+    public boolean shouldRetryProcessing(DataSetRegistrationContext context, Exception problem)
+            throws NotImplementedException
+    {
+        throw new NotImplementedException();
+    }
+}
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2TestcaseRegistrationContext.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2TestcaseRegistrationContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..747471f5c0dcb446edda96433be57a8d2e33660c
--- /dev/null
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2TestcaseRegistrationContext.java
@@ -0,0 +1,146 @@
+/*
+ * 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.etlserver.registrator;
+
+import java.io.IOException;
+
+import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel;
+import ch.systemsx.cisd.common.exceptions.NotImplementedException;
+import ch.systemsx.cisd.etlserver.registrator.api.v1.IDataSet;
+import ch.systemsx.cisd.etlserver.registrator.api.v2.IDataSetRegistrationTransactionV2;
+import ch.systemsx.cisd.etlserver.registrator.api.v2.IJavaDataSetRegistrationDropboxV2;
+
+/**
+ * @author Pawel Glyzewski
+ */
+public class JavaV2TestcaseRegistrationContext implements IJavaDataSetRegistrationDropboxV2
+{
+    private JythonHookTestTool jythonHookTestTool = JythonHookTestTool.createInTest();
+
+    @Override
+    public void process(IDataSetRegistrationTransactionV2 transaction)
+    {
+        IDataSet dataSet = transaction.createNewDataSet();
+        transaction.moveFile(transaction.getIncoming().getPath() + "/sub_data_set_1", dataSet);
+        dataSet.setDataSetType("O1");
+        dataSet.setExperiment(transaction.getExperiment("/SPACE/PROJECT/EXP"));
+        transaction.getRegistrationContext().getPersistentMap().put("body", "1");
+    }
+
+    @Override
+    public void postStorage(DataSetRegistrationContext context)
+    {
+        assert_context_content(context, "post_storage", "body", "1");
+        assert_context_content(context, "post_storage", "pre_metadata_registration", "2");
+        assert_context_content(context, "post_storage", "post_metadata_registration", "3");
+
+        context.getPersistentMap().put("post_storage", "4");
+        try
+        {
+            jythonHookTestTool.log("post_storage");
+        } catch (IOException ex)
+        {
+            throw CheckedExceptionTunnel.wrapIfNecessary(ex);
+        }
+    }
+
+    @Override
+    public void preMetadataRegistration(DataSetRegistrationContext context)
+    {
+        assert_context_content(context, "pre_metadata_registration", "body", "1");
+        assert_context_content(context, "pre_metadata_registration", "pre_metadata_registration",
+                null);
+        assert_context_content(context, "pre_metadata_registration", "post_storage", null);
+
+        context.getPersistentMap().put("pre_metadata_registration", "2");
+        try
+        {
+            jythonHookTestTool.log("pre_metadata_registration");
+        } catch (IOException ex)
+        {
+            throw CheckedExceptionTunnel.wrapIfNecessary(ex);
+        }
+    }
+
+    @Override
+    public void postMetadataRegistration(DataSetRegistrationContext context)
+    {
+        assert_context_content(context, "post_metadata_registration", "body", "1");
+        assert_context_content(context, "post_metadata_registration", "pre_metadata_registration",
+                "2");
+        assert_context_content(context, "post_metadata_registration", "post_storage", null);
+
+        context.getPersistentMap().put("post_metadata_registration", "3");
+        try
+        {
+            jythonHookTestTool.log("post_metadata_registration");
+        } catch (IOException ex)
+        {
+            throw CheckedExceptionTunnel.wrapIfNecessary(ex);
+        }
+    }
+
+    @Override
+    public void rollbackPreRegistration(DataSetRegistrationContext context, Throwable throwable)
+    {
+        try
+        {
+            jythonHookTestTool.log("rollback_pre_registration");
+        } catch (IOException ex)
+        {
+            throw CheckedExceptionTunnel.wrapIfNecessary(ex);
+        }
+    }
+
+    @Override
+    public boolean isRetryFunctionDefined()
+    {
+        return false;
+    }
+
+    @Override
+    public boolean shouldRetryProcessing(DataSetRegistrationContext context, Exception problem)
+            throws NotImplementedException
+    {
+        throw new NotImplementedException();
+    }
+
+    private void assert_context_content(DataSetRegistrationContext context, String caller,
+            String name, String expected)
+    {
+        Object value = context.getPersistentMap().get(name);
+        if ((expected == null && expected != value)
+                || (expected != null && (false == expected.equals(value))))
+        {
+            if (value != null && expected != null && value.getClass() != expected.getClass())
+            {
+                value = String.format("%s:%s", value.getClass(), value);
+                expected = String.format("%s:%s", expected.getClass(), expected);
+            }
+            try
+            {
+                jythonHookTestTool
+                        .log(String
+                                .format("transaction context failed.in %s the value of %s should have been '%s', but was '%s'",
+                                        caller, name, expected, value));
+            } catch (IOException ex)
+            {
+                throw CheckedExceptionTunnel.wrapIfNecessary(ex);
+            }
+        }
+    }
+}
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2TestcaseRollback.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2TestcaseRollback.java
new file mode 100644
index 0000000000000000000000000000000000000000..350e2eda670d7ffd4a9b33bf978dcfc8f9dbe642
--- /dev/null
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2TestcaseRollback.java
@@ -0,0 +1,54 @@
+/*
+ * 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.etlserver.registrator;
+
+import ch.systemsx.cisd.common.exceptions.NotImplementedException;
+import ch.systemsx.cisd.etlserver.registrator.api.v1.IDataSet;
+import ch.systemsx.cisd.etlserver.registrator.api.v2.IDataSetRegistrationTransactionV2;
+import ch.systemsx.cisd.etlserver.registrator.api.v2.IJavaDataSetRegistrationDropboxV2;
+
+/**
+ * @author Pawel Glyzewski
+ */
+public class JavaV2TestcaseRollback extends JavaAllHooks implements
+        IJavaDataSetRegistrationDropboxV2
+{
+
+    @Override
+    public void process(IDataSetRegistrationTransactionV2 transaction)
+    {
+        IDataSet dataSet = transaction.createNewDataSet();
+        transaction.moveFile(transaction.getIncoming().getPath() + "/sub_data_set_1", dataSet);
+        dataSet.setDataSetType("O1");
+        dataSet.setExperiment(transaction.getExperiment("/SPACE/PROJECT/EXP"));
+        throw new NullPointerException();
+    }
+
+    @Override
+    public boolean isRetryFunctionDefined()
+    {
+        return false;
+    }
+
+    @Override
+    public boolean shouldRetryProcessing(DataSetRegistrationContext context, Exception problem)
+            throws NotImplementedException
+    {
+        throw new NotImplementedException();
+    }
+
+}
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2TestcaseWithoutPostStorage.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2TestcaseWithoutPostStorage.java
new file mode 100644
index 0000000000000000000000000000000000000000..db7257d6be56b6984c41f550486e9c79b5ec9c19
--- /dev/null
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JavaV2TestcaseWithoutPostStorage.java
@@ -0,0 +1,67 @@
+/*
+ * 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.etlserver.registrator;
+
+import java.io.File;
+
+import ch.systemsx.cisd.common.exceptions.NotImplementedException;
+import ch.systemsx.cisd.etlserver.registrator.api.v1.IDataSet;
+import ch.systemsx.cisd.etlserver.registrator.api.v2.IDataSetRegistrationTransactionV2;
+import ch.systemsx.cisd.etlserver.registrator.api.v2.IJavaDataSetRegistrationDropboxV2;
+
+/**
+ * @author Pawel Glyzewski
+ */
+public class JavaV2TestcaseWithoutPostStorage extends JavaAllHooks implements
+        IJavaDataSetRegistrationDropboxV2
+{
+    @Override
+    public void process(IDataSetRegistrationTransactionV2 transaction)
+    {
+        IDataSet dataSet = transaction.createNewDataSet();
+        File incoming = transaction.getIncoming();
+        transaction.moveFile(incoming.getPath() + "/sub_data_set_1", dataSet);
+        dataSet.setDataSetType("O1");
+        dataSet.setExperiment(transaction.getExperiment("/SPACE/PROJECT/EXP"));
+    }
+
+    @Override
+    public void postStorage(DataSetRegistrationContext context)
+    {
+        throw new NotImplementedException();
+    }
+
+    @Override
+    public void rollbackPreRegistration(DataSetRegistrationContext context, Throwable throwable)
+    {
+        throw new NotImplementedException();
+    }
+
+    @Override
+    public boolean isRetryFunctionDefined()
+    {
+        return false;
+    }
+
+    @Override
+    public boolean shouldRetryProcessing(DataSetRegistrationContext context, Exception problem)
+            throws NotImplementedException
+    {
+        throw new NotImplementedException();
+    }
+
+}
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JythonDropboxRecoveryTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JythonDropboxRecoveryTest.java
index 7f72234a53abe2c7c33303dc948696a8dd9a630c..c48ca6aaf2ce011e5a22123e49fbd43a3ed266d7 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JythonDropboxRecoveryTest.java
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JythonDropboxRecoveryTest.java
@@ -247,7 +247,7 @@ public class JythonDropboxRecoveryTest extends AbstractJythonDataSetHandlerTest
         createData();
 
         Properties properties =
-                createThreadPropertiesRelativeToScriptsFolder(testCase.dropboxScriptPath,
+                createThreadPropertiesRelativeToScriptsFolder(testCase.dropboxScriptPath, null,
                         testCase.overrideProperties);
 
         createHandler(properties, true, false);
@@ -580,7 +580,7 @@ public class JythonDropboxRecoveryTest extends AbstractJythonDataSetHandlerTest
         createData();
 
         Properties properties =
-                createThreadPropertiesRelativeToScriptsFolder(testCase.dropboxScriptPath,
+                createThreadPropertiesRelativeToScriptsFolder(testCase.dropboxScriptPath, null,
                         testCase.overrideProperties);
 
         createHandler(properties, true, false);
@@ -650,7 +650,7 @@ public class JythonDropboxRecoveryTest extends AbstractJythonDataSetHandlerTest
         createData();
 
         Properties properties =
-                createThreadPropertiesRelativeToScriptsFolder(testCase.dropboxScriptPath,
+                createThreadPropertiesRelativeToScriptsFolder(testCase.dropboxScriptPath, null,
                         testCase.overrideProperties);
 
         createHandler(properties, true, false);
@@ -736,7 +736,7 @@ public class JythonDropboxRecoveryTest extends AbstractJythonDataSetHandlerTest
         createData();
 
         Properties properties =
-                createThreadPropertiesRelativeToScriptsFolder(testCase.dropboxScriptPath,
+                createThreadPropertiesRelativeToScriptsFolder(testCase.dropboxScriptPath, null,
                         testCase.overrideProperties);
 
         properties.setProperty(ThreadParameters.DATASET_REGISTRATION_MAX_RETRY_COUNT,
@@ -827,7 +827,7 @@ public class JythonDropboxRecoveryTest extends AbstractJythonDataSetHandlerTest
         createData();
 
         Properties properties =
-                createThreadPropertiesRelativeToScriptsFolder(testCase.dropboxScriptPath,
+                createThreadPropertiesRelativeToScriptsFolder(testCase.dropboxScriptPath, null,
                         testCase.overrideProperties);
 
         properties.setProperty(ThreadParameters.DATASET_REGISTRATION_MAX_RETRY_COUNT,
@@ -910,7 +910,7 @@ public class JythonDropboxRecoveryTest extends AbstractJythonDataSetHandlerTest
         createData();
 
         Properties properties =
-                createThreadPropertiesRelativeToScriptsFolder("v2-retry-process.py",
+                createThreadPropertiesRelativeToScriptsFolder("v2-retry-process.py", null,
                         testCase.overrideProperties);
 
         properties.setProperty(ThreadParameters.PROCESS_MAX_RETRY_COUNT, "100"); // we dont want it
@@ -981,7 +981,7 @@ public class JythonDropboxRecoveryTest extends AbstractJythonDataSetHandlerTest
         createData();
 
         Properties properties =
-                createThreadPropertiesRelativeToScriptsFolder("v2-retry-process.py",
+                createThreadPropertiesRelativeToScriptsFolder("v2-retry-process.py", null,
                         testCase.overrideProperties);
 
         properties.setProperty(ThreadParameters.PROCESS_MAX_RETRY_COUNT, "10"); // we dont want it
@@ -1062,7 +1062,8 @@ public class JythonDropboxRecoveryTest extends AbstractJythonDataSetHandlerTest
         createData();
 
         Properties properties =
-                createThreadPropertiesRelativeToScriptsFolder(script, testCase.overrideProperties);
+                createThreadPropertiesRelativeToScriptsFolder(script, null,
+                        testCase.overrideProperties);
 
         createHandler(properties, true, false);
 
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JythonTopLevelDataSetRegistratorTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JythonTopLevelDataSetRegistratorTest.java
index 8775843ce4ae59e9ca14dcf1fad5a6bdd955f405..f9aa9a1e152c836e122caea40a0778fb276a25e7 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JythonTopLevelDataSetRegistratorTest.java
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/JythonTopLevelDataSetRegistratorTest.java
@@ -53,9 +53,9 @@ import ch.systemsx.cisd.common.test.RecordingMatcher;
 import ch.systemsx.cisd.common.utilities.IDelegatedAction;
 import ch.systemsx.cisd.common.utilities.IPredicate;
 import ch.systemsx.cisd.etlserver.IStorageProcessorTransactional;
-import ch.systemsx.cisd.etlserver.TopLevelDataSetRegistratorGlobalState;
 import ch.systemsx.cisd.etlserver.IStorageProcessorTransactional.UnstoreDataAction;
 import ch.systemsx.cisd.etlserver.ThreadParameters;
+import ch.systemsx.cisd.etlserver.TopLevelDataSetRegistratorGlobalState;
 import ch.systemsx.cisd.openbis.dss.generic.shared.utils.DatasetLocationUtil;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClause;
@@ -110,6 +110,7 @@ public class JythonTopLevelDataSetRegistratorTest extends AbstractJythonDataSetH
         ArrayList<TestCaseParameters> list = new ArrayList<TestCaseParameters>(2);
         list.add(params);
         list.add(versionV2(params));
+        list.add(versionJavaV2(params));
         return list;
     }
 
@@ -125,12 +126,55 @@ public class JythonTopLevelDataSetRegistratorTest extends AbstractJythonDataSetH
         return params;
     }
 
+    public TestCaseParameters versionJavaV2(final TestCaseParameters other)
+    {
+        TestCaseParameters params = other.clone();
+        params.overrideProperties = new HashMap<String, String>(params.overrideProperties);
+        params.overrideProperties.put("TEST_JAVA_V2_API", "");
+        params.dontCallOldApiJythonHooks = true;
+        params.title += " - JavaV2";
+        params.shouldUseAutoRecovery = true;
+        params.javaProgramClass = programJavaV2(other.dropboxScriptPath);
+        return params;
+    }
+
     private static String scriptPathV2(String scriptPath)
     {
         File script = new File(scriptPath);
         return new File(script.getParentFile(), "v2-" + script.getName()).getPath();
     }
 
+    private static String programJavaV2(String scriptPath)
+    {
+        if (scriptPath.endsWith("simple-testcase.py"))
+        {
+            return JavaV2SimpleTestcase.class.getCanonicalName();
+        } else if (scriptPath.endsWith("testcase-without-post-storage.py"))
+        {
+            return JavaV2TestcaseWithoutPostStorage.class.getCanonicalName();
+        } else if (scriptPath.endsWith("file-not-found.py"))
+        {
+            return JavaV2FileNotFound.class.getCanonicalName();
+        } else if (scriptPath.endsWith("testcase-registration-context.py"))
+        {
+            return JavaV2TestcaseRegistrationContext.class.getCanonicalName();
+        } else if (scriptPath.endsWith("testcase-preregistration-hook-failed.py"))
+        {
+            return JavaV2TestcasePreregistrationHookFailed.class.getCanonicalName();
+        } else if (scriptPath.endsWith("testcase-postregistration-hook-failed.py"))
+        {
+            return JavaV2TestcasePostregistrationHookFailed.class.getCanonicalName();
+        } else if (scriptPath.endsWith("dying-script.py"))
+        {
+            return JavaV2DyingProgram.class.getCanonicalName();
+        } else if (scriptPath.endsWith("testcase-rollback.py"))
+        {
+            return JavaV2TestcaseRollback.class.getCanonicalName();
+        }
+
+        return null;
+    }
+
     private static <T> Object[][] asObjectArray(List<T> testCases)
     {
         // here is crappy code for
@@ -266,10 +310,18 @@ public class JythonTopLevelDataSetRegistratorTest extends AbstractJythonDataSetH
                 @Override
                 public boolean execute(Exception arg)
                 {
-                    PyException pyException = (PyException) arg;
-                    IOExceptionUnchecked tunnel = (IOExceptionUnchecked) pyException.getCause();
-                    FileNotFoundException ex = (FileNotFoundException) tunnel.getCause();
-                    return ex.getMessage().startsWith("Neither '/non/existent/path' nor '");
+                    if (arg instanceof IOExceptionUnchecked)
+                    {
+                        IOExceptionUnchecked tunnel = (IOExceptionUnchecked) arg;
+                        FileNotFoundException ex = (FileNotFoundException) tunnel.getCause();
+                        return ex.getMessage().startsWith("Neither '/non/existent/path' nor '");
+                    } else
+                    {
+                        PyException pyException = (PyException) arg;
+                        IOExceptionUnchecked tunnel = (IOExceptionUnchecked) pyException.getCause();
+                        FileNotFoundException ex = (FileNotFoundException) tunnel.getCause();
+                        return ex.getMessage().startsWith("Neither '/non/existent/path' nor '");
+                    }
                 }
             };
         testCases.addAll(multipleVersionsOfTestCase(testCase));
@@ -378,6 +430,11 @@ public class JythonTopLevelDataSetRegistratorTest extends AbstractJythonDataSetH
          */
         protected String dropboxScriptPath = "simple-testcase.py";
 
+        /**
+         * Java program class name for pure Java dropboxes
+         */
+        protected String javaProgramClass;
+
         /**
          * Specifies what properties should be overriden for this test case.
          */
@@ -483,7 +540,7 @@ public class JythonTopLevelDataSetRegistratorTest extends AbstractJythonDataSetH
 
         Properties properties =
                 createThreadPropertiesRelativeToScriptsFolder(testCase.dropboxScriptPath,
-                        testCase.overrideProperties);
+                        testCase.javaProgramClass, testCase.overrideProperties);
 
         createHandler(properties, false, testCase.shouldThrowExceptionDuringRegistration);
 
@@ -885,6 +942,11 @@ public class JythonTopLevelDataSetRegistratorTest extends AbstractJythonDataSetH
          */
         protected String dropboxScriptPath = scriptPathV2("simple-testcase.py");
 
+        /**
+         * Java program class name for pure Java dropboxes
+         */
+        protected String javaProgramClass = programJavaV2("simple-testcase.py");
+
         /**
          * Specifies what properties should be overriden for this test case.
          */
@@ -992,7 +1054,7 @@ public class JythonTopLevelDataSetRegistratorTest extends AbstractJythonDataSetH
 
         Properties properties =
                 createThreadPropertiesRelativeToScriptsFolder(testCase.dropboxScriptPath,
-                        testCase.overrideProperties);
+                        testCase.javaProgramClass, testCase.overrideProperties);
 
         // Create the handler
         TopLevelDataSetRegistratorGlobalState globalState = createGlobalState(properties);
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/TestingDataSetHandlerJavaV2.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/TestingDataSetHandlerJavaV2.java
new file mode 100644
index 0000000000000000000000000000000000000000..c48a6af2c35fce6f43a6b4153d0caebec9bd072a
--- /dev/null
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/etlserver/registrator/TestingDataSetHandlerJavaV2.java
@@ -0,0 +1,81 @@
+/*
+ * 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.etlserver.registrator;
+
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.etlserver.TopLevelDataSetRegistratorGlobalState;
+import ch.systemsx.cisd.etlserver.registrator.api.v1.impl.DataSetRegistrationTransaction;
+import ch.systemsx.cisd.etlserver.registrator.api.v2.JavaTopLevelDataSetHandlerV2;
+import ch.systemsx.cisd.openbis.dss.generic.shared.dto.DataSetInformation;
+import ch.systemsx.cisd.openbis.generic.shared.dto.NewExternalData;
+
+/**
+ * @author Pawel Glyzewski
+ */
+public class TestingDataSetHandlerJavaV2 extends JavaTopLevelDataSetHandlerV2<DataSetInformation>
+        implements ITestingDataSetHandler
+{
+    protected final TestingDataSetHandlerExpectations expectations;
+
+    public TestingDataSetHandlerJavaV2(TopLevelDataSetRegistratorGlobalState globalState,
+            boolean shouldRegistrationFail, boolean shouldReThrowRollbackException)
+    {
+        super(globalState);
+
+        this.expectations =
+                new TestingDataSetHandlerExpectations(shouldRegistrationFail,
+                        shouldReThrowRollbackException);
+    }
+
+    @Override
+    public void registerDataSetInApplicationServer(DataSetInformation dataSetInformation,
+            NewExternalData data) throws Throwable
+    {
+        if (expectations.shouldRegistrationFail)
+        {
+            throw new UserFailureException("Didn't work.");
+        } else
+        {
+            super.registerDataSetInApplicationServer(dataSetInformation, data);
+        }
+    }
+
+    @Override
+    public void rollback(DataSetRegistrationService<DataSetInformation> service, Throwable throwable)
+    {
+        super.rollback(service, throwable);
+        expectations.didServiceRollbackHappen = true;
+        expectations.handleRollbackException(throwable);
+    }
+
+    @Override
+    public void didRollbackTransaction(DataSetRegistrationService<DataSetInformation> service,
+            DataSetRegistrationTransaction<DataSetInformation> transaction,
+            DataSetStorageAlgorithmRunner<DataSetInformation> algorithmRunner, Throwable throwable)
+    {
+        super.didRollbackTransaction(service, transaction, algorithmRunner, throwable);
+
+        expectations.didTransactionRollbackHappen = true;
+        expectations.handleRollbackException(throwable);
+    }
+
+    @Override
+    public TestingDataSetHandlerExpectations getExpectations()
+    {
+        return expectations;
+    }
+}