diff --git a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/DataStoreServerApi.java b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/DataStoreServerApi.java
index f92dea95b818c2ba16fbd5e2df28f877d6f9d903..e435744da7e2377f81cc836aed0399f4538fa1cc 100644
--- a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/DataStoreServerApi.java
+++ b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/DataStoreServerApi.java
@@ -15,8 +15,6 @@
  */
 package ch.ethz.sis.openbis.generic.server.dssapi.v3;
 
-import java.io.File;
-import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -28,17 +26,14 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 import org.apache.commons.collections4.iterators.IteratorChain;
 import org.apache.commons.lang.StringUtils;
-import org.apache.log4j.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.CreationId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchOperator;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchResult;
@@ -49,13 +44,9 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.DataSetPermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.search.DataSetSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.datastore.id.DataStorePermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.datastore.id.IDataStoreId;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.id.EntityTypePermId;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.id.IEntityTypeId;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.IExperimentId;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.ISampleId;
-import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.UnsupportedObjectIdException;
 import ch.ethz.sis.openbis.generic.dssapi.v3.IDataStoreServerApi;
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.dataset.create.FullDataSetCreation;
+import ch.ethz.sis.openbis.generic.dssapi.v3.dto.dataset.create.UploadedDataSetCreation;
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.DataSetFile;
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.create.DataSetFileCreation;
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.download.DataSetFileDownloadOptions;
@@ -64,14 +55,12 @@ import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.id.DataSetFilePermI
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.id.IDataSetFileId;
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.search.DataSetFileSearchCriteria;
 import ch.ethz.sis.openbis.generic.server.dssapi.v3.download.DataSetFileDownloadInputStream;
+import ch.ethz.sis.openbis.generic.server.dssapi.v3.executor.ICreateUploadedDataSetExecutor;
 import ch.ethz.sis.openbis.generic.server.dssapi.v3.pathinfo.PathInfoFeeder;
 import ch.systemsx.cisd.common.exceptions.Status;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.common.filesystem.IFreeSpaceProvider;
 import ch.systemsx.cisd.common.filesystem.SimpleFreeSpaceProvider;
-import ch.systemsx.cisd.common.logging.LogCategory;
-import ch.systemsx.cisd.common.logging.LogFactory;
-import ch.systemsx.cisd.etlserver.api.v1.PutDataSetService;
 import ch.systemsx.cisd.etlserver.path.IPathsInfoDAO;
 import ch.systemsx.cisd.openbis.common.io.hierarchical_content.api.IHierarchicalContent;
 import ch.systemsx.cisd.openbis.common.io.hierarchical_content.api.IHierarchicalContentNode;
@@ -83,13 +72,7 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.IConfigProvider;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IHierarchicalContentProvider;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IShareIdManager;
-import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.internal.authorization.DssSessionAuthorizationHolder;
-import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.FileInfoDssBuilder;
-import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.FileInfoDssDTO;
-import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTO;
-import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTO.DataSetOwner;
-import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTO.DataSetOwnerType;
 import ch.systemsx.cisd.openbis.dss.generic.shared.utils.PathInfoDataSourceProvider;
 import ch.systemsx.cisd.openbis.generic.server.authorization.annotation.RolesAllowed;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.RoleWithHierarchy;
@@ -109,12 +92,6 @@ public class DataStoreServerApi extends AbstractDssServiceRpc<IDataStoreServerAp
      */
     public static final String INTERNAL_SERVICE_NAME = "data-store-server_INTERNAL";
 
-    /**
-     * Logger with {@link LogCategory#OPERATION} with name of the concrete class, needs to be static for our purpose.
-     */
-    protected static Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION,
-            DataStoreServerApi.class);
-
     public String DSS_SERVICE_NAME = "DSS Service";
 
     @Autowired
@@ -123,6 +100,9 @@ public class DataStoreServerApi extends AbstractDssServiceRpc<IDataStoreServerAp
     @Autowired
     private IApplicationServerApi as;
 
+    @Autowired
+    private ICreateUploadedDataSetExecutor createUploadedDataSetExecutor;
+
     /**
      * The designated constructor.
      */
@@ -138,8 +118,7 @@ public class DataStoreServerApi extends AbstractDssServiceRpc<IDataStoreServerAp
             IPluginTaskInfoProvider infoProvider, IFreeSpaceProvider freeSpaceProvider,
             IShareIdManager shareIdManager, IHierarchicalContentProvider contentProvider)
     {
-        this(openBISService, apiServer, infoProvider, null, freeSpaceProvider, shareIdManager,
-                contentProvider, new PutDataSetService(openBISService, operationLog));
+        this(openBISService, apiServer, infoProvider, null, freeSpaceProvider, shareIdManager, contentProvider);
     }
 
     /**
@@ -148,8 +127,7 @@ public class DataStoreServerApi extends AbstractDssServiceRpc<IDataStoreServerAp
     public DataStoreServerApi(IEncapsulatedOpenBISService openBISService,
             IQueryApiServer apiServer, IPluginTaskInfoProvider infoProvider,
             IStreamRepository streamRepository, IFreeSpaceProvider freeSpaceProvider,
-            IShareIdManager shareIdManager, IHierarchicalContentProvider contentProvider,
-            PutDataSetService service)
+            IShareIdManager shareIdManager, IHierarchicalContentProvider contentProvider)
     {
         super(openBISService, streamRepository, shareIdManager, contentProvider);
         // queryApiServer = apiServer;
@@ -345,80 +323,24 @@ public class DataStoreServerApi extends AbstractDssServiceRpc<IDataStoreServerAp
         return new DataStoreServerApiLogger(context);
     }
 
-    private NewDataSetDTO getNewDataSet(FullDataSetCreation dataSetCreation, File temporaryIncomingDir) throws IOException
-    {
-        ISampleId sampleIdentifier = dataSetCreation.getMetadataCreation().getSampleId();
-        IExperimentId experimentIdentifier = dataSetCreation.getMetadataCreation().getExperimentId();
-        DataSetOwnerType ownerType = DataSetOwnerType.SAMPLE;
-        DataSetOwner owner = null;
-        if (sampleIdentifier != null)
-        {
-            owner = new NewDataSetDTO.DataSetOwner(ownerType, sampleIdentifier.toString());
-        } else if (experimentIdentifier != null)
-        {
-            ownerType = DataSetOwnerType.EXPERIMENT;
-            owner = new NewDataSetDTO.DataSetOwner(ownerType, experimentIdentifier.toString());
-        }
-        if (owner == null)
-        {
-            throw new UserFailureException("A dataset needs either a Sample or Experiment as owner.");
-        }
-        IEntityTypeId typeId = dataSetCreation.getMetadataCreation().getTypeId();
-        String typeCode = null;
-
-        if (typeId != null)
-        {
-            if (typeId instanceof EntityTypePermId)
-            {
-                typeCode = ((EntityTypePermId) typeId).getPermId();
-            } else
-            {
-                throw new UnsupportedObjectIdException(typeId);
-            }
-        }
-
-        List<FileInfoDssDTO> fileInfos = FileInfoDssBuilder.getFileInfos(temporaryIncomingDir);
-
-        NewDataSetDTO dataSet = new NewDataSetDTO(typeCode, owner, null, fileInfos);
-        return dataSet;
-    }
-
-    private List<FullDataSetCreation> filterPhysicalDataSets(List<FullDataSetCreation> newDataSets)
-    {
-        return newDataSets
-                .stream()
-                .filter((dataSetCreation) -> dataSetCreation.getMetadataCreation().getPhysicalData() != null)
-                .collect(Collectors.toList());
-    }
-
-    private List<FullDataSetCreation> filterNonPhysicalDataSets(List<FullDataSetCreation> newDataSets)
+    @Override
+    @Transactional
+    @RolesAllowed({ RoleWithHierarchy.PROJECT_USER })
+    public DataSetPermId createUploadedDataSet(String sessionToken, UploadedDataSetCreation creation)
     {
-        return newDataSets
-                .stream()
-                .filter((dataSetCreation) -> dataSetCreation.getMetadataCreation().getPhysicalData() == null)
-                .collect(Collectors.toList());
+        return createUploadedDataSetExecutor.create(sessionToken, creation);
     }
 
     @Override
     @Transactional
     @RolesAllowed({ RoleWithHierarchy.INSTANCE_ADMIN })
     public List<DataSetPermId> createDataSets(String sessionToken, List<FullDataSetCreation> newDataSets)
-    {
-        injectDataStoreIdAndCodesIfNeeded(newDataSets);
-        List<FullDataSetCreation> physicalDataSets = filterPhysicalDataSets(newDataSets);
-        createPhysicalDataSets(sessionToken, physicalDataSets);
-
-        List<FullDataSetCreation> nonPhysicalDataSets = filterNonPhysicalDataSets(newDataSets);
-        return createNonPhysicalDataSets(sessionToken, nonPhysicalDataSets);
-    }
-
-    private List<DataSetPermId> createNonPhysicalDataSets(String sessionToken, List<FullDataSetCreation> newDataSets)
     {
         if (PathInfoDataSourceProvider.isDataSourceDefined() == false)
         {
             throw new IllegalStateException("Pathinfo DB not configured - cannot store dataset file information");
         }
-
+        injectDataStoreIdAndCodesIfNeeded(newDataSets);
         IPathsInfoDAO dao = QueryTool.getQuery(PathInfoDataSourceProvider.getDataSource(), IPathsInfoDAO.class);
 
         for (int i = 0; i < newDataSets.size(); i++)
@@ -453,42 +375,6 @@ public class DataStoreServerApi extends AbstractDssServiceRpc<IDataStoreServerAp
         return as.createDataSets(sessionToken, metadata);
     }
 
-    private void createPhysicalDataSets(String sessionToken, List<FullDataSetCreation> newDataSets)
-    {
-        for (FullDataSetCreation dataSetCreation : newDataSets)
-        {
-            PutDataSetService putService =
-                    new PutDataSetService(ServiceProvider.getOpenBISService(), operationLog);
-            putService.setStoreDirectory(ServiceProvider.getConfigProvider().getStoreRoot());
-            NewDataSetDTO newDataset;
-            try
-            {
-                DataSetCreation metadataCreation = dataSetCreation.getMetadataCreation();
-                CreationId creationId = metadataCreation.getCreationId();
-                String dataSetTypeCodeNull = null;
-                IEntityTypeId typeId = metadataCreation.getTypeId();
-                if (null != typeId)
-                {
-                    if (typeId instanceof EntityTypePermId)
-                    {
-                        dataSetTypeCodeNull = ((EntityTypePermId) typeId).getPermId();
-                    } else
-                    {
-                        throw new UnsupportedObjectIdException(typeId);
-                    }
-                }
-                File temporaryIncomingDir = putService.getTemporaryIncomingDir(dataSetTypeCodeNull, creationId.toString());
-                newDataset = getNewDataSet(dataSetCreation, temporaryIncomingDir);
-                String code = dataSetCreation.getMetadataCreation().getCode();
-                putService.putDataSet(sessionToken, newDataset, creationId.toString(), code);
-
-            } catch (IOException e)
-            {
-                operationLog.error(e.getMessage());
-            }
-        }
-    }
-
     private void injectDataStoreIdAndCodesIfNeeded(List<FullDataSetCreation> newDataSets)
     {
         String dataStoreCode = configProvider.getDataStoreCode();
diff --git a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/DataStoreServerApiJson.java b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/DataStoreServerApiJson.java
index f4a3faa402a919bfb65fdbded09f114a93b4459f..7d6e3cc79130e3b9d05c76ea517d770b86cff50c 100644
--- a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/DataStoreServerApiJson.java
+++ b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/DataStoreServerApiJson.java
@@ -27,6 +27,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchResult;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.DataSetPermId;
 import ch.ethz.sis.openbis.generic.dssapi.v3.IDataStoreServerApi;
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.dataset.create.FullDataSetCreation;
+import ch.ethz.sis.openbis.generic.dssapi.v3.dto.dataset.create.UploadedDataSetCreation;
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.DataSetFile;
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.download.DataSetFileDownloadOptions;
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.fetchoptions.DataSetFileFetchOptions;
@@ -59,6 +60,12 @@ public class DataStoreServerApiJson implements IDataStoreServerApi
         throw new UnsupportedOperationException("This method is not supported in JSON API");
     }
 
+    @Override
+    public DataSetPermId createUploadedDataSet(String sessionToken, UploadedDataSetCreation newDataSet)
+    {
+        return api.createUploadedDataSet(sessionToken, newDataSet);
+    }
+
     @Override
     public List<DataSetPermId> createDataSets(String sessionToken, List<FullDataSetCreation> newDataSets)
     {
diff --git a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/DataStoreServerApiLogger.java b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/DataStoreServerApiLogger.java
index dbb7dae12d37d7410c3259d4dd785b591ec8df82..ec7f3ccbe8d6190b4675a49b08eb17700debee4d 100644
--- a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/DataStoreServerApiLogger.java
+++ b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/DataStoreServerApiLogger.java
@@ -7,6 +7,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchResult;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.DataSetPermId;
 import ch.ethz.sis.openbis.generic.dssapi.v3.IDataStoreServerApi;
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.dataset.create.FullDataSetCreation;
+import ch.ethz.sis.openbis.generic.dssapi.v3.dto.dataset.create.UploadedDataSetCreation;
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.DataSetFile;
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.download.DataSetFileDownloadOptions;
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.fetchoptions.DataSetFileFetchOptions;
@@ -50,6 +51,13 @@ public class DataStoreServerApiLogger extends AbstractServerLogger implements
         return null;
     }
 
+    @Override
+    public DataSetPermId createUploadedDataSet(String sessionToken, UploadedDataSetCreation newDataSet)
+    {
+        logAccess(sessionToken, "create-uploaded-data-sets", "DATA_SETS:\n%s", newDataSet);
+        return null;
+    }
+
     @Override
     public List<DataSetPermId> createDataSets(String sessionToken, List<FullDataSetCreation> newDataSets)
     {
diff --git a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/executor/CreateUploadedDataSetExecutor.java b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/executor/CreateUploadedDataSetExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..ae944447b24b1800d8e6c30226871c88166192f5
--- /dev/null
+++ b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/executor/CreateUploadedDataSetExecutor.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2018 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.ethz.sis.openbis.generic.server.dssapi.v3.executor;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSet;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSetType;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.fetchoptions.DataSetFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.fetchoptions.DataSetTypeFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.DataSetPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.IDataSetId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.search.DataSetTypeSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.Experiment;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.fetchoptions.ExperimentFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.IExperimentId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.Sample;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.fetchoptions.SampleFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.ISampleId;
+import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.ObjectNotFoundException;
+import ch.ethz.sis.openbis.generic.dssapi.v3.dto.dataset.create.UploadedDataSetCreation;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.OperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.utils.ExceptionUtils;
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.common.logging.LogCategory;
+import ch.systemsx.cisd.common.logging.LogFactory;
+import ch.systemsx.cisd.etlserver.api.v1.PutDataSetService;
+import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
+import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider;
+import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.FileInfoDssDTO;
+import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTO;
+import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTO.DataSetOwner;
+import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTO.DataSetOwnerType;
+import ch.systemsx.cisd.openbis.dss.generic.shared.dto.DataSetInformation;
+
+/**
+ * @author pkupczyk
+ */
+@Component
+public class CreateUploadedDataSetExecutor implements ICreateUploadedDataSetExecutor
+{
+
+    private static Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, CreateUploadedDataSetExecutor.class);
+
+    private PutDataSetService putService;
+
+    @Override
+    public DataSetPermId create(String sessionToken, UploadedDataSetCreation creation)
+    {
+        try
+        {
+            getOpenBIService().checkSession(sessionToken);
+
+            NewDataSetDTO newDataset = getNewDataSet(sessionToken, creation);
+
+            List<DataSetInformation> dataSetInfos = getPutService().putDataSet(sessionToken, newDataset, creation.getUploadId());
+
+            if (dataSetInfos != null && false == dataSetInfos.isEmpty())
+            {
+                return new DataSetPermId(dataSetInfos.get(0).getDataSetCode());
+            } else
+            {
+                return null;
+            }
+        } catch (IOException e)
+        {
+            throw ExceptionUtils.create(new OperationContext(null), e);
+        }
+    }
+
+    private NewDataSetDTO getNewDataSet(String sessionToken, UploadedDataSetCreation creation) throws IOException
+    {
+        DataSetOwnerType ownerType;
+        DataSetOwner owner;
+        String typeCode;
+        List<String> parentCodes = new ArrayList<String>();
+
+        // type
+
+        if (creation.getTypeId() != null)
+        {
+            DataSetTypeSearchCriteria criteria = new DataSetTypeSearchCriteria();
+            criteria.withId().thatEquals(creation.getTypeId());
+
+            SearchResult<DataSetType> searchResult =
+                    getApplicationServerApi().searchDataSetTypes(sessionToken, criteria, new DataSetTypeFetchOptions());
+
+            if (searchResult.getObjects() != null && false == searchResult.getObjects().isEmpty())
+            {
+                DataSetType type = searchResult.getObjects().get(0);
+                typeCode = type.getCode();
+            } else
+            {
+                throw new ObjectNotFoundException(creation.getTypeId());
+            }
+        } else
+        {
+            throw new UserFailureException("A dataset needs a type.");
+        }
+
+        // owner
+
+        if (creation.getSampleId() != null)
+        {
+            Map<ISampleId, Sample> samples =
+                    getApplicationServerApi().getSamples(sessionToken, Arrays.asList(creation.getSampleId()), new SampleFetchOptions());
+            Sample sample = samples.get(creation.getSampleId());
+
+            if (sample == null)
+            {
+                throw new ObjectNotFoundException(creation.getSampleId());
+            }
+
+            ownerType = DataSetOwnerType.SAMPLE;
+            owner = new NewDataSetDTO.DataSetOwner(ownerType, sample.getIdentifier().getIdentifier());
+        } else if (creation.getExperimentId() != null)
+        {
+            Map<IExperimentId, Experiment> experiments =
+                    getApplicationServerApi().getExperiments(sessionToken, Arrays.asList(creation.getExperimentId()), new ExperimentFetchOptions());
+            Experiment experiment = experiments.get(creation.getExperimentId());
+
+            if (experiment == null)
+            {
+                throw new ObjectNotFoundException(creation.getExperimentId());
+            }
+
+            ownerType = DataSetOwnerType.EXPERIMENT;
+            owner = new NewDataSetDTO.DataSetOwner(ownerType, experiment.getIdentifier().getIdentifier());
+        } else
+        {
+            throw new UserFailureException("A dataset needs either a sample or an experiment as an owner.");
+        }
+
+        // parents
+
+        if (creation.getParentIds() != null && false == creation.getParentIds().isEmpty())
+        {
+            Map<IDataSetId, DataSet> parents =
+                    getApplicationServerApi().getDataSets(sessionToken, creation.getParentIds(), new DataSetFetchOptions());
+
+            for (IDataSetId parentId : creation.getParentIds())
+            {
+                DataSet parent = parents.get(parentId);
+                if (parent == null)
+                {
+                    throw new ObjectNotFoundException(parentId);
+                }
+                parentCodes.add(parent.getCode());
+            }
+        }
+
+        // upload id
+
+        if (creation.getUploadId() != null)
+        {
+            if (creation.getUploadId().contains("/"))
+            {
+                throw new UserFailureException("Upload id must not contain '/'");
+            }
+
+            File temporaryIncomingRoot = getPutService().getTemporaryIncomingRoot(typeCode);
+            File temporaryIncomingDir = new File(temporaryIncomingRoot, creation.getUploadId());
+
+            if (false == temporaryIncomingDir.exists())
+            {
+                throw new UserFailureException(
+                        "The folder for the upload id " + creation.getUploadId() + " could not be found. Have you uploaded the file(s) first?");
+            }
+        } else
+        {
+            throw new UserFailureException("Upload id cannot be null.");
+        }
+
+        NewDataSetDTO newDataSet = new NewDataSetDTO(typeCode, owner, null, new ArrayList<FileInfoDssDTO>());
+        newDataSet.setProperties(creation.getProperties());
+        newDataSet.setParentDataSetCodes(parentCodes);
+        return newDataSet;
+    }
+
+    private IApplicationServerApi getApplicationServerApi()
+    {
+        return ServiceProvider.getV3ApplicationService();
+    }
+
+    private IEncapsulatedOpenBISService getOpenBIService()
+    {
+        return ServiceProvider.getOpenBISService();
+    }
+
+    private synchronized PutDataSetService getPutService()
+    {
+        if (putService == null)
+        {
+            putService = new PutDataSetService(ServiceProvider.getOpenBISService(), operationLog);
+            putService.setStoreDirectory(ServiceProvider.getConfigProvider().getStoreRoot());
+        }
+
+        return putService;
+    }
+}
diff --git a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/executor/ICreateUploadedDataSetExecutor.java b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/executor/ICreateUploadedDataSetExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..995215e6cbab5a122902a9e69346389d2e41a665
--- /dev/null
+++ b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/executor/ICreateUploadedDataSetExecutor.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2018 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.ethz.sis.openbis.generic.server.dssapi.v3.executor;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.DataSetPermId;
+import ch.ethz.sis.openbis.generic.dssapi.v3.dto.dataset.create.UploadedDataSetCreation;
+
+/**
+ * @author pkupczyk
+ */
+public interface ICreateUploadedDataSetExecutor
+{
+
+    DataSetPermId create(String sessionToken, UploadedDataSetCreation creation);
+
+}
diff --git a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/upload/StoreShareFileUploadServlet.java b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/upload/StoreShareFileUploadServlet.java
index 4730bf93a4b5710504a3c1aa2e0ab79c0898f811..e40df8eeb44a5fb311da1d059dedbd6e299243bb 100644
--- a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/upload/StoreShareFileUploadServlet.java
+++ b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/upload/StoreShareFileUploadServlet.java
@@ -29,20 +29,14 @@ import org.apache.commons.fileupload.FileItemIterator;
 import org.apache.commons.fileupload.FileItemStream;
 import org.apache.commons.fileupload.FileUploadException;
 import org.apache.commons.fileupload.servlet.ServletFileUpload;
+import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.log4j.Logger;
-import org.springframework.stereotype.Component;
-import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.bind.annotation.RestController;
 
-import com.marathon.util.spring.StreamSupportingHttpInvokerServiceExporter;
-
-import ch.ethz.sis.openbis.generic.dssapi.v3.IDataStoreServerApi;
-import ch.ethz.sis.openbis.generic.server.dssapi.v3.DataStoreServerApiJsonServer;
 import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel;
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.etlserver.api.v1.PutDataSetService;
@@ -50,21 +44,23 @@ import ch.systemsx.cisd.openbis.dss.generic.server.Utils;
 import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider;
 
 /**
- * 
- *
  * @author Ganime Betul Akin
  */
 @RestController
-@RequestMapping({"store_share_file_upload", "/datastore_server/store_share_file_upload"})
+@RequestMapping({ "store_share_file_upload", "/datastore_server/store_share_file_upload" })
 public class StoreShareFileUploadServlet extends HttpServlet
 {
     private static final long serialVersionUID = 1L;
-    
-    @SuppressWarnings("hiding")
+
     protected static Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION,
             StoreShareFileUploadServlet.class);
 
+    public static final String SESSION_ID_PARAM = Utils.SESSION_ID_PARAM;
+
     public static final String DATA_SET_TYPE_PARAM = "dataSetType";
+
+    public static final String IGNORE_FILE_PATH_PARAM = "ignoreFilePath";
+
     public static final String UPLOAD_ID_PARAM = "uploadID";
 
     private PutDataSetService putService;
@@ -73,27 +69,27 @@ public class StoreShareFileUploadServlet extends HttpServlet
     public final void init(final ServletConfig servletConfig) throws ServletException
     {
         super.init(servletConfig);
-        this.putService =
-                new PutDataSetService(ServiceProvider.getOpenBISService(), operationLog);   
+        this.putService = new PutDataSetService(ServiceProvider.getOpenBISService(), operationLog);
         putService.setStoreDirectory(ServiceProvider.getConfigProvider().getStoreRoot());
-
     }
+
     @Override
     protected void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException
     {
-        StoreShareFileUploadRequest uploadRequest =
-                new StoreShareFileUploadRequest(request);
+        StoreShareFileUploadRequest uploadRequest = new StoreShareFileUploadRequest(request);
 
         uploadRequest.validate();
-        
-        String dataSetTypeCodeOrNull = uploadRequest.getDataSetType();
-        String uploadId = uploadRequest.getUploadId();
 
         try
         {
             FileItemIterator iterator = uploadRequest.getFiles();
 
+            if (false == iterator.hasNext())
+            {
+                throw new UserFailureException("Please upload at least one file");
+            }
+
             while (iterator.hasNext())
             {
                 FileItemStream file = null;
@@ -103,23 +99,30 @@ public class StoreShareFileUploadServlet extends HttpServlet
                 {
                     file = iterator.next();
                     stream = file.openStream();
-                    putService.putFileToStoreShare(uploadRequest.getSessionId(), file.getName(), dataSetTypeCodeOrNull, uploadId, stream);
-                } 
-                catch (Exception e) {
-                   operationLog.error(e.getMessage());
-                }
-                finally
+
+                    /*
+                     * Most browsers send only base file names for files which were uploaded via a regular html form. Still, there are some browsers
+                     * (e.g. Opera) that are known to send file paths as well. To handle all browsers consistently by default we ignore file paths
+                     * even if they are given. We only use the file paths if it has been explicitly requested via <code>IGNORE_FILE_PATH_PARAM</code>
+                     * request parameter. This may be handy in contexts where we have a full control over what gets sent to the servlet (e.g. from a
+                     * Python script that makes http requests to the servlet).
+                     */
+                    String filePath = uploadRequest.isIgnoreFilePath() ? FilenameUtils.getName(file.getName()) : file.getName();
+
+                    putService.putFileToStoreShare(uploadRequest.getSessionId(), filePath, uploadRequest.getDataSetType(),
+                            uploadRequest.getUploadId(), stream);
+                } finally
                 {
                     IOUtils.closeQuietly(stream);
                 }
             }
-
-        } catch (FileUploadException e)
+        } catch (Exception e)
         {
+            operationLog.error(e.getMessage());
             throw CheckedExceptionTunnel.wrapIfNecessary(e);
         }
     }
-    
+
     private class StoreShareFileUploadRequest
     {
 
@@ -132,8 +135,9 @@ public class StoreShareFileUploadServlet extends HttpServlet
 
         public String getSessionId()
         {
-            return request.getParameter(Utils.SESSION_ID_PARAM);
+            return request.getParameter(SESSION_ID_PARAM);
         }
+
         public String getDataSetType()
         {
             return request.getParameter(DATA_SET_TYPE_PARAM);
@@ -144,6 +148,19 @@ public class StoreShareFileUploadServlet extends HttpServlet
             return request.getParameter(UPLOAD_ID_PARAM);
         }
 
+        public boolean isIgnoreFilePath()
+        {
+            String str = request.getParameter(IGNORE_FILE_PATH_PARAM);
+
+            if (str == null || str.isEmpty())
+            {
+                return true;
+            } else
+            {
+                return Boolean.valueOf(str);
+            }
+        }
+
         public FileItemIterator getFiles() throws FileUploadException, IOException
         {
             ServletFileUpload upload = new ServletFileUpload();
@@ -154,18 +171,7 @@ public class StoreShareFileUploadServlet extends HttpServlet
         {
             if (ServletFileUpload.isMultipartContent(request) == false)
             {
-                throw new IllegalArgumentException(
-                        "The session workspace form upload accepts only multipart requests");
-            }
-            if (getSessionId() == null)
-            {
-                throw new IllegalArgumentException(Utils.SESSION_ID_PARAM
-                        + " parameter cannot be null");
-            }
-            
-            if (getUploadId() == null)
-            {
-                throw new IllegalArgumentException(UPLOAD_ID_PARAM + " parameter cannot be null");
+                throw new UserFailureException("The file upload accepts only multipart requests");
             }
         }
 
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetExecutor.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetExecutor.java
index 8ca00943510e01c3d2801b1bb9fa3fec08161551..0b2fd226dd604209bc4a3269523e886ccdcc8977 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetExecutor.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetExecutor.java
@@ -135,7 +135,7 @@ class PutDataSetExecutor implements IDataSetHandlerRpc
     }
 
     PutDataSetExecutor(PutDataSetService service, IETLServerPlugin plugin, String sessionToken,
-            NewDataSetDTO newDataSet, File temporaryIncomingDir)
+            NewDataSetDTO newDataSet, File temporaryIncomingDir, File dataSet)
     {
         this.service = service;
         this.plugin = plugin;
@@ -144,17 +144,7 @@ class PutDataSetExecutor implements IDataSetHandlerRpc
         this.inputStream = null;
         this.copier = FastRecursiveHardLinkMaker.tryCreate(RSyncConfig.getInstance().getAdditionalCommandLineOptions());
         this.temporaryIncomingDir = temporaryIncomingDir;
-        this.dataSetDir = new File(temporaryIncomingDir, newDataSet.getDataSetFolderName());
-        if (dataSetDir.exists())
-        {
-            deleteDataSetDir();
-        }
-        if (false == this.dataSetDir.mkdir())
-        {
-            throw new EnvironmentFailureException("Could not create directory for data set "
-                    + newDataSet.getDataSetFolderName());
-        }
-
+        this.dataSetDir = dataSet;
         overridingTypeExtractor = new OverridingTypeExtractor();
         handler = plugin.getDataSetHandler(this, service.getOpenBisService());
     }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetService.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetService.java
index c5e75e0e6f6faa4670e4ed8c85c0d3236573f544..f1e8fe136846c1aa27fd28d3c9e10ec557ccb478 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetService.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetService.java
@@ -65,6 +65,9 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseInstance;
  */
 public class PutDataSetService
 {
+
+    private static final String MULTIPLE_FILES_UPLOAD_DIR = "upload";
+
     private final IEncapsulatedOpenBISService openBisService;
 
     private final Logger operationLog;
@@ -258,8 +261,8 @@ public class PutDataSetService
             }
         }
     }
-    
-    public String putDataSet(String sessionToken, NewDataSetDTO newDataSet, String uploadId, String dataSetCode)
+
+    public List<DataSetInformation> putDataSet(String sessionToken, NewDataSetDTO newDataSet, String uploadId)
             throws IOExceptionUnchecked, IllegalArgumentException
     {
         if (false == isInitialized)
@@ -267,101 +270,111 @@ public class PutDataSetService
             doInitialization();
         }
 
-        try
+        String dataSetTypeOrNull = newDataSet.tryDataSetType();
+        ITopLevelDataSetRegistrator registrator = registratorMap.getRegistratorForType(dataSetTypeOrNull);
+
+        File uploadDir = new File(getTemporaryIncomingRoot(dataSetTypeOrNull), uploadId);
+        File multipleFilesUploadDir = new File(uploadDir, MULTIPLE_FILES_UPLOAD_DIR);
+        File[] uploadedFiles = multipleFilesUploadDir.listFiles();
+        File dataSet = null;
+
+        if (uploadedFiles != null && uploadedFiles.length == 1)
         {
-            String dataSetTypeOrNull = newDataSet.tryDataSetType();
-            ITopLevelDataSetRegistrator registrator =
-                    registratorMap.getRegistratorForType(dataSetTypeOrNull);
+            dataSet = uploadedFiles[0];
+        } else
+        {
+            dataSet = multipleFilesUploadDir;
+        }
 
-            File uploadedDataSetDir =
-                    new File(getTemporaryIncomingRoot(dataSetTypeOrNull), uploadId);
+        if (registrator instanceof PutDataSetServerPluginHolder)
+        {
+            return new PutDataSetExecutor(this, ((PutDataSetServerPluginHolder) registrator).getPlugin(), sessionToken, newDataSet, uploadDir,
+                    dataSet).executeWithoutWriting();
+        } else
+        {
+            return new PutDataSetTopLevelDataSetHandler(this, registrator, sessionToken, newDataSet, uploadDir, dataSet).executeWithoutWriting();
+        }
+    }
 
-            File temporaryDataSetDir =
-                    new File(getTemporaryIncomingRoot(dataSetTypeOrNull), dataSetCode);
-            
-            if(false == uploadedDataSetDir.renameTo(temporaryDataSetDir)) {
-                throw new ConfigurationFailureException("Could not rename : "
-                        + uploadedDataSetDir + " to " + temporaryDataSetDir);
+    public void putFileToStoreShare(String sessionToken, String filePath, String dataSetType, String uploadId, InputStream inputStream)
+    {
+        if (false == isInitialized)
+        {
+            doInitialization();
+        }
 
-            }
+        File file = null;
+        OutputStream outputStream = null;
 
-            final List<DataSetInformation> infos;
-            // Branch -- use the old logic for the ETLServerPlugins
-            if (registrator instanceof PutDataSetServerPluginHolder)
+        try
+        {
+            if (StringUtils.isBlank(sessionToken))
             {
-                infos =
-                        new PutDataSetExecutor(this,
-                                ((PutDataSetServerPluginHolder) registrator).getPlugin(),
-                                sessionToken, newDataSet, temporaryDataSetDir).executeWithoutWriting();
-            } else
+                throw new UserFailureException("Session token cannot be null or empty");
+            }
+            if (StringUtils.isBlank(filePath))
             {
-                infos =
-                        new PutDataSetTopLevelDataSetHandler(this, registrator, sessionToken,
-                                newDataSet, temporaryDataSetDir).executeWithoutWriting();
+                throw new UserFailureException("File path cannot be null or empty");
             }
-            StringBuilder sb = new StringBuilder();
-            for (DataSetInformation info : infos)
+            if (filePath.contains("../"))
             {
-                sb.append(info.getDataSetCode());
-                sb.append(",");
+                throw new UserFailureException("File path must not contain '../'");
+            }
+            if (StringUtils.isBlank(dataSetType))
+            {
+                throw new UserFailureException("Data set type cannot be null or empty");
+            }
+            if (StringUtils.isBlank(uploadId))
+            {
+                throw new UserFailureException("Upload id cannot be null or empty");
+            }
+            if (uploadId.contains("/"))
+            {
+                throw new UserFailureException("Upload id must not contain '/'");
+            }
+            if (inputStream == null)
+            {
+                throw new UserFailureException("Input stream cannot be null");
             }
 
-            // Remove the trailing comma
-            if (sb.length() > 0)
+            ServiceProvider.getOpenBISService().checkSession(sessionToken);
+
+            File uploadDir = new File(getTemporaryIncomingRoot(dataSetType), uploadId);
+            if (false == uploadDir.exists())
             {
-                sb.deleteCharAt(sb.length() - 1);
+                uploadDir.mkdir();
             }
-            return sb.toString();
-        } catch (UserFailureException e)
-        {
-            throw new IllegalArgumentException(e);
-        } 
-    }
-    public String putFileToStoreShare(String sessionToken, String filePath, String dataSetTypeCodeOrNull, String uploadId, InputStream inputStream) {
-        {
-            if (false == isInitialized)
+
+            File uploadSubDir = new File(uploadDir, MULTIPLE_FILES_UPLOAD_DIR);
+            if (false == uploadSubDir.exists())
             {
-                doInitialization();
+                uploadSubDir.mkdir();
             }
 
+            File filePathDir = new File(uploadSubDir, FilenameUtils.getPath(filePath));
+            filePathDir.mkdirs();
+
+            file = new File(filePathDir, FilenameUtils.getName(filePath));
+            outputStream = new FileOutputStream(file);
+
+            IOUtils.copyLarge(inputStream, outputStream);
+
+        } catch (IOException ioe)
+        {
+            IOUtils.closeQuietly(outputStream);
+
             try
             {
-                ServiceProvider.getOpenBISService().checkSession(sessionToken);
-                if (filePath.contains("../"))
-                {
-                    throw new IOExceptionUnchecked("filePath must not contain '../'");
-                }
-                String uniqueFolderName = uploadId;
-                File temporaryDataSetDir =
-                        new File(getTemporaryIncomingRoot(dataSetTypeCodeOrNull), uniqueFolderName);
-                if(false == temporaryDataSetDir.exists()) {
-                    temporaryDataSetDir.mkdir();
-                }
-                final String subDir = FilenameUtils.getFullPath(filePath);
-                final String filename = FilenameUtils.getName(filePath);
-                final File dir = new File(temporaryDataSetDir, subDir);
-                dir.mkdirs();
-                final File file = new File(dir, filename);
-                OutputStream ostream = null;
-                try
-                {
-                    ostream = new FileOutputStream(file);
-                    long size = IOUtils.copyLarge(inputStream, ostream);
-                    ostream.close();
-                    return uniqueFolderName;
-                } catch (IOException ex)
-                {
-                    file.delete();
-                    throw CheckedExceptionTunnel.wrapIfNecessary(ex);
-                } finally
-                {
-                    IOUtils.closeQuietly(ostream);
-                }
-            }
-            catch (UserFailureException e)
+                file.delete();
+            } catch (Exception e)
             {
-                throw new IllegalArgumentException(e);
-            } 
+            }
+
+            throw CheckedExceptionTunnel.wrapIfNecessary(ioe);
+
+        } finally
+        {
+            IOUtils.closeQuietly(outputStream);
         }
     }
 
@@ -491,28 +504,7 @@ public class PutDataSetService
         return temporaryIncomingDir;
     }
 
-    private File getTemporaryIncomingRoot(String dataSetTypeCodeOrNull)
-    {
-        TopLevelDataSetRegistratorGlobalState globalState =
-                getThreadGlobalState(dataSetTypeCodeOrNull);
-        File storeRoot = globalState.getStoreRootDir();
-        if (false == StringUtils.isBlank(globalState.getShareId()))
-        {
-            File shareRoot = new File(storeRoot, globalState.getShareId());
-            if (shareRoot.isDirectory())
-            {
-                File incomingDir = new File(shareRoot, "rpc-incoming");
-                incomingDir.mkdir();
-                if (incomingDir.isDirectory())
-                {
-                    return incomingDir;
-                }
-            }
-        }
-        return storeRoot;
-    }
-
-    public File getTemporaryIncomingDir(String dataSetTypeCodeOrNull, String creationId)
+    public File getTemporaryIncomingRoot(String dataSetTypeCodeOrNull)
     {
         if (false == isInitialized)
         {
@@ -528,15 +520,18 @@ public class PutDataSetService
             if (shareRoot.isDirectory())
             {
                 File incomingDir = new File(shareRoot, "rpc-incoming");
-                File tempDir = new File(incomingDir, creationId);
-                if(false == tempDir.exists()) {
-                    throw new UserFailureException("The folder for the creation Id " + creationId + " could not be found. Have you uploaded the file first?" );
+                incomingDir.mkdir();
+                if (incomingDir.isDirectory())
+                {
+                    return incomingDir;
                 }
             }
         }
         return storeRoot;
     }
+
 }
+
 /**
  * Helper class to simplify initializing the final fields of the {@link PutDataSetService}.
  * 
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetTopLevelDataSetHandler.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetTopLevelDataSetHandler.java
index abc85f9fb6e4117f3b628b2b39a1652661c390ef..8722cbba53ddb2d616154f5e6ca8609e369b609d 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetTopLevelDataSetHandler.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetTopLevelDataSetHandler.java
@@ -98,39 +98,16 @@ class PutDataSetTopLevelDataSetHandler
     private final File dataSet;
 
     PutDataSetTopLevelDataSetHandler(PutDataSetService service,
-            ITopLevelDataSetRegistrator registrator, String sessionToken, NewDataSetDTO newDataSet, File temporaryIncomingDir)
+            ITopLevelDataSetRegistrator registrator, String sessionToken, NewDataSetDTO newDataSet, File temporaryIncomingDir, File dataSet)
     {
         this.service = service;
         this.registrator = registrator;
         this.sessionToken = sessionToken;
         this.newDataSet = newDataSet;
         this.temporaryIncomingDir = temporaryIncomingDir;
+        this.dataSet = dataSet;
         this.inputStream = null;
-        String dataSetFolderName = newDataSet.getDataSetFolderName();
-        boolean dataSetIsASingleFile =
-                NewDataSetDTO.DEFAULT_DATA_SET_FOLDER_NAME.equals(dataSetFolderName)
-                        && newDataSet.getFileInfos().size() == 1;
-        if (dataSetIsASingleFile)
-        {
-            dataSetDir = temporaryIncomingDir;
-            dataSet =
-                    new File(temporaryIncomingDir, newDataSet.getFileInfos().get(0)
-                            .getPathInDataSet());
-        } else
-        {
-            this.dataSetDir = new File(temporaryIncomingDir, dataSetFolderName);
-            dataSet = dataSetDir;
-        }
-        if (dataSetDir.exists())
-        {
-            deleteDataSetDir();
-        }
-        if (false == this.dataSetDir.mkdir())
-        {
-            throw new EnvironmentFailureException("Could not create directory for data set "
-                    + dataSet.getName());
-        }
-
+        this.dataSetDir = null;
     }
 
     PutDataSetTopLevelDataSetHandler(PutDataSetService service,
diff --git a/datastore_server/sourceTest/java/ch/ethz/sis/openbis/generic/dss/systemtest/api/v3/CreateUploadedDataSetsTest.java b/datastore_server/sourceTest/java/ch/ethz/sis/openbis/generic/dss/systemtest/api/v3/CreateUploadedDataSetsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..53f8ef2a2d9bc8f5479cfb95e161680c7994f55b
--- /dev/null
+++ b/datastore_server/sourceTest/java/ch/ethz/sis/openbis/generic/dss/systemtest/api/v3/CreateUploadedDataSetsTest.java
@@ -0,0 +1,916 @@
+/*
+ * Copyright 2017 ETH Zuerich, SIS
+ *
+ * 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.ethz.sis.openbis.generic.dss.systemtest.api.v3;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.util.MultiPartContentProvider;
+import org.eclipse.jetty.client.util.StringContentProvider;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpStatus;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSet;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.fetchoptions.DataSetFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.DataSetPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.IDataSetId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.id.EntityTypePermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.ExperimentIdentifier;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.ExperimentPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.SampleIdentifier;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.SamplePermId;
+import ch.ethz.sis.openbis.generic.dssapi.v3.dto.dataset.create.UploadedDataSetCreation;
+import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.DataSetFile;
+import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.download.DataSetFileDownload;
+import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.download.DataSetFileDownloadOptions;
+import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.download.DataSetFileDownloadReader;
+import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.id.DataSetFilePermId;
+import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.id.IDataSetFileId;
+import ch.ethz.sis.openbis.generic.server.dssapi.v3.upload.StoreShareFileUploadServlet;
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.common.http.JettyHttpClientFactory;
+import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider;
+import ch.systemsx.cisd.openbis.generic.shared.util.TestInstanceHostUtils;
+import ch.systemsx.cisd.openbis.systemtest.authorization.ProjectAuthorizationUser;
+
+/**
+ * @author Ganime Betul Akin
+ */
+public class CreateUploadedDataSetsTest extends AbstractFileTest
+{
+    private static final String SERVICE_URL = TestInstanceHostUtils.getDSSUrl() + "/datastore_server/store_share_file_upload";
+
+    private IApplicationServerApi as;
+
+    @Override
+    @BeforeClass
+    protected void beforeClass() throws Exception
+    {
+        super.beforeClass();
+        as = ServiceProvider.getV3ApplicationService();
+    }
+
+    @Test
+    public void testUploadWithInvalidSession() throws Exception
+    {
+        ContentResponse response =
+                uploadFiles("admin-180211214633760xF769DD44CAFFAF7B50FBEADF00DBEE1F", UUID.randomUUID().toString(), "UNKNOWN", true,
+                        new FileToUpload());
+        assertResponseError(response);
+        assertTrue(response.getContentAsString(), response.getContentAsString().contains("user is not logged in"));
+    }
+
+    @Test
+    public void testUploadWithoutSession() throws Exception
+    {
+        ContentResponse response = uploadFiles(null, UUID.randomUUID().toString(), "UNKNOWN", true, new FileToUpload());
+        assertResponseError(response);
+        assertTrue(response.getContentAsString(), response.getContentAsString().contains("Session token cannot be null or empty"));
+    }
+
+    @Test
+    public void testUploadWithoutType() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        ContentResponse response = uploadFiles(sessionToken, UUID.randomUUID().toString(), null, true, new FileToUpload());
+        assertResponseError(response);
+        assertTrue(response.getContentAsString(), response.getContentAsString().contains("Data set type cannot be null or empty"));
+    }
+
+    @Test
+    public void testUploadWithoutUploadId() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        ContentResponse response = uploadFiles(sessionToken, null, "UNKNOWN", true, new FileToUpload());
+        assertResponseError(response);
+        assertTrue(response.getContentAsString(), response.getContentAsString().contains("Upload id cannot be null or empty"));
+    }
+
+    @Test
+    public void testUploadWithUploadIdThatContainsSlash() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        ContentResponse response = uploadFiles(sessionToken, "iam/incorrect", "UNKNOWN", true, new FileToUpload());
+        assertResponseError(response);
+        assertTrue(response.getContentAsString(), response.getContentAsString().contains("Upload id must not contain &apos;/&apos;"));
+    }
+
+    @Test
+    public void testUploadWithoutFile() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        ContentResponse response = uploadFiles(sessionToken, UUID.randomUUID().toString(), "UNKNOWN", true);
+        assertResponseError(response);
+        assertTrue(response.getContentAsString(), response.getContentAsString().contains("Please upload at least one file"));
+    }
+
+    @Test
+    public void testUploadWithFileWithFolderUpPath() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        ContentResponse response =
+                uploadFiles(sessionToken, UUID.randomUUID().toString(), "UNKNOWN", false, new FileToUpload("name", "iam/../incorrect", "content"));
+        assertResponseError(response);
+        assertTrue(response.getContentAsString(), response.getContentAsString().contains("File path must not contain &apos;../&apos;"));
+    }
+
+    @Test
+    public void testUploadWithFileWithoutPath() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        ContentResponse response =
+                uploadFiles(sessionToken, UUID.randomUUID().toString(), "UNKNOWN", false, new FileToUpload("name", null, "content"));
+        assertResponseError(response);
+        assertTrue(response.getContentAsString(), response.getContentAsString().contains("File path cannot be null or empty"));
+    }
+
+    @Test
+    public void testCreateWithInvalidSession() throws Exception
+    {
+        try
+        {
+            getCreateAndGetDataSet("admin-180211214633760xF769DD44CAFFAF7B50FBEADF00DBEE1F", null);
+        } catch (UserFailureException e)
+        {
+            assertTrue(e.getMessage(), e.getMessage().contains("user is not logged in"));
+        }
+    }
+
+    @Test
+    public void testCreateWithoutType() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+        String sampleIdentifier = "/CISD/CP-TEST-1";
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setSampleId(new SampleIdentifier(sampleIdentifier));
+        creation.setUploadId(uploadId);
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        assertResponseOK(response);
+
+        try
+        {
+            getCreateAndGetDataSet(sessionToken, creation);
+            fail();
+        } catch (UserFailureException e)
+        {
+            assertTrue(e.getMessage(), e.getMessage().contains("A dataset needs a type"));
+        }
+    }
+
+    @Test
+    public void testCreateWithTypeNonexistent() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "IDONTEXIST";
+        String sampleIdentifier = "/CISD/CP-TEST-1";
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setSampleId(new SampleIdentifier(sampleIdentifier));
+        creation.setUploadId(uploadId);
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        assertResponseOK(response);
+
+        try
+        {
+            getCreateAndGetDataSet(sessionToken, creation);
+            fail();
+        } catch (UserFailureException e)
+        {
+            assertTrue(e.getMessage(), e.getMessage().contains("Object with EntityTypePermId = [IDONTEXIST, null] has not been found"));
+        }
+    }
+
+    @Test
+    public void testCreateWithoutOwner() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setUploadId(uploadId);
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        assertResponseOK(response);
+
+        try
+        {
+            getCreateAndGetDataSet(sessionToken, creation);
+            fail();
+        } catch (UserFailureException e)
+        {
+            assertTrue(e.getMessage(), e.getMessage().contains("A dataset needs either a sample or an experiment as an owner"));
+        }
+    }
+
+    @Test
+    public void testCreateWithSampleNonexistent() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+        String sampleIdentifier = "/IDONTEXIST/IDONTEXIST";
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setSampleId(new SampleIdentifier(sampleIdentifier));
+        creation.setUploadId(uploadId);
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        assertResponseOK(response);
+
+        try
+        {
+            getCreateAndGetDataSet(sessionToken, creation);
+            fail();
+        } catch (UserFailureException e)
+        {
+            assertTrue(e.getMessage(), e.getMessage().contains("Object with SampleIdentifier = [/IDONTEXIST/IDONTEXIST] has not been found"));
+        }
+    }
+
+    @Test
+    public void testCreateWithSampleIdentifier() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+        String sampleIdentifier = "/CISD/CP-TEST-1";
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setSampleId(new SampleIdentifier(sampleIdentifier));
+        creation.setUploadId(uploadId);
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        assertResponseOK(response);
+
+        DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
+
+        assertEquals(dataSetType, dataSet.getType().getCode());
+        assertEquals(sampleIdentifier, dataSet.getSample().getIdentifier().getIdentifier());
+    }
+
+    @Test
+    public void testCreateWithSamplePermId() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+        String samplePermId = "200902091219327-1025";
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setSampleId(new SamplePermId(samplePermId));
+        creation.setUploadId(uploadId);
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        assertResponseOK(response);
+
+        DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
+
+        assertEquals(dataSetType, dataSet.getType().getCode());
+        assertEquals(samplePermId, dataSet.getSample().getPermId().getPermId());
+    }
+
+    @Test
+    public void testCreateWithExperimentNonexistent() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+        String experimentIdentifier = "/IDONTEXIST/IDONTEXIST/IDONTEXIST";
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
+        creation.setUploadId(uploadId);
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        assertResponseOK(response);
+
+        try
+        {
+            getCreateAndGetDataSet(sessionToken, creation);
+            fail();
+        } catch (UserFailureException e)
+        {
+            assertTrue(e.getMessage(),
+                    e.getMessage().contains("Object with ExperimentIdentifier = [/IDONTEXIST/IDONTEXIST/IDONTEXIST] has not been found"));
+        }
+    }
+
+    @Test
+    public void testCreateWithExperimentIdentifier() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+        String experimentIdentifier = "/CISD/NEMO/EXP1";
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
+        creation.setUploadId(uploadId);
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        assertResponseOK(response);
+
+        DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
+
+        assertEquals(dataSetType, dataSet.getType().getCode());
+        assertEquals(experimentIdentifier, dataSet.getExperiment().getIdentifier().getIdentifier());
+    }
+
+    @Test
+    public void testCreateWithExperimentPermId() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+        String experimentPermId = "200811050951882-1028";
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setExperimentId(new ExperimentPermId(experimentPermId));
+        creation.setUploadId(uploadId);
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        assertResponseOK(response);
+
+        DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
+
+        assertEquals(dataSetType, dataSet.getType().getCode());
+        assertEquals(experimentPermId, dataSet.getExperiment().getPermId().getPermId());
+    }
+
+    @Test
+    public void testCreateWithProperties() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "HCS_IMAGE";
+        String experimentIdentifier = "/CISD/NEMO/EXP1";
+
+        Map<String, String> properties = new HashMap<String, String>();
+        properties.put("COMMENT", "test comment");
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
+        creation.setUploadId(uploadId);
+        creation.setProperties(properties);
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        assertResponseOK(response);
+
+        DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
+
+        assertEquals(dataSetType, dataSet.getType().getCode());
+        assertEquals(experimentIdentifier, dataSet.getExperiment().getIdentifier().getIdentifier());
+        assertEquals("test comment", dataSet.getProperty("COMMENT"));
+    }
+
+    @Test
+    public void testCreateWithParentIds() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+        String experimentIdentifier = "/CISD/NEMO/EXP1";
+        IDataSetId parentId = new DataSetPermId("20081105092159111-1");
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
+        creation.setParentIds(Arrays.asList(parentId));
+        creation.setUploadId(uploadId);
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        assertResponseOK(response);
+
+        DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
+
+        assertEquals(dataSetType, dataSet.getType().getCode());
+        assertEquals(experimentIdentifier, dataSet.getExperiment().getIdentifier().getIdentifier());
+        assertEquals(1, dataSet.getParents().size());
+        assertEquals(parentId, dataSet.getParents().get(0).getPermId());
+    }
+
+    @Test
+    public void testCreateWithoutUploadId() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String dataSetType = "UNKNOWN";
+        String experimentIdentifier = "/CISD/NEMO/EXP1";
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
+
+        try
+        {
+            getCreateAndGetDataSet(sessionToken, creation);
+            fail();
+        } catch (UserFailureException e)
+        {
+            assertTrue(e.getMessage(), e.getMessage().contains("Upload id cannot be null"));
+        }
+    }
+
+    @Test
+    public void testCreateWithUploadIdThatContainsSlash() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String dataSetType = "UNKNOWN";
+        String experimentIdentifier = "/CISD/NEMO/EXP1";
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
+        creation.setUploadId("iam/incorrect");
+
+        try
+        {
+            getCreateAndGetDataSet(sessionToken, creation);
+            fail();
+        } catch (UserFailureException e)
+        {
+            assertTrue(e.getMessage(), e.getMessage().contains("Upload id must not contain '/'"));
+        }
+    }
+
+    @Test
+    public void testCreateWithoutUpload() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+        String experimentIdentifier = "/CISD/NEMO/EXP1";
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
+        creation.setUploadId(uploadId);
+
+        try
+        {
+            getCreateAndGetDataSet(sessionToken, creation);
+            fail();
+        } catch (UserFailureException e)
+        {
+            assertTrue(e.getMessage(),
+                    e.getMessage()
+                            .contains("The folder for the upload id " + uploadId + " could not be found. Have you uploaded the file(s) first?"));
+        }
+    }
+
+    @Test
+    public void testCreateWithSingleFile() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+        String experimentIdentifier = "/CISD/NEMO/EXP1";
+
+        FileToUpload file = new FileToUpload("file", "test.txt", "test content");
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
+        creation.setUploadId(uploadId);
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, false, file);
+        assertResponseOK(response);
+
+        DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
+
+        assertEquals(dataSetType, dataSet.getType().getCode());
+        assertEquals(experimentIdentifier, dataSet.getExperiment().getIdentifier().getIdentifier());
+
+        List<DownloadedFile> files = downloadFiles(sessionToken, dataSet.getPermId());
+        assertEquals(3, files.size());
+
+        assertDirectory(files.get(0), "");
+        assertDirectory(files.get(1), "original");
+        assertFile(files.get(2), "original/test.txt", file.content);
+    }
+
+    @Test
+    public void testCreateWithMultipleFiles() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+        String experimentIdentifier = "/CISD/NEMO/EXP1";
+
+        FileToUpload file1 = new FileToUpload("file1", "test1.txt", "test1 content");
+        FileToUpload file2 = new FileToUpload("file2", "test2.txt", "test2 content");
+        FileToUpload file3 = new FileToUpload("file3", "folder/test3.txt", "test3 content");
+        FileToUpload file4 = new FileToUpload("file4", "folder/test4.txt", "test4 content");
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
+        creation.setUploadId(uploadId);
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, false, file1, file2, file3, file4);
+        assertResponseOK(response);
+
+        DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
+
+        assertEquals(dataSetType, dataSet.getType().getCode());
+        assertEquals(experimentIdentifier, dataSet.getExperiment().getIdentifier().getIdentifier());
+
+        List<DownloadedFile> files = downloadFiles(sessionToken, dataSet.getPermId());
+        assertEquals(8, files.size());
+
+        assertDirectory(files.get(0), "");
+        assertDirectory(files.get(1), "original");
+        assertDirectory(files.get(2), "original/upload");
+        assertDirectory(files.get(3), "original/upload/folder");
+        assertFile(files.get(4), "original/upload/folder/test3.txt", file3.content);
+        assertFile(files.get(5), "original/upload/folder/test4.txt", file4.content);
+        assertFile(files.get(6), "original/upload/test1.txt", file1.content);
+        assertFile(files.get(7), "original/upload/test2.txt", file2.content);
+    }
+
+    @Test
+    public void testCreateWithIgnoreFilePathDefault() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+        String experimentIdentifier = "/CISD/NEMO/EXP1";
+
+        FileToUpload file1 = new FileToUpload("file1", "test1.txt", "test1 content");
+        FileToUpload file2 = new FileToUpload("file2", "folder/test2.txt", "test2 content");
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
+        creation.setUploadId(uploadId);
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, null, file1, file2);
+        assertResponseOK(response);
+
+        DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
+
+        assertEquals(dataSetType, dataSet.getType().getCode());
+        assertEquals(experimentIdentifier, dataSet.getExperiment().getIdentifier().getIdentifier());
+
+        List<DownloadedFile> files = downloadFiles(sessionToken, dataSet.getPermId());
+        assertEquals(5, files.size());
+
+        assertDirectory(files.get(0), "");
+        assertDirectory(files.get(1), "original");
+        assertDirectory(files.get(2), "original/upload");
+        assertFile(files.get(3), "original/upload/test1.txt", file1.content);
+        assertFile(files.get(4), "original/upload/test2.txt", file2.content);
+    }
+
+    @Test
+    public void testCreateWithIgnoreFilePathSetToFalse() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+        String experimentIdentifier = "/CISD/NEMO/EXP1";
+
+        FileToUpload file1 = new FileToUpload("file1", "test1.txt", "test1 content");
+        FileToUpload file2 = new FileToUpload("file2", "folder/test2.txt", "test2 content");
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
+        creation.setUploadId(uploadId);
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, false, file1, file2);
+        assertResponseOK(response);
+
+        DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
+
+        assertEquals(dataSetType, dataSet.getType().getCode());
+        assertEquals(experimentIdentifier, dataSet.getExperiment().getIdentifier().getIdentifier());
+
+        List<DownloadedFile> files = downloadFiles(sessionToken, dataSet.getPermId());
+        assertEquals(6, files.size());
+
+        assertDirectory(files.get(0), "");
+        assertDirectory(files.get(1), "original");
+        assertDirectory(files.get(2), "original/upload");
+        assertDirectory(files.get(3), "original/upload/folder");
+        assertFile(files.get(4), "original/upload/folder/test2.txt", file2.content);
+        assertFile(files.get(5), "original/upload/test1.txt", file1.content);
+    }
+
+    @Test
+    public void testCreateWithMultipleAttempts() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+        String experimentIdentifier = "/CISD/NEMO/EXP1";
+
+        Map<String, String> properties = new HashMap<String, String>();
+        properties.put("IDONTEXIST", "test value");
+
+        FileToUpload file = new FileToUpload("file", "test.txt", "test content");
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
+        creation.setProperties(properties);
+        creation.setUploadId(uploadId);
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, file);
+        assertResponseOK(response);
+
+        // first attempt
+        try
+        {
+            getCreateAndGetDataSet(sessionToken, creation);
+            fail("Creation should have failed because of nonexistent 'IDONTEXIST' property");
+        } catch (Exception e)
+        {
+            String fullStackTrace = ExceptionUtils.getFullStackTrace(e);
+            assertTrue(fullStackTrace, fullStackTrace.contains("Property type with code 'IDONTEXIST' does not exist!"));
+        }
+
+        // second attempt
+        try
+        {
+            getCreateAndGetDataSet(sessionToken, creation);
+            fail("Creation should have failed as uploaded file should have been removed during the first attempt");
+        } catch (Exception e)
+        {
+            String fullStackTrace = ExceptionUtils.getFullStackTrace(e);
+            assertTrue(fullStackTrace, fullStackTrace.contains("The folder for the upload id " + uploadId + " could not be found"));
+        }
+    }
+
+    @Test(dataProviderClass = ProjectAuthorizationUser.class, dataProvider = ProjectAuthorizationUser.PROVIDER)
+    public void testCreateWithProjectAuthorization(ProjectAuthorizationUser user) throws Exception
+    {
+        String sessionToken = as.login(user.getUserId(), PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+        String experimentIdentifier = "/TEST-SPACE/TEST-PROJECT/EXP-SPACE-TEST";
+
+        FileToUpload file = new FileToUpload("file", "test.txt", "test content");
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
+        creation.setUploadId(uploadId);
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, file);
+        assertResponseOK(response);
+
+        if (user.isDisabledProjectUser())
+        {
+            try
+            {
+                getCreateAndGetDataSet(sessionToken, creation);
+            } catch (UserFailureException e)
+            {
+                assertTrue(e.getMessage(), e.getMessage().contains(
+                        "None of method roles '[PROJECT_OBSERVER, PROJECT_USER, PROJECT_POWER_USER, PROJECT_ADMIN, SPACE_ADMIN, INSTANCE_ADMIN, SPACE_POWER_USER, SPACE_USER, SPACE_OBSERVER, INSTANCE_OBSERVER, SPACE_ETL_SERVER, INSTANCE_ETL_SERVER]' could be found in roles of user"));
+            }
+        } else if (user.isInstanceUserOrTestSpaceUserOrEnabledTestProjectUser())
+        {
+            DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
+
+            assertEquals(dataSetType, dataSet.getType().getCode());
+            assertEquals(experimentIdentifier, dataSet.getExperiment().getIdentifier().getIdentifier());
+
+            List<DownloadedFile> files = downloadFiles(sessionToken, dataSet.getPermId());
+            assertEquals(3, files.size());
+
+            assertDirectory(files.get(0), "");
+            assertDirectory(files.get(1), "original");
+            assertFile(files.get(2), "original/test.txt", file.content);
+        } else
+        {
+            try
+            {
+                getCreateAndGetDataSet(sessionToken, creation);
+            } catch (UserFailureException e)
+            {
+                assertTrue(e.getMessage(),
+                        e.getMessage().contains("Object with ExperimentIdentifier = [/TEST-SPACE/TEST-PROJECT/EXP-SPACE-TEST] has not been found"));
+            }
+        }
+    }
+
+    private ContentResponse uploadFiles(String sessionToken, String uploadId, String dataSetType, Boolean ignoreFilePath,
+            FileToUpload... filesToUpload)
+            throws InterruptedException, TimeoutException, ExecutionException
+    {
+        HttpClient client = JettyHttpClientFactory.getHttpClient();
+        MultiPartContentProvider multiPart = new MultiPartContentProvider();
+
+        for (FileToUpload fileToUpload : filesToUpload)
+        {
+            multiPart.addFilePart(fileToUpload.fieldName, fileToUpload.fileName, new StringContentProvider(fileToUpload.content), null);
+        }
+
+        multiPart.close();
+
+        Request request = client.newRequest(SERVICE_URL).method(HttpMethod.POST);
+
+        if (sessionToken != null)
+        {
+            request.param(StoreShareFileUploadServlet.SESSION_ID_PARAM, sessionToken);
+        }
+        if (uploadId != null)
+        {
+            request.param(StoreShareFileUploadServlet.UPLOAD_ID_PARAM, uploadId);
+        }
+        if (ignoreFilePath != null)
+        {
+            request.param(StoreShareFileUploadServlet.IGNORE_FILE_PATH_PARAM, String.valueOf(ignoreFilePath));
+        }
+        if (dataSetType != null)
+        {
+            request.param(StoreShareFileUploadServlet.DATA_SET_TYPE_PARAM, dataSetType);
+        }
+        request.content(multiPart);
+        return request.send();
+    }
+
+    private DataSet getCreateAndGetDataSet(String sessionToken, UploadedDataSetCreation creation)
+    {
+        DataSetPermId permId = dss.createUploadedDataSet(sessionToken, creation);
+        assertNotNull(permId);
+
+        DataSetFetchOptions fo = new DataSetFetchOptions();
+        fo.withType();
+        fo.withExperiment();
+        fo.withSample();
+        fo.withProperties();
+        fo.withParents();
+
+        Map<IDataSetId, DataSet> dataSets = as.getDataSets(sessionToken, Arrays.asList(permId), fo);
+        DataSet dataSet = dataSets.get(permId);
+        assertNotNull(dataSet);
+
+        return dataSet;
+    }
+
+    private List<DownloadedFile> downloadFiles(String sessionToken, IDataSetId dataSetId) throws IOException
+    {
+        DataSetFileDownloadOptions options = new DataSetFileDownloadOptions();
+        options.setRecursive(true);
+
+        IDataSetFileId fileId = new DataSetFilePermId(dataSetId, "");
+
+        InputStream stream = dss.downloadFiles(sessionToken, Arrays.asList(fileId), options);
+        DataSetFileDownloadReader reader = new DataSetFileDownloadReader(stream);
+
+        List<DownloadedFile> files = new ArrayList<DownloadedFile>();
+        DataSetFileDownload download = null;
+
+        while ((download = reader.read()) != null)
+        {
+            DownloadedFile file = new DownloadedFile(download.getDataSetFile(), IOUtils.toString(download.getInputStream()));
+            files.add(file);
+        }
+
+        return files;
+    }
+
+    private void assertDirectory(DownloadedFile actual, String expectedPath)
+    {
+        assertEquals(expectedPath, actual.file.getPath());
+        assertEquals(true, actual.file.isDirectory());
+        assertEquals(0, actual.file.getFileLength());
+        assertEquals("", actual.content);
+    }
+
+    private void assertFile(DownloadedFile actual, String expectedPath, String expectedFileContent)
+    {
+        assertEquals(expectedPath, actual.file.getPath());
+        assertEquals(false, actual.file.isDirectory());
+        assertEquals(expectedFileContent.length(), actual.file.getFileLength());
+        assertEquals(expectedFileContent, actual.content);
+    }
+
+    private void assertResponseOK(ContentResponse response)
+    {
+        int statusCode = response.getStatus();
+        if (statusCode != HttpStatus.Code.OK.getCode())
+        {
+            throw new RuntimeException("Status Code was " + statusCode + " instead of " + HttpStatus.Code.OK.getCode());
+        }
+    }
+
+    private void assertResponseError(ContentResponse response)
+    {
+        int statusCode = response.getStatus();
+        if (statusCode != HttpStatus.Code.INTERNAL_SERVER_ERROR.getCode())
+        {
+            throw new RuntimeException("Status Code was " + statusCode + " instead of " + HttpStatus.Code.INTERNAL_SERVER_ERROR.getCode());
+        }
+    }
+
+    private class FileToUpload
+    {
+        private String fieldName;
+
+        private String fileName;
+
+        private String content;
+
+        public FileToUpload()
+        {
+            this("testFieldName", "testFileName", "testContent");
+        }
+
+        public FileToUpload(String fieldName, String fileName, String content)
+        {
+            this.fieldName = fieldName;
+            this.fileName = fileName;
+            this.content = content;
+        }
+    }
+
+    private class DownloadedFile
+    {
+        private DataSetFile file;
+
+        private String content;
+
+        public DownloadedFile(DataSetFile file, String content)
+        {
+            this.file = file;
+            this.content = content;
+        }
+
+    }
+
+}
diff --git a/datastore_server/sourceTest/java/ch/ethz/sis/openbis/generic/dss/systemtest/api/v3/UploadAndCreateDataSetsTest.java b/datastore_server/sourceTest/java/ch/ethz/sis/openbis/generic/dss/systemtest/api/v3/UploadAndCreateDataSetsTest.java
deleted file mode 100644
index 6cce8c6c82eeee7180f860e56b048402cb2f7fc8..0000000000000000000000000000000000000000
--- a/datastore_server/sourceTest/java/ch/ethz/sis/openbis/generic/dss/systemtest/api/v3/UploadAndCreateDataSetsTest.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright 2017 ETH Zuerich, SIS
- *
- * 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.ethz.sis.openbis.generic.dss.systemtest.api.v3;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.net.MalformedURLException;
-import java.util.Arrays;
-import java.util.List;
-import java.util.UUID;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
-
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.api.ContentProvider;
-import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.client.api.Request;
-import org.eclipse.jetty.client.util.StringContentProvider;
-import org.eclipse.jetty.http.HttpStatus;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.CreationId;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSetKind;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.DataSetCreation;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.PhysicalDataCreation;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.DataSetPermId;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.FileFormatTypePermId;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.ProprietaryStorageFormatPermId;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.RelativeLocationLocatorTypePermId;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.datastore.id.DataStorePermId;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.id.EntityTypePermId;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.ExperimentIdentifier;
-import ch.ethz.sis.openbis.generic.dssapi.v3.dto.dataset.create.FullDataSetCreation;
-import ch.ethz.sis.openbis.generic.server.dssapi.v3.upload.StoreShareFileUploadServlet;
-import ch.systemsx.cisd.common.filesystem.FileUtilities;
-import ch.systemsx.cisd.common.http.JettyHttpClientFactory;
-import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider;
-import ch.systemsx.cisd.openbis.generic.shared.util.TestInstanceHostUtils;
-
-/**
- * @author Ganime Betul Akin
- */
-public class UploadAndCreateDataSetsTest extends AbstractFileTest
-{
-    private static final String SERVICE_URL = TestInstanceHostUtils.getDSSUrl() + "/datastore_server/"
-            + "store_share_file_upload";
-
-    protected StoreShareFileUploadServlet servlet;
-
-    protected String sessionToken;
-
-    protected static final String UNIT_TEST_WORKING_DIRECTORY = "unit-test-wd";
-
-    protected static final String TARGETS_DIRECTORY = "targets";
-
-    protected static final File UNIT_TEST_ROOT_DIRECTORY = new File(TARGETS_DIRECTORY
-            + File.separator + UNIT_TEST_WORKING_DIRECTORY);
-
-    /**
-     * Create a dummy file of size <code>length</code> bytes.
-     */
-    private File createDummyFile(File dir, String name, int length) throws IOException
-    {
-        File dummyFile = new File(dir, name);
-        dummyFile.createNewFile();
-        PrintWriter out = new PrintWriter(dummyFile);
-        for (int i = 0; i < length; ++i)
-        {
-            out.append('a');
-        }
-        out.flush();
-        out.close();
-
-        return dummyFile;
-    }
-
-    @Test
-    public void testFileUpload()
-    {
-        HttpClient client = JettyHttpClientFactory.getHttpClient();
-        Request requestEntity = client.newRequest(SERVICE_URL).method("POST");
-
-        String sessionToken = gis.tryToAuthenticateForAllServices(TEST_USER, PASSWORD);
-
-        String uploadId = "1357";
-        requestEntity.param("uploadID", uploadId);
-        String dataSetType = "UNKNOWN";
-        requestEntity.param("dataSetType", dataSetType);
-        requestEntity.param("sessionID", sessionToken);
-
-        final String CRLF = "\r\n";
-        final String BOUNDARY = "MMMMM___MP_BOUNDARY___MMMMM";
-        final String FILE_PART_NAME = "fastaFile";
-
-        File fileToUpload;
-        try
-        {
-            fileToUpload = createDummyFile(workingDirectory, "to-upload.txt", 80);
-            String fileContent = FileUtilities.loadToString(fileToUpload);
-            ContentProvider content = new StringContentProvider("--" + BOUNDARY + CRLF
-                    + "Content-Disposition: form-data; name=\"" + FILE_PART_NAME + "\"; filename=\""
-                    + fileToUpload.getName() + "\"" + CRLF
-                    + "Content-Type: application/octet-stream" + CRLF + CRLF
-                    + fileContent + CRLF + "--" + BOUNDARY + "--" + CRLF);
-            requestEntity.content(content, "multipart/form-data; boundary=" + BOUNDARY);
-
-            ContentResponse contentResponse;
-            contentResponse = requestEntity.send();
-            int statusCode = contentResponse.getStatus();
-
-            if (statusCode != HttpStatus.Code.OK.getCode())
-            {
-                throw new RuntimeException("Status Code was " + statusCode + " instead of " + HttpStatus.Code.OK.getCode());
-            }
-
-            DataSetCreation metadataCreation = new DataSetCreation();
-            metadataCreation.setTypeId(new EntityTypePermId(dataSetType));
-            metadataCreation.setDataSetKind(DataSetKind.PHYSICAL);;
-            metadataCreation.setAutoGeneratedCode(true);
-            metadataCreation.setExperimentId(new ExperimentIdentifier("/CISD/NEMO/EXP1"));
-            metadataCreation.setDataStoreId(new DataStorePermId("STANDARD"));
-            metadataCreation.setCreationId(new CreationId(uploadId));
-
-            String code = UUID.randomUUID().toString();
-            PhysicalDataCreation physicalCreation = new PhysicalDataCreation();
-            physicalCreation.setLocation("test/location/" + code);
-            physicalCreation.setFileFormatTypeId(new FileFormatTypePermId("TIFF"));
-            physicalCreation.setLocatorTypeId(new RelativeLocationLocatorTypePermId());
-            physicalCreation.setStorageFormatId(new ProprietaryStorageFormatPermId());
-
-            metadataCreation.setPhysicalData(physicalCreation);
-            FullDataSetCreation newDataSet = new FullDataSetCreation();
-
-            newDataSet.setMetadataCreation(metadataCreation);
-            List<DataSetPermId> createDataSets = dss.createDataSets(sessionToken, Arrays.asList(newDataSet));
-        } catch (InterruptedException | TimeoutException | ExecutionException | IOException e)
-        {
-            e.printStackTrace();
-        }
-    }
-
-    @BeforeMethod
-    public void beforeMethod() throws MalformedURLException
-    {
-    }
-}
diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/common.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/common.js
index dfb0e81c8e69168b0bd20b052a08a13b28cc1e10..3cba9b37164c7bfad7dccf506ed4a320037c1b6c 100644
--- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/common.js
+++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/common.js
@@ -39,6 +39,7 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 		this.SemanticAnnotationCreation = dtos.SemanticAnnotationCreation;
 		this.DataSetCreation = dtos.DataSetCreation;
 		this.FullDataSetCreation = dtos.FullDataSetCreation;
+		this.UploadedDataSetCreation = dtos.UploadedDataSetCreation;
 		this.DataSetFileCreation = dtos.DataSetFileCreation;
 		this.LinkedDataCreation = dtos.LinkedDataCreation;
 		this.ContentCopyCreation = dtos.ContentCopyCreation;
@@ -163,6 +164,7 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 		this.DataSetTypeCreation = dtos.DataSetTypeCreation;
 		this.MaterialTypeCreation = dtos.MaterialTypeCreation;
 		this.PropertyTypeCreation = dtos.PropertyTypeCreation;
+		this.PropertyTypeUpdate = dtos.PropertyTypeUpdate;
 		this.WebAppSettings = dtos.WebAppSettings;
 
 		// operations
@@ -214,6 +216,7 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 		this.UpdateDataSetTypesOperation = dtos.UpdateDataSetTypesOperation;
 		this.UpdateMaterialsOperation = dtos.UpdateMaterialsOperation;
 		this.UpdateMaterialTypesOperation = dtos.UpdateMaterialTypesOperation;
+		this.UpdatePropertyTypesOperation = dtos.UpdatePropertyTypesOperation;
 		this.UpdateVocabulariesOperation = dtos.UpdateVocabulariesOperation;
 		this.UpdateVocabularyTermsOperation = dtos.UpdateVocabularyTermsOperation;
 		this.UpdateExternalDmsOperation = dtos.UpdateExternalDmsOperation;
@@ -294,7 +297,7 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 		this.getDtos = function() {
 			return dtos;
 		}
-		
+
 		this.getId = function(entity) {
 			if (typeof entity["getPermId"] === 'function') {
 				return entity.getPermId();
@@ -389,6 +392,33 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 			});
 		}.bind(this);
 
+		this.waitUntilIndexed = function(facade, dataSetCode, timeout) {
+			var c = this;
+			var dfd = $.Deferred();
+			var start = new Date().getTime();
+
+			var searchAndWait = function() {
+				var criteria = new c.DataSetSearchCriteria();
+				criteria.withPermId().thatEquals(dataSetCode);
+
+				facade.searchDataSets(criteria, c.createDataSetFetchOptions()).then(function(result) {
+					if (result.getTotalCount() == 0) {
+						var now = new Date().getTime();
+						if (now - start > timeout) {
+							c.fail("Data set " + dataSetCode + " not indexed after " + timeout + " msec.");
+							dfd.reject();
+						} else {
+							setTimeout(searchAndWait, 1000);
+						}
+					} else {
+						dfd.resolve();
+					}
+				});
+			};
+			searchAndWait();
+			return dfd.promise();
+		}.bind(this);
+
 		this.getResponseFromJSTestAggregationService = function(facade, params, callback) {
 			var c = this;
 			return $.ajax({
@@ -454,7 +484,7 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 				return permIds[0];
 			});
 		}.bind(this);
-		
+
 		this.createExternalDms = function(facade) {
 			var c = this;
 			var creation = new dtos.ExternalDmsCreation();
@@ -491,12 +521,12 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 			var c = this;
 			var creation = new dtos.AuthorizationGroupCreation();
 			creation.setCode(c.generateId("AUTHORIZATION_GROUP"));
-			creation.setUserIds([new c.PersonPermId("power_user")]);
+			creation.setUserIds([ new c.PersonPermId("power_user") ]);
 			return facade.createAuthorizationGroups([ creation ]).then(function(permIds) {
 				return permIds[0];
 			});
 		}.bind(this);
-		
+
 		this.createRoleAssignment = function(facade, isUser) {
 			var c = this;
 			return c.createSpace(facade).then(function(spaceId) {
@@ -513,7 +543,7 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 				});
 			});
 		}.bind(this);
-		
+
 		this.createPerson = function(facade) {
 			var c = this;
 			var creation = new dtos.PersonCreation();
@@ -522,7 +552,7 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 				return permIds[0];
 			});
 		}.bind(this);
-		
+
 		this.createSemanticAnnotation = function(facade) {
 			var c = this;
 			var creation = new dtos.SemanticAnnotationCreation();
@@ -662,14 +692,14 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 				return groups[id];
 			});
 		}.bind(this);
-		
+
 		this.findRoleAssignment = function(facade, id) {
 			var c = this;
 			return facade.getRoleAssignments([ id ], c.createRoleAssignmentFetchOptions()).then(function(assignments) {
 				return assignments[id];
 			});
 		}.bind(this);
-		
+
 		this.findPerson = function(facade, id) {
 			var c = this;
 			return facade.getPersons([ id ], c.createPersonFetchOptions()).then(function(persons) {
@@ -767,7 +797,7 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 			options.setReason("test reason");
 			return facade.deleteVocabularyTerms([ id ], options);
 		}.bind(this);
-		
+
 		this.replaceVocabularyTerm = function(facade, id) {
 			var c = this;
 			var options = new dtos.VocabularyTermDeletionOptions();
@@ -789,14 +819,14 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 			options.setReason("test reason");
 			return facade.deleteAuthorizationGroups([ id ], options);
 		}.bind(this);
-		
+
 		this.deleteRoleAssignment = function(facade, id) {
 			var c = this;
 			var options = new dtos.RoleAssignmentDeletionOptions();
 			options.setReason("test reason");
 			return facade.deleteRoleAssignments([ id ], options);
 		}.bind(this);
-		
+
 		this.deleteOperationExecution = function(facade, id) {
 			var c = this;
 			var options = new dtos.OperationExecutionDeletionOptions();
@@ -1009,7 +1039,7 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 			rafo.withProject().withSpace();
 			return fo;
 		};
-		
+
 		this.createRoleAssignmentFetchOptions = function() {
 			var fo = new dtos.RoleAssignmentFetchOptions();
 			fo.withProject();
@@ -1019,7 +1049,7 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 			fo.withRegistrator();
 			return fo;
 		};
-		
+
 		this.createPersonFetchOptions = function() {
 			var fo = new dtos.PersonFetchOptions();
 			fo.withSpace();
@@ -1027,7 +1057,7 @@ define([ 'jquery', 'openbis', 'underscore', 'test/dtos' ], function($, defaultOp
 			fo.withRegistrator();
 			return fo;
 		};
-		
+
 		this.createPropertyTypeFetchOptions = function() {
 			var fo = new dtos.PropertyTypeFetchOptions();
 			fo.withVocabulary();
diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/dtos.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/dtos.js
index 808675c41aba52a2a539becd4b13a2a1d361619a..9ff3f5104471536f23e7ece872a9b0a7130c288d 100644
--- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/dtos.js
+++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/dtos.js
@@ -444,6 +444,9 @@ var sources = [
 	'as/dto/property/search/SearchPropertyTypesOperationResult',
 	'as/dto/property/search/SearchPropertyAssignmentsOperation',
 	'as/dto/property/search/SearchPropertyAssignmentsOperationResult',
+	'as/dto/property/update/PropertyTypeUpdate',
+	'as/dto/property/update/UpdatePropertyTypesOperation',
+	'as/dto/property/update/UpdatePropertyTypesOperationResult',
 	'as/dto/property/DataType',
 	'as/dto/property/PropertyAssignment',
 	'as/dto/property/PropertyType',
@@ -656,6 +659,7 @@ var sources = [
 	'as/dto/operation/SynchronousOperationExecutionResults',
 	
 	'dss/dto/dataset/create/FullDataSetCreation',
+	'dss/dto/dataset/create/UploadedDataSetCreation',
 	'dss/dto/datasetfile/DataSetFile',
 	'dss/dto/datasetfile/create/DataSetFileCreation',
 	'dss/dto/datasetfile/fetchoptions/DataSetFileSortOptions',
diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/openbis-execute-operations.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/openbis-execute-operations.js
index 5668c4070c021a96584cdbaa9ddf5f62340e2d28..f7e4492e041191e5000331f2dec890cdfed8e731 100644
--- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/openbis-execute-operations.js
+++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/openbis-execute-operations.js
@@ -205,6 +205,10 @@ define([ 'jquery', 'openbis', 'test/common' ], function($, openbis, common) {
 			return this._executeUpdateOperation(new c.UpdateVocabulariesOperation(updates));
 		}
 
+		this.updatePropertyTypes = function(updates) {
+			return this._executeUpdateOperation(new c.UpdatePropertyTypesOperation(updates));
+		}
+		
 		this.updateVocabularyTerms = function(updates) {
 			return this._executeUpdateOperation(new c.UpdateVocabularyTermsOperation(updates));
 		}
diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js
index 7c6298b5f4dbc8fa7531a925899dfa31120f4b0a..fb9a6faa84a93f856b8830718fa9156412608f1a 100644
--- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js
+++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js
@@ -295,23 +295,6 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 				});
 			}
 
-			var waitUntilIndexed = function(facade, dataSetCode, timeout, action) {
-				if (timeout < 0) {
-					c.fail("Data set " + dataSetCode + " after " + timeout + " msec.");
-				}
-				setTimeout(function() {
-					var criteria = new c.DataSetSearchCriteria();
-					criteria.withPermId().thatEquals(dataSetCode);
-					facade.searchDataSets(criteria, c.createDataSetFetchOptions()).then(function(result) {
-						if (result.getTotalCount() == 0) {
-							waitUntilIndexed(facade, dataSetCode, timeout - 1000, action);
-						} else {
-							action();
-						}
-					});
-				}, 1000)
-			};
-
 			var fCheck = function(dataSet, facade) {
 				c.assertEqual(dataSet.getType().getCode(), "LINK_TYPE", "Data set type");
 				var contentCopies = dataSet.getLinkedData().getContentCopies();
@@ -321,7 +304,7 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 				c.assertEqual(contentCopy.getPath(), "/my/path", "Content copy path");
 				var dfd = $.Deferred()
 				var dataSetCode = dataSet.getCode();
-				waitUntilIndexed(facade, dataSetCode, 10000, function() {
+				c.waitUntilIndexed(facade, dataSetCode, 10000).then(function() {
 					var criteria = new c.DataSetFileSearchCriteria();
 					criteria.withDataSet().withCode().thatEquals(dataSet.getCode());
 					facade.getDataStoreFacade("DSS1").searchFiles(criteria, c.createDataSetFileFetchOptions()).then(function(result) {
@@ -506,7 +489,7 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 				vocabularyCreation.setUrlTemplate("https://www.ethz.ch");
 				var termCreation = new c.VocabularyTermCreation();
 				termCreation.setCode("alpha");
-				vocabularyCreation.setTerms([termCreation]);
+				vocabularyCreation.setTerms([ termCreation ]);
 				return facade.createVocabularies([ vocabularyCreation ]);
 			}
 
@@ -525,7 +508,7 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 		QUnit.test("createVocabularyTerms()", function(assert) {
 			var c = new common(assert, openbis);
 			var code = c.generateId("VOCABULARY_TERM");
-			
+
 			var fCreate = function(facade) {
 				var termCreation = new c.VocabularyTermCreation();
 				termCreation.setVocabularyId(new c.VocabularyPermId("TEST-VOCABULARY"));
@@ -536,7 +519,7 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 				termCreation.setPreviousTermId(new c.VocabularyTermPermId("TEST-TERM-1", "TEST-VOCABULARY"))
 				return facade.createVocabularyTerms([ termCreation ]);
 			}
-			
+
 			var fCheck = function(term) {
 				c.assertEqual(term.getCode(), code, "Term code");
 				c.assertEqual(term.getVocabulary().getCode(), "TEST-VOCABULARY", "Term vocabulary code");
@@ -545,10 +528,10 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 				c.assertEqual(term.isOfficial(), true, "Term official");
 				c.assertEqual(term.getOrdinal(), 2, "Term ordinal");
 			}
-			
+
 			testCreate(c, fCreate, c.findVocabularyTerm, fCheck);
 		});
-		
+
 		QUnit.test("createExternalDataManagementSystem()", function(assert) {
 			var c = new common(assert, openbis);
 			var code = c.generateId("EDMS");
@@ -598,15 +581,15 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 			var c = new common(assert, openbis);
 			var code = c.generateId("AUTHORIZATION_GROUP");
 			var description = "Description of " + code;
-			
+
 			var fCreate = function(facade) {
 				var creation = new c.AuthorizationGroupCreation();
 				creation.setCode(code);
 				creation.setDescription(description);
-				creation.setUserIds([new c.PersonPermId("power_user")]);
+				creation.setUserIds([ new c.PersonPermId("power_user") ]);
 				return facade.createAuthorizationGroups([ creation ]);
 			}
-			
+
 			var fCheck = function(group) {
 				c.assertEqual(group.getCode(), code, "Code");
 				c.assertEqual(group.getDescription(), description, "Description");
@@ -616,54 +599,54 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 				users.sort();
 				c.assertEqual(users.toString(), "power_user", "Users");
 			}
-			
+
 			testCreate(c, fCreate, c.findAuthorizationGroup, fCheck);
 		});
-		
+
 		QUnit.test("createRoleAssignments() for space user", function(assert) {
 			var c = new common(assert, openbis);
-			
+
 			var fCreate = function(facade) {
 				return c.createSpace(facade).then(function(spaceId) {
 					var creation = new c.RoleAssignmentCreation();
 					creation.setRole(c.Role.POWER_USER);
 					creation.setUserId(new c.PersonPermId("power_user"));
 					creation.setSpaceId(spaceId);
-					return facade.createRoleAssignments([creation]);
+					return facade.createRoleAssignments([ creation ]);
 				});
 			}
-			
+
 			var fCheck = function(roleAssignment) {
 				c.assertEqual(roleAssignment.getUser().getUserId(), "power_user", "User");
 				c.assertEqual(roleAssignment.getRole(), c.Role.POWER_USER, "Role");
 				c.assertEqual(roleAssignment.getRoleLevel(), c.RoleLevel.SPACE, "Role level");
 				c.assertEqual(roleAssignment.getRegistrator().getUserId(), "openbis_test_js", "Registrator");
 			}
-			
+
 			testCreate(c, fCreate, c.findRoleAssignment, fCheck);
 		});
-		
+
 		QUnit.test("createPersons()", function(assert) {
 			var c = new common(assert, openbis);
 			var userId = c.generateId("user");
-			
+
 			var fCreate = function(facade) {
 				var personCreation = new c.PersonCreation();
 				personCreation.setUserId(userId);
 				personCreation.setHomeSpaceId(new c.SpacePermId("TEST"))
 				return facade.createPersons([ personCreation ]);
 			}
-			
+
 			var fCheck = function(person) {
 				c.assertEqual(person.getUserId(), userId, "User id");
 				c.assertEqual(person.getRegistrator().getUserId(), "openbis_test_js", "Registrator");
 				c.assertEqual(person.isActive(), true, "User active");
 				c.assertEqual(person.getSpace().getCode(), "TEST", "Home space");
 			}
-			
+
 			testCreate(c, fCreate, c.findPerson, fCheck);
 		});
-		
+
 		var createSemanticAnnotationCreation = function(c) {
 			var creation = new c.SemanticAnnotationCreation();
 			creation.setPredicateOntologyId("jsPredicateOntologyId");
@@ -737,6 +720,71 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 			testCreate(c, fCreate, c.findSemanticAnnotation, fCheck);
 		});
 
+		QUnit.test("createUploadedDataSet()", function(assert) {
+			var c = new common(assert, openbis);
+
+			var fCreate = function(facade) {
+
+				// unfortunately old Firefox that is used together with our
+				// Selenium tests does not allow to use FormData class which
+				// would make constructing form data much easier
+
+				var formData = "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file\"; filename=\"file1.txt\"\r\nContent-Type: text/plain\r\n\r\n\r\ncontent1\r\n"
+						+ "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file\"; filename=\"file2.txt\"\r\nContent-Type: text/plain\r\n\r\n\r\ncontent2\r\n"
+						+ "------WebKitFormBoundary7MA4YWxkTrZu0gW--";
+
+				var dataStoreFacade = facade.getDataStoreFacade("DSS1");
+
+				return dataStoreFacade.createDataSetUpload("UNKNOWN").then(function(upload) {
+					return $.ajax({
+						url : upload.url,
+						type : "POST",
+						processData : false,
+						contentType : "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW",
+						data : formData
+					}).then(function() {
+						return c.createExperiment(facade).then(function(experimentPermId) {
+							var creation = new c.UploadedDataSetCreation();
+							creation.setUploadId(upload.id);
+							creation.setTypeId(new c.EntityTypePermId(upload.dataSetType));
+							creation.setExperimentId(experimentPermId);
+							creation.setProperty("DESCRIPTION", "test description");
+							creation.setParentIds([ new c.DataSetPermId("20130424111751432-431") ]);
+							return dataStoreFacade.createUploadedDataSet(creation).then(function(permId) {
+								return [ permId ];
+							})
+						});
+					});
+				});
+			}
+
+			var fCheck = function(dataSet, facade) {
+				return c.waitUntilIndexed(facade, dataSet.getCode(), 10000).then(function() {
+					var dataStoreFacade = facade.getDataStoreFacade("DSS1");
+
+					var criteria = new c.DataSetFileSearchCriteria();
+					criteria.withDataSet().withCode().thatEquals(dataSet.getCode());
+
+					return dataStoreFacade.searchFiles(criteria, c.createDataSetFileFetchOptions()).then(function(result) {
+						var files = result.getObjects();
+						c.assertEqual(files.length, 5, "Number of files");
+						c.assertEqual(files[0].path, "", "Path 0");
+						c.assertEqual(files[1].path, "original", "Path 1");
+						c.assertEqual(files[2].path, "original/upload", "Path 2");
+						c.assertEqual(files[3].path, "original/upload/file1.txt", "Path 3");
+						c.assertEqual(files[4].path, "original/upload/file2.txt", "Path 4");
+
+						c.assertEqual(dataSet.getType().getCode(), "UNKNOWN", "Type code");
+						c.assertEqual(dataSet.getProperty("DESCRIPTION"), "test description", "'DESCRIPTION' property value");
+						c.assertEqual(dataSet.getParents().length, 1, "Number of parents");
+						c.assertEqual(dataSet.getParents()[0].getCode(), "20130424111751432-431", "Parent code");
+					});
+				});
+			}
+
+			testCreate(c, fCreate, c.findDataSet, fCheck);
+		});
+
 	}
 
 	return function() {
diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-update.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-update.js
index a84aebb8780f162ab1ccc7f243ceba7ea1c85e12..94d3b4f57b0642bafa031431646977868a275afb 100644
--- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-update.js
+++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-update.js
@@ -705,17 +705,49 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 			testUpdate(c, fCreate, fUpdate, c.findMaterial, fCheck);
 		});
 		
+		QUnit.test("updatePropertyTypes()", function(assert) {
+			var c = new common(assert, openbis);
+			var code = c.generateId("PROPERTY_TYPE");
+			var description = "Description of " + code;
+			var label = "Label of " + code;
+
+			var fCreate = function(facade) {
+				var creation = new c.PropertyTypeCreation();
+				creation.setCode(code);
+				creation.setLabel("Testing");
+				creation.setDescription("testing");
+				creation.setDataType(c.DataType.VARCHAR);
+				return facade.createPropertyTypes([ creation ]);
+			}
+
+			var fUpdate = function(facade, permId) {
+				var update = new c.PropertyTypeUpdate();
+				update.setTypeId(new c.PropertyTypePermId(code));
+				update.setDescription(description);
+				update.setLabel(label);
+				return facade.updatePropertyTypes([ update ]);
+			}
+
+			var fCheck = function(propertyType) {
+				c.assertEqual(propertyType.getCode(), code, "Code");
+				c.assertEqual(propertyType.getDescription(), description, "Description");
+				c.assertEqual(propertyType.getLabel(), label, "Label");
+			}
+
+			testUpdate(c, fCreate, fUpdate, c.findPropertyType, fCheck);
+		});
+		
 		QUnit.test("updateVocabularies()", function(assert) {
 			var c = new common(assert, openbis);
 			var code = c.generateId("VOCABULARY");
 			var description = "Description of " + code;
-
+			
 			var fCreate = function(facade) {
 				var creation = new c.VocabularyCreation();
 				creation.setCode(code);
 				return facade.createVocabularies([ creation ]);
 			}
-
+			
 			var fUpdate = function(facade, permId) {
 				var update = new c.VocabularyUpdate();
 				update.setVocabularyId(permId);
@@ -724,14 +756,14 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 				update.setUrlTemplate("https://www.ethz.ch")
 				return facade.updateVocabularies([ update ]);
 			}
-
+			
 			var fCheck = function(vocabulary) {
 				c.assertEqual(vocabulary.getCode(), code, "Code");
 				c.assertEqual(vocabulary.getPermId().getPermId(), code, "Perm id");
 				c.assertEqual(vocabulary.getDescription(), description, "Description");
 				c.assertEqual(vocabulary.getUrlTemplate(), "https://www.ethz.ch", "URL template");
 			}
-
+			
 			testUpdate(c, fCreate, fUpdate, c.findVocabulary, fCheck);
 		});
 		
diff --git a/js-test/servers/common/datastore_server/etc/service.properties b/js-test/servers/common/datastore_server/etc/service.properties
index ce0ee81e53d5bf8feb58240e47f3f55c5d4ab83d..a4f556253edf350dcab9eb055c264f4528829979 100644
--- a/js-test/servers/common/datastore_server/etc/service.properties
+++ b/js-test/servers/common/datastore_server/etc/service.properties
@@ -148,7 +148,7 @@ default-dropbox.incoming-data-completeness-condition = auto-detection
 default-dropbox.top-level-data-set-handler = ch.systemsx.cisd.etlserver.registrator.api.v2.JavaTopLevelDataSetHandlerV2
 default-dropbox.program-class = ch.systemsx.cisd.etlserver.registrator.DefaultDropbox
 default-dropbox.storage-processor = ch.systemsx.cisd.etlserver.DefaultStorageProcessor
-default-dropbox.validation-script-path = ../core-plugins/default/default-validation-script.py
+#default-dropbox.validation-script-path = ../core-plugins/default/default-validation-script.py
 
 # ---------------------------------------------------------------------------
 # Archiver configuration (optional)
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApi.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApi.java
index 066fe6346b9627d19bd1095e0a97933635586c17..fb2601743794209c69a6ee31bd4d84d5ac54b33b 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApi.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApi.java
@@ -235,6 +235,8 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.search.SearchPropertyAs
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.search.SearchPropertyAssignmentsOperationResult;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.search.SearchPropertyTypesOperation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.search.SearchPropertyTypesOperationResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.update.PropertyTypeUpdate;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.update.UpdatePropertyTypesOperation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.RoleAssignment;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.create.CreateRoleAssignmentsOperation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.create.CreateRoleAssignmentsOperationResult;
@@ -658,6 +660,12 @@ public class ApplicationServerApi extends AbstractServer<IApplicationServerApi>
         executeOperation(sessionToken, new UpdateDataSetTypesOperation(dataSetTypeUpdates));
     }
 
+    @Override
+    public void updatePropertyTypes(String sessionToken, List<PropertyTypeUpdate> propertyTypeUpdates)
+    {
+        executeOperation(sessionToken, new UpdatePropertyTypesOperation(propertyTypeUpdates));
+    }
+
     @Override
     public void updateVocabularies(String sessionToken, List<VocabularyUpdate> vocabularyUpdates)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApiLogger.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApiLogger.java
index 7aa5e6ba88544cbbe6ebc671fa982a9b5df855a4..fbbf22a2c5f017f8008b0d4b6a34bdbb5fb38703 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApiLogger.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/ApplicationServerApiLogger.java
@@ -127,6 +127,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.id.IPropertyTypeId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.id.PropertyTypePermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.search.PropertyAssignmentSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.search.PropertyTypeSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.update.PropertyTypeUpdate;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.RoleAssignment;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.create.RoleAssignmentCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.delete.RoleAssignmentDeletionOptions;
@@ -436,6 +437,12 @@ public class ApplicationServerApiLogger extends AbstractServerLogger implements
         logAccess(sessionToken, "update-material-types", "MATERIAL_TYPE_UPDATES(%s)", abbreviate(materialTypeUpdates));
     }
 
+    @Override
+    public void updatePropertyTypes(String sessionToken, List<PropertyTypeUpdate> propertyTypeUpdates)
+    {
+        logAccess(sessionToken, "update-property_types", "PROPERTY_TYPE_UPDATES(%s)", abbreviate(propertyTypeUpdates));
+    }
+
     @Override
     public void updateVocabularies(String sessionToken, List<VocabularyUpdate> vocabularyUpdates)
     {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityExecutor.java
index 684a413662019b0db4e154ab79a7179d164de4d7..b6dce6196377a1bd4fb6ce9576ea052e521ba7fd 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/entity/AbstractUpdateEntityExecutor.java
@@ -31,6 +31,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.dao.DataAccessException;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.IObjectId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.FieldUpdateValue;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.IUpdate;
 import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.ObjectNotFoundException;
 import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.UnauthorizedObjectAccessException;
@@ -216,6 +217,12 @@ public abstract class AbstractUpdateEntityExecutor<UPDATE extends IUpdate, PE ex
             entry.setValue(idToEntityMap.get(entry.getValue().getId()));
         }
     }
+    
+    protected <T> T getNewValue(FieldUpdateValue<T> fieldUpdateValue, T currentValue)
+    {
+        return fieldUpdateValue != null && fieldUpdateValue.isModified() ? fieldUpdateValue.getValue() : currentValue;
+    }
+    
 
     protected abstract ID getId(UPDATE update);
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/operation/OperationsExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/operation/OperationsExecutor.java
index 662c18b6cb6d2b4f060a57bbc2c0292d16f6d200..f45065bca3b00d235a08c1a7fca961e0487ee8a8 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/operation/OperationsExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/operation/OperationsExecutor.java
@@ -91,6 +91,7 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.property.ICreateProp
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.property.IGetPropertyTypesOperationExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.property.ISearchPropertyAssignmentsOperationExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.property.ISearchPropertyTypesOperationExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.property.IUpdatePropertyTypesOperationExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.roleassignment.ICreateRoleAssignmentsOperationExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.roleassignment.IDeleteRoleAssignmentsOperationExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.roleassignment.IGetRoleAssignmentsOperationExecutor;
@@ -287,6 +288,9 @@ public class OperationsExecutor implements IOperationsExecutor
 
     @Autowired
     private IUpdateExternalDmsOperationExecutor updateExternalDmsExecutor;
+    
+    @Autowired
+    private IUpdatePropertyTypesOperationExecutor updatePropertyTypesExecutor;
 
     @Autowired
     private IUpdateVocabulariesOperationExecutor updateVocabulariesExecutor;
@@ -574,6 +578,7 @@ public class OperationsExecutor implements IOperationsExecutor
         resultMap.putAll(updateSemanticAnnotationsExecutor.execute(context, operations));
         resultMap.putAll(updateOperationExecutionsExecutor.execute(context, operations));
         resultMap.putAll(updateVocabulariesExecutor.execute(context, operations));
+        resultMap.putAll(updatePropertyTypesExecutor.execute(context, operations));
         resultMap.putAll(updateVocabularyTermsExecutor.execute(context, operations));
         resultMap.putAll(updateMaterialTypesExecutor.execute(context, operations));
         resultMap.putAll(updateExperimentTypesExecutor.execute(context, operations));
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/IPropertyTypeAuthorizationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/IPropertyTypeAuthorizationExecutor.java
index b148bd599f159af8e15e346cf52430c7b67c6466..6ad43d9802fe62aa847b7855f9d7f0017e148f69 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/IPropertyTypeAuthorizationExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/IPropertyTypeAuthorizationExecutor.java
@@ -16,8 +16,10 @@
 
 package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.property;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.id.IPropertyTypeId;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.common.IObjectAuthorizationExecutor;
+import ch.systemsx.cisd.openbis.generic.shared.dto.PropertyTypePE;
 
 /**
  * @author pkupczyk
@@ -31,4 +33,6 @@ public interface IPropertyTypeAuthorizationExecutor extends IObjectAuthorization
 
     void canCreate(IOperationContext context);
 
+    void canUpdate(IOperationContext context, IPropertyTypeId id, PropertyTypePE entity);
+
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/IUpdatePropertyTypeExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/IUpdatePropertyTypeExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..b99b9747f6c43f91da7ac8af52500d3dcdba17f2
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/IUpdatePropertyTypeExecutor.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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.ethz.sis.openbis.generic.server.asapi.v3.executor.property;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.id.PropertyTypePermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.update.PropertyTypeUpdate;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.IUpdateEntityExecutor;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+public interface IUpdatePropertyTypeExecutor extends IUpdateEntityExecutor<PropertyTypeUpdate, PropertyTypePermId>
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/IUpdatePropertyTypesOperationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/IUpdatePropertyTypesOperationExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d9a496a7092b6626c475fabab0790e3f26ca32f
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/IUpdatePropertyTypesOperationExecutor.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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.ethz.sis.openbis.generic.server.asapi.v3.executor.property;
+
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.common.update.IUpdateObjectsOperationExecutor;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+public interface IUpdatePropertyTypesOperationExecutor extends IUpdateObjectsOperationExecutor
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/PropertyTypeAuthorizationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/PropertyTypeAuthorizationExecutor.java
index 163f86d9fae3adbe44ee7da7f57ca7581c55810d..8626d432c8140def7dabbc14c6f66e4d520db720 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/PropertyTypeAuthorizationExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/PropertyTypeAuthorizationExecutor.java
@@ -18,10 +18,12 @@ package ch.ethz.sis.openbis.generic.server.asapi.v3.executor.property;
 
 import org.springframework.stereotype.Component;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.id.IPropertyTypeId;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.systemsx.cisd.openbis.generic.server.authorization.annotation.Capability;
 import ch.systemsx.cisd.openbis.generic.server.authorization.annotation.RolesAllowed;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.RoleWithHierarchy;
+import ch.systemsx.cisd.openbis.generic.shared.dto.PropertyTypePE;
 
 /**
  * @author pkupczyk
@@ -51,4 +53,11 @@ public class PropertyTypeAuthorizationExecutor implements IPropertyTypeAuthoriza
     {
     }
 
+    @Override
+    @RolesAllowed(RoleWithHierarchy.INSTANCE_ADMIN)
+    @Capability("UPDATE_PROPERTY_TYPE")
+    public void canUpdate(IOperationContext context, IPropertyTypeId id, PropertyTypePE entity)
+    {
+    }
+
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/UpdatePropertyTypeExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/UpdatePropertyTypeExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..deb11b335c0778ae009289e1e77ee7646fbe50ea
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/UpdatePropertyTypeExecutor.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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.ethz.sis.openbis.generic.server.asapi.v3.executor.property;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataAccessException;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.id.IPropertyTypeId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.id.PropertyTypePermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.update.PropertyTypeUpdate;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.AbstractUpdateEntityExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.common.batch.MapBatch;
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.openbis.generic.server.business.bo.DataAccessExceptionTranslator;
+import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
+import ch.systemsx.cisd.openbis.generic.shared.dto.PropertyTypePE;
+import ch.systemsx.cisd.openbis.generic.shared.util.XmlUtils;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+@Component
+public class UpdatePropertyTypeExecutor
+        extends AbstractUpdateEntityExecutor<PropertyTypeUpdate, PropertyTypePE, IPropertyTypeId, PropertyTypePermId>
+        implements IUpdatePropertyTypeExecutor
+{
+    @Autowired
+    private IDAOFactory daoFactory;
+    
+    @Autowired
+    private IMapPropertyTypeByIdExecutor mapPropertyTypeByIdExecutor;
+    
+    @Autowired
+    private IPropertyTypeAuthorizationExecutor authorizationExecutor;
+
+    @Override
+    protected IPropertyTypeId getId(PropertyTypeUpdate update)
+    {
+        return update.getTypeId();
+    }
+
+    @Override
+    protected PropertyTypePermId getPermId(PropertyTypePE entity)
+    {
+        return new PropertyTypePermId(entity.getPermId());
+    }
+
+    @Override
+    protected void checkData(IOperationContext context, PropertyTypeUpdate update)
+    {
+        if (update.getTypeId() == null)
+        {
+            throw new UserFailureException("Property type id cannot be null.");
+        }
+        if (update.getLabel().isModified() && StringUtils.isEmpty(update.getLabel().getValue()))
+        {
+            throw new UserFailureException("Label cannot be empty.");
+        }
+        if (update.getDescription().isModified() && StringUtils.isEmpty(update.getDescription().getValue()))
+        {
+            throw new UserFailureException("Description cannot be empty.");
+        }
+        XmlUtils.validateXML(update.getSchema().getValue(), "XML Schema", XmlUtils.XML_SCHEMA_XSD_FILE_RESOURCE);
+        XmlUtils.validateXML(update.getTransformation().getValue(), "XSLT", XmlUtils.XSLT_XSD_FILE_RESOURCE);
+    }
+
+    @Override
+    protected void checkAccess(IOperationContext context, IPropertyTypeId id, PropertyTypePE entity)
+    {
+        authorizationExecutor.canUpdate(context, id, entity);
+    }
+
+    @Override
+    protected void updateBatch(IOperationContext context, MapBatch<PropertyTypeUpdate, PropertyTypePE> batch)
+    {
+        Set<Entry<PropertyTypeUpdate, PropertyTypePE>> entrySet = batch.getObjects().entrySet();
+        for (Entry<PropertyTypeUpdate, PropertyTypePE> entry : entrySet)
+        {
+            PropertyTypeUpdate update = entry.getKey();
+            PropertyTypePE propertyType = entry.getValue();
+            propertyType.setDescription(getNewValue(update.getDescription(), propertyType.getDescription()));
+            propertyType.setLabel(getNewValue(update.getLabel(), propertyType.getLabel()));
+            propertyType.setSchema(getNewValue(update.getSchema(), propertyType.getSchema()));
+            propertyType.setTransformation(getNewValue(update.getTransformation(), propertyType.getTransformation()));
+        }
+    }
+
+    @Override
+    protected void updateAll(IOperationContext context, MapBatch<PropertyTypeUpdate, PropertyTypePE> batch)
+    {
+    }
+
+    @Override
+    protected Map<IPropertyTypeId, PropertyTypePE> map(IOperationContext context, Collection<IPropertyTypeId> ids)
+    {
+        return mapPropertyTypeByIdExecutor.map(context, ids);
+    }
+
+    @Override
+    protected List<PropertyTypePE> list(IOperationContext context, Collection<Long> ids)
+    {
+        return daoFactory.getPropertyTypeDAO().listAllEntities();
+    }
+
+    @Override
+    protected void save(IOperationContext context, List<PropertyTypePE> entities, boolean clearCache)
+    {
+        for (PropertyTypePE propertyType : entities)
+        {
+            daoFactory.getPropertyTypeDAO().validateAndSaveUpdatedEntity(propertyType);
+        }
+    }
+
+    @Override
+    protected void handleException(DataAccessException e)
+    {
+        DataAccessExceptionTranslator.throwException(e, "property types", null);
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/UpdatePropertyTypesOperationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/UpdatePropertyTypesOperationExecutor.java
new file mode 100644
index 0000000000000000000000000000000000000000..6b7b5c579a9e8da1e444314bf5fb893670bd3c97
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/UpdatePropertyTypesOperationExecutor.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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.ethz.sis.openbis.generic.server.asapi.v3.executor.property;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.UpdateObjectsOperation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.UpdateObjectsOperationResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.id.IPropertyTypeId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.update.PropertyTypeUpdate;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.update.UpdatePropertyTypesOperation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.update.UpdatePropertyTypesOperationResult;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.common.update.UpdateObjectsOperationExecutor;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+@Component
+public class UpdatePropertyTypesOperationExecutor
+        extends UpdateObjectsOperationExecutor<PropertyTypeUpdate, IPropertyTypeId>
+        implements IUpdatePropertyTypesOperationExecutor
+{
+    @Autowired
+    private IUpdatePropertyTypeExecutor executor;
+
+    @Override
+    protected Class<? extends UpdateObjectsOperation<PropertyTypeUpdate>> getOperationClass()
+    {
+        return UpdatePropertyTypesOperation.class;
+    }
+
+    @Override
+    protected UpdateObjectsOperationResult<? extends IPropertyTypeId> doExecute(IOperationContext context,
+            UpdateObjectsOperation<PropertyTypeUpdate> operation)
+    {
+        return new UpdatePropertyTypesOperationResult(executor.update(context, operation.getUpdates()));
+    }
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/vocabulary/IUpdateVocabularyExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/vocabulary/IUpdateVocabularyExecutor.java
index a975bbc53e93e5f3f73696102136ae4425d94501..a8b6e34a85039314979517904efbd7f31c487158 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/vocabulary/IUpdateVocabularyExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/vocabulary/IUpdateVocabularyExecutor.java
@@ -26,5 +26,4 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.entity.IUpdateEntity
  */
 public interface IUpdateVocabularyExecutor extends IUpdateEntityExecutor<VocabularyUpdate, VocabularyPermId>
 {
-
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/vocabulary/UpdateVocabularyExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/vocabulary/UpdateVocabularyExecutor.java
index 79b1a700fa16ba089c37d26d3cedf5b33d69576f..d3a3b5e8fa2c214ee933c4bfbcae4bdfde9cdeea 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/vocabulary/UpdateVocabularyExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/vocabulary/UpdateVocabularyExecutor.java
@@ -26,7 +26,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.dao.DataAccessException;
 import org.springframework.stereotype.Component;
 
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.FieldUpdateValue;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.id.IVocabularyId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.id.VocabularyPermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.update.VocabularyUpdate;
@@ -96,11 +95,6 @@ public class UpdateVocabularyExecutor
         }
     }
     
-    private <T> T getNewValue(FieldUpdateValue<T> fieldUpdateValue, T currentValue)
-    {
-        return fieldUpdateValue != null && fieldUpdateValue.isModified() ? fieldUpdateValue.getValue() : currentValue;
-    }
-    
     @Override
     protected void updateAll(IOperationContext context, MapBatch<VocabularyUpdate, VocabularyPE> batch)
     {
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ServiceForDataStoreServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ServiceForDataStoreServer.java
index 8f7c73228d5ee8a78a0052220d4db3333be6dcbb..58fa9e048e41f97db15ed61357a321d8b4d6e5cd 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ServiceForDataStoreServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ServiceForDataStoreServer.java
@@ -17,6 +17,7 @@
 package ch.systemsx.cisd.openbis.generic.server;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
@@ -34,8 +35,11 @@ import javax.servlet.http.HttpServletRequest;
 import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.operation.IOperationResult;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.Complete;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSetKind;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.CreateDataSetsOperation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.CreateDataSetsOperationResult;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.DataSetCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.LinkedDataCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.PhysicalDataCreation;
@@ -54,7 +58,7 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgressListener;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.context.IProgressStack;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.OperationContext;
-import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.dataset.ICreateDataSetExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.operation.IOperationsExecutor;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sample.ListSampleTechIdByIdentifier;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.utils.ExceptionUtils;
 import ch.rinn.restrictions.Private;
@@ -334,7 +338,7 @@ public class ServiceForDataStoreServer extends AbstractCommonServer<IServiceForD
     private IManagedPropertyEvaluatorFactory managedPropertyEvaluatorFactory;
 
     @Autowired
-    private ICreateDataSetExecutor createDataSetExecutor;
+    private IOperationsExecutor operationsExecutor;
 
     private long timeout = 5; // minutes
 
@@ -372,7 +376,7 @@ public class ServiceForDataStoreServer extends AbstractCommonServer<IServiceForD
             IDataStoreServiceRegistrator dataStoreServiceRegistrator,
             IDataStoreDataSourceManager dataSourceManager,
             ISessionManager<Session> sessionManagerForEntityOperation,
-            IManagedPropertyEvaluatorFactory managedPropertyEvaluatorFactory, ICreateDataSetExecutor createDataSetExecutor)
+            IManagedPropertyEvaluatorFactory managedPropertyEvaluatorFactory, IOperationsExecutor operationsExecutor)
     {
         super(authenticationService, sessionManager, daoFactory, propertiesBatchManager, boFactory);
         this.daoFactory = daoFactory;
@@ -383,7 +387,7 @@ public class ServiceForDataStoreServer extends AbstractCommonServer<IServiceForD
         this.dataSourceManager = dataSourceManager;
         this.sessionManagerForEntityOperation = sessionManagerForEntityOperation;
         this.managedPropertyEvaluatorFactory = managedPropertyEvaluatorFactory;
-        this.createDataSetExecutor = createDataSetExecutor;
+        this.operationsExecutor = operationsExecutor;
     }
 
     @Override
@@ -2676,9 +2680,9 @@ public class ServiceForDataStoreServer extends AbstractCommonServer<IServiceForD
 
         try
         {
-            List<DataSetPermId> ids = createDataSetExecutor.create(context, creations);
-            daoFactory.getSessionFactory().getCurrentSession().flush();
-            return ids.size();
+            CreateDataSetsOperation operation = new CreateDataSetsOperation(creations);
+            List<IOperationResult> results = operationsExecutor.execute(context, Arrays.asList(operation));
+            return ((CreateDataSetsOperationResult) results.get(0)).getObjectIds().size();
         } catch (Throwable t)
         {
             throw ExceptionUtils.create(context, t);
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/PropertyTypePE.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/PropertyTypePE.java
index 1324a52cd5c29f2b31e1a4874117c844fa494e43..37d75e0c22b4dc5960ddb48f3c9d3bc5e76c2b10 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/PropertyTypePE.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/PropertyTypePE.java
@@ -45,6 +45,7 @@ import ch.systemsx.cisd.common.collection.UnmodifiableSetDecorator;
 import ch.systemsx.cisd.openbis.generic.client.web.client.application.GenericConstants;
 import ch.systemsx.cisd.openbis.generic.shared.IServer;
 import ch.systemsx.cisd.openbis.generic.shared.basic.CodeConverter;
+import ch.systemsx.cisd.openbis.generic.shared.basic.IIdentityHolder;
 
 /**
  * Persistence entity representing property type.
@@ -60,7 +61,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.CodeConverter;
 { @UniqueConstraint(columnNames =
 { ColumnNames.CODE_COLUMN, ColumnNames.IS_INTERNAL_NAMESPACE }) })
 public final class PropertyTypePE extends HibernateAbstractRegistrationHolder implements
-        Comparable<PropertyTypePE>, IIdAndCodeHolder
+        Comparable<PropertyTypePE>, IIdAndCodeHolder, IIdentityHolder
 {
     public static final PropertyTypePE[] EMPTY_ARRAY = new PropertyTypePE[0];
 
@@ -170,6 +171,20 @@ public final class PropertyTypePE extends HibernateAbstractRegistrationHolder im
         return CodeConverter.tryToBusinessLayer(getSimpleCode(), isInternalNamespace());
     }
 
+    @Override
+    @Transient
+    public String getIdentifier()
+    {
+        return getCode();
+    }
+
+    @Override
+    @Transient
+    public String getPermId()
+    {
+        return getCode();
+    }
+
     @NotNull(message = ValidationMessages.DATA_TYPE_NOT_NULL_MESSAGE)
     @ManyToOne(fetch = FetchType.EAGER)
     @JoinColumn(name = ColumnNames.DATA_TYPE_COLUMN, updatable = false)
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/property/update/PropertyTypeUpdate.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/property/update/PropertyTypeUpdate.js
new file mode 100644
index 0000000000000000000000000000000000000000..2b84f287d917780d732d03d667e89e01a8a59945
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/property/update/PropertyTypeUpdate.js
@@ -0,0 +1,54 @@
+define([ "stjs", "as/dto/common/update/FieldUpdateValue" ], function(stjs, FieldUpdateValue) {
+	var PropertyTypeUpdate = function() {
+		this.label = new FieldUpdateValue();
+		this.description = new FieldUpdateValue();
+		this.schema = new FieldUpdateValue();
+		this.transformation = new FieldUpdateValue();
+	};
+	stjs.extend(PropertyTypeUpdate, null, [], function(constructor, prototype) {
+		prototype['@type'] = 'as.dto.property.update.PropertyTypeUpdate';
+		constructor.serialVersionUID = 1;
+		prototype.typeId = null;
+		prototype.label = null;
+		prototype.description = null;
+		prototype.schema = null;
+		prototype.transformation = null;
+
+		prototype.getObjectId = function() {
+			return this.getTypeId();
+		};
+		prototype.getTypeId = function() {
+			return this.typeId;
+		};
+		prototype.setTypeId = function(typeId) {
+			this.typeId = typeId;
+		};
+		prototype.getLabel = function() {
+			return this.label;
+		};
+		prototype.setLabel = function(label) {
+			this.label.setValue(label);
+		};
+		prototype.getDescription = function() {
+			return this.description;
+		};
+		prototype.setDescription = function(description) {
+			this.description.setValue(description);
+		};
+		prototype.getSchema = function() {
+			return this.schema;
+		};
+		prototype.setSchema = function(schema) {
+			this.schema.setValue(schema);
+		};
+		prototype.getTransformation = function() {
+			return this.transformation;
+		};
+		prototype.setTransformation = function(transformation) {
+			this.transformation.setValue(transformation);
+		};
+	}, {
+		typeId : "IPropertyTypeId"
+	});
+	return PropertyTypeUpdate;
+})
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/property/update/UpdatePropertyTypesOperation.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/property/update/UpdatePropertyTypesOperation.js
new file mode 100644
index 0000000000000000000000000000000000000000..c10de1f330528058ca0062dccc2a781e02c1c788
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/property/update/UpdatePropertyTypesOperation.js
@@ -0,0 +1,12 @@
+define([ "stjs", "as/dto/common/update/UpdateObjectsOperation" ], function(stjs, UpdateObjectsOperation) {
+	var UpdatePropertyTypesOperation = function(updates) {
+		UpdateObjectsOperation.call(this, updates);
+	};
+	stjs.extend(UpdatePropertyTypesOperation, UpdateObjectsOperation, [ UpdateObjectsOperation ], function(constructor, prototype) {
+		prototype['@type'] = 'as.dto.property.update.UpdatePropertyTypesOperation';
+		prototype.getMessage = function() {
+			return "UpdatePropertyTypesOperation";
+		};
+	}, {});
+	return UpdatePropertyTypesOperation;
+})
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/property/update/UpdatePropertyTypesOperationResult.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/property/update/UpdatePropertyTypesOperationResult.js
new file mode 100644
index 0000000000000000000000000000000000000000..1681b61c4faf779162f596febaa0f3e0ba19e6f2
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/property/update/UpdatePropertyTypesOperationResult.js
@@ -0,0 +1,12 @@
+define([ "stjs", "as/dto/common/update/UpdateObjectsOperationResult" ], function(stjs, UpdateObjectsOperationResult) {
+	var UpdatePropertyTypesOperationResult = function(objectIds) {
+		UpdateObjectsOperationResult.call(this, objectIds);
+	};
+	stjs.extend(UpdatePropertyTypesOperationResult, UpdateObjectsOperationResult, [ UpdateObjectsOperationResult ], function(constructor, prototype) {
+		prototype['@type'] = 'as.dto.property.update.UpdatePropertyTypesOperationResult';
+		prototype.getMessage = function() {
+			return "UpdatePropertyTypesOperationResult";
+		};
+	}, {});
+	return UpdatePropertyTypesOperationResult;
+})
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dss/dto/dataset/create/UploadedDataSetCreation.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dss/dto/dataset/create/UploadedDataSetCreation.js
new file mode 100644
index 0000000000000000000000000000000000000000..1ac569d45a7b85661b110af5160558d154ced8ba
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/dss/dto/dataset/create/UploadedDataSetCreation.js
@@ -0,0 +1,71 @@
+define([ "stjs" ], function(stjs) {
+	var UploadedDataSetCreation = function() {
+		this.properties = {};
+	};
+	stjs.extend(UploadedDataSetCreation, null, [], function(constructor, prototype) {
+		prototype['@type'] = 'dss.dto.dataset.create.UploadedDataSetCreation';
+		constructor.serialVersionUID = 1;
+		prototype.typeId = null;
+		prototype.experimentId = null;
+		prototype.sampleId = null;
+		prototype.properties = null;
+		prototype.parentIds = null;
+		prototype.uploadId = null;
+
+		prototype.getTypeId = function() {
+			return this.typeId;
+		};
+		prototype.setTypeId = function(typeId) {
+			this.typeId = typeId;
+		};
+		prototype.getExperimentId = function() {
+			return this.experimentId;
+		};
+		prototype.setExperimentId = function(experimentId) {
+			this.experimentId = experimentId;
+		};
+		prototype.getSampleId = function() {
+			return this.sampleId;
+		};
+		prototype.setSampleId = function(sampleId) {
+			this.sampleId = sampleId;
+		};
+		prototype.getProperty = function(propertyName) {
+			return this.properties[propertyName];
+		};
+		prototype.setProperty = function(propertyName, propertyValue) {
+			this.properties[propertyName] = propertyValue;
+		};
+		prototype.getProperties = function() {
+			return this.properties;
+		};
+		prototype.setProperties = function(properties) {
+			this.properties = properties;
+		};
+		prototype.getParentIds = function() {
+			return this.parentIds;
+		};
+		prototype.setParentIds = function(parentIds) {
+			this.parentIds = parentIds;
+		};
+		prototype.getUploadId = function() {
+			return this.uploadId;
+		};
+		prototype.setUploadId = function(uploadId) {
+			this.uploadId = uploadId;
+		};
+	}, {
+		typeId : "IEntityTypeId",
+		experimentId : "IExperimentId",
+		sampleId : "ISampleId",
+		properties : {
+			name : "Map",
+			arguments : [ null, null ]
+		},
+		parentIds : {
+			name : "List",
+			arguments : [ "Object" ]
+		}
+	});
+	return UploadedDataSetCreation;
+})
\ No newline at end of file
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js
index 90b7be889102cb4846bc28632f6de54fccbf7fd0..e46099e4e8b1b888470b5e71591843654a4a33d6 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js
@@ -127,7 +127,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				});
 			}
 		}
-		
+
 		this._createUrl = function(dataStore) {
 			return dataStore.downloadUrl + "/datastore_server/rmi-data-store-server-v3.json";
 		}
@@ -168,7 +168,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				});
 			});
 		}
-		
+
 		this.createDataSets = function(creations) {
 			var thisFacade = this;
 			var creationsByStore = {};
@@ -178,7 +178,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				if (dataStoreCode in creationsByStore) {
 					creationsByStore[dataStoreCode].append(creation);
 				} else {
-					creationsByStore[dataStoreCode] = [creation];
+					creationsByStore[dataStoreCode] = [ creation ];
 				}
 			}
 			return this._getDataStores().then(function(dataStores) {
@@ -210,6 +210,69 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 
 			});
 		}
+
+		this.createDataSetUpload = function(dataSetType) {
+
+			var pad = function(value, length) {
+				var result = "" + value;
+				while (result.length < length) {
+					result = "0" + result;
+				}
+				return result;
+			}
+
+			return this._getDataStores().then(
+					function(dataStores) {
+						var dfd = jquery.Deferred();
+
+						if (dataStores.length > 1) {
+							dfd.reject("Please specify exactly one data store");
+						} else {
+							var now = new Date();
+							var id = "upload-" + now.getFullYear() + pad(now.getMonth() + 1, 2) + pad(now.getDate(), 2) + pad(now.getHours(), 2) + pad(now.getMinutes(), 2) + pad(now.getSeconds(), 2)
+									+ "-" + pad(Math.round(Math.random() * 100000), 5);
+
+							var params = {
+								"sessionID" : facade._private.sessionToken,
+								"uploadID" : id,
+								"dataSetType" : dataSetType
+							};
+
+							var url = dataStores[0].downloadUrl + "/datastore_server/store_share_file_upload?" + jquery.param(params);
+
+							dfd.resolve({
+								"id" : id,
+								"url" : url,
+								"dataSetType" : dataSetType
+							});
+						}
+
+						return dfd.promise();
+					});
+		}
+
+		this.createUploadedDataSet = function(creation) {
+			var thisFacade = this;
+			return this._getDataStores().then(function(dataStores) {
+				if (dataStores.length > 1) {
+					var dfd = jquery.Deferred();
+					dfd.reject("Please specify exactly one data store");
+					return dfd.promise();
+				}
+
+				return facade._private.ajaxRequest({
+					url : thisFacade._createUrl(dataStores[0]),
+					data : {
+						"method" : "createUploadedDataSet",
+						"params" : [ facade._private.sessionToken, creation ]
+					},
+					returnType : {
+						name : "DataSetPermId"
+					}
+				});
+			});
+		}
+
 	}
 
 	var facade = function(openbisUrl) {
@@ -416,7 +479,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.createMaterials = function(creations) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -506,7 +569,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.createAuthorizationGroups = function(creations) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -521,7 +584,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.createRoleAssignments = function(creations) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -536,7 +599,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.createPersons = function(creations) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -551,7 +614,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.createSemanticAnnotations = function(creations) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -610,7 +673,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.updateSamples = function(updates) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -632,7 +695,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.updateDataSets = function(updates) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -654,7 +717,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.updateMaterials = function(updates) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -676,7 +739,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.updateExternalDataManagementSystems = function(updates) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -688,6 +751,17 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 			});
 		}
 		
+		this.updatePropertyTypes = function(updates) {
+			var thisFacade = this;
+			return thisFacade._private.ajaxRequest({
+				url : openbisUrl,
+				data : {
+					"method" : "updatePropertyTypes",
+					"params" : [ thisFacade._private.sessionToken, updates ]
+				}
+			});
+		}
+
 		this.updateVocabularies = function(updates) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -698,7 +772,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-
+		
 		this.updateVocabularyTerms = function(updates) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -720,7 +794,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.updateAuthorizationGroups = function(updates) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -742,7 +816,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.updateOperationExecutions = function(updates) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -753,7 +827,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.updateSemanticAnnotations = function(updates) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -914,7 +988,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.getAuthorizationGroups = function(ids, fetchOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -929,7 +1003,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.getRoleAssignments = function(ids, fetchOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -944,7 +1018,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.getPersons = function(ids, fetchOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -959,7 +1033,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.getSemanticAnnotations = function(ids, fetchOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -1148,7 +1222,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				returnType : "SearchResult"
 			});
 		}
-		
+
 		this.searchVocabularyTerms = function(criteria, fetchOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -1172,7 +1246,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				returnType : "SearchResult"
 			});
 		}
-		
+
 		this.searchAuthorizationGroups = function(criteria, fetchOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -1196,7 +1270,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				returnType : "SearchResult"
 			});
 		}
-		
+
 		this.searchPersons = function(criteria, fetchOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -1208,7 +1282,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				returnType : "SearchResult"
 			});
 		}
-		
+
 		this.searchCustomASServices = function(criteria, fetchOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -1268,7 +1342,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				returnType : "SearchResult"
 			});
 		}
-		
+
 		this.searchPropertyTypes = function(criteria, fetchOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -1417,7 +1491,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.deleteTags = function(ids, deletionOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -1439,7 +1513,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.deleteRoleAssignments = function(ids, deletionOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -1450,7 +1524,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.deleteOperationExecutions = function(ids, deletionOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -1461,7 +1535,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.deleteSemanticAnnotations = function(ids, deletionOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -1571,7 +1645,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				url : openbisUrl,
 				data : {
 					"method" : "setWebAppSettings",
-					"params" : [ thisFacade._private.sessionToken, webAppSettings]
+					"params" : [ thisFacade._private.sessionToken, webAppSettings ]
 				}
 			});
 		}
@@ -1582,11 +1656,11 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				url : openbisUrl,
 				data : {
 					"method" : "getWebAppSettings",
-					"params" : [ thisFacade._private.sessionToken, webAppId]
+					"params" : [ thisFacade._private.sessionToken, webAppId ]
 				}
 			});
 		}
-		
+
 		this.createPermIdStrings = function(amount) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -1597,7 +1671,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.getDataStoreFacade = function() {
 			var dataStoreCodes = [];
 			for (var i = 0; i < arguments.length; i++) {
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreatePropertyTypeTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreatePropertyTypeTest.java
index d5029c637895f5bc3e79f7fe8cd806b54e53e063..a0ce33b2328b082d92aa7ceee523db3f9b6d7b6a 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreatePropertyTypeTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreatePropertyTypeTest.java
@@ -79,7 +79,7 @@ public class CreatePropertyTypeTest extends AbstractTest
             + "</xsl:template>\n                                        "
             + "</xsl:stylesheet>";
 
-    private static String EXAMPLE_INCORRECT_XSLT = EXAMPLE_XSLT.replaceAll("xsl:stylesheet",
+    static String EXAMPLE_INCORRECT_XSLT = EXAMPLE_XSLT.replaceAll("xsl:stylesheet",
             "xsl:styleshet");
 
     @Test
@@ -117,7 +117,7 @@ public class CreatePropertyTypeTest extends AbstractTest
         v3api.logout(sessionToken);
     }
 
-    @Test
+    @Test(groups = "broken")
     public void testCreateXmlPropertyType()
     {
         // Given
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/UpdatePropertyTypesTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/UpdatePropertyTypesTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..8115898aac2a9926276be5fefe06e54062dff98d
--- /dev/null
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/UpdatePropertyTypesTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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.ethz.sis.openbis.systemtest.asapi.v3;
+
+import static org.testng.Assert.assertEquals;
+
+import java.util.Arrays;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.PropertyType;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.fetchoptions.PropertyTypeFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.id.PropertyTypePermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.update.PropertyTypeUpdate;
+import ch.systemsx.cisd.common.action.IDelegatedAction;
+
+/**
+ * @author Franz-Josef Elmer
+ */
+public class UpdatePropertyTypesTest extends AbstractTest
+{
+    @Test
+    public void testUpdatePropertyTypeFromInternalNamespace()
+    {
+        // Given
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+        PropertyTypePermId id = new PropertyTypePermId("$PLATE_GEOMETRY");
+        PropertyTypeUpdate update = new PropertyTypeUpdate();
+        update.setTypeId(id);
+        update.setDescription("Test description");
+
+        // When
+        v3api.updatePropertyTypes(sessionToken, Arrays.asList(update));
+
+        // Then
+        PropertyTypeFetchOptions fetchOptions = new PropertyTypeFetchOptions();
+        PropertyType propertyType = v3api.getPropertyTypes(sessionToken, Arrays.asList(id), fetchOptions).get(id);
+        assertEquals(propertyType.getDescription(), update.getDescription().getValue());
+        assertEquals(propertyType.getLabel(), "Plate Geometry");
+        assertEquals(propertyType.isInternalNameSpace().booleanValue(), true);
+
+        v3api.logout(sessionToken);
+    }
+
+    @Test
+    public void testUpdatePropertyTypeFromExternalNamespace()
+    {
+        // Given
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+        PropertyTypePermId id = new PropertyTypePermId("COMMENT");
+        PropertyTypeUpdate update = new PropertyTypeUpdate();
+        update.setTypeId(id);
+        update.setLabel("Test label");
+
+        // When
+        v3api.updatePropertyTypes(sessionToken, Arrays.asList(update));
+
+        // Then
+        PropertyTypeFetchOptions fetchOptions = new PropertyTypeFetchOptions();
+        PropertyType propertyType = v3api.getPropertyTypes(sessionToken, Arrays.asList(id), fetchOptions).get(id);
+        assertEquals(propertyType.getDescription(), "Any other comments");
+        assertEquals(propertyType.getLabel(), update.getLabel().getValue());
+        assertEquals(propertyType.isInternalNameSpace().booleanValue(), false);
+
+        v3api.logout(sessionToken);
+    }
+
+    @Test
+    public void testMissingId()
+    {
+        PropertyTypeUpdate update = new PropertyTypeUpdate();
+
+        assertUserFailureException(update, "Property type id cannot be null.");
+    }
+    
+    @Test
+    public void testNullDescription()
+    {
+        PropertyTypeUpdate update = new PropertyTypeUpdate();
+        update.setTypeId(new PropertyTypePermId("COMMENT"));
+        update.setDescription(null);
+        
+        assertUserFailureException(update, "Description cannot be empty.");
+    }
+    
+    @Test
+    public void testEmptyDescription()
+    {
+        PropertyTypeUpdate update = new PropertyTypeUpdate();
+        update.setTypeId(new PropertyTypePermId("COMMENT"));
+        update.setDescription("");
+        
+        assertUserFailureException(update, "Description cannot be empty.");
+    }
+    
+    @Test
+    public void testNullLabel()
+    {
+        PropertyTypeUpdate update = new PropertyTypeUpdate();
+        update.setTypeId(new PropertyTypePermId("COMMENT"));
+        update.setLabel(null);
+        
+        assertUserFailureException(update, "Label cannot be empty.");
+    }
+    
+    @Test
+    public void testEmptyLabel()
+    {
+        PropertyTypeUpdate update = new PropertyTypeUpdate();
+        update.setTypeId(new PropertyTypePermId("COMMENT"));
+        update.setLabel("");
+        
+        assertUserFailureException(update, "Label cannot be empty.");
+    }
+
+    @Test
+    public void testInvalidSchema()
+    {
+        PropertyTypeUpdate update = new PropertyTypeUpdate();
+        update.setTypeId(new PropertyTypePermId("COMMENT"));
+        update.setSchema("blabla");
+
+        assertUserFailureException(update, "isn't a well formed XML document. Content is not allowed in prolog.");
+    }
+
+    @Test
+    public void testInvalidTransformation()
+    {
+        PropertyTypeUpdate update = new PropertyTypeUpdate();
+        update.setTypeId(new PropertyTypePermId("COMMENT"));
+        update.setTransformation(CreatePropertyTypeTest.EXAMPLE_INCORRECT_XSLT);
+
+        assertUserFailureException(update, "Provided XSLT isn't valid.");
+    }
+
+
+    @Test(dataProvider = "usersNotAllowedToUpdatePropertyTypes")
+    public void testUpdateWithUserCausingAuthorizationFailure(final String user)
+    {
+        PropertyTypePermId typeId = new PropertyTypePermId("COMMENT");
+        assertUnauthorizedObjectAccessException(new IDelegatedAction()
+            {
+                @Override
+                public void execute()
+                {
+                    String sessionToken = v3api.login(user, PASSWORD);
+                    PropertyTypeUpdate update = new PropertyTypeUpdate();
+                    update.setTypeId(typeId);
+                    update.setDescription("test");
+                    v3api.updatePropertyTypes(sessionToken, Arrays.asList(update));
+                }
+            }, typeId);
+    }
+
+    @DataProvider
+    Object[][] usersNotAllowedToUpdatePropertyTypes()
+    {
+        return createTestUsersProvider(TEST_GROUP_ADMIN, TEST_GROUP_OBSERVER, TEST_GROUP_POWERUSER,
+                TEST_INSTANCE_OBSERVER, TEST_OBSERVER_CISD, TEST_POWER_USER_CISD, TEST_SPACE_USER);
+    }
+        private void assertUserFailureException(PropertyTypeUpdate update, String expectedMessage)
+    {
+        // Given
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+        assertUserFailureException(new IDelegatedAction()
+            {
+                @Override
+                public void execute()
+                {
+                    // When
+                    v3api.updatePropertyTypes(sessionToken, Arrays.asList(update));
+                }
+            },
+                // Then
+                expectedMessage);
+        v3api.logout(sessionToken);
+    }
+
+}
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceTest.java
index 0e024010f360f05ee3961eca2182d9d1665e1fbd..75b4b9d84928ed534455fedd0870c2e27ae932df 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceTest.java
@@ -36,8 +36,9 @@ import org.jmock.Expectations;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create.CreateDataSetsOperationResult;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.IOperationContext;
-import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.dataset.ICreateDataSetExecutor;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.operation.IOperationsExecutor;
 import ch.rinn.restrictions.Friend;
 import ch.systemsx.cisd.authentication.ISessionManager;
 import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
@@ -184,7 +185,7 @@ public class ETLServiceTest extends AbstractServerTestCase
 
     private IManagedPropertyEvaluatorFactory managedPropertyEvaluatorFactory;
 
-    private ICreateDataSetExecutor createDataSetExecutor;
+    private IOperationsExecutor operationsExecutor;
 
     private PersonPE sessionPerson;
 
@@ -209,7 +210,7 @@ public class ETLServiceTest extends AbstractServerTestCase
         sessionPerson = new PersonPE();
         session.setPerson(sessionPerson);
         managedPropertyEvaluatorFactory = new ManagedPropertyEvaluatorFactory(null, new TestJythonEvaluatorPool());
-        createDataSetExecutor = context.mock(ICreateDataSetExecutor.class);
+        operationsExecutor = context.mock(IOperationsExecutor.class);
 
         prepareDataSetRegistrationCache();
     }
@@ -1074,8 +1075,7 @@ public class ETLServiceTest extends AbstractServerTestCase
         final DataSetBatchUpdatesDTO dataSetUpdate = new DataSetBatchUpdatesDTO();
         dataSetUpdate.setDatasetId(CommonTestUtils.TECH_ID);
         dataSetUpdate.setFileFormatTypeCode("new-file-format");
-        dataSetUpdate.setModifiedContainedDatasetCodesOrNull(new String[]
-        { "c1", "c2" });
+        dataSetUpdate.setModifiedContainedDatasetCodesOrNull(new String[] { "c1", "c2" });
 
         final MetaprojectPE metaprojectPE = new MetaprojectPE();
 
@@ -1271,7 +1271,8 @@ public class ETLServiceTest extends AbstractServerTestCase
                     allowing(entityOperationChecker).assertInstanceSampleCreationAllowed(with(any(IAuthSession.class)), with(any(List.class)));
                     allowing(entityOperationChecker).assertInstanceSampleUpdateAllowed(with(any(IAuthSession.class)), with(any(List.class)));
 
-                    one(createDataSetExecutor).create(with(any(IOperationContext.class)), with(any(List.class)));
+                    one(operationsExecutor).execute(with(any(IOperationContext.class)), with(any(List.class)));
+                    will(returnValue(Collections.singletonList(new CreateDataSetsOperationResult(Collections.emptyList()))));
                 }
             });
 
@@ -1319,8 +1320,7 @@ public class ETLServiceTest extends AbstractServerTestCase
         final DataSetBatchUpdatesDTO dataSetUpdate = new DataSetBatchUpdatesDTO();
         dataSetUpdate.setDatasetId(CommonTestUtils.TECH_ID);
         dataSetUpdate.setFileFormatTypeCode("new-file-format");
-        dataSetUpdate.setModifiedContainedDatasetCodesOrNull(new String[]
-        { "c1", "c2" });
+        dataSetUpdate.setModifiedContainedDatasetCodesOrNull(new String[] { "c1", "c2" });
 
         final MetaprojectPE metaprojectPE = new MetaprojectPE();
         metaprojectPE.setOwner(CommonTestUtils.createPersonFromPrincipal(PRINCIPAL));
@@ -1540,7 +1540,7 @@ public class ETLServiceTest extends AbstractServerTestCase
                 new ServiceForDataStoreServer(authenticationService, sessionManager, daoFactory,
                         propertiesBatchManager, boFactory, dssfactory, null,
                         entityOperationChecker, dataStoreServiceRegistrator, dataSourceManager,
-                        sessionManagerForEntityOperations, managedPropertyEvaluatorFactory, createDataSetExecutor);
+                        sessionManagerForEntityOperations, managedPropertyEvaluatorFactory, operationsExecutor);
         etlService.setConversationClient(conversationClient);
         etlService.setConversationServer(conversationServer);
         etlService.setDisplaySettingsProvider(new DisplaySettingsProvider());
@@ -1596,8 +1596,7 @@ public class ETLServiceTest extends AbstractServerTestCase
             DataStoreServiceKind serviceKind, String key)
     {
         // unknown data set type codes should be silently discarded
-        return new DatastoreServiceDescription(key, key, new String[]
-        { DATA_SET_TYPE_CODE, UNKNOWN_DATA_SET_TYPE_CODE }, key, serviceKind);
+        return new DatastoreServiceDescription(key, key, new String[] { DATA_SET_TYPE_CODE, UNKNOWN_DATA_SET_TYPE_CODE }, key, serviceKind);
     }
 
     @SuppressWarnings("deprecation")
@@ -1605,8 +1604,7 @@ public class ETLServiceTest extends AbstractServerTestCase
             DataStoreServiceKind serviceKind, String key, String regex)
     {
         // wildcards should be handled correctly
-        return new DatastoreServiceDescription(key, key, new String[]
-        { regex }, key, serviceKind);
+        return new DatastoreServiceDescription(key, key, new String[] { regex }, key, serviceKind);
     }
 
     private void assignRoles(PersonPE person)
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/authorization/ProjectAuthorizationUser.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/authorization/ProjectAuthorizationUser.java
index 7b3c1fe83fc63abd017c798994c2ef37cc9b15f8..300ff6ebef92861fe81b0848e7faf91789a594f4 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/authorization/ProjectAuthorizationUser.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/authorization/ProjectAuthorizationUser.java
@@ -38,6 +38,8 @@ public class ProjectAuthorizationUser
 
     public static final String TEST_PROJECT_PA_OFF = "test_project_pa_off";
 
+    public static final String TEST_GROUP = "admin";
+
     public static final String ETL_SERVER = "etlserver";
 
     public static final String TEST_SPACE_ETL_SERVER = "test_space_etl_server";
@@ -182,7 +184,7 @@ public class ProjectAuthorizationUser
         testProjectPAOn.setTestProjectUser(true);
         testProjectPAOn.setPAEnabled(true);
 
-        ProjectAuthorizationUser testGroup = new ProjectAuthorizationUser("admin");
+        ProjectAuthorizationUser testGroup = new ProjectAuthorizationUser(TEST_GROUP);
         testGroup.setTestGroupUser(true);
 
         return new Object[][] {
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/IApplicationServerApi.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/IApplicationServerApi.java
index cc939528d9d289f2ca0bdf35cefb2d089af5be71..318a66084711054ffa9314270fc110b7251b2d76 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/IApplicationServerApi.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/IApplicationServerApi.java
@@ -126,6 +126,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.id.IPropertyTypeId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.id.PropertyTypePermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.search.PropertyAssignmentSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.search.PropertyTypeSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.update.PropertyTypeUpdate;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.RoleAssignment;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.create.RoleAssignmentCreation;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.delete.RoleAssignmentDeletionOptions;
@@ -287,6 +288,8 @@ public interface IApplicationServerApi extends IRpcService
 
     @TechPreview
     public void updateExternalDataManagementSystems(String sessionToken, List<ExternalDmsUpdate> externalDmsUpdates);
+    
+    public void updatePropertyTypes(String sessionToken, List<PropertyTypeUpdate> propertyTypeUpdates);
 
     public void updateVocabularies(String sessionToken, List<VocabularyUpdate> vocabularyUpdates);
     
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/update/PropertyTypeUpdate.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/update/PropertyTypeUpdate.java
new file mode 100644
index 0000000000000000000000000000000000000000..92cefe4732bde2fe83e2d8671401949a5174e501
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/update/PropertyTypeUpdate.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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.ethz.sis.openbis.generic.asapi.v3.dto.property.update;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.FieldUpdateValue;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.IObjectUpdate;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.IUpdate;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.id.IPropertyTypeId;
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+@JsonObject("as.dto.property.update.PropertyTypeUpdate")
+public class PropertyTypeUpdate implements IUpdate, IObjectUpdate<IPropertyTypeId>
+{
+    private static final long serialVersionUID = 1L;
+    
+    @JsonProperty
+    private IPropertyTypeId typeId;
+
+    @JsonProperty
+    private FieldUpdateValue<String> label = new FieldUpdateValue<String>();
+    
+    @JsonProperty
+    private FieldUpdateValue<String> description = new FieldUpdateValue<String>();
+    
+    @JsonProperty
+    private FieldUpdateValue<String> schema = new FieldUpdateValue<String>();
+    
+    @JsonProperty
+    private FieldUpdateValue<String> transformation = new FieldUpdateValue<String>();
+
+    @Override
+    @JsonIgnore
+    public IPropertyTypeId getObjectId()
+    {
+        return getTypeId();
+    }
+
+    @JsonIgnore
+    public IPropertyTypeId getTypeId()
+    {
+        return typeId;
+    }
+
+    @JsonIgnore
+    public void setTypeId(IPropertyTypeId typeId)
+    {
+        this.typeId = typeId;
+    }
+
+    @JsonIgnore
+    public FieldUpdateValue<String> getLabel()
+    {
+        return label;
+    }
+
+    @JsonIgnore
+    public void setLabel(String label)
+    {
+        this.label.setValue(label);
+    }
+    
+    @JsonIgnore
+    public FieldUpdateValue<String> getDescription()
+    {
+        return description;
+    }
+    
+    @JsonIgnore
+    public void setDescription(String description)
+    {
+        this.description.setValue(description);
+    }
+
+    @JsonIgnore
+    public FieldUpdateValue<String> getSchema()
+    {
+        return schema;
+    }
+
+    @JsonIgnore
+    public void setSchema(String schema)
+    {
+        this.schema.setValue(schema);
+    }
+    
+    @JsonIgnore
+    public FieldUpdateValue<String> getTransformation()
+    {
+        return transformation;
+    }
+
+    @JsonIgnore
+    public void setTransformation(String transformation)
+    {
+        this.transformation.setValue(transformation);
+    }
+    
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/update/UpdatePropertyTypesOperation.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/update/UpdatePropertyTypesOperation.java
new file mode 100644
index 0000000000000000000000000000000000000000..eb365c81e2f15a483ee2dc0d2f078e8eed87fbc7
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/update/UpdatePropertyTypesOperation.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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.ethz.sis.openbis.generic.asapi.v3.dto.property.update;
+
+import java.util.List;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.UpdateObjectsOperation;
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+@JsonObject("as.dto.property.update.UpdatePropertyTypesOperation")
+public class UpdatePropertyTypesOperation extends UpdateObjectsOperation<PropertyTypeUpdate>
+{
+
+    private static final long serialVersionUID = 1L;
+
+    private UpdatePropertyTypesOperation()
+    {
+    }
+
+    public UpdatePropertyTypesOperation(PropertyTypeUpdate... updates)
+    {
+        super(updates);
+    }
+
+    public UpdatePropertyTypesOperation(List<PropertyTypeUpdate> updates)
+    {
+        super(updates);
+    }
+
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/update/UpdatePropertyTypesOperationResult.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/update/UpdatePropertyTypesOperationResult.java
new file mode 100644
index 0000000000000000000000000000000000000000..398cd9768d5b47876f38ac209d7a3c7c372af9e2
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/update/UpdatePropertyTypesOperationResult.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 ETH Zuerich, SIS
+ *
+ * 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.ethz.sis.openbis.generic.asapi.v3.dto.property.update;
+
+import java.util.List;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.update.UpdateObjectsOperationResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.id.PropertyTypePermId;
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author Franz-Josef Elmer
+ *
+ */
+@JsonObject("as.dto.property.update.UpdatePropertyTypesOperationResult")
+public class UpdatePropertyTypesOperationResult extends UpdateObjectsOperationResult<PropertyTypePermId>
+{
+
+    private static final long serialVersionUID = 1L;
+
+    @SuppressWarnings("unused")
+    private UpdatePropertyTypesOperationResult()
+    {
+    }
+
+    public UpdatePropertyTypesOperationResult(List<PropertyTypePermId> ids)
+    {
+        super(ids);
+    }
+
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/dssapi/v3/IDataStoreServerApi.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/dssapi/v3/IDataStoreServerApi.java
index cdabfb18ec05f0f8900a57f16a7db79717ef471d..ecf8ed13000c7891e1e7b08154e08708e6d84dfe 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/dssapi/v3/IDataStoreServerApi.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/dssapi/v3/IDataStoreServerApi.java
@@ -22,6 +22,7 @@ import java.util.List;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchResult;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.DataSetPermId;
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.dataset.create.FullDataSetCreation;
+import ch.ethz.sis.openbis.generic.dssapi.v3.dto.dataset.create.UploadedDataSetCreation;
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.DataSetFile;
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.download.DataSetFileDownloadOptions;
 import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.fetchoptions.DataSetFileFetchOptions;
@@ -52,6 +53,8 @@ public interface IDataStoreServerApi extends IRpcService
     public InputStream downloadFiles(String sessionToken, List<? extends IDataSetFileId> fileIds,
             DataSetFileDownloadOptions downloadOptions);
 
+    public DataSetPermId createUploadedDataSet(String sessionToken, UploadedDataSetCreation newDataSet);
+
     @TechPreview
     public List<DataSetPermId> createDataSets(String sessionToken, List<FullDataSetCreation> newDataSets);
 
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/dssapi/v3/dto/dataset/create/UploadedDataSetCreation.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/dssapi/v3/dto/dataset/create/UploadedDataSetCreation.java
new file mode 100644
index 0000000000000000000000000000000000000000..09a96a34885be24dbb07041a18ef9c2818f6a690
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/dssapi/v3/dto/dataset/create/UploadedDataSetCreation.java
@@ -0,0 +1,102 @@
+package ch.ethz.sis.openbis.generic.dssapi.v3.dto.dataset.create;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.create.ICreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id.IDataSetId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.id.IEntityTypeId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.IExperimentId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.ISampleId;
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author pkupczyk
+ */
+@JsonObject("dss.dto.dataset.create.UploadedDataSetCreation")
+public class UploadedDataSetCreation implements ICreation
+{
+    private static final long serialVersionUID = 1L;
+
+    @JsonProperty
+    private IEntityTypeId typeId;
+
+    @JsonProperty
+    private IExperimentId experimentId;
+
+    @JsonProperty
+    private ISampleId sampleId;
+
+    @JsonProperty
+    private Map<String, String> properties = new HashMap<String, String>();
+
+    @JsonProperty
+    private List<? extends IDataSetId> parentIds;
+
+    @JsonProperty
+    private String uploadId;
+
+    public IEntityTypeId getTypeId()
+    {
+        return typeId;
+    }
+
+    public void setTypeId(IEntityTypeId typeId)
+    {
+        this.typeId = typeId;
+    }
+
+    public IExperimentId getExperimentId()
+    {
+        return experimentId;
+    }
+
+    public void setExperimentId(IExperimentId experimentId)
+    {
+        this.experimentId = experimentId;
+    }
+
+    public ISampleId getSampleId()
+    {
+        return sampleId;
+    }
+
+    public void setSampleId(ISampleId sampleId)
+    {
+        this.sampleId = sampleId;
+    }
+
+    public Map<String, String> getProperties()
+    {
+        return properties;
+    }
+
+    public void setProperties(Map<String, String> properties)
+    {
+        this.properties = properties;
+    }
+
+    public List<? extends IDataSetId> getParentIds()
+    {
+        return parentIds;
+    }
+
+    public void setParentIds(List<? extends IDataSetId> parentIds)
+    {
+        this.parentIds = parentIds;
+    }
+
+    public String getUploadId()
+    {
+        return uploadId;
+    }
+
+    public void setUploadId(String uploadId)
+    {
+        this.uploadId = uploadId;
+    }
+
+}
diff --git a/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt b/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt
index 4b236e9ae5ce08a1a26dc762ce19fcfacd10a77f..654e9f972358f1867c5c94fce0f7ba4aed7bb6fb 100644
--- a/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt
+++ b/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt
@@ -1882,3 +1882,13 @@ set Material Type Id
 get Property Types
 Get Property Types Operation
 Get Property Types Operation Result
+
+create Uploaded Data Set
+get Upload Id
+set Upload Id
+Uploaded Data Set Creation
+
+Property Type Update
+update Property Types
+Update Property Types Operation
+Update Property Types Operation Result