diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java index f1d5c1798b3d276bd4d3e4f1d6587c7302e232b5..2a03aec30459d386e15eb82ac25cf716da91c21b 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLService.java @@ -22,6 +22,7 @@ import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -29,12 +30,11 @@ import java.util.Set; import javax.servlet.http.HttpServletRequest; -import org.apache.commons.lang.StringUtils; - import ch.systemsx.cisd.authentication.DefaultSessionManager; import ch.systemsx.cisd.authentication.DummyAuthenticationService; import ch.systemsx.cisd.authentication.IAuthenticationService; import ch.systemsx.cisd.authentication.ISessionManager; +import ch.systemsx.cisd.common.collections.GroupingDAG; import ch.systemsx.cisd.common.conversation.context.ServiceConversationsThreadContext; import ch.systemsx.cisd.common.conversation.progress.IServiceConversationProgressListener; import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException; @@ -1662,19 +1662,81 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements IServiceConversationProgressListener progress, boolean authorize) { List<NewSample> newSamples = operationDetails.getSampleRegistrations(); - List<NewSample> containerSamples = new ArrayList<NewSample>(); - List<NewSample> containedSamples = new ArrayList<NewSample>(); - List<NewSample> instanceSamples = new ArrayList<NewSample>(); - List<NewSample> spaceSamples = new ArrayList<NewSample>(); - for (NewSample newSample : newSamples) + + if (authorize) { - if (StringUtils.isEmpty(newSample.getContainerIdentifierForNewSample())) + authorizeSampleCreation(session, newSamples); + } + String userIdOrNull = operationDetails.tryUserIdOrNull(); + PersonPE registratorOrNull = tryFindPersonForUserIdOrEmail(userIdOrNull); + final ISampleTable sampleTable = businessObjectFactory.createSampleTable(session); + + List<List<NewSample>> sampleGroups = splitIntoDependencyGroups(newSamples); + + for (List<NewSample> groupOfSamples : sampleGroups) + { + BatchOperationExecutor.executeInBatches(new SampleBatchRegistration(sampleTable, + groupOfSamples, registratorOrNull), getBatchSize(operationDetails), progress, + "createContainerSamples"); + } + return newSamples.size(); + } + + /** + * Splits the samples using the grouping dag into groups, that can be executed in batches one + * after another, that samples in later batches depend only on the samples from earlier batches + */ + private List<List<NewSample>> splitIntoDependencyGroups(List<NewSample> newSamples) + { + HashMap<String, NewSample> identifierToSample = new HashMap<String, NewSample>(); + HashMap<String, Collection<String>> adjacencyGraph = + new HashMap<String, Collection<String>>(); + for (NewSample sample : newSamples) + { + identifierToSample.put(sample.getIdentifier(), sample); + adjacencyGraph.put(sample.getIdentifier(), new LinkedList<String>()); + } + + for (NewSample sample : newSamples) + { + if (sample.getContainerIdentifier() != null) { - containerSamples.add(newSample); - } else + adjacencyGraph.get(sample.getContainerIdentifier()).add(sample.getIdentifier()); + } + String[] parents = sample.getParentsOrNull(); + if (parents != null) { - containedSamples.add(newSample); + for (String parent : parents) + { + adjacencyGraph.get(parent).add(sample.getIdentifier()); + } } + } + + List<List<String>> identifierGroups = GroupingDAG.groupByDepencies(adjacencyGraph); + + List<List<NewSample>> sampleGroups = new LinkedList<List<NewSample>>(); + + for (List<String> listOfIdentifiers : identifierGroups) + { + List<NewSample> listOfSamples = new LinkedList<NewSample>(); + + for (String identifier : listOfIdentifiers) + { + listOfSamples.add(identifierToSample.get(identifier)); + } + sampleGroups.add(listOfSamples); + } + return sampleGroups; + } + + private void authorizeSampleCreation(Session session, List<NewSample> newSamples) + { + List<NewSample> instanceSamples = new ArrayList<NewSample>(); + List<NewSample> spaceSamples = new ArrayList<NewSample>(); + + for (NewSample newSample : newSamples) + { SampleIdentifier sampleIdentifier = SampleIdentifierFactory.parse(newSample); if (sampleIdentifier.isDatabaseInstanceLevel()) { @@ -1685,29 +1747,8 @@ public class ETLService extends AbstractCommonServer<IETLLIMSService> implements } } - if (authorize) - { - checkInstanceSampleCreationAllowed(session, instanceSamples); - checkSpaceSampleCreationAllowed(session, spaceSamples); - } - - String userIdOrNull = operationDetails.tryUserIdOrNull(); - PersonPE registratorOrNull = tryFindPersonForUserIdOrEmail(userIdOrNull); - - final ISampleTable sampleTable = businessObjectFactory.createSampleTable(session); - - // in the first pass register samples without container to avoid dependency inversion - BatchOperationExecutor.executeInBatches(new SampleBatchRegistration(sampleTable, - containerSamples, registratorOrNull), getBatchSize(operationDetails), progress, - "createContainerSamples"); - - // register samples with a container identifier - // (container should have been created in the first pass) - BatchOperationExecutor.executeInBatches(new SampleBatchRegistration(sampleTable, - containedSamples, registratorOrNull), getBatchSize(operationDetails), progress, - "createContainedSamples"); - - return newSamples.size(); + checkInstanceSampleCreationAllowed(session, instanceSamples); + checkSpaceSampleCreationAllowed(session, spaceSamples); } private void checkInstanceSampleCreationAllowed(Session session, List<NewSample> instanceSamples)