diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/dss/phosphonetx/server/plugins/MsInjectionCopier.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/dss/phosphonetx/server/plugins/MsInjectionCopier.java new file mode 100644 index 0000000000000000000000000000000000000000..425327175037945004da898ac644e8e34e508da4 --- /dev/null +++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/dss/phosphonetx/server/plugins/MsInjectionCopier.java @@ -0,0 +1,281 @@ +/* + * Copyright 2010 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.dss.phosphonetx.server.plugins; + +import java.io.File; +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.log4j.Logger; + +import ch.systemsx.cisd.common.exceptions.Status; +import ch.systemsx.cisd.common.filesystem.BooleanStatus; +import ch.systemsx.cisd.common.filesystem.FileUtilities; +import ch.systemsx.cisd.common.filesystem.IPathCopier; +import ch.systemsx.cisd.common.filesystem.ssh.ISshCommandExecutor; +import ch.systemsx.cisd.common.highwatermark.HostAwareFile; +import ch.systemsx.cisd.common.highwatermark.HostAwareFileWithHighwaterMark; +import ch.systemsx.cisd.common.logging.LogCategory; +import ch.systemsx.cisd.common.logging.LogFactory; +import ch.systemsx.cisd.common.process.ProcessResult; +import ch.systemsx.cisd.common.utilities.PropertyUtils; +import ch.systemsx.cisd.common.utilities.StringUtilities; +import ch.systemsx.cisd.openbis.dss.generic.server.plugins.standard.Copier; +import ch.systemsx.cisd.openbis.dss.generic.server.plugins.standard.DataSetCopier; +import ch.systemsx.cisd.openbis.dss.generic.server.plugins.standard.IPathCopierFactory; +import ch.systemsx.cisd.openbis.dss.generic.server.plugins.standard.ISshCommandExecutorFactory; +import ch.systemsx.cisd.openbis.dss.generic.shared.IPostRegistrationDatasetHandler; +import ch.systemsx.cisd.openbis.dss.generic.shared.dto.DataSetInformation; + +/** + * + * + * @author Franz-Josef Elmer + */ +class MsInjectionCopier implements Serializable, IPostRegistrationDatasetHandler +{ + + private static final class ExceptionWithStatus extends RuntimeException + { + private static final long serialVersionUID = 1L; + private final Status status; + + ExceptionWithStatus(Status status) + { + this.status = status; + } + + public Status getStatus() + { + return status; + } + } + + private static interface IExecutor + { + BooleanStatus exists(File file); + void deleteFolder(File folder); + void copyDataSet(File dataSet, File destination); + void renameTo(File newFile, File oldFile); + } + + private static final class LocalExcecutor implements IExecutor + { + public BooleanStatus exists(File file) + { + return BooleanStatus.createFromBoolean(file.exists()); + } + + public void deleteFolder(File folder) + { + boolean successful = folder.delete(); + if (successful == false) + { + operationLog.error("Deletion of '" + folder + "' failed for some unknown reason."); + throw new ExceptionWithStatus(Status.createError("couldn't delete")); + } + } + + public void copyDataSet(File dataSet, File destination) + { + try + { + FileUtilities.copyFileTo(dataSet, destination, true); + } catch (Exception ex) + { + operationLog.error("Couldn't copy '" + dataSet + "' to '" + destination + "'."); + throw new ExceptionWithStatus(Status.createError("copy failed")); + } + } + + public void renameTo(File newFile, File oldFile) + { + boolean result = oldFile.renameTo(newFile); + if (result == false) + { + operationLog.error("Couldn't rename '" + oldFile + "' to '" + newFile + "'."); + throw new ExceptionWithStatus(Status.createError("rename failed")); + } + } + + } + + private static final class RemoteExecutor implements IExecutor + { + private final ISshCommandExecutor executor; + private final IPathCopier copier; + private final String host; + private final String rsyncModuleNameOrNull; + private final String rsyncPasswordFileOrNull; + + public RemoteExecutor(ISshCommandExecutor executor, IPathCopier copier, String host, + String rsyncModuleNameOrNull, String rsyncPasswordFileOrNull) + { + this.executor = executor; + this.copier = copier; + this.host = host; + this.rsyncModuleNameOrNull = rsyncModuleNameOrNull; + this.rsyncPasswordFileOrNull = rsyncPasswordFileOrNull; + } + + public BooleanStatus exists(File file) + { + return executor.exists(file, DataSetCopier.SSH_TIMEOUT_MILLIS); + } + + public void deleteFolder(File folder) + { + ProcessResult result = + executor.tryExecuteCommandRemotely("delete " + folder.getPath(), + DataSetCopier.SSH_TIMEOUT_MILLIS); + if (result.isOK() == false) + { + operationLog.error("Remote deletion of '" + folder + + "' failed with exit value: " + result.getExitValue()); + throw new ExceptionWithStatus(Status.createError("couldn't delete")); + } + List<String> output = result.getOutput(); + if (output.isEmpty() == false) + { + operationLog.error("Remote deletion of '" + folder + + "' seemed to be successful but produced following output:\n" + + StringUtilities.concatenateWithNewLine(output)); + throw new ExceptionWithStatus(Status.createError("deletion leads to a problem")); + } + } + + public void copyDataSet(File dataSet, File destination) + { + Status result = + copier.copyToRemote(dataSet, destination, host, rsyncModuleNameOrNull, + rsyncPasswordFileOrNull); + if (result.isError()) + { + throw new ExceptionWithStatus(result); + } + } + + public void renameTo(File newFile, File oldFile) + { + ProcessResult result = + executor.tryExecuteCommandRemotely("mv " + oldFile.getPath() + " " + + newFile.getPath(), DataSetCopier.SSH_TIMEOUT_MILLIS); + if (result.isOK() == false) + { + operationLog.error("Remote move of '" + oldFile + + "' to '" + newFile + "' failed with exit value: " + result.getExitValue()); + throw new ExceptionWithStatus(Status.createError("couldn't move")); + } + List<String> output = result.getOutput(); + if (output.isEmpty() == false) + { + operationLog.error("Remote move of '" + oldFile + + "' to '" + newFile + + "' seemed to be successful but produced following output:\n" + + StringUtilities.concatenateWithNewLine(output)); + throw new ExceptionWithStatus(Status.createError("moving leads to a problem")); + } + } + } + + private static final long serialVersionUID = 1L; + + private final static Logger operationLog = + LogFactory.getLogger(LogCategory.OPERATION, MsInjectionCopier.class); + + private final IExecutor executor; + + private final File destination; + + MsInjectionCopier(Properties properties, IPathCopierFactory pathCopierFactory, + ISshCommandExecutorFactory sshCommandExecutorFactory) + { + String hostFile = + PropertyUtils.getMandatoryProperty(properties, DataSetCopier.DESTINATION_KEY); + HostAwareFile hostAwareFile = HostAwareFileWithHighwaterMark.create(hostFile, -1); + String hostOrNull = hostAwareFile.tryGetHost(); + destination = hostAwareFile.getFile(); + if (hostOrNull == null) + { + executor = new LocalExcecutor(); + } else + { + File sshExecutable = Copier.getExecutable(properties, DataSetCopier.SSH_EXEC); + File rsyncExecutable = Copier.getExecutable(properties, DataSetCopier.RSYNC_EXEC); + IPathCopier copier = pathCopierFactory.create(rsyncExecutable, sshExecutable); + copier.check(); + String rsyncModule = hostAwareFile.tryGetRsyncModule(); + String rsyncPasswordFile = + properties.getProperty(DataSetCopier.RSYNC_PASSWORD_FILE_KEY); + FileUtilities.checkPathCopier(copier, hostOrNull, null, rsyncModule, rsyncPasswordFile); + ISshCommandExecutor sshCommandExecutor = + sshCommandExecutorFactory.create(sshExecutable, hostOrNull); + executor = + new RemoteExecutor(sshCommandExecutor, copier, hostOrNull, rsyncModule, + rsyncPasswordFile); + } + } + + public void undoLastOperation() + { + } + + public Status handle(File originalData, DataSetInformation dataSetInformation, + Map<String, String> parameterBindings) + { + try + { + String target = createTargetFolderName(dataSetInformation, parameterBindings); + File targetFolder = new File(destination, target); + deleteTargetFolder(targetFolder); + executor.copyDataSet(originalData, destination); + executor.renameTo(targetFolder, new File(destination, originalData.getName())); + return null; + + } catch (ExceptionWithStatus ex) + { + return ex.getStatus(); + } + } + + private String createTargetFolderName(DataSetInformation dataSetInformation, + Map<String, String> parameterBindings) + { + String dataSetTypeCode = dataSetInformation.getDataSetType().getCode(); + String sampleCode = parameterBindings.get(dataSetInformation.getDataSetCode()); + return (sampleCode == null ? "sample-unknown" : sampleCode) + "_" + dataSetTypeCode; + } + + private void deleteTargetFolder(File targetFolder) + { + BooleanStatus targetExists = executor.exists(targetFolder); + if (targetExists.isError()) + { + operationLog.error("Could not check existence of '" + targetFolder + "': " + + targetExists.tryGetMessage()); + throw new ExceptionWithStatus(Status.createError("couldn't check existence")); + } + if (targetExists.isSuccess()) + { + executor.deleteFolder(targetFolder); + } + } + +} + diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/dss/phosphonetx/server/plugins/MsInjectionDataSetCopier.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/dss/phosphonetx/server/plugins/MsInjectionDataSetCopier.java new file mode 100644 index 0000000000000000000000000000000000000000..b89716caebfb8ae76582f848c7d4ea06a98a5fe6 --- /dev/null +++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/dss/phosphonetx/server/plugins/MsInjectionDataSetCopier.java @@ -0,0 +1,49 @@ +/* + * Copyright 2010 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.dss.phosphonetx.server.plugins; + +import java.io.File; +import java.util.Properties; + +import ch.rinn.restrictions.Private; +import ch.systemsx.cisd.openbis.dss.generic.server.plugins.standard.AbstractDropboxProcessingPlugin; +import ch.systemsx.cisd.openbis.dss.generic.server.plugins.standard.IPathCopierFactory; +import ch.systemsx.cisd.openbis.dss.generic.server.plugins.standard.ISshCommandExecutorFactory; +import ch.systemsx.cisd.openbis.dss.generic.server.plugins.standard.RsyncCopierFactory; +import ch.systemsx.cisd.openbis.dss.generic.server.plugins.standard.SshCommandExecutorFactory; + +/** + * + * + * @author Franz-Josef Elmer + */ +public class MsInjectionDataSetCopier extends AbstractDropboxProcessingPlugin +{ + private static final long serialVersionUID = 1L; + + public MsInjectionDataSetCopier(Properties properties, File storeRoot) + { + this(properties, storeRoot, new RsyncCopierFactory(), new SshCommandExecutorFactory()); + } + + @Private MsInjectionDataSetCopier(Properties properties, File storeRoot, IPathCopierFactory pathCopierFactory, + ISshCommandExecutorFactory sshCommandExecutorFactory) + { + super(properties, storeRoot, new MsInjectionCopier(properties, pathCopierFactory, + sshCommandExecutorFactory)); + } +} diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/RawDataServiceInternal.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/RawDataServiceInternal.java index c56856f69e52feaba2a14742d556ebf4c9d1f090..b98389ce70508e1832eef1b14f27e5304ca34f8a 100644 --- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/RawDataServiceInternal.java +++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/server/RawDataServiceInternal.java @@ -102,6 +102,7 @@ public class RawDataServiceInternal extends AbstractServer<IRawDataServiceIntern List<MsInjectionSample> samples = loadAllRawDataSamples(session); Set<Long> sampleIDs = new HashSet<Long>(); List<String> dataSetCodes = new ArrayList<String>(); + Map<String, String> parameterBindings = new HashMap<String, String>(); for (MsInjectionSample sample : samples) { if (RAW_DATA_SAMPLE_VALIDATOR.isValid(person, sample)) @@ -111,7 +112,9 @@ public class RawDataServiceInternal extends AbstractServer<IRawDataServiceIntern ExternalData latestDataSet = latestDataSets.get(dataSetType); if (latestDataSet != null) { - dataSetCodes.add(latestDataSet.getCode()); + String code = latestDataSet.getCode(); + dataSetCodes.add(code); + parameterBindings.put(code, sample.getSample().getCode()); } } } @@ -119,7 +122,6 @@ public class RawDataServiceInternal extends AbstractServer<IRawDataServiceIntern String dataStoreServerCode = findDataStoreServer(dataSetProcessingKey); IExternalDataTable externalDataTable = businessObjectFactory.createExternalDataTable(session); - Map<String, String> parameterBindings = new HashMap<String, String>(); externalDataTable.processDatasets(dataSetProcessingKey, dataStoreServerCode, dataSetCodes, parameterBindings); }