diff --git a/rtd_cina/source/java/ch/systemsx/cisd/cina/client/util/cli/CommandGetReplica.java b/rtd_cina/source/java/ch/systemsx/cisd/cina/client/util/cli/CommandGetReplica.java index 2382b597a5a8959b454d5de4169a71fedd1b4ebc..0eddef3c95b61327117f2ef06dfa5423230d64f6 100644 --- a/rtd_cina/source/java/ch/systemsx/cisd/cina/client/util/cli/CommandGetReplica.java +++ b/rtd_cina/source/java/ch/systemsx/cisd/cina/client/util/cli/CommandGetReplica.java @@ -18,6 +18,7 @@ package ch.systemsx.cisd.cina.client.util.cli; import java.io.File; import java.util.ArrayList; +import java.util.List; import ch.systemsx.cisd.args4j.Option; import ch.systemsx.cisd.cina.client.util.v1.ICinaUtilities; @@ -37,11 +38,24 @@ public class CommandGetReplica extends @Option(name = "o", longName = "output", usage = "Path for output") private String output = ""; + public String tryBundleMetadataOwnerIdentifier() + { + String replicaId = getArguments().get(0); + if (replicaId.length() > 0) + { + return replicaId.toUpperCase(); + } + return null; + } + public ArrayList<String> getReplicaIdentifiers() { ArrayList<String> replicaIds = new ArrayList<String>(); - for (String replicaId : getArguments()) + List<String> args = getArguments(); + int size = args.size(); + for (int i = 1; i < size; ++i) { + String replicaId = args.get(i); if (replicaId.length() > 0) { replicaIds.add(replicaId.toUpperCase()); @@ -58,7 +72,12 @@ public class CommandGetReplica extends @Override public boolean isComplete() { - if (getArguments().size() < 1) + if (getArguments().size() < 2) + { + return false; + } + + if (null == tryBundleMetadataOwnerIdentifier()) { return false; } @@ -92,11 +111,15 @@ public class CommandGetReplica extends File outputDir = getOutputDir(); outputDir.mkdirs(); + // Grid Id must be non-null & non-empty -- otherwise, we wouldn't be here + String gridIdentifier = arguments.tryBundleMetadataOwnerIdentifier(); + // Find all datasets connected to this sample - for (String sampleCode : arguments.getReplicaIdentifiers()) + for (String sampleIdentifier : arguments.getReplicaIdentifiers()) { ReplicaDownloader downloader = - new ReplicaDownloader(component, sampleCode, outputDir); + new ReplicaDownloader(component, gridIdentifier, sampleIdentifier, + outputDir); downloader.download(); } return ResultCode.OK; @@ -136,6 +159,6 @@ public class CommandGetReplica extends @Override protected String getRequiredArgumentsString() { - return "<replica identifier> [<replica identifier> ...]"; + return "<grid identifier> <replica identifier> [<replica identifier> ...]"; } } diff --git a/rtd_cina/source/java/ch/systemsx/cisd/cina/client/util/cli/ReplicaDownloader.java b/rtd_cina/source/java/ch/systemsx/cisd/cina/client/util/cli/ReplicaDownloader.java index 070044cee82dacc781e3cb9520a1b9d04f01afd7..cecc2db4c352af991c8665eeb804acf05fde9ee1 100644 --- a/rtd_cina/source/java/ch/systemsx/cisd/cina/client/util/cli/ReplicaDownloader.java +++ b/rtd_cina/source/java/ch/systemsx/cisd/cina/client/util/cli/ReplicaDownloader.java @@ -17,15 +17,24 @@ package ch.systemsx.cisd.cina.client.util.cli; import java.io.File; +import java.util.Collections; import java.util.List; import ch.systemsx.cisd.cina.client.util.v1.ICinaUtilities; import ch.systemsx.cisd.cina.shared.constants.BundleStructureConstants; import ch.systemsx.cisd.cina.shared.constants.CinaConstants; +import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException; +import ch.systemsx.cisd.common.exceptions.UserFailureException; import ch.systemsx.cisd.openbis.dss.client.api.v1.FileInfoDssDownloader; import ch.systemsx.cisd.openbis.dss.client.api.v1.IDataSetDss; import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.FileInfoDssDTO; import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet; +import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample; +import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria; +import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClause; +import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClauseAttribute; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifierFactory; /** * Utility class for downloading replicas. @@ -36,14 +45,18 @@ class ReplicaDownloader { private final ICinaUtilities component; - private final String replicaCode; + private final SampleIdentifier replicaIdentifier; + + private final SampleIdentifier gridIdentifier; private final File outputDir; - ReplicaDownloader(ICinaUtilities component, String code, File outputDir) + ReplicaDownloader(ICinaUtilities component, String bundleIdentifier, String replicaIdentifier, + File outputDir) { this.component = component; - this.replicaCode = code; + this.replicaIdentifier = SampleIdentifierFactory.parse(replicaIdentifier); + this.gridIdentifier = SampleIdentifierFactory.parse(bundleIdentifier); this.outputDir = outputDir; } @@ -78,10 +91,24 @@ class ReplicaDownloader } } + /** + * Retrieve the raw data and current metadata for the replica sample as well as the current + * metadata for the parent grid of the replica. + */ protected void download() { - // Find all datasets connected to this sample - List<DataSet> dataSets = component.listDataSetsForSampleCode(replicaCode); + // Get the replica and parent grid samples. + Sample replicaSample = searchForSample(replicaIdentifier); + downloadReplica(replicaSample); + + Sample gridSample = searchForSample(gridIdentifier); + downloadGrid(gridSample); + } + + private void downloadReplica(Sample replicaSample) + { + // Find all datasets connected to the replica sample + List<DataSet> dataSets = component.listDataSets(Collections.singletonList(replicaSample)); DataSet mostRecentMetadata = null; @@ -96,15 +123,7 @@ class ReplicaDownloader if (typeCode.equals(CinaConstants.METADATA_DATA_SET_TYPE_CODE)) { - if (null == mostRecentMetadata) - { - mostRecentMetadata = dataSet; - } else if (mostRecentMetadata.getRegistrationDate().compareTo( - dataSet.getRegistrationDate()) < 0) - { - // This element is newer than the current value - mostRecentMetadata = dataSet; - } + mostRecentMetadata = compareReturningMoreRecent(mostRecentMetadata, dataSet); } } @@ -115,12 +134,74 @@ class ReplicaDownloader } } - private void downloadDataSet(DataSet dataSet, String subfolderName) + private void downloadGrid(Sample parentGridSample) + { + // Find all datasets connected to the grid sample + List<DataSet> dataSets = + component.listDataSets(Collections.singletonList(parentGridSample)); + + DataSet mostRecentMetadata = null; + + for (DataSet dataSet : dataSets) + { + String typeCode = dataSet.getDataSetTypeCode(); + if (typeCode.equals(CinaConstants.METADATA_DATA_SET_TYPE_CODE)) + { + mostRecentMetadata = compareReturningMoreRecent(mostRecentMetadata, dataSet); + } + } + + // Download the most recent metadata data set + if (null != mostRecentMetadata) + { + downloadDataSet(mostRecentMetadata, null); + } + } + + private DataSet compareReturningMoreRecent(DataSet mostRecentReplicaMetadata, DataSet dataSet) + { + if (null == mostRecentReplicaMetadata) + { + return dataSet; + } else if (mostRecentReplicaMetadata.getRegistrationDate().compareTo( + dataSet.getRegistrationDate()) < 0) + { + // This element is newer than the current value + return dataSet; + } + return mostRecentReplicaMetadata; + } + + private Sample searchForSample(SampleIdentifier identifier) + { + // Get the sample + // Find the sample that matches the given code (there should only be 1) + SearchCriteria searchCriteria = new SearchCriteria(); + searchCriteria.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.CODE, + identifier.getSampleCode())); + List<Sample> samples = component.searchForSamples(searchCriteria); + if (samples.size() < 1) + { + throw new UserFailureException("No sample with specified code."); + } + + // There should only be 1 + if (samples.size() > 1) + { + throw new EnvironmentFailureException( + "Found multiple matching samples -- this should not happen. Please contact administrator to resolve this problem."); + } + return samples.get(0); + } + + private void downloadDataSet(DataSet dataSet, String subfolderNameOrNull) { IDataSetDss dataSetDss = component.getDataSet(dataSet.getCode()); FileInfoDssDTO[] fileInfos = dataSetDss.listFiles("/original/", true); - File targetDir = new File(outputDir, subfolderName); + File targetDir = + (subfolderNameOrNull != null) ? new File(outputDir, subfolderNameOrNull) + : outputDir; FileInfoDssDownloader downloader = new FileInfoDssDownloader(dataSetDss, fileInfos, targetDir, new DownloaderListener( targetDir)); diff --git a/rtd_cina/source/java/ch/systemsx/cisd/cina/client/util/v1/ICinaUtilities.java b/rtd_cina/source/java/ch/systemsx/cisd/cina/client/util/v1/ICinaUtilities.java index 4c3f406bd31c747ebba307e7bcf2f6a3fbf32604..c9fd6f29e6ab8f0433afa0acab9d11d5a86dde47 100644 --- a/rtd_cina/source/java/ch/systemsx/cisd/cina/client/util/v1/ICinaUtilities.java +++ b/rtd_cina/source/java/ch/systemsx/cisd/cina/client/util/v1/ICinaUtilities.java @@ -80,15 +80,14 @@ public interface ICinaUtilities /** * Return a list of data sets for the sample specified by code. * - * @param sampleCode The code of the sample we are interested in. It is assumed that the code is - * unique. + * @param samples The samples we want to retrieve data sets for * @return The data sets connected to the sample * @throws IllegalStateException Thrown if the user has not yet been authenticated. * @throws EnvironmentFailureException Thrown in cases where it is not possible to connect to * the server or if there are multiple samples with the given code. * @throws UserFailureException Thrown if no sample exists with the specified code. */ - public List<DataSet> listDataSetsForSampleCode(String sampleCode) throws IllegalStateException, + public List<DataSet> listDataSets(List<Sample> samples) throws IllegalStateException, EnvironmentFailureException, UserFailureException; /** diff --git a/rtd_cina/source/java/ch/systemsx/cisd/cina/client/util/v1/impl/CinaUtilitiesFacade.java b/rtd_cina/source/java/ch/systemsx/cisd/cina/client/util/v1/impl/CinaUtilitiesFacade.java index 2adf207556c6db78a17f060de0e844e3285f44cc..17f2e85d07aefcff90d3c33c03bc190a939ba077 100644 --- a/rtd_cina/source/java/ch/systemsx/cisd/cina/client/util/v1/impl/CinaUtilitiesFacade.java +++ b/rtd_cina/source/java/ch/systemsx/cisd/cina/client/util/v1/impl/CinaUtilitiesFacade.java @@ -39,8 +39,6 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Experiment; import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Project; import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample; import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria; -import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClause; -import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClauseAttribute; import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SpaceWithProjectsAndRoleAssignments; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleType; @@ -239,10 +237,10 @@ public class CinaUtilitiesFacade implements ICinaUtilities return state.listVisibleExperiments(experimentType); } - public List<DataSet> listDataSetsForSampleCode(String sampleCode) throws IllegalStateException, + public List<DataSet> listDataSets(List<Sample> samples) throws IllegalStateException, EnvironmentFailureException { - return state.listDataSetsForSampleCode(sampleCode); + return state.listDataSets(samples); } public IDataSetDss getDataSet(String dataSetCode) throws IllegalStateException, @@ -292,7 +290,7 @@ abstract class AbstractCinaFacadeState implements ICinaUtilities throw new IllegalStateException("Please log in"); } - public List<DataSet> listDataSetsForSampleCode(String sampleCode) throws IllegalStateException, + public List<DataSet> listDataSets(List<Sample> samples) throws IllegalStateException, EnvironmentFailureException { throw new IllegalStateException("Please log in"); @@ -460,7 +458,7 @@ class AuthenticatedState extends AbstractCinaFacadeState } @Override - public List<DataSet> listDataSetsForSampleCode(String sampleCode) throws IllegalStateException, + public List<DataSet> listDataSets(List<Sample> samples) throws IllegalStateException, EnvironmentFailureException { // This functionality has only been supported since version 1.1 @@ -470,23 +468,6 @@ class AuthenticatedState extends AbstractCinaFacadeState throw new EnvironmentFailureException("Server does not support this feature."); } - // Find the sample that matches the given code (there should only be 1) - SearchCriteria searchCriteria = new SearchCriteria(); - searchCriteria.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.CODE, - sampleCode)); - List<Sample> samples = service.searchForSamples(sessionToken, searchCriteria); - if (samples.size() < 1) - { - throw new UserFailureException("No sample with specified code."); - } - - // There should only be 1 - if (samples.size() > 1) - { - throw new EnvironmentFailureException( - "Found multiple matching samples -- this should not happen. Please contact administrator to resolve this problem."); - } - return service.listDataSets(sessionToken, samples); } diff --git a/rtd_cina/sourceTest/java/ch/systemsx/cisd/cina/client/util/cli/CommandGetReplicaTest.java b/rtd_cina/sourceTest/java/ch/systemsx/cisd/cina/client/util/cli/CommandGetReplicaTest.java index 0c85866972c517cbcfc50e2e05fad6a447121d2d..104974b3d77e1e81b4330b9f64c2d1dc111c1b0b 100644 --- a/rtd_cina/sourceTest/java/ch/systemsx/cisd/cina/client/util/cli/CommandGetReplicaTest.java +++ b/rtd_cina/sourceTest/java/ch/systemsx/cisd/cina/client/util/cli/CommandGetReplicaTest.java @@ -126,11 +126,16 @@ public class CommandGetReplicaTest extends AbstractFileSystemTestCase context.checking(new Expectations() { { - SearchCriteria searchCriteria = new SearchCriteria(); - searchCriteria.addMatchClause(MatchClause.createAttributeMatch( + // Expectations for the replica samples + SearchCriteria replicaSearchCriteria = new SearchCriteria(); + replicaSearchCriteria.addMatchClause(MatchClause.createAttributeMatch( MatchClauseAttribute.CODE, sampleCode)); - ArrayList<Sample> samples = new ArrayList<Sample>(); + SearchCriteria gridSearchCriteria = new SearchCriteria(); + gridSearchCriteria.addMatchClause(MatchClause.createAttributeMatch( + MatchClauseAttribute.CODE, "GRID-ID")); + + ArrayList<Sample> replicaSamples = new ArrayList<Sample>(); SampleInitializer sampInitializer = new SampleInitializer(); sampInitializer.setCode(sampleCode); sampInitializer.setId((long) 1); @@ -138,10 +143,10 @@ public class CommandGetReplicaTest extends AbstractFileSystemTestCase sampInitializer.setPermId("PERM-ID"); sampInitializer.setSampleTypeCode("SAMPLE-TYPE"); sampInitializer.setSampleTypeId((long) 1); - samples.add(new Sample(sampInitializer)); + replicaSamples.add(new Sample(sampInitializer)); - one(service).searchForSamples(SESSION_TOKEN, searchCriteria); - will(returnValue(samples)); + one(service).searchForSamples(SESSION_TOKEN, replicaSearchCriteria); + will(returnValue(replicaSamples)); ArrayList<DataSet> dataSets = new ArrayList<DataSet>(); DataSetInitializer dsInitializer = new DataSetInitializer(); @@ -162,8 +167,32 @@ public class CommandGetReplicaTest extends AbstractFileSystemTestCase dsInitializer.setRegistrationDate(new GregorianCalendar(2010, 1, 1).getTime()); dataSets.add(new DataSet(dsInitializer)); - one(service).listDataSets(SESSION_TOKEN, samples); + one(service).listDataSets(SESSION_TOKEN, replicaSamples); will(returnValue(dataSets)); + + // Expectations for the grid samples + ArrayList<Sample> gridSamples = new ArrayList<Sample>(); + sampInitializer = new SampleInitializer(); + sampInitializer.setCode("GRID-ID"); + sampInitializer.setId((long) 2); + sampInitializer.setIdentifier("SPACE/GRID-ID"); + sampInitializer.setPermId("GRID-PERM-ID"); + sampInitializer.setSampleTypeCode("GRID-SAMPLE-TYPE"); + sampInitializer.setSampleTypeId((long) 2); + gridSamples.add(new Sample(sampInitializer)); + + one(service).searchForSamples(SESSION_TOKEN, gridSearchCriteria); + will(returnValue(gridSamples)); + + ArrayList<DataSet> gridDataSets = new ArrayList<DataSet>(); + dsInitializer = new DataSetInitializer(); + dsInitializer.setCode("BUNDLE-METADATA"); + dsInitializer.setDataSetTypeCode(CinaConstants.METADATA_DATA_SET_TYPE_CODE); + dsInitializer.setRegistrationDate(new GregorianCalendar(2010, 0, 1).getTime()); + gridDataSets.add(new DataSet(dsInitializer)); + one(service).listDataSets(SESSION_TOKEN, gridSamples); + will(returnValue(gridDataSets)); + } }); } @@ -188,6 +217,14 @@ public class CommandGetReplicaTest extends AbstractFileSystemTestCase (metadataInfos.size() > 0) ? metadataInfos.toArray(new FileInfoDssDTO[metadataInfos .size()]) : new FileInfoDssDTO[0]; + ArrayList<FileInfoDssDTO> bundleMetadataInfos = + getFileInfosForPath(new File(parent, "Bundle"), + new File(parent, "Bundle/original"), "BundleMetadata", "BundleMetadata"); + final FileInfoDssDTO[] bundleMetadataInfosArray = + (bundleMetadataInfos.size() > 0) ? bundleMetadataInfos + .toArray(new FileInfoDssDTO[bundleMetadataInfos.size()]) + : new FileInfoDssDTO[0]; + context.checking(new Expectations() { { @@ -209,6 +246,14 @@ public class CommandGetReplicaTest extends AbstractFileSystemTestCase "Metadata/original/ReplicaMetadata/Metadata.txt")))); // The command should not ask for the -METADATA-OLD dataset! + + one(dssComponent).getDataSet("BUNDLE-METADATA"); + will(returnValue(dataSetDss)); + one(dataSetDss).listFiles(startPath, true); + will(returnValue(bundleMetadataInfosArray)); + one(dataSetDss).getFile(startPath + "BundleMetadata.xml"); + will(returnValue(new FileInputStream(new File(parent, + "Bundle/original/BundleMetadata.xml")))); } }); } @@ -284,9 +329,10 @@ public class CommandGetReplicaTest extends AbstractFileSystemTestCase { String[] bundleContents = outputFolder.list(); Arrays.sort(bundleContents); - assertEquals(2, bundleContents.length); + assertEquals(3, bundleContents.length); assertEquals(BundleStructureConstants.METADATA_FOLDER_NAME, bundleContents[0]); - assertEquals(BundleStructureConstants.RAW_IMAGES_FOLDER_NAME, bundleContents[1]); + assertEquals(BundleStructureConstants.BUNDLE_METADATA_FILE_NAME, bundleContents[1]); + assertEquals(BundleStructureConstants.RAW_IMAGES_FOLDER_NAME, bundleContents[2]); } @Test @@ -303,7 +349,7 @@ public class CommandGetReplicaTest extends AbstractFileSystemTestCase ResultCode exitCode = command.execute(new String[] { "-s", "url", "-u", USER_ID, "-p", PASSWORD, "-o", outputFolder.getPath(), - "REPLICA-ID" }); + "GRID-ID", "REPLICA-ID" }); assertEquals(ResultCode.OK, exitCode); @@ -333,7 +379,7 @@ public class CommandGetReplicaTest extends AbstractFileSystemTestCase ResultCode exitCode = command.execute(new String[] { "-s", "url", "-u", USER_ID, "-p", PASSWORD, "-o", outputFolder.getPath(), - "REPLICA-ID1", "REPLICA-ID2" }); + "GRID-ID", "REPLICA-ID1", "REPLICA-ID2" }); assertEquals(ResultCode.OK, exitCode); @@ -367,7 +413,7 @@ public class CommandGetReplicaTest extends AbstractFileSystemTestCase try { command.execute(new String[] - { "-s", "url", "-u", USER_ID, "-p", PASSWORD, "REPLICA-ID" }); + { "-s", "url", "-u", USER_ID, "-p", PASSWORD, "GRID-ID", "REPLICA-ID" }); fail("Command should throw an exception when run against an older version of the interface."); } catch (EnvironmentFailureException e) {