diff --git a/openbis/dist/server/service.properties b/openbis/dist/server/service.properties
index a8107147edbea5d16925a5925874f0058baa2676..f8c3d9e88b0ca0e3c26cd587f140000132c1cb41 100644
--- a/openbis/dist/server/service.properties
+++ b/openbis/dist/server/service.properties
@@ -33,6 +33,9 @@ database-instance = TEST
 # Base URL of the Data Store Server
 data-store-server-base-url = https://localhost:8444
 
+# The URL of the CIFEX server
+cifex-url = https://cifex.ethz.ch:443
+
 # Hibernate Search
 # The working directory.
 hibernate.search.index-base = ./indices
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientService.java
index 807c9bac0b5544581fd59c554262dd11580dfaa1..9ce4d92410bf1a3b88b2952d93f6b4a5b98d801f 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientService.java
@@ -433,4 +433,8 @@ public interface ICommonClientService extends IClientService {
 	 */
 	public List<DataSetType> listDataSetTypes() throws UserFailureException;
 
+    /** Uploads the specified data sets to the specified CIFEX server using the specified password. */
+    public void uploadDataSets(List<String> dataSetCodes, String cifexURL, String password)
+            throws UserFailureException;
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientServiceAsync.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientServiceAsync.java
index 412354b5d668eb557c58c3d0698935539aea60cf..219c0b3a621f692916e00432ef5fd5ebba50dbef 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientServiceAsync.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/ICommonClientServiceAsync.java
@@ -373,4 +373,8 @@ public interface ICommonClientServiceAsync extends IClientServiceAsync {
 
 	/** @see ICommonClientService#listDataSetTypes() */
 	public void listDataSetTypes(AsyncCallback<List<DataSetType>> callback);
+
+    /** @see ICommonClientService#uploadDataSets(List, String, String) */
+    public void uploadDataSets(List<String> dataSetCodes, String cifexURL, String password,
+            AsyncCallback<Void> abstractAsyncCallback);
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java
index 126b776587c9a7f457f45a217372a637359c5905..2b04cf16b6fce44ddead2a9d65404543d0dd37e5 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java
@@ -310,10 +310,16 @@ public abstract class Dict {
 
 	public static final String CONFIRM_DATASET_DELETION_MSG = "confirm_dataset_deletion_msg";
 
-	//
-	// Sample Registration
-	//
-
+    public static final String BUTTON_UPLOAD_DATASETS = "button_upload_datasets";
+    
+    public static final String CONFIRM_DATASET_UPLOAD_TITLE = "confirm_dataset_upload_title";
+    
+    public static final String CONFIRM_DATASET_UPLOAD_MSG = "confirm_dataset_upload_msg";
+    
+    //
+    // Sample Registration
+    //
+    
 	public static final String INSTANCE_SAMPLE = "instance_sample";
 
 	public static final String GENERATED_FROM_SAMPLE = "generated_from_sample";
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/data/AbstractExternalDataGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/data/AbstractExternalDataGrid.java
index eb275269f49ad3909ca57e6a9dc7f642ccbe22f5..77c754e90b045bf8266dc5011996e5b7ef7a9b94 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/data/AbstractExternalDataGrid.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/data/AbstractExternalDataGrid.java
@@ -53,6 +53,31 @@ public abstract class AbstractExternalDataGrid extends AbstractSimpleBrowserGrid
 {
     public static final String GRID_POSTFIX = "-grid";
     
+    private static abstract class AbstractConfirmationDialog extends Dialog
+    {
+        protected final IViewContext<?> viewContext;
+
+        protected final List<String> dataSetCodes;
+
+        protected final IBrowserGridActionInvoker invoker;
+
+        AbstractConfirmationDialog(IViewContext<?> viewContext, List<ExternalData> dataSets,
+                IBrowserGridActionInvoker invoker, String titleKey)
+        {
+            this.viewContext = viewContext;
+            this.invoker = invoker;
+            dataSetCodes = new ArrayList<String>();
+            for (ExternalData externalData : dataSets)
+            {
+                dataSetCodes.add(externalData.getCode());
+            }
+            setHeading(viewContext.getMessage(titleKey));
+            setButtons(Dialog.OKCANCEL);
+            setHideOnButtonClick(true);
+            setModal(true);
+        }
+    }
+    
     static final class DeletionCallback extends AbstractAsyncCallback<Void>
     {
         private final IBrowserGridActionInvoker invoker;
@@ -70,28 +95,14 @@ public abstract class AbstractExternalDataGrid extends AbstractSimpleBrowserGrid
         }
     }
     
-    private static final class DeletionConfirmationDialog extends Dialog
+    private static final class DeletionConfirmationDialog extends AbstractConfirmationDialog
     {
-        private final IViewContext<?> viewContext;
-
-        private final List<String> dataSetCodes;
-
-        private final IBrowserGridActionInvoker invoker;
-
         private final TextField<String> reason;
 
         public DeletionConfirmationDialog(IViewContext<?> viewContext, List<ExternalData> dataSets,
                 IBrowserGridActionInvoker invoker)
         {
-            this.viewContext = viewContext;
-            this.invoker = invoker;
-            dataSetCodes = new ArrayList<String>();
-            for (ExternalData externalData : dataSets)
-            {
-                dataSetCodes.add(externalData.getCode());
-            }
-            setHeading(viewContext.getMessage(Dict.CONFIRM_DATASET_DELETION_TITLE));
-            setButtons(Dialog.OKCANCEL);
+            super(viewContext, dataSets, invoker, Dict.CONFIRM_DATASET_DELETION_TITLE);
             addText(viewContext.getMessage(Dict.CONFIRM_DATASET_DELETION_MSG, dataSets.size()));
             reason = new TextField<String>();
             reason.setSelectOnFocus(true);
@@ -107,8 +118,6 @@ public abstract class AbstractExternalDataGrid extends AbstractSimpleBrowserGrid
                     }
                 });
             add(reason);
-            setHideOnButtonClick(true);
-            setModal(true);
         }
         
         @Override
@@ -123,6 +132,73 @@ public abstract class AbstractExternalDataGrid extends AbstractSimpleBrowserGrid
         }
     }
     
+    static final class UploadCallback extends AbstractAsyncCallback<Void>
+    {
+        private UploadCallback(IViewContext<?> viewContext)
+        {
+            super(viewContext);
+        }
+        
+        @Override
+        protected void process(Void result)
+        {
+        }
+    }
+    
+    private static final class UploadConfirmationDialog extends AbstractConfirmationDialog
+    {
+        private final String cifexURL;
+        private final TextField<String> password;
+
+        public UploadConfirmationDialog(IViewContext<?> viewContext, List<ExternalData> dataSets,
+                IBrowserGridActionInvoker invoker)
+        {
+            super(viewContext, dataSets, invoker, Dict.CONFIRM_DATASET_UPLOAD_TITLE);
+            cifexURL = viewContext.getModel().getApplicationInfo().getCIFEXURL();
+            addText(viewContext.getMessage(Dict.CONFIRM_DATASET_UPLOAD_MSG, dataSets.size(), cifexURL));
+            password = new TextField<String>();
+            password.setPassword(true);
+            password.setSelectOnFocus(true);
+            password.setHideLabel(true);
+            password.setWidth("100%");
+            password.setMaxLength(50);
+            add(password);
+        }
+        
+        @Override
+        protected void onButtonPressed(Button button)
+        {
+            super.onButtonPressed(button);
+            if (button.getItemId().equals(Dialog.OK))
+            {
+                viewContext.getCommonService().uploadDataSets(dataSetCodes, cifexURL, password.getValue(),
+                        new UploadCallback(viewContext));
+            }
+        }
+    }
+    
+    private abstract class AbstractDataSetAction extends SelectionListener<ButtonEvent>
+    {
+        @Override
+        public void componentSelected(ButtonEvent ce)
+        {
+            List<BaseEntityModel<ExternalData>> items = getSelectedItems();
+            if (items.isEmpty() == false)
+            {
+                List<ExternalData> dataSets = new ArrayList<ExternalData>();
+                for (BaseEntityModel<ExternalData> item : items)
+                {
+                    dataSets.add(item.getBaseObject());
+                }
+                IBrowserGridActionInvoker invoker = asActionInvoker();
+                createDialog(dataSets, invoker).show();
+            }
+        }
+
+        protected abstract Dialog createDialog(List<ExternalData> dataSets,
+                IBrowserGridActionInvoker invoker);
+    }
+    
     protected AbstractExternalDataGrid(final IViewContext<ICommonClientServiceAsync> viewContext,
             String browserId)
     {
@@ -135,29 +211,33 @@ public abstract class AbstractExternalDataGrid extends AbstractSimpleBrowserGrid
                             DataSetUtils.showDataSet(rowItem, viewContext.getModel());
                         }
                     });
-        Button deleteButton = new Button(viewContext.getMessage(Dict.BUTTON_DELETE_DATASETS));
-        deleteButton.addSelectionListener(new SelectionListener<ButtonEvent>()
+        addButton(Dict.BUTTON_DELETE_DATASETS, new AbstractDataSetAction()
             {
                 @Override
-                public void componentSelected(ButtonEvent ce)
+                protected Dialog createDialog(List<ExternalData> dataSets,
+                        IBrowserGridActionInvoker invoker)
                 {
-                    List<BaseEntityModel<ExternalData>> items = getSelectedItems();
-                    if (items.isEmpty() == false)
-                    {
-                        List<ExternalData> dataSets = new ArrayList<ExternalData>();
-                        for (BaseEntityModel<ExternalData> item : items)
-                        {
-                            dataSets.add(item.getBaseObject());
-                        }
-                        IBrowserGridActionInvoker invoker = asActionInvoker();
-                        new DeletionConfirmationDialog(viewContext, dataSets, invoker).show();
-                    }
-                    
+                    return new DeletionConfirmationDialog(viewContext, dataSets, invoker);
+                }
+            });
+        addButton(Dict.BUTTON_UPLOAD_DATASETS, new AbstractDataSetAction()
+            {
+                @Override
+                protected Dialog createDialog(List<ExternalData> dataSets,
+                        IBrowserGridActionInvoker invoker)
+                {
+                    return new UploadConfirmationDialog(viewContext, dataSets, invoker);
                 }
             });
-        pagingToolbar.add(new AdapterToolItem(deleteButton));
         allowMultipleSelection();
     }
+    
+    private void addButton(String labelKey, SelectionListener<ButtonEvent> action)
+    {
+        Button button = new Button(viewContext.getMessage(labelKey));
+        button.addSelectionListener(action);
+        pagingToolbar.add(new AdapterToolItem(button));
+    }
 
     @Override
     protected IColumnDefinitionKind<ExternalData>[] getStaticColumnsDefinition()
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/ApplicationInfo.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/ApplicationInfo.java
index fa9a03d106753889393651deddf31d310ef3486f..900357ba2fe4bda92fe24dec64505dc5298d35b1 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/ApplicationInfo.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/dto/ApplicationInfo.java
@@ -26,6 +26,7 @@ import com.google.gwt.user.client.rpc.IsSerializable;
 public final class ApplicationInfo implements IsSerializable
 {
     private String version;
+    private String cifexURL;
 
     public final String getVersion()
     {
@@ -37,4 +38,14 @@ public final class ApplicationInfo implements IsSerializable
         this.version = version;
     }
 
+    public void setCIFEXURL(String cifexURL)
+    {
+        this.cifexURL = cifexURL;
+    }
+
+    public final String getCIFEXURL()
+    {
+        return cifexURL;
+    }
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientService.java
index 35bd0fe7844f4eb455a007dd06ff0d34c8278172..186683f5550b459f903e636a94d0553712138255 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientService.java
@@ -125,16 +125,19 @@ public final class CommonClientService extends AbstractClientService implements
 		ICommonClientService {
 	private final ICommonServer commonServer;
 
-	private final String dataStoreBaseURL;
+    private String dataStoreBaseURL;
 
-	public CommonClientService(final ICommonServer commonServer,
-			final IRequestContextProvider requestContextProvider,
-			String dataStoreBaseURL) {
-		super(requestContextProvider);
-		this.commonServer = commonServer;
-		this.dataStoreBaseURL = dataStoreBaseURL + "/"
-				+ DATA_STORE_SERVER_WEB_APPLICATION_NAME;
-	}
+    public CommonClientService(final ICommonServer commonServer,
+            final IRequestContextProvider requestContextProvider)
+    {
+        super(requestContextProvider);
+        this.commonServer = commonServer;
+    }
+
+    public final void setDataStoreBaseURL(String dataStoreBaseURL)
+    {
+        this.dataStoreBaseURL = dataStoreBaseURL + "/" + DATA_STORE_SERVER_WEB_APPLICATION_NAME;
+    }
 
 	@Override
 	protected final IServer getServer() {
@@ -946,6 +949,35 @@ public final class CommonClientService extends AbstractClientService implements
 
 	}
 
+    public void uploadDataSets(List<String> dataSetCodes, String cifexURL, String password)
+            throws ch.systemsx.cisd.openbis.generic.client.web.client.exception.UserFailureException
+    {
+        try
+        {
+            final String sessionToken = getSessionToken();
+            commonServer.uploadDataSets(sessionToken, dataSetCodes, cifexURL, password);
+        } catch (final UserFailureException e)
+        {
+            throw UserFailureExceptionTranslator.translate(e);
+        }
+    }
+
+    public void updateMaterial(String materialIdentifier, List<MaterialProperty> properties,
+            Date version)
+            throws ch.systemsx.cisd.openbis.generic.client.web.client.exception.UserFailureException
+    {
+        try
+        {
+            final String sessionToken = getSessionToken();
+            final MaterialIdentifier identifier =
+                    MaterialIdentifier.tryParseIdentifier(materialIdentifier);
+            commonServer.editMaterial(sessionToken, identifier, properties, version);
+        } catch (final ch.systemsx.cisd.common.exceptions.UserFailureException e)
+        {
+            throw UserFailureExceptionTranslator.translate(e);
+        }
+    }
+    
 	public void deleteDataSets(List<String> dataSetCodes, String reason)
 			throws ch.systemsx.cisd.openbis.generic.client.web.client.exception.UserFailureException {
 		try {
@@ -955,22 +987,7 @@ public final class CommonClientService extends AbstractClientService implements
 			throw UserFailureExceptionTranslator.translate(e);
 		}
 	}
-
-	public void updateMaterial(String materialIdentifier,
-			List<MaterialProperty> properties, Date version)
-			throws ch.systemsx.cisd.openbis.generic.client.web.client.exception.UserFailureException {
-		try {
-			final String sessionToken = getSessionToken();
-			final MaterialIdentifier identifier = MaterialIdentifier
-					.tryParseIdentifier(materialIdentifier);
-			commonServer.editMaterial(sessionToken, identifier, properties,
-					version);
-		} catch (final ch.systemsx.cisd.common.exceptions.UserFailureException e) {
-			throw UserFailureExceptionTranslator.translate(e);
-		}
-
-	}
-
+	
 	public void updateSample(
 			String sampleIdentifier,
 			List<SampleProperty> properties,
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
index 01fcb06ebe57e72252ef29e8a8e76a89539c3511..fba8b36a1ab34e3986b429bd3ab34764ca8d8aa3 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
@@ -17,16 +17,12 @@
 package ch.systemsx.cisd.openbis.generic.server;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
-import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Set;
 
 import org.springframework.dao.DataAccessException;
 
-import ch.rinn.restrictions.Private;
 import ch.systemsx.cisd.authentication.IAuthenticationService;
 import ch.systemsx.cisd.authentication.ISessionManager;
 import ch.systemsx.cisd.authentication.Principal;
@@ -49,12 +45,10 @@ import ch.systemsx.cisd.openbis.generic.server.business.bo.ISampleBO;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.ISampleTable;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.IVocabularyBO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
-import ch.systemsx.cisd.openbis.generic.server.dataaccess.IExternalDataDAO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IHibernateSearchDAO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IRoleAssignmentDAO;
 import ch.systemsx.cisd.openbis.generic.server.util.GroupIdentifierHelper;
 import ch.systemsx.cisd.openbis.generic.shared.ICommonServer;
-import ch.systemsx.cisd.openbis.generic.shared.IDataStoreService;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetSearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentType;
@@ -67,7 +61,6 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Vocabulary;
 import ch.systemsx.cisd.openbis.generic.shared.dto.AttachmentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetTypePE;
-import ch.systemsx.cisd.openbis.generic.shared.dto.DataStoreServerSession;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DatabaseInstancePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePE;
@@ -104,11 +97,8 @@ import ch.systemsx.cisd.openbis.generic.shared.util.HibernateUtils;
  * 
  * @author Franz-Josef Elmer
  */
-public final class CommonServer extends AbstractServer<ICommonServer> implements
-		ICommonServer {
-	@Private
-	static final String DELETION_DESCRIPTION = "single deletion";
-
+public final class CommonServer extends AbstractServer<ICommonServer> implements ICommonServer
+{
 	private final IAuthenticationService authenticationService;
 
 	private final ICommonBusinessObjectFactory businessObjectFactory;
@@ -577,59 +567,37 @@ public final class CommonServer extends AbstractServer<ICommonServer> implements
 		}
 	}
 
-	public void deleteDataSets(String sessionToken, List<String> dataSetCodes,
-			String reason) {
-		Session session = getSessionManager().getSession(sessionToken);
-		IExternalDataDAO externalDataDAO = getDAOFactory().getExternalDataDAO();
-		List<ExternalDataPE> dataSets = new ArrayList<ExternalDataPE>();
-		List<String> locations = new ArrayList<String>();
-		for (String dataSetCode : dataSetCodes) {
-			ExternalDataPE dataSet = externalDataDAO
-					.tryToFindFullDataSetByCode(dataSetCode);
-			if (dataSet != null) {
-				dataSets.add(dataSet);
-				locations.add(dataSet.getLocation());
-			}
-		}
-		assertDataSetsAreKnown(dataSets, locations);
-
-		for (ExternalDataPE dataSet : dataSets) {
-			externalDataDAO.markAsDeleted(dataSet, session.tryGetPerson(),
-					DELETION_DESCRIPTION, reason);
-		}
-		Collection<DataStoreServerSession> sessions = dssSessionManager
-				.getSessions();
-		for (DataStoreServerSession dssSession : sessions) {
-			dssSession.getService().deleteDataSets(
-					dssSession.getSessionToken(), locations);
-		}
-	}
-
-	private void assertDataSetsAreKnown(List<ExternalDataPE> dataSets,
-			List<String> locations) {
-		Set<String> knownLocations = new LinkedHashSet<String>();
-		Collection<DataStoreServerSession> sessions = dssSessionManager
-				.getSessions();
-		for (DataStoreServerSession session : sessions) {
-			IDataStoreService service = session.getService();
-			String dssSessionToken = session.getSessionToken();
-			knownLocations.addAll(service.getKnownDataSets(dssSessionToken,
-					locations));
-		}
-		List<String> unknownDataSets = new ArrayList<String>();
-		for (ExternalDataPE dataSet : dataSets) {
-			if (knownLocations.contains(dataSet.getLocation()) == false) {
-				unknownDataSets.add(dataSet.getCode());
-			}
-		}
-		if (unknownDataSets.isEmpty() == false) {
-			throw new UserFailureException(
-					"The following data sets are unknown by any registered Data Store Server. "
-							+ "May be the responsible Data Store Server is not running.\n"
-							+ unknownDataSets);
-		}
-	}
-
+    public void deleteDataSets(String sessionToken, List<String> dataSetCodes, String reason)
+    {
+        Session session = getSessionManager().getSession(sessionToken);
+        try
+        {
+            IExternalDataTable externalDataTable =
+                    businessObjectFactory.createExternalDataTable(session);
+            externalDataTable.loadByDataSetCodes(dataSetCodes);
+            externalDataTable.deleteLoadedDataSets(dssSessionManager, reason);
+        } catch (final DataAccessException ex)
+        {
+            throw createUserFailureException(ex);
+        }
+    }
+
+    public void uploadDataSets(String sessionToken, List<String> dataSetCodes, String cifexURL,
+            String password)
+    {
+        Session session = getSessionManager().getSession(sessionToken);
+        try
+        {
+            IExternalDataTable externalDataTable =
+                    businessObjectFactory.createExternalDataTable(session);
+            externalDataTable.loadByDataSetCodes(dataSetCodes);
+            externalDataTable.uploadLoadedDataSetsToCIFEX(dssSessionManager, cifexURL, password);
+        } catch (final DataAccessException ex)
+        {
+            throw createUserFailureException(ex);
+        }
+    }
+    
 	public void editExperiment(String sessionToken,
 			ExperimentIdentifier identifier,
 			List<ExperimentProperty> properties,
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServerLogger.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServerLogger.java
index 915965eb48eb9cf835bdc14dc58aaf3f23eda8d6..ef782cd1d0c2e608a91739f7c878f92b5f9dd3b0 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServerLogger.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServerLogger.java
@@ -325,11 +325,16 @@ final class CommonServerLogger extends AbstractServerLogger implements
 				dataSetCodes, reason);
 	}
 
-	public void editMaterial(String sessionToken,
-			MaterialIdentifier identifier, List<MaterialProperty> properties,
-			Date version) {
-		logTracking(sessionToken, "edit_material", "MATERIAL(%s)", identifier);
-
+    public void uploadDataSets(String sessionToken, List<String> dataSetCodes, String cifexURL,
+            String password)
+    {
+        logTracking(sessionToken, "upload_data_sets", "CODES(%s) CIFEX-URL(%s)", dataSetCodes, cifexURL);
+    }
+
+    public void editMaterial(String sessionToken, MaterialIdentifier identifier,
+            List<MaterialProperty> properties, Date version)
+    {
+        logTracking(sessionToken, "edit_material", "MATERIAL(%s)", identifier);
 	}
 
 	public void editSample(String sessionToken, SampleIdentifier identifier,
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataTable.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataTable.java
index 02dbcaf7ceb42e0358435e4dba25ffc8995c38c8..813b02203151ebac5c8548edf9d68087c743e662 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataTable.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataTable.java
@@ -17,11 +17,20 @@
 package ch.systemsx.cisd.openbis.generic.server.business.bo;
 
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
+import ch.rinn.restrictions.Private;
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.openbis.generic.server.DataStoreServerSessionManager;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IExternalDataDAO;
+import ch.systemsx.cisd.openbis.generic.shared.IDataStoreService;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetUploadContext;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataStoreServerSession;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ProcedurePE;
@@ -44,6 +53,11 @@ import ch.systemsx.cisd.openbis.generic.shared.util.HibernateUtils;
 public final class ExternalDataTable extends AbstractExternalDataBusinessObject implements
         IExternalDataTable
 {
+    private static final String NEW_LINE = "\n";
+    private static final int MAX_LENGTH_OF_CIFEX_COMMENT = 1000;
+    private static final String AND_MORE_TEMPLATE = "and %d more.";
+    @Private static final String DELETION_DESCRIPTION = "single deletion";
+
     private List<ExternalDataPE> externalData;
 
     public ExternalDataTable(final IDAOFactory daoFactory, final Session session)
@@ -61,6 +75,20 @@ public final class ExternalDataTable extends AbstractExternalDataBusinessObject
         return externalData;
     }
 
+    public void loadByDataSetCodes(List<String> dataSetCodes)
+    {
+        IExternalDataDAO externalDataDAO = getExternalDataDAO();
+        externalData = new ArrayList<ExternalDataPE>();
+        for (String dataSetCode : dataSetCodes)
+        {
+            ExternalDataPE dataSet = externalDataDAO.tryToFindFullDataSetByCode(dataSetCode);
+            if (dataSet != null)
+            {
+                externalData.add(dataSet);
+            }
+        }
+    }
+
     public final void loadBySampleIdentifier(final SampleIdentifier sampleIdentifier)
     {
         assert sampleIdentifier != null : "Unspecified sample identifier";
@@ -101,4 +129,100 @@ public final class ExternalDataTable extends AbstractExternalDataBusinessObject
             }
         }
     }
+
+    public void deleteLoadedDataSets(DataStoreServerSessionManager dssSessionManager, String reason)
+    {
+        assertDataSetsAreKnown(dssSessionManager);
+        for (ExternalDataPE dataSet : externalData)
+        {
+            IExternalDataDAO externalDataDAO = getExternalDataDAO();
+            externalDataDAO.markAsDeleted(dataSet, session.tryGetPerson(), DELETION_DESCRIPTION, reason);
+        }
+        Collection<DataStoreServerSession> sessions = dssSessionManager.getSessions();
+        List<String> locations = getLocations();
+        for (DataStoreServerSession dssSession : sessions)
+        {
+            dssSession.getService().deleteDataSets(dssSession.getSessionToken(), locations);
+        }
+    }
+
+    public void uploadLoadedDataSetsToCIFEX(DataStoreServerSessionManager dssSessionManager,
+            String cifexURL, String password)
+    {
+        assertDataSetsAreKnown(dssSessionManager);
+        Collection<DataStoreServerSession> sessions = dssSessionManager.getSessions();
+        List<String> locations = getLocations();
+        DataSetUploadContext uploadContext = new DataSetUploadContext();
+        uploadContext.setCifexURL(cifexURL);
+        uploadContext.setUserID(session.getUserName());
+        uploadContext.setPassword(password);
+        uploadContext.setComment(createUploadComment());
+        for (DataStoreServerSession dssSession : sessions)
+        {
+            dssSession.getService().uploadDataSetsToCIFEX(dssSession.getSessionToken(), locations,
+                    uploadContext);
+        }
+    }
+
+    private String createUploadComment()
+    {
+        StringBuilder builder = new StringBuilder();
+        builder.append("Uploaded zip file contains the following data sets:");
+        for (int i = 0, n = externalData.size(); i < n; i++)
+        {
+            builder.append(NEW_LINE);
+            String code = externalData.get(i).getCode();
+            int length = builder.length() + code.length();
+            if (i < n - 1)
+            {
+                length += NEW_LINE.length() + String.format(AND_MORE_TEMPLATE, n - i - 1).length();
+            }
+            if (length < MAX_LENGTH_OF_CIFEX_COMMENT)
+            {
+                builder.append(code);
+            } else
+            {
+                builder.append(String.format(AND_MORE_TEMPLATE, n - i));
+            }
+        }
+        return builder.toString();
+    }
+    
+    private void assertDataSetsAreKnown(DataStoreServerSessionManager dssSessionManager)
+    {
+        List<String> locations = getLocations();
+        Set<String> knownLocations = new LinkedHashSet<String>();
+        Collection<DataStoreServerSession> sessions = dssSessionManager.getSessions();
+        for (DataStoreServerSession dssSession : sessions)
+        {
+            IDataStoreService service = dssSession.getService();
+            String dssSessionToken = dssSession.getSessionToken();
+            knownLocations.addAll(service.getKnownDataSets(dssSessionToken, locations));
+        }
+        List<String> unknownDataSets = new ArrayList<String>();
+        for (ExternalDataPE dataSet : externalData)
+        {
+            if (knownLocations.contains(dataSet.getLocation()) == false)
+            {
+                unknownDataSets.add(dataSet.getCode());
+            }
+        }
+        if (unknownDataSets.isEmpty() == false)
+        {
+            throw new UserFailureException(
+                    "The following data sets are unknown by any registered Data Store Server. "
+                            + "May be the responsible Data Store Server is not running.\n"
+                            + unknownDataSets);
+        }
+    }
+    
+    private List<String> getLocations()
+    {
+        List<String> locations = new ArrayList<String>();
+        for (ExternalDataPE dataSet : externalData)
+        {
+            locations.add(dataSet.getLocation());
+        }
+        return locations;
+    }
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IExternalDataTable.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IExternalDataTable.java
index 8c4f278b537d829bd6464385d7b6326d229271f4..89452baaf84bdcc25e3b3b71b6e606fb7beb69ff 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IExternalDataTable.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/IExternalDataTable.java
@@ -18,6 +18,7 @@ package ch.systemsx.cisd.openbis.generic.server.business.bo;
 
 import java.util.List;
 
+import ch.systemsx.cisd.openbis.generic.server.DataStoreServerSessionManager;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifier;
 import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier;
@@ -29,7 +30,12 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier;
  */
 public interface IExternalDataTable
 {
-
+    /**
+     * Loads data sets specified by their codes. Data set codes will be ignored if no 
+     * {@link ExternalDataPE} could be found.
+     */
+    void loadByDataSetCodes(List<String> dataSetCodes);
+    
     /**
      * Loads the internal {@link ExternalDataPE} for given <var>identifier</var>.
      */
@@ -45,5 +51,9 @@ public interface IExternalDataTable
      * Returns the loaded {@link ExternalDataPE}.
      */
     List<ExternalDataPE> getExternalData();
+    
+    void deleteLoadedDataSets(DataStoreServerSessionManager dssSessionManager, String reason);
+    
+    void uploadLoadedDataSetsToCIFEX(DataStoreServerSessionManager dssSessionManager, String cifexURL, String password);
 
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java
index 2f64236ae77a43fcc218ea71b93503d39b6aa47a..b083f232e16a325cb1d1dd0b8bc2d8d3223c84af 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java
@@ -366,18 +366,25 @@ public interface ICommonServer extends IServer {
 			@AuthorizationGuard(guardClass = DataSetCodePredicate.class) List<String> dataSetCodes,
 			String reason);
 
-	/**
-	 * Saves changed experiment.
-	 */
-	@Transactional
-	@RolesAllowed(RoleSet.USER)
-	public void editExperiment(
-			String sessionToken,
-			@AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) ExperimentIdentifier experimentIdentifier,
-			List<ExperimentProperty> properties,
-			List<AttachmentPE> attachments,
-			@AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) ProjectIdentifier newProjectIdentifier,
-			Date version);
+    /**
+     * Uploads specified data sets to CIFEX server of specified URL with specified password.
+     */
+    @Transactional
+    @RolesAllowed(RoleSet.OBSERVER)
+    public void uploadDataSets(String sessionToken, List<String> dataSetCodes, String cifexURL,
+            String password);
+    
+    /**
+     * Saves changed experiment.
+     */
+    @Transactional
+    @RolesAllowed(RoleSet.USER)
+    public void editExperiment(String sessionToken,
+            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class)
+            ExperimentIdentifier experimentIdentifier, List<ExperimentProperty> properties,
+            List<AttachmentPE> attachments,
+            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class)
+            ProjectIdentifier newProjectIdentifier, Date version);
 
 	/**
 	 * Saves changed material.
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IDataStoreService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IDataStoreService.java
index 844f1795c03824d2c2d9ba8eba74dac6a8b0adb1..ca85adb3529aba37ccd8f42847e7e15873e58a63 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IDataStoreService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IDataStoreService.java
@@ -19,6 +19,7 @@ package ch.systemsx.cisd.openbis.generic.shared;
 import java.util.List;
 
 import ch.systemsx.cisd.common.exceptions.InvalidAuthenticationException;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataSetUploadContext;
 
 /**
  * Service interface of Data Store Server.
@@ -63,12 +64,12 @@ public interface IDataStoreService
             throws InvalidAuthenticationException;
     
     /**
-     * Uploads the specified data sets to CIFEX for the specified user (id and password) and with
-     * specified comment.
+     * Uploads the specified data sets to CIFEX.
      * 
      * @param sessionToken Valid token to identify authorised access.
+     * @param context Context data needed for uploading.
      * @throws InvalidAuthenticationException if <code>sessionToken</code> is invalid.
      */
     public void uploadDataSetsToCIFEX(String sessionToken, List<String> dataSetLocations,
-            String comment, String userID, String password) throws InvalidAuthenticationException;
+            DataSetUploadContext context) throws InvalidAuthenticationException;
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataSetUploadContext.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataSetUploadContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..019d96db8859b19ab91c15e0ec501de7b40029ca
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataSetUploadContext.java
@@ -0,0 +1,79 @@
+/*
+ * 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.shared.dto;
+
+import java.io.Serializable;
+
+import ch.systemsx.cisd.openbis.generic.shared.GenericSharedConstants;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+public class DataSetUploadContext implements Serializable
+{
+    private static final long serialVersionUID = GenericSharedConstants.VERSION;
+    
+    private String cifexURL;
+
+    private String userID;
+    
+    private String password;
+    
+    private String comment;
+
+    public final String getCifexURL()
+    {
+        return cifexURL;
+    }
+
+    public final void setCifexURL(String cifexURL)
+    {
+        this.cifexURL = cifexURL;
+    }
+
+    public final String getUserID()
+    {
+        return userID;
+    }
+
+    public final void setUserID(String userID)
+    {
+        this.userID = userID;
+    }
+
+    public final String getPassword()
+    {
+        return password;
+    }
+
+    public final void setPassword(String password)
+    {
+        this.password = password;
+    }
+
+    public final String getComment()
+    {
+        return comment;
+    }
+
+    public final void setComment(String comment)
+    {
+        this.comment = comment;
+    }
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/AbstractClientService.java b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/AbstractClientService.java
index 771c18e7639c6fc5a803752c8442295a4a24cff5..9588c01ed24c89b751d13f35a03725eaaa5fc264 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/plugin/AbstractClientService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/plugin/AbstractClientService.java
@@ -54,6 +54,8 @@ public abstract class AbstractClientService implements IClientService
     @Resource(name = "request-context-provider")
     private IRequestContextProvider requestContextProvider;
 
+    private String cifexURL;
+
     protected AbstractClientService()
     {
     }
@@ -63,6 +65,11 @@ public abstract class AbstractClientService implements IClientService
         this.requestContextProvider = requestContextProvider;
     }
 
+    public final void setCifexURL(String cifexURL)
+    {
+        this.cifexURL = cifexURL;
+    }
+
     private final SessionContext createSessionContext(final Session session)
     {
         final SessionContext sessionContext = new SessionContext();
@@ -144,6 +151,7 @@ public abstract class AbstractClientService implements IClientService
     {
         final ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.setVersion(BuildAndEnvironmentInfo.INSTANCE.getFullVersion());
+        applicationInfo.setCIFEXURL(cifexURL);
         return applicationInfo;
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js
index 204534a0113a06b4449a1aaa960a8c3aee066dba..da4e551e22f8da43a1edd4828c7926ac593399c1 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js
@@ -278,7 +278,10 @@ var common = {
  data_producer_code: "Producer",
  button_delete_datasets: "Delete",
  confirm_dataset_deletion_title: "Data Sets Deletion Confirmation",
- confirm_dataset_deletion_msg: "You are deleting {0} data set(s). Please enter a reason:",
+ confirm_dataset_deletion_msg: "You are deleting {0} data set(s). Please, enter a reason:",
+ button_upload_datasets: "Upload to CIFEX",
+ confirm_dataset_upload_title: "Uploading Confirmation and Authentication",
+ confirm_dataset_upload_msg: "You are uploading {0} data set(s) to CIFEX ({1}). Please, enter your CIFEX password:", 
  
  
  //
diff --git a/openbis/source/java/genericApplicationContext.xml b/openbis/source/java/genericApplicationContext.xml
index f2c99099d3c169e149b87527fde82cbafa7b2f63..2b6c496e7f05be0c9bebb42badb15e9ae36aec13 100644
--- a/openbis/source/java/genericApplicationContext.xml
+++ b/openbis/source/java/genericApplicationContext.xml
@@ -79,7 +79,8 @@
     <bean id="common-service" class="ch.systemsx.cisd.openbis.generic.client.web.server.CommonClientService">
         <constructor-arg ref="common-server" />
         <constructor-arg ref="request-context-provider" />
-        <constructor-arg value="${data-store-server-base-url}"/>
+        <property name="dataStoreBaseURL" value="${data-store-server-base-url}"/>
+        <property name="cifexURL" value="${cifex-url}"/>
     </bean>
     
     <!-- 
diff --git a/openbis/source/java/service.properties b/openbis/source/java/service.properties
index d1d62a51c650db46e6fbb9b3453463a98943b1e4..cb65b4ea8d1a3cc22f054378577eb58d9580e933 100644
--- a/openbis/source/java/service.properties
+++ b/openbis/source/java/service.properties
@@ -36,6 +36,9 @@ database-instance = CISD
 # Base URL of the Data Store Server
 data-store-server-base-url = https://localhost:8889
 
+# The URL of the CIFEX server
+cifex-url = https://cifex.ethz.ch:443
+
 # Hibernate Search
 # The working directory.
 hibernate.search.index-base = ./targets/indices
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientServiceTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientServiceTest.java
index 3fb9cf11420f70428830768f6c8c565b61b64397..efbe36a83d2b691c6aec7753a5d9f65ff4bbe273 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientServiceTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/CommonClientServiceTest.java
@@ -64,6 +64,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityDataType;
 public final class CommonClientServiceTest extends AbstractClientServiceTest
 {
     private static final String DATA_STORE_BASE_URL = "baseURL";
+    private static final String CIFEX_URL = "cifexURL";
 
     private CommonClientService commonClientService;
 
@@ -123,7 +124,9 @@ public final class CommonClientServiceTest extends AbstractClientServiceTest
     {
         super.setUp();
         commonServer = context.mock(ICommonServer.class);
-        commonClientService = new CommonClientService(commonServer, requestContextProvider, DATA_STORE_BASE_URL);
+        commonClientService = new CommonClientService(commonServer, requestContextProvider);
+        commonClientService.setDataStoreBaseURL(DATA_STORE_BASE_URL);
+        commonClientService.setCifexURL(CIFEX_URL);
     }
 
     @Test
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/CommonServerTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/CommonServerTest.java
index 3b561de87186a187b38358662f7f7b0dd81ab29a..c36dfe32535d3960d0ec62f82f4a73d4813cdcd4 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/CommonServerTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/CommonServerTest.java
@@ -16,8 +16,6 @@
 
 package ch.systemsx.cisd.openbis.generic.server;
 
-import static ch.systemsx.cisd.openbis.generic.server.CommonServer.DELETION_DESCRIPTION;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -28,13 +26,11 @@ import org.jmock.Expectations;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import ch.rinn.restrictions.Friend;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.business.bo.ICommonBusinessObjectFactory;
 import ch.systemsx.cisd.openbis.generic.shared.AbstractServerTestCase;
 import ch.systemsx.cisd.openbis.generic.shared.CommonTestUtils;
 import ch.systemsx.cisd.openbis.generic.shared.ICommonServer;
-import ch.systemsx.cisd.openbis.generic.shared.IDataStoreService;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialIdentifier;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialProperty;
@@ -42,7 +38,6 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.PropertyType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Vocabulary;
 import ch.systemsx.cisd.openbis.generic.shared.dto.AttachmentPE;
-import ch.systemsx.cisd.openbis.generic.shared.dto.DataStoreServerSession;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.EntityTypePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
@@ -70,7 +65,6 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
  * 
  * @author Franz-Josef Elmer
  */
-@Friend(toClasses=CommonServer.class)
 public final class CommonServerTest extends AbstractServerTestCase
 {
     private static final String MATERIAL_TYPE_1 = "MATERIAL-TYPE-1";
@@ -91,8 +85,6 @@ public final class CommonServerTest extends AbstractServerTestCase
 
     private DataStoreServerSessionManager dssSessionManager;
 
-    private IDataStoreService dataStoreService;
-
     private final ICommonServer createServer()
     {
         return new CommonServer(authenticationService, sessionManager, dssSessionManager,
@@ -116,7 +108,6 @@ public final class CommonServerTest extends AbstractServerTestCase
     {
         super.setUp();
         dssSessionManager = new DataStoreServerSessionManager();
-        dataStoreService = context.mock(IDataStoreService.class);
         commonBusinessObjectFactory = context.mock(ICommonBusinessObjectFactory.class);
     }
 
@@ -724,75 +715,6 @@ public final class CommonServerTest extends AbstractServerTestCase
         context.assertIsSatisfied();
     }
 
-    @Test
-    public void testDeleteDataSetButDataStoreServerIsDown()
-    {
-        prepareGetSession();
-        final ExternalDataPE d1 = createDataSet("d1");
-        context.checking(new Expectations()
-            {
-                {
-                    one(externalDataDAO).tryToFindFullDataSetByCode(d1.getCode());
-                    will(returnValue(d1));
-                }
-            });
-
-        try
-        {
-            createServer().deleteDataSets(SESSION_TOKEN, Arrays.asList(d1.getCode()), "");
-            fail("UserFailureException expected");
-        } catch (UserFailureException e)
-        {
-            assertEquals(
-                    "The following data sets are unknown by any registered Data Store Server. "
-                            + "May be the responsible Data Store Server is not running.\n[d1]", e
-                            .getMessage());
-        }
-
-        context.assertIsSatisfied();
-    }
-
-    @Test
-    public void testDeleteDataSetsButOneDataSetIsUnknown()
-    {
-        prepareGetSession();
-        dssSessionManager.registerDataStoreServer(new DataStoreServerSession("url",
-                dataStoreService));
-        final ExternalDataPE d1 = createDataSet("d1");
-        final ExternalDataPE d2 = createDataSet("d2");
-        context.checking(new Expectations()
-            {
-                {
-                    one(externalDataDAO).tryToFindFullDataSetByCode(d1.getCode());
-                    will(returnValue(d1));
-
-                    one(externalDataDAO).tryToFindFullDataSetByCode(d2.getCode());
-                    will(returnValue(d2));
-
-                    List<String> locations = Arrays.asList(d1.getLocation(), d2.getLocation());
-                    one(dataStoreService).getKnownDataSets(with(any(String.class)),
-                            with(equal(locations)));
-                    will(returnValue(Arrays.asList(d1.getLocation())));
-
-                }
-            });
-
-        try
-        {
-            createServer().deleteDataSets(SESSION_TOKEN, Arrays.asList(d1.getCode(), d2.getCode()),
-                    "");
-            fail("UserFailureException expected");
-        } catch (UserFailureException e)
-        {
-            assertEquals(
-                    "The following data sets are unknown by any registered Data Store Server. "
-                            + "May be the responsible Data Store Server is not running.\n[d2]", e
-                            .getMessage());
-        }
-
-        context.assertIsSatisfied();
-    }
-
     @Test
     public void testEditMaterialNothingChanged() throws Exception
     {
@@ -871,42 +793,42 @@ public final class CommonServerTest extends AbstractServerTestCase
     public void testDeleteDataSets()
     {
         prepareGetSession();
-        dssSessionManager.registerDataStoreServer(new DataStoreServerSession("url",
-                dataStoreService));
-        final ExternalDataPE d1 = createDataSet("d1");
-        final ExternalDataPE d2 = createDataSet("d2");
+        final List<String> dataSetCodes = Arrays.asList("a", "b");
         context.checking(new Expectations()
             {
                 {
-                    one(externalDataDAO).tryToFindFullDataSetByCode(d1.getCode());
-                    will(returnValue(d1));
-
-                    one(externalDataDAO).tryToFindFullDataSetByCode(d2.getCode());
-                    will(returnValue(d2));
-
-                    List<String> locations = Arrays.asList(d1.getLocation(), d2.getLocation());
-                    one(dataStoreService).getKnownDataSets(with(any(String.class)),
-                            with(equal(locations)));
-                    will(returnValue(locations));
-
-                    one(externalDataDAO).markAsDeleted(d1, SESSION.tryGetPerson(), DELETION_DESCRIPTION, "reason");
-                    one(externalDataDAO).markAsDeleted(d2, SESSION.tryGetPerson(), DELETION_DESCRIPTION, "reason");
-                    one(dataStoreService).deleteDataSets(with(any(String.class)),
-                            with(equal(locations)));
+                    one(commonBusinessObjectFactory).createExternalDataTable(SESSION);
+                    will(returnValue(externalDataTable));
+                    
+                    one(externalDataTable).loadByDataSetCodes(dataSetCodes);
+                    one(externalDataTable).deleteLoadedDataSets(dssSessionManager, "reason");
                 }
             });
 
-        createServer().deleteDataSets(SESSION_TOKEN, Arrays.asList(d1.getCode(), d2.getCode()),
-                "reason");
+        createServer().deleteDataSets(SESSION_TOKEN, dataSetCodes, "reason");
 
         context.assertIsSatisfied();
     }
-
-    private ExternalDataPE createDataSet(String code)
+    
+    @Test
+    public void testUploadDataSets()
     {
-        ExternalDataPE data = new ExternalDataPE();
-        data.setCode(code);
-        data.setLocation("here/" + code);
-        return data;
+        prepareGetSession();
+        final List<String> dataSetCodes = Arrays.asList("a", "b");
+        context.checking(new Expectations()
+        {
+            {
+                one(commonBusinessObjectFactory).createExternalDataTable(SESSION);
+                will(returnValue(externalDataTable));
+                
+                one(externalDataTable).loadByDataSetCodes(dataSetCodes);
+                one(externalDataTable).uploadLoadedDataSetsToCIFEX(dssSessionManager, "cifexURL", "pwd");
+            }
+        });
+        
+        createServer().uploadDataSets(SESSION_TOKEN, dataSetCodes, "cifexURL", "pwd");
+        
+        context.assertIsSatisfied();
     }
+
 }
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataTableTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataTableTest.java
index 4617845e5f59c6d7df0ef30676ae0049e8b42ab4..414b7ff748ffe50bfaf36166a8be6f29a85d85d6 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataTableTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/business/bo/ExternalDataTableTest.java
@@ -16,21 +16,29 @@
 
 package ch.systemsx.cisd.openbis.generic.server.business.bo;
 
+import static ch.systemsx.cisd.openbis.generic.server.business.bo.ExternalDataTable.DELETION_DESCRIPTION;
+
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 
 import org.jmock.Expectations;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import ch.rinn.restrictions.Friend;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.openbis.generic.server.DataStoreServerSessionManager;
 import ch.systemsx.cisd.openbis.generic.server.business.ManagerTestTool;
+import ch.systemsx.cisd.openbis.generic.shared.IDataStoreService;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DataPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.DataStoreServerSession;
 import ch.systemsx.cisd.openbis.generic.shared.dto.DatabaseInstancePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.GroupPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.HierarchyType;
+import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ProcedurePE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ProjectPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE;
@@ -45,14 +53,26 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier;
  * 
  * @author Christian Ribeaud
  */
+@Friend(toClasses=ExternalDataTable.class)
 public final class ExternalDataTableTest extends AbstractBOTest
 {
+    private DataStoreServerSessionManager dssSessionManager;
+    private IDataStoreService dataStoreService;
 
     private final ExternalDataTable createExternalDataTable()
     {
         return new ExternalDataTable(daoFactory, ManagerTestTool.EXAMPLE_SESSION);
     }
 
+    @BeforeMethod
+    @Override
+    public void beforeMethod()
+    {
+        super.beforeMethod();
+        dssSessionManager = new DataStoreServerSessionManager();
+        dataStoreService = context.mock(IDataStoreService.class);
+    }
+
     @Test
     public final void testLoadBySampleIdentifierWithNullSampleIdentifier()
     {
@@ -193,4 +213,143 @@ public final class ExternalDataTableTest extends AbstractBOTest
         
         context.assertIsSatisfied();
     }
+    
+    @Test
+    public void testDeleteLoadedDataSetsButDataStoreServerIsDown()
+    {
+        final ExternalDataPE d1 = createDataSet("d1");
+        context.checking(new Expectations()
+            {
+                {
+                    one(externalDataDAO).tryToFindFullDataSetByCode(d1.getCode());
+                    will(returnValue(d1));
+                }
+            });
+
+        ExternalDataTable externalDataTable = createExternalDataTable();
+        externalDataTable.loadByDataSetCodes(Arrays.asList(d1.getCode()));
+        try
+        {
+            externalDataTable.deleteLoadedDataSets(dssSessionManager, "");
+            fail("UserFailureException expected");
+        } catch (UserFailureException e)
+        {
+            assertEquals(
+                    "The following data sets are unknown by any registered Data Store Server. "
+                            + "May be the responsible Data Store Server is not running.\n[d1]", e
+                            .getMessage());
+        }
+        
+        context.assertIsSatisfied();
+    }
+    
+    @Test
+    public void testLoadByDataSetCodes()
+    {
+        final ExternalDataPE d1 = createDataSet("d1");
+        final ExternalDataPE d2 = createDataSet("d2");
+        context.checking(new Expectations()
+            {
+                {
+                    one(externalDataDAO).tryToFindFullDataSetByCode(d1.getCode());
+                    will(returnValue(d1));
+
+                    one(externalDataDAO).tryToFindFullDataSetByCode(d2.getCode());
+                    will(returnValue(null));
+                }
+            });
+
+        ExternalDataTable externalDataTable = createExternalDataTable();
+        externalDataTable.loadByDataSetCodes(Arrays.asList(d1.getCode(), d2.getCode()));
+        
+        assertEquals(1, externalDataTable.getExternalData().size());
+        assertSame(d1, externalDataTable.getExternalData().get(0));
+        
+        context.assertIsSatisfied();
+    }
+    
+    @Test
+    public void testDeleteLoadedDataSetsButOneDataSetIsUnknown()
+    {
+        dssSessionManager.registerDataStoreServer(new DataStoreServerSession("url",
+                dataStoreService));
+        final ExternalDataPE d1 = createDataSet("d1");
+        final ExternalDataPE d2 = createDataSet("d2");
+        context.checking(new Expectations()
+        {
+            {
+                one(externalDataDAO).tryToFindFullDataSetByCode(d1.getCode());
+                will(returnValue(d1));
+                
+                one(externalDataDAO).tryToFindFullDataSetByCode(d2.getCode());
+                will(returnValue(d2));
+                
+                List<String> locations = Arrays.asList(d1.getLocation(), d2.getLocation());
+                one(dataStoreService).getKnownDataSets(with(any(String.class)),
+                        with(equal(locations)));
+                will(returnValue(Arrays.asList(d1.getLocation())));
+                
+            }
+        });
+        
+        ExternalDataTable externalDataTable = createExternalDataTable();
+        externalDataTable.loadByDataSetCodes(Arrays.asList(d1.getCode(), d2.getCode()));
+        try
+        {
+            externalDataTable.deleteLoadedDataSets(dssSessionManager, "");
+            fail("UserFailureException expected");
+        } catch (UserFailureException e)
+        {
+            assertEquals(
+                    "The following data sets are unknown by any registered Data Store Server. "
+                            + "May be the responsible Data Store Server is not running.\n[d2]", e
+                            .getMessage());
+        }
+
+        context.assertIsSatisfied();
+    }
+    
+    @Test
+    public void testDeleteLoadedDataSets()
+    {
+        dssSessionManager.registerDataStoreServer(new DataStoreServerSession("url",
+                dataStoreService));
+        final ExternalDataPE d1 = createDataSet("d1");
+        final ExternalDataPE d2 = createDataSet("d2");
+        context.checking(new Expectations()
+            {
+                {
+                    one(externalDataDAO).tryToFindFullDataSetByCode(d1.getCode());
+                    will(returnValue(d1));
+
+                    one(externalDataDAO).tryToFindFullDataSetByCode(d2.getCode());
+                    will(returnValue(d2));
+
+                    List<String> locations = Arrays.asList(d1.getLocation(), d2.getLocation());
+                    one(dataStoreService).getKnownDataSets(with(any(String.class)),
+                            with(equal(locations)));
+                    will(returnValue(locations));
+
+                    PersonPE person = ManagerTestTool.EXAMPLE_SESSION.tryGetPerson();
+                    one(externalDataDAO).markAsDeleted(d1, person, DELETION_DESCRIPTION, "reason");
+                    one(externalDataDAO).markAsDeleted(d2, person, DELETION_DESCRIPTION, "reason");
+                    one(dataStoreService).deleteDataSets(with(any(String.class)),
+                            with(equal(locations)));
+                }
+            });
+
+        ExternalDataTable externalDataTable = createExternalDataTable();
+        externalDataTable.loadByDataSetCodes(Arrays.asList(d1.getCode(), d2.getCode()));
+        externalDataTable.deleteLoadedDataSets(dssSessionManager, "reason");
+
+        context.assertIsSatisfied();
+    }
+    
+    private ExternalDataPE createDataSet(String code)
+    {
+        ExternalDataPE data = new ExternalDataPE();
+        data.setCode(code);
+        data.setLocation("here/" + code);
+        return data;
+    }
 }
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java.expected b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java.expected
index b81a82be2b514c863f1f54cd9391b5e990715afd..a02048f244eb353fba6e800db9ea54805ed0f51f 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java.expected
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java.expected
@@ -77,345 +77,352 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
  * @author Franz-Josef Elmer
  */
 public interface ICommonServer extends IServer {
-	/**
-	 * Returns all groups which belong to the specified database instance. *
-	 * 
-	 * @return a sorted list of {@link GroupPE}.
-	 */
-	@Transactional(readOnly = true)
-	@RolesAllowed(RoleSet.OBSERVER)
-	@ReturnValueFilter(validatorClass = GroupValidator.class)
-	public List<GroupPE> listGroups(String sessionToken,
-			DatabaseInstanceIdentifier identifier);
-
-	/**
-	 * Registers a new group with specified code and optional description and
-	 * group leader ID.
-	 */
-	@Transactional
-	@RolesAllowed(RoleSet.INSTANCE_ADMIN)
-	public void registerGroup(String sessionToken, String groupCode,
-			String descriptionOrNull, String groupLeaderOrNull);
-
-	/**
-	 * Returns all persons from current instance.
-	 * 
-	 * @return a sorted list of {@link PersonPE}.
-	 */
-	@Transactional(readOnly = true)
-	@RolesAllowed(RoleSet.OBSERVER)
-	public List<PersonPE> listPersons(String sessionToken);
-
-	/**
-	 * Returns all projects.
-	 * 
-	 * @return a sorted list of {@link ProjectPE}.
-	 */
-	@Transactional(readOnly = true)
-	@RolesAllowed(RoleSet.OBSERVER)
-	@ReturnValueFilter(validatorClass = ProjectValidator.class)
-	public List<ProjectPE> listProjects(String sessionToken);
-
-	/**
-	 * Registers a new person.
-	 */
-	@Transactional
-	@RolesAllowed(RoleSet.INSTANCE_ADMIN)
-	public void registerPerson(String sessionToken, String userID);
-
-	/**
-	 * Returns a list of all roles.
-	 */
-	@Transactional(readOnly = true)
-	@RolesAllowed(RoleSet.GROUP_ADMIN)
-	public List<RoleAssignmentPE> listRoles(String sessionToken);
-
-	/**
-	 * Registers a new group role.
-	 */
-	@Transactional
-	@RolesAllowed(RoleSet.GROUP_ADMIN)
-	public void registerGroupRole(
-			String sessionToken,
-			RoleCode roleCode,
-			@AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) GroupIdentifier identifier,
-			String person);
-
-	/**
-	 * Registers a new instance role.
-	 */
-	@Transactional
-	@RolesAllowed(RoleSet.INSTANCE_ADMIN)
-	public void registerInstanceRole(String sessionToken, RoleCode roleCode,
-			String person);
-
-	/**
-	 * Deletes role described by given role code, group identifier and user id.
-	 */
-	@Transactional
-	@RolesAllowed(RoleSet.GROUP_ADMIN)
-	public void deleteGroupRole(
-			String sessionToken,
-			RoleCode roleCode,
-			@AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) GroupIdentifier groupIdentifier,
-			String person);
-
-	/**
-	 * Deletes role described by given role code and user id.
-	 */
-	@Transactional
-	@RolesAllowed(RoleSet.INSTANCE_ADMIN)
-	public void deleteInstanceRole(String sessionToken, RoleCode roleCode,
-			String person);
-
-	/**
-	 * Lists sample types which are appropriate for listing.
-	 * 
-	 * @return a sorted list of {@link SampleTypePE}.
-	 */
-	@Transactional(readOnly = true)
-	@RolesAllowed(RoleSet.OBSERVER)
-	public List<SampleTypePE> listSampleTypes(String sessionToken);
-
-	/**
-	 * Lists samples using given configuration.
-	 * 
-	 * @return a sorted list of {@link SamplePE}.
-	 */
-	@Transactional(readOnly = true)
-	@RolesAllowed(RoleSet.OBSERVER)
-	public List<SamplePE> listSamples(final String sessionToken,
-			final ListSampleCriteriaDTO criteria);
-
-	/**
-	 * Lists experiments.
-	 * 
-	 * @return a sorted list of {@link ExperimentPE}.
-	 */
-	@Transactional(readOnly = true)
-	@RolesAllowed(RoleSet.OBSERVER)
-	public List<ExperimentPE> listExperiments(
-			final String sessionToken,
-			ExperimentTypePE experimentType,
-			@AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) ProjectIdentifier project);
-
-	/**
-	 * For given {@link SampleIdentifier} returns the corresponding list of
-	 * {@link ExternalDataPE}.
-	 * 
-	 * @return a sorted list of {@link ExternalDataPE}.
-	 */
-	@Transactional(readOnly = true)
-	@RolesAllowed(RoleSet.OBSERVER)
-	public List<ExternalDataPE> listExternalData(
-			final String sessionToken,
-			@AuthorizationGuard(guardClass = SampleOwnerIdentifierPredicate.class) final SampleIdentifier identifier);
-
-	/**
-	 * For given {@link ExperimentIdentifier} returns the corresponding list of
-	 * {@link ExternalDataPE}.
-	 * 
-	 * @return a sorted list of {@link ExternalDataPE}.
-	 */
-	@Transactional(readOnly = true)
-	@RolesAllowed(RoleSet.OBSERVER)
-	public List<ExternalDataPE> listExternalData(
-			final String sessionToken,
-			@AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) final ExperimentIdentifier identifier);
-
-	/**
-	 * Performs an <i>Hibernate Search</i> based on given parameters.
-	 */
-	@Transactional(readOnly = true)
-	@RolesAllowed(RoleSet.OBSERVER)
-	@ReturnValueFilter(validatorClass = MatchingEntityValidator.class)
-	public List<SearchHit> listMatchingEntities(final String sessionToken,
-			final SearchableEntity[] searchableEntities, final String queryText);
-
-	/**
-	 * List experiment types.
-	 * 
-	 * @return a sorted list of {@link ExperimentTypePE}.
-	 */
-	@Transactional(readOnly = true)
-	@RolesAllowed(RoleSet.OBSERVER)
-	public List<ExperimentTypePE> listExperimentTypes(String sessionToken);
-
-	/**
-	 * List property types.
-	 * 
-	 * @return a sorted list of {@link PropertyTypePE}.
-	 */
-	@Transactional(readOnly = true)
-	@RolesAllowed(RoleSet.OBSERVER)
-	public List<PropertyTypePE> listPropertyTypes(final String sessionToken);
-
-	/**
-	 * Lists data types.
-	 * 
-	 * @return a sorted list of {@link DataTypePE}.
-	 */
-	@Transactional(readOnly = true)
-	@RolesAllowed(RoleSet.OBSERVER)
-	public List<DataTypePE> listDataTypes(final String sessionToken);
-
-	/**
-	 * Lists vocabularies.
-	 * 
-	 * @return a sorted list of {@link VocabularyPE}.
-	 */
-	@Transactional(readOnly = true)
-	@RolesAllowed(RoleSet.OBSERVER)
-	public List<VocabularyPE> listVocabularies(final String sessionToken,
-			final boolean withTerms, boolean excludeInternal);
-
-	/**
-	 * Registers given {@link PropertyType}.
-	 */
-	@Transactional
-	@RolesAllowed(RoleSet.INSTANCE_ADMIN)
-	public void registerPropertyType(final String sessionToken,
-			final PropertyType propertyType);
-
-	/**
-	 * Assigns property type to entity type.
-	 */
-	@Transactional
-	@RolesAllowed(RoleSet.INSTANCE_ADMIN)
-	public String assignPropertyType(final String sessionToken,
-			final EntityKind entityKind, final String propertyTypeCode,
-			final String entityTypeCode, final boolean isMandatory,
-			final String defaultValue);
-
-	/**
-	 * Registers given {@link Vocabulary}.
-	 */
-	@Transactional
-	@RolesAllowed(RoleSet.INSTANCE_ADMIN)
-	public void registerVocabulary(final String sessionToken,
-			final Vocabulary vocabulary);
-
-	/**
-	 * Registers new project.
-	 */
-	@Transactional
-	@RolesAllowed(RoleSet.GROUP_ADMIN)
-	public void registerProject(
-			String sessionToken,
-			@AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) ProjectIdentifier projectIdentifier,
-			String description, String leaderId);
-
-	/**
-	 * Performs an <i>Hibernate Search</i> based on given parameters.
-	 */
-	@Transactional(readOnly = true)
-	@RolesAllowed(RoleSet.OBSERVER)
-	@ReturnValueFilter(validatorClass = ExternalDataValidator.class)
-	public List<ExternalDataPE> searchForDataSets(String sessionToken,
-			DataSetSearchCriteria criteria);
-
-	/**
-	 * List material types.
-	 * 
-	 * @return a sorted list of {@link MaterialTypePE}.
-	 */
-	@Transactional(readOnly = true)
-	@RolesAllowed(RoleSet.OBSERVER)
-	public List<MaterialTypePE> listMaterialTypes(String sessionToken);
-
-	/**
-	 * Lists materials.
-	 * 
-	 * @return a sorted list of {@link MaterialPE}.
-	 */
-	@Transactional(readOnly = true)
-	@RolesAllowed(RoleSet.OBSERVER)
-	public List<MaterialPE> listMaterials(String sessionToken,
-			MaterialTypePE materialType);
-
-	/**
-	 * Creates a new material type.
-	 */
-	@Transactional
-	@RolesAllowed(RoleSet.INSTANCE_ADMIN)
-	public void registerMaterialType(String sessionToken,
-			MaterialType entityType);
-
-	/**
-	 * Creates a new sample type.
-	 */
-	@Transactional
-	@RolesAllowed(RoleSet.INSTANCE_ADMIN)
-	public void registerSampleType(String sessionToken, SampleType entityType);
-
-	/**
-	 * Creates a new experiment type.
-	 */
-	@Transactional
-	@RolesAllowed(RoleSet.INSTANCE_ADMIN)
-	public void registerExperimentType(String sessionToken,
-			ExperimentType entityType);
-
-	/**
-	 * Deletes specified data sets.
-	 */
-	@Transactional
-	@RolesAllowed(RoleSet.GROUP_ADMIN)
-	public void deleteDataSets(
-			String sessionToken,
-			@AuthorizationGuard(guardClass = DataSetCodePredicate.class) List<String> dataSetCodes,
-			String reason);
-
-	/**
-	 * Saves changed experiment.
-	 */
-	@Transactional
-	@RolesAllowed(RoleSet.USER)
-	public void editExperiment(
-			String sessionToken,
-			@AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) ExperimentIdentifier experimentIdentifier,
-			List<ExperimentProperty> properties,
-			List<AttachmentPE> attachments,
-			@AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) ProjectIdentifier newProjectIdentifier,
-			Date version);
-
-	/**
-	 * Saves changed material.
-	 */
-	@Transactional
-	@RolesAllowed(RoleSet.INSTANCE_ADMIN)
-	public void editMaterial(String sessionToken,
-			MaterialIdentifier identifier, List<MaterialProperty> properties,
-			Date version);
-
-	/**
-	 * Saves changed sample.
-	 */
-	@Transactional
-	@RolesAllowed(RoleSet.USER)
-	public void editSample(
-			String sessionToken,
-			@AuthorizationGuard(guardClass = SampleOwnerIdentifierPredicate.class) SampleIdentifier identifier,
-			List<SampleProperty> properties,
-			@AuthorizationGuard(guardClass = NullableGroupIdentifierPredicate.class) ExperimentIdentifier experimentIdentifierOrNull,
-			Date version);
-
-	/**
-	 * Lists vocabulary terms of a given vocabulary. Includes terms usage
-	 * statistics.
-	 */
-	@Transactional
-	@RolesAllowed(RoleSet.USER)
-	public List<VocabularyTermWithStats> listVocabularyTerms(
-			String sessionToken, Vocabulary vocabulary);
-
-	/**
-	 * List data set types.
-	 * 
-	 * @return a sorted list of {@link DataSetTypePE}.
-	 */
-	@Transactional(readOnly = true)
-	@RolesAllowed(RoleSet.OBSERVER)
-	public List<DataSetTypePE> listDataSetTypes(String sessionToken);
+        /**
+         * Returns all groups which belong to the specified database instance. *
+         * 
+         * @return a sorted list of {@link GroupPE}.
+         */
+        @Transactional(readOnly = true)
+        @RolesAllowed(RoleSet.OBSERVER)
+        @ReturnValueFilter(validatorClass = GroupValidator.class)
+        public List<GroupPE> listGroups(String sessionToken,
+                        DatabaseInstanceIdentifier identifier);
+
+        /**
+         * Registers a new group with specified code and optional description and
+         * group leader ID.
+         */
+        @Transactional
+        @RolesAllowed(RoleSet.INSTANCE_ADMIN)
+        public void registerGroup(String sessionToken, String groupCode,
+                        String descriptionOrNull, String groupLeaderOrNull);
+
+        /**
+         * Returns all persons from current instance.
+         * 
+         * @return a sorted list of {@link PersonPE}.
+         */
+        @Transactional(readOnly = true)
+        @RolesAllowed(RoleSet.OBSERVER)
+        public List<PersonPE> listPersons(String sessionToken);
+
+        /**
+         * Returns all projects.
+         * 
+         * @return a sorted list of {@link ProjectPE}.
+         */
+        @Transactional(readOnly = true)
+        @RolesAllowed(RoleSet.OBSERVER)
+        @ReturnValueFilter(validatorClass = ProjectValidator.class)
+        public List<ProjectPE> listProjects(String sessionToken);
+
+        /**
+         * Registers a new person.
+         */
+        @Transactional
+        @RolesAllowed(RoleSet.INSTANCE_ADMIN)
+        public void registerPerson(String sessionToken, String userID);
+
+        /**
+         * Returns a list of all roles.
+         */
+        @Transactional(readOnly = true)
+        @RolesAllowed(RoleSet.GROUP_ADMIN)
+        public List<RoleAssignmentPE> listRoles(String sessionToken);
+
+        /**
+         * Registers a new group role.
+         */
+        @Transactional
+        @RolesAllowed(RoleSet.GROUP_ADMIN)
+        public void registerGroupRole(
+                        String sessionToken,
+                        RoleCode roleCode,
+                        @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) GroupIdentifier identifier,
+                        String person);
+
+        /**
+         * Registers a new instance role.
+         */
+        @Transactional
+        @RolesAllowed(RoleSet.INSTANCE_ADMIN)
+        public void registerInstanceRole(String sessionToken, RoleCode roleCode,
+                        String person);
+
+        /**
+         * Deletes role described by given role code, group identifier and user id.
+         */
+        @Transactional
+        @RolesAllowed(RoleSet.GROUP_ADMIN)
+        public void deleteGroupRole(
+                        String sessionToken,
+                        RoleCode roleCode,
+                        @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) GroupIdentifier groupIdentifier,
+                        String person);
+
+        /**
+         * Deletes role described by given role code and user id.
+         */
+        @Transactional
+        @RolesAllowed(RoleSet.INSTANCE_ADMIN)
+        public void deleteInstanceRole(String sessionToken, RoleCode roleCode,
+                        String person);
+
+        /**
+         * Lists sample types which are appropriate for listing.
+         * 
+         * @return a sorted list of {@link SampleTypePE}.
+         */
+        @Transactional(readOnly = true)
+        @RolesAllowed(RoleSet.OBSERVER)
+        public List<SampleTypePE> listSampleTypes(String sessionToken);
+
+        /**
+         * Lists samples using given configuration.
+         * 
+         * @return a sorted list of {@link SamplePE}.
+         */
+        @Transactional(readOnly = true)
+        @RolesAllowed(RoleSet.OBSERVER)
+        public List<SamplePE> listSamples(final String sessionToken,
+                        final ListSampleCriteriaDTO criteria);
+
+        /**
+         * Lists experiments.
+         * 
+         * @return a sorted list of {@link ExperimentPE}.
+         */
+        @Transactional(readOnly = true)
+        @RolesAllowed(RoleSet.OBSERVER)
+        public List<ExperimentPE> listExperiments(
+                        final String sessionToken,
+                        ExperimentTypePE experimentType,
+                        @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) ProjectIdentifier project);
+
+        /**
+         * For given {@link SampleIdentifier} returns the corresponding list of
+         * {@link ExternalDataPE}.
+         * 
+         * @return a sorted list of {@link ExternalDataPE}.
+         */
+        @Transactional(readOnly = true)
+        @RolesAllowed(RoleSet.OBSERVER)
+        public List<ExternalDataPE> listExternalData(
+                        final String sessionToken,
+                        @AuthorizationGuard(guardClass = SampleOwnerIdentifierPredicate.class) final SampleIdentifier identifier);
+
+        /**
+         * For given {@link ExperimentIdentifier} returns the corresponding list of
+         * {@link ExternalDataPE}.
+         * 
+         * @return a sorted list of {@link ExternalDataPE}.
+         */
+        @Transactional(readOnly = true)
+        @RolesAllowed(RoleSet.OBSERVER)
+        public List<ExternalDataPE> listExternalData(
+                        final String sessionToken,
+                        @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) final ExperimentIdentifier identifier);
+
+        /**
+         * Performs an <i>Hibernate Search</i> based on given parameters.
+         */
+        @Transactional(readOnly = true)
+        @RolesAllowed(RoleSet.OBSERVER)
+        @ReturnValueFilter(validatorClass = MatchingEntityValidator.class)
+        public List<SearchHit> listMatchingEntities(final String sessionToken,
+                        final SearchableEntity[] searchableEntities, final String queryText);
+
+        /**
+         * List experiment types.
+         * 
+         * @return a sorted list of {@link ExperimentTypePE}.
+         */
+        @Transactional(readOnly = true)
+        @RolesAllowed(RoleSet.OBSERVER)
+        public List<ExperimentTypePE> listExperimentTypes(String sessionToken);
+
+        /**
+         * List property types.
+         * 
+         * @return a sorted list of {@link PropertyTypePE}.
+         */
+        @Transactional(readOnly = true)
+        @RolesAllowed(RoleSet.OBSERVER)
+        public List<PropertyTypePE> listPropertyTypes(final String sessionToken);
+
+        /**
+         * Lists data types.
+         * 
+         * @return a sorted list of {@link DataTypePE}.
+         */
+        @Transactional(readOnly = true)
+        @RolesAllowed(RoleSet.OBSERVER)
+        public List<DataTypePE> listDataTypes(final String sessionToken);
+
+        /**
+         * Lists vocabularies.
+         * 
+         * @return a sorted list of {@link VocabularyPE}.
+         */
+        @Transactional(readOnly = true)
+        @RolesAllowed(RoleSet.OBSERVER)
+        public List<VocabularyPE> listVocabularies(final String sessionToken,
+                        final boolean withTerms, boolean excludeInternal);
+
+        /**
+         * Registers given {@link PropertyType}.
+         */
+        @Transactional
+        @RolesAllowed(RoleSet.INSTANCE_ADMIN)
+        public void registerPropertyType(final String sessionToken,
+                        final PropertyType propertyType);
+
+        /**
+         * Assigns property type to entity type.
+         */
+        @Transactional
+        @RolesAllowed(RoleSet.INSTANCE_ADMIN)
+        public String assignPropertyType(final String sessionToken,
+                        final EntityKind entityKind, final String propertyTypeCode,
+                        final String entityTypeCode, final boolean isMandatory,
+                        final String defaultValue);
+
+        /**
+         * Registers given {@link Vocabulary}.
+         */
+        @Transactional
+        @RolesAllowed(RoleSet.INSTANCE_ADMIN)
+        public void registerVocabulary(final String sessionToken,
+                        final Vocabulary vocabulary);
+
+        /**
+         * Registers new project.
+         */
+        @Transactional
+        @RolesAllowed(RoleSet.GROUP_ADMIN)
+        public void registerProject(
+                        String sessionToken,
+                        @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class) ProjectIdentifier projectIdentifier,
+                        String description, String leaderId);
+
+        /**
+         * Performs an <i>Hibernate Search</i> based on given parameters.
+         */
+        @Transactional(readOnly = true)
+        @RolesAllowed(RoleSet.OBSERVER)
+        @ReturnValueFilter(validatorClass = ExternalDataValidator.class)
+        public List<ExternalDataPE> searchForDataSets(String sessionToken,
+                        DataSetSearchCriteria criteria);
+
+        /**
+         * List material types.
+         * 
+         * @return a sorted list of {@link MaterialTypePE}.
+         */
+        @Transactional(readOnly = true)
+        @RolesAllowed(RoleSet.OBSERVER)
+        public List<MaterialTypePE> listMaterialTypes(String sessionToken);
+
+        /**
+         * Lists materials.
+         * 
+         * @return a sorted list of {@link MaterialPE}.
+         */
+        @Transactional(readOnly = true)
+        @RolesAllowed(RoleSet.OBSERVER)
+        public List<MaterialPE> listMaterials(String sessionToken,
+                        MaterialTypePE materialType);
+
+        /**
+         * Creates a new material type.
+         */
+        @Transactional
+        @RolesAllowed(RoleSet.INSTANCE_ADMIN)
+        public void registerMaterialType(String sessionToken,
+                        MaterialType entityType);
+
+        /**
+         * Creates a new sample type.
+         */
+        @Transactional
+        @RolesAllowed(RoleSet.INSTANCE_ADMIN)
+        public void registerSampleType(String sessionToken, SampleType entityType);
+
+        /**
+         * Creates a new experiment type.
+         */
+        @Transactional
+        @RolesAllowed(RoleSet.INSTANCE_ADMIN)
+        public void registerExperimentType(String sessionToken,
+                        ExperimentType entityType);
+
+        /**
+         * Deletes specified data sets.
+         */
+        @Transactional
+        @RolesAllowed(RoleSet.GROUP_ADMIN)
+        public void deleteDataSets(
+                        String sessionToken,
+                        @AuthorizationGuard(guardClass = DataSetCodePredicate.class) List<String> dataSetCodes,
+                        String reason);
+
+    /**
+     * Uploads specified data sets to CIFEX server of specified URL with specified password.
+     */
+    @Transactional
+    @RolesAllowed(RoleSet.OBSERVER)
+    public void uploadDataSets(String sessionToken, List<String> dataSetCodes, String cifexURL,
+            String password);
+    
+    /**
+     * Saves changed experiment.
+     */
+    @Transactional
+    @RolesAllowed(RoleSet.USER)
+    public void editExperiment(String sessionToken,
+            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class)
+            ExperimentIdentifier experimentIdentifier, List<ExperimentProperty> properties,
+            List<AttachmentPE> attachments,
+            @AuthorizationGuard(guardClass = GroupIdentifierPredicate.class)
+            ProjectIdentifier newProjectIdentifier, Date version);
+
+        /**
+         * Saves changed material.
+         */
+        @Transactional
+        @RolesAllowed(RoleSet.INSTANCE_ADMIN)
+        public void editMaterial(String sessionToken,
+                        MaterialIdentifier identifier, List<MaterialProperty> properties,
+                        Date version);
+
+        /**
+         * Saves changed sample.
+         */
+        @Transactional
+        @RolesAllowed(RoleSet.USER)
+        public void editSample(
+                        String sessionToken,
+                        @AuthorizationGuard(guardClass = SampleOwnerIdentifierPredicate.class) SampleIdentifier identifier,
+                        List<SampleProperty> properties,
+                        @AuthorizationGuard(guardClass = NullableGroupIdentifierPredicate.class) ExperimentIdentifier experimentIdentifierOrNull,
+                        Date version);
+
+        /**
+         * Lists vocabulary terms of a given vocabulary. Includes terms usage
+         * statistics.
+         */
+        @Transactional
+        @RolesAllowed(RoleSet.USER)
+        public List<VocabularyTermWithStats> listVocabularyTerms(
+                        String sessionToken, Vocabulary vocabulary);
+
+        /**
+         * List data set types.
+         * 
+         * @return a sorted list of {@link DataSetTypePE}.
+         */
+        @Transactional(readOnly = true)
+        @RolesAllowed(RoleSet.OBSERVER)
+        public List<DataSetTypePE> listDataSetTypes(String sessionToken);
 
 }