diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/cifs/DataSetCifsView.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/cifs/DataSetCifsView.java index c76784fdd0bd921dd417b390467823adfd1acaf4..bd007981b481a2aa6568af417e8c96e8f1e16dd4 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/cifs/DataSetCifsView.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/cifs/DataSetCifsView.java @@ -40,6 +40,7 @@ import org.apache.ftpserver.ftplet.FtpFile; import org.apache.log4j.Logger; import org.springframework.extensions.config.ConfigElement; +import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi; import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel; import ch.systemsx.cisd.common.logging.LogCategory; import ch.systemsx.cisd.common.logging.LogFactory; @@ -48,38 +49,44 @@ import ch.systemsx.cisd.openbis.dss.generic.server.ftp.Cache; import ch.systemsx.cisd.openbis.dss.generic.server.ftp.DSSFileSystemView; import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpPathResolverConfig; import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpPathResolverRegistry; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.IFtpPathResolverRegistry; import ch.systemsx.cisd.openbis.dss.generic.server.ftp.NonExistingFtpFile; import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider; import ch.systemsx.cisd.openbis.generic.shared.IServiceForDataStoreServer; import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationService; /** - * - * * @author Franz-Josef Elmer */ public class DataSetCifsView implements DiskInterface { private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, DataSetCifsView.class); - + private Cache cache; + private IServiceForDataStoreServer dssService; + private IGeneralInformationService generalInfoService; - - private FtpPathResolverRegistry pathResolverRegistry; + + private IApplicationServerApi v3api; + + private IFtpPathResolverRegistry pathResolverRegistry; + public DataSetCifsView() { operationLog.info("CIFS view onto the data store created"); cache = new Cache(SystemTimeProvider.SYSTEM_TIME_PROVIDER); } - DataSetCifsView(IServiceForDataStoreServer openBisService, IGeneralInformationService generalInfoService, Cache cache) + DataSetCifsView(IServiceForDataStoreServer openBisService, IGeneralInformationService generalInfoService, IApplicationServerApi v3api, + Cache cache) { this.dssService = openBisService; this.generalInfoService = generalInfoService; + this.v3api = v3api; this.cache = cache; } - + @Override public DeviceContext createContext(String shareName, ConfigElement args) throws DeviceContextException { @@ -95,7 +102,7 @@ public class DataSetCifsView implements DiskInterface { operationLog.info("tree " + tree + " opened"); } - + @Override public void treeClosed(SrvSession sess, TreeConnection tree) { @@ -186,7 +193,7 @@ public class DataSetCifsView implements DiskInterface } operationLog.info("Open file '" + path + "'."); NetworkFile networkFile = new CifsFile(file); - + networkFile.setAttributes(FileAttribute.ReadOnly); networkFile.setCreationDate(file.getLastModified()); networkFile.setModifyDate(file.getLastModified()); @@ -208,7 +215,7 @@ public class DataSetCifsView implements DiskInterface } @Override - public int readFile(SrvSession sess, TreeConnection tree, NetworkFile file, byte[] buf, int bufPos, + public int readFile(SrvSession sess, TreeConnection tree, NetworkFile file, byte[] buf, int bufPos, int size, long filePos) throws IOException { if (file.isDirectory()) @@ -296,7 +303,8 @@ public class DataSetCifsView implements DiskInterface try { String sessionToken = getSessionToken(session); - return new DSSFileSystemView(sessionToken, getDssService(), getGeneralInfoService(), pathResolverRegistry, cache); + return new DSSFileSystemView(sessionToken, getDssService(), getGeneralInfoService(), getApplicationServerApi(), pathResolverRegistry, + cache); } catch (FtpException ex) { throw CheckedExceptionTunnel.wrapIfNecessary(ex); @@ -316,7 +324,7 @@ public class DataSetCifsView implements DiskInterface } return dssService; } - + private IGeneralInformationService getGeneralInfoService() { if (generalInfoService == null) @@ -325,7 +333,16 @@ public class DataSetCifsView implements DiskInterface } return generalInfoService; } - + + private IApplicationServerApi getApplicationServerApi() + { + if (v3api == null) + { + v3api = ServiceProvider.getV3ApplicationService(); + } + return v3api; + } + private String getSessionToken(SrvSession sess) { ClientInfo client = sess.getClientInformation(); diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/DSSFileSystemView.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/DSSFileSystemView.java index 48ffa03f1dbe6fb9c68fac19e07c5cfe211f324c..5cf17b1f02a4065c188f3e197589358d0f0cfd91 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/DSSFileSystemView.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/DSSFileSystemView.java @@ -33,6 +33,7 @@ import org.apache.ftpserver.ftplet.FtpException; import org.apache.ftpserver.ftplet.FtpFile; import org.apache.log4j.Logger; +import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi; import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel; import ch.systemsx.cisd.common.exceptions.UserFailureException; import ch.systemsx.cisd.common.logging.LogCategory; @@ -101,26 +102,30 @@ public class DSSFileSystemView implements FileSystemView private final IGeneralInformationService generalInfoService; + private final IApplicationServerApi v3api; + private FtpFile workingDirectory; private final IFtpPathResolverRegistry pathResolverRegistry; public DSSFileSystemView(String sessionToken, final IServiceForDataStoreServer service, - IGeneralInformationService generalInfoService, + IGeneralInformationService generalInfoService, IApplicationServerApi v3api, IFtpPathResolverRegistry pathResolverRegistry) throws FtpException { - this(sessionToken, service, generalInfoService, pathResolverRegistry, + this(sessionToken, service, generalInfoService, v3api, pathResolverRegistry, new Cache(SystemTimeProvider.SYSTEM_TIME_PROVIDER)); } + public DSSFileSystemView(String sessionToken, final IServiceForDataStoreServer service, - IGeneralInformationService generalInfoService, + IGeneralInformationService generalInfoService, IApplicationServerApi v3api, IFtpPathResolverRegistry pathResolverRegistry, Cache cache) throws FtpException { this.sessionToken = sessionToken; this.service = - (IServiceForDataStoreServer) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] - { IServiceForDataStoreServer.class }, new ServiceInvocationHandler(service)); + (IServiceForDataStoreServer) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] { IServiceForDataStoreServer.class }, + new ServiceInvocationHandler(service)); this.generalInfoService = generalInfoService; + this.v3api = v3api; this.pathResolverRegistry = pathResolverRegistry; this.workingDirectory = getFile(FtpConstants.ROOT_DIRECTORY, cache); } @@ -161,7 +166,7 @@ public class DSSFileSystemView implements FileSystemView try { FtpPathResolverContext context = - new FtpPathResolverContext(sessionToken, service, generalInfoService, + new FtpPathResolverContext(sessionToken, service, generalInfoService, v3api, pathResolverRegistry, cache); return pathResolverRegistry.resolve(normalizedPath, context); } catch (RuntimeException rex) diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpPathResolverContext.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpPathResolverContext.java index 518e66315f5b77c7f560d945292d755b9ec19246..f24a7204e42bed9fefac4556361be67cbb73ac61 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpPathResolverContext.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpPathResolverContext.java @@ -22,8 +22,11 @@ import java.util.Collections; import java.util.EnumSet; import java.util.List; +import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi; import ch.systemsx.cisd.common.exceptions.UserFailureException; import ch.systemsx.cisd.common.server.ISessionTokenProvider; +import ch.systemsx.cisd.openbis.dss.generic.shared.IHierarchicalContentProvider; +import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider; import ch.systemsx.cisd.openbis.generic.shared.IServiceForDataStoreServer; import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationService; import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet; @@ -48,18 +51,23 @@ public class FtpPathResolverContext implements ISessionTokenProvider private final IGeneralInformationService generalInfoService; + private final IApplicationServerApi v3api; + private final IFtpPathResolverRegistry resolverRegistry; private final Cache cache; + private IHierarchicalContentProvider contentProvider; + public FtpPathResolverContext(String sessionToken, IServiceForDataStoreServer service, - IGeneralInformationService generalInfoService, + IGeneralInformationService generalInfoService, IApplicationServerApi v3api, IFtpPathResolverRegistry resolverRegistry, Cache cache) { this.sessionToken = sessionToken; this.service = service; this.generalInfoService = generalInfoService; this.resolverRegistry = resolverRegistry; + this.v3api = v3api; this.cache = cache; } @@ -74,11 +82,25 @@ public class FtpPathResolverContext implements ISessionTokenProvider return service; } + public IApplicationServerApi getV3Api() + { + return v3api; + } + public Cache getCache() { return cache; } + public IHierarchicalContentProvider getContentProvider() + { + if (contentProvider == null) + { + contentProvider = ServiceProvider.getHierarchicalContentProvider(); + } + return contentProvider.cloneFor(this); + } + public DataSet getDataSet(String dataSetCode) { DataSet dataSet = cache.getDataSet(dataSetCode); @@ -170,7 +192,7 @@ public class FtpPathResolverContext implements ISessionTokenProvider } return availableDataSets; } - + public IGeneralInformationService getGeneralInfoService() { return generalInfoService; diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServer.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServer.java index bf8cd934175ae1a4ef6b142f6be52c4c61b34ac5..8fd9eb20ceb88ed64b29214d951ecffb1bfac9e5 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServer.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServer.java @@ -69,6 +69,7 @@ import org.apache.sshd.server.SshFile; import org.apache.sshd.server.session.ServerSession; import org.apache.sshd.server.sftp.SftpSubsystem; +import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi; import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel; import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException; import ch.systemsx.cisd.common.filesystem.FileUtilities; @@ -76,6 +77,7 @@ import ch.systemsx.cisd.common.logging.LogCategory; import ch.systemsx.cisd.common.logging.LogFactory; import ch.systemsx.cisd.common.properties.PropertyParametersUtil; import ch.systemsx.cisd.common.utilities.SystemTimeProvider; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.v3.V3FtpPathResolverRegistry; import ch.systemsx.cisd.openbis.dss.generic.shared.utils.DssPropertyParametersUtil; import ch.systemsx.cisd.openbis.generic.shared.IServiceForDataStoreServer; import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationService; @@ -104,19 +106,23 @@ public class FtpServer implements FileSystemFactory, org.apache.sshd.server.File private final IGeneralInformationService generalInfoService; + private final IApplicationServerApi v3api; + private SshServer sshServer; - public FtpServer(IServiceForDataStoreServer openBisService, IGeneralInformationService generalInfoService, + public FtpServer(IServiceForDataStoreServer openBisService, IGeneralInformationService generalInfoService, IApplicationServerApi v3api, UserManager userManager) throws Exception { this.openBisService = openBisService; this.generalInfoService = generalInfoService; + this.v3api = v3api; this.userManager = userManager; Properties ftpProperties = PropertyParametersUtil.extractSingleSectionProperties( DssPropertyParametersUtil.loadServiceProperties(), "ftp.server", true).getProperties(); this.config = new FtpServerConfig(ftpProperties); FtpPathResolverConfig resolverConfig = new FtpPathResolverConfig(ftpProperties); - this.pathResolverRegistry = new FtpPathResolverRegistry(resolverConfig); + this.pathResolverRegistry = new V3FtpPathResolverRegistry(resolverConfig); + // this.pathResolverRegistry = new FtpPathResolverRegistry(resolverConfig); if (config.isStartServer()) { @@ -266,7 +272,7 @@ public class FtpServer implements FileSystemFactory, org.apache.sshd.server.File if (user instanceof FtpUser) { String sessionToken = ((FtpUser) user).getSessionToken(); - return new DSSFileSystemView(sessionToken, openBisService, generalInfoService, + return new DSSFileSystemView(sessionToken, openBisService, generalInfoService, v3api, pathResolverRegistry); } else { diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3ExperimentLevelResolver.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3ExperimentLevelResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..005b2759cfe189dcbadaf2dd7695b104e056e704 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3ExperimentLevelResolver.java @@ -0,0 +1,71 @@ +/* + * Copyright 2016 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.generic.server.ftp.v3; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.apache.ftpserver.ftplet.FtpFile; + +import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSet; +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.systemsx.cisd.openbis.common.io.hierarchical_content.api.IHierarchicalContent; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpPathResolverContext; + +class V3ExperimentLevelResolver extends V3Resolver +{ + private IExperimentId experimentId; + + public V3ExperimentLevelResolver(IExperimentId experimentId, FtpPathResolverContext resolverContext) + { + super(resolverContext); + this.experimentId = experimentId; + } + + @Override + public FtpFile resolve(String fullPath, String[] subPath) + { + if (subPath.length == 0) + { + ExperimentFetchOptions fetchOptions = new ExperimentFetchOptions(); + fetchOptions.withDataSets(); + + Map<IExperimentId, Experiment> experiments = api.getExperiments(sessionToken, Collections.singletonList(experimentId), fetchOptions); + Experiment exp = experiments.get(experimentId); + + List<FtpFile> files = new LinkedList<>(); + for (DataSet dataSet : exp.getDataSets()) + { + files.add(createDirectoryScaffolding(fullPath, dataSet.getCode())); + } + return createDirectoryWithContent(fullPath, files); + } else + { + String dataSetCode = subPath[0]; + IHierarchicalContent content = resolverContext.getContentProvider().asContent(dataSetCode); + String[] remaining = Arrays.copyOfRange(subPath, 1, subPath.length); + V3HierarchicalContentResolver resolver = new V3HierarchicalContentResolver(content, resolverContext); + return resolver.resolve(fullPath, remaining); + } + } + +} \ No newline at end of file diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3FtpPathResolverRegistry.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3FtpPathResolverRegistry.java new file mode 100644 index 0000000000000000000000000000000000000000..c3fbeba6256a0cd6ef25c342b48c5b43a7830a61 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3FtpPathResolverRegistry.java @@ -0,0 +1,74 @@ +/* + * Copyright 2011 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.generic.server.ftp.v3; + +import org.apache.ftpserver.ftplet.FtpFile; +import org.apache.log4j.Logger; + +import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi; +import ch.systemsx.cisd.common.logging.LogCategory; +import ch.systemsx.cisd.common.logging.LogFactory; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpPathResolverConfig; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpPathResolverContext; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.IFtpPathResolver; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.IFtpPathResolverRegistry; +import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider; + +/** + * A registry of ftp resolvers. It keeps the style of old-style resolver regisrty, but actually only calls itself root resolver. + * + * @author Jakub Straszewski + */ +public class V3FtpPathResolverRegistry implements IFtpPathResolverRegistry +{ + + private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, + V3FtpPathResolverRegistry.class); + + private IApplicationServerApi v3api; + + public IApplicationServerApi getV3api() + { + if (v3api == null) + v3api = ServiceProvider.getV3ApplicationService(); + return v3api; + } + + /** + * initializes the registry with all known {@link IFtpPathResolver}-s. + */ + public V3FtpPathResolverRegistry(FtpPathResolverConfig config) + { + + } + + @Override + public FtpFile resolve(String path, FtpPathResolverContext resolverContext) + { + try + { + V3RootLevelResolver resolver = new V3RootLevelResolver(resolverContext); + String[] split = path.equals("/") ? new String[] {} : path.substring(1).split("/"); + return resolver.resolve(path, split); + } catch (Exception e) + { + operationLog.warn(e); + } + return V3Resolver.getNonExistingFile(path, path); + } + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3HierarchicalContentResolver.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3HierarchicalContentResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..4a1518db664fd433d4eeaa03adc04f4bc607a06f --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3HierarchicalContentResolver.java @@ -0,0 +1,71 @@ +/* + * Copyright 2016 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.generic.server.ftp.v3; + +import java.util.LinkedList; +import java.util.List; + +import org.apache.ftpserver.ftplet.FtpFile; + +import ch.systemsx.cisd.common.shared.basic.string.StringUtils; +import ch.systemsx.cisd.openbis.common.io.hierarchical_content.api.IHierarchicalContent; +import ch.systemsx.cisd.openbis.common.io.hierarchical_content.api.IHierarchicalContentNode; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpPathResolverContext; + +class V3HierarchicalContentResolver extends V3Resolver +{ + + private IHierarchicalContent content; + + public V3HierarchicalContentResolver(IHierarchicalContent content, FtpPathResolverContext resolverContext) + { + super(resolverContext); + this.content = content; + } + + @Override + public FtpFile resolve(String fullPath, String[] subPath) + { + IHierarchicalContentNode rootNode; + if (subPath.length == 0) + { + rootNode = content.getRootNode(); + } else + { + rootNode = content.getNode(StringUtils.join(subPath, "/")); + } + + if (false == rootNode.isDirectory()) + { + return createFileWithContent(fullPath, rootNode); + } + + List<FtpFile> files = new LinkedList<>(); + for (IHierarchicalContentNode node : rootNode.getChildNodes()) + { + if (node.isDirectory()) + { + files.add(createDirectoryScaffolding(fullPath, node.getName())); + } else + { + files.add(createFileScaffolding(fullPath, node.getName(), node)); + } + } + return createDirectoryWithContent(fullPath, files); + } + +} \ No newline at end of file diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3ProjectLevelResolver.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3ProjectLevelResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..0b28a49c0c3ddcddceb5212a1c3f253ff3b79398 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3ProjectLevelResolver.java @@ -0,0 +1,71 @@ +/* + * Copyright 2016 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.generic.server.ftp.v3; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.apache.ftpserver.ftplet.FtpFile; + +import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.Experiment; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id.ExperimentIdentifier; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.Project; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.fetchoptions.ProjectFetchOptions; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.id.IProjectId; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.id.ProjectIdentifier; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpPathResolverContext; + +class V3ProjectLevelResolver extends V3Resolver +{ + private ProjectIdentifier projectIdentifier; + + public V3ProjectLevelResolver(String spaceCode, String projectCode, FtpPathResolverContext resolverContext) + { + super(resolverContext); + this.projectIdentifier = new ProjectIdentifier(spaceCode, projectCode); + } + + @Override + public FtpFile resolve(String fullPath, String[] subPath) + { + if (subPath.length == 0) + { + ProjectFetchOptions fetchOptions = new ProjectFetchOptions(); + fetchOptions.withExperiments(); + + Map<IProjectId, Project> projects = api.getProjects(sessionToken, Collections.singletonList(projectIdentifier), fetchOptions); + Project project = projects.get(projectIdentifier); + + List<FtpFile> files = new LinkedList<>(); + for (Experiment exp : project.getExperiments()) + { + files.add(createDirectoryScaffolding(fullPath, exp.getCode())); + } + return createDirectoryWithContent(fullPath, files); + } else + { + String item = subPath[0]; + String[] remaining = Arrays.copyOfRange(subPath, 1, subPath.length); + V3ExperimentLevelResolver resolver = + new V3ExperimentLevelResolver(new ExperimentIdentifier(projectIdentifier.getIdentifier() + "/" + item), resolverContext); + return resolver.resolve(fullPath, remaining); + } + } +} \ No newline at end of file diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3Resolver.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3Resolver.java new file mode 100644 index 0000000000000000000000000000000000000000..9cdb66842ad4c5c990c776537771bb7b26e90ab8 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3Resolver.java @@ -0,0 +1,162 @@ +/* + * Copyright 2016 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.generic.server.ftp.v3; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import org.apache.ftpserver.ftplet.FtpFile; + +import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi; +import ch.systemsx.cisd.openbis.common.io.hierarchical_content.api.IHierarchicalContentNode; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpPathResolverContext; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.NonExistingFtpFile; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.resolver.AbstractFtpFile; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.resolver.AbstractFtpFolder; + +public abstract class V3Resolver +{ + protected FtpPathResolverContext resolverContext; + + protected IApplicationServerApi api; + + protected String sessionToken; + + public V3Resolver(FtpPathResolverContext resolverContext) + { + this.resolverContext = resolverContext; + this.sessionToken = resolverContext.getSessionToken(); + this.api = resolverContext.getV3Api(); + } + + public abstract FtpFile resolve(String fullPath, String[] subPath); + + public static FtpFile createDirectoryScaffolding(String parentPath, String name) + { + return new AbstractFtpFolder(parentPath + "/" + name) + { + @Override + public List<FtpFile> unsafeListFiles() throws RuntimeException + { + throw new IllegalStateException("Don't expect to sak for file listing of scaffolding directory"); + } + }; + } + + public static FtpFile createDirectoryWithContent(String name, final List<FtpFile> files) + { + return new AbstractFtpFolder(name) + { + + @Override + public List<FtpFile> unsafeListFiles() throws RuntimeException + { + return files; + } + + }; + } + + protected FtpFile createFileWithContent(String name, final IHierarchicalContentNode node) + { + return new AbstractFtpFile(name) + { + + @Override + public boolean isFile() + { + return true; + } + + @Override + public boolean isDirectory() + { + return false; + } + + @Override + public long getSize() + { + throw new IllegalStateException("Size is not required for content node"); + } + + @Override + public InputStream createInputStream(long offset) throws IOException + { + return node.getInputStream(); + } + + @Override + public List<FtpFile> unsafeListFiles() throws RuntimeException + { + throw new IllegalStateException("Don't expect to sak for file listing of file"); + } + }; + } + + protected FtpFile createFileScaffolding(String parentPath, String name, final IHierarchicalContentNode node) + { + return new AbstractFtpFile(parentPath + "/" + name) + { + + @Override + public boolean isFile() + { + return true; + } + + @Override + public boolean isDirectory() + { + return false; + } + + @Override + public long getSize() + { + return node.getFileLength(); + } + + @Override + public long getLastModified() + { + return node.getLastModified(); + } + + @Override + public InputStream createInputStream(long offset) throws IOException + { + throw new IllegalStateException("Don't expect to ask for input stream of scaffolding file"); + } + + @Override + public List<FtpFile> unsafeListFiles() throws RuntimeException + { + throw new IllegalStateException("Don't expect to ask for file listing of scaffolding file"); + } + }; + } + + /** + * Create a representation for a non-existing {@link FtpFile}, optionally providing an error message. + */ + public static final FtpFile getNonExistingFile(final String path, final String errorMsgOrNull) + { + return new NonExistingFtpFile(path, errorMsgOrNull); + } +} \ No newline at end of file diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3RootLevelResolver.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3RootLevelResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..8e609432608456a438850f88f8735ea75eb39587 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3RootLevelResolver.java @@ -0,0 +1,58 @@ +/* + * Copyright 2016 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.generic.server.ftp.v3; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import org.apache.ftpserver.ftplet.FtpFile; + +import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.Space; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.fetchoptions.SpaceFetchOptions; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.search.SpaceSearchCriteria; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpPathResolverContext; + +class V3RootLevelResolver extends V3Resolver +{ + public V3RootLevelResolver(FtpPathResolverContext resolverContext) + { + super(resolverContext); + } + + @Override + public FtpFile resolve(String fullPath, String[] subPath) + { + if (subPath.length == 0) + { + List<Space> spaces = + api.searchSpaces(sessionToken, new SpaceSearchCriteria(), new SpaceFetchOptions()).getObjects(); + List<FtpFile> files = new LinkedList<>(); + for (Space space : spaces) + { + files.add(createDirectoryScaffolding(fullPath, space.getCode())); + } + return createDirectoryWithContent(fullPath, files); + } else + { + String item = subPath[0]; + String[] remaining = Arrays.copyOfRange(subPath, 1, subPath.length); + V3SpaceLevelResolver resolver = new V3SpaceLevelResolver(item, resolverContext); + return resolver.resolve(fullPath, remaining); + } + } +} \ No newline at end of file diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3SpaceLevelResolver.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3SpaceLevelResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..cda8e3bc9d4eb306c32f633a03618064d539f0df --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/v3/V3SpaceLevelResolver.java @@ -0,0 +1,64 @@ +/* + * Copyright 2016 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.generic.server.ftp.v3; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import org.apache.ftpserver.ftplet.FtpFile; + +import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.Project; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.fetchoptions.ProjectFetchOptions; +import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.search.ProjectSearchCriteria; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpPathResolverContext; + +class V3SpaceLevelResolver extends V3Resolver +{ + String spaceCode; + + public V3SpaceLevelResolver(String spaceCode, FtpPathResolverContext resolverContext) + { + super(resolverContext); + this.spaceCode = spaceCode; + } + + @Override + public FtpFile resolve(String fullPath, String[] subPath) + { + if (subPath.length == 0) + { + ProjectSearchCriteria searchCriteria = new ProjectSearchCriteria(); + searchCriteria.withSpace().withCode().thatEquals(spaceCode); + ProjectFetchOptions fetchOptions = new ProjectFetchOptions(); + List<Project> projects = + api.searchProjects(sessionToken, searchCriteria, fetchOptions).getObjects(); + List<FtpFile> files = new LinkedList<>(); + for (Project project : projects) + { + files.add(V3Resolver.createDirectoryScaffolding(fullPath, project.getCode())); + } + return V3Resolver.createDirectoryWithContent(fullPath, files); + } else + { + String item = subPath[0]; + String[] remaining = Arrays.copyOfRange(subPath, 1, subPath.length); + V3ProjectLevelResolver resolver = new V3ProjectLevelResolver(spaceCode, item, resolverContext); + return resolver.resolve(fullPath, remaining); + } + } +} \ No newline at end of file diff --git a/datastore_server/source/java/dssApplicationContext.xml b/datastore_server/source/java/dssApplicationContext.xml index f88e1176d822042416992baca301a2671b457443..4434f98b5b11f8f426b11881f459f369647175c0 100644 --- a/datastore_server/source/java/dssApplicationContext.xml +++ b/datastore_server/source/java/dssApplicationContext.xml @@ -276,6 +276,7 @@ <bean id="ftp-server" class="ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpServer" destroy-method="stop"> <constructor-arg ref="etl-lims-service"/> <constructor-arg ref="general-information-service"/> + <constructor-arg ref="v3-application-service"/> <constructor-arg ref="adapted-ftp-user-manager"/> </bean> diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/DSSFileSystemViewTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/DSSFileSystemViewTest.java index 154da444bd1265f6626f10440e424a42e059139f..219e706220939ce9f147c9dc25d01c6fb77df12c 100644 --- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/DSSFileSystemViewTest.java +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/DSSFileSystemViewTest.java @@ -29,6 +29,7 @@ import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi; import ch.systemsx.cisd.common.test.RecordingMatcher; import ch.systemsx.cisd.common.test.TrackingMockery; import ch.systemsx.cisd.openbis.dss.generic.server.ftp.resolver.AbstractFtpFile; @@ -48,6 +49,8 @@ public class DSSFileSystemViewTest extends AssertJUnit private IGeneralInformationService infoService; + private IApplicationServerApi v3api; + private IFtpPathResolverRegistry registry; private DSSFileSystemView view; @@ -59,8 +62,9 @@ public class DSSFileSystemViewTest extends AssertJUnit service = context.mock(IServiceForDataStoreServer.class); infoService = context.mock(IGeneralInformationService.class); registry = context.mock(IFtpPathResolverRegistry.class); + v3api = context.mock(IApplicationServerApi.class); prepareTryResolve("/"); - view = new DSSFileSystemView(SESSION_TOKEN, service, infoService, registry); + view = new DSSFileSystemView(SESSION_TOKEN, service, infoService, v3api, registry); } @AfterMethod diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/TemplateBasedDataSetResourceResolverTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/TemplateBasedDataSetResourceResolverTest.java index e04dafdce3946746dae222dea29a0cc1db9d76d7..35e10507c0438f4bd0e751b7d5564d29eefcf3a9 100644 --- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/TemplateBasedDataSetResourceResolverTest.java +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/TemplateBasedDataSetResourceResolverTest.java @@ -35,6 +35,7 @@ import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi; import ch.rinn.restrictions.Friend; import ch.systemsx.cisd.base.tests.AbstractFileSystemTestCase; import ch.systemsx.cisd.common.action.IDelegatedAction; @@ -164,6 +165,8 @@ public class TemplateBasedDataSetResourceResolverTest extends AbstractFileSystem private IGeneralInformationService generalInfoService; + private IApplicationServerApi v3api; + private SimpleFileContentProvider simpleFileContentProvider; private PhysicalDataSet ds1; @@ -192,6 +195,7 @@ public class TemplateBasedDataSetResourceResolverTest extends AbstractFileSystem context = new TrackingMockery(); service = context.mock(IServiceForDataStoreServer.class); generalInfoService = context.mock(IGeneralInformationService.class); + v3api = context.mock(IApplicationServerApi.class); hierarchicalContentProvider = context.mock(IHierarchicalContentProvider.class); File root = new File(workingDirectory, "data-sets"); @@ -199,7 +203,7 @@ public class TemplateBasedDataSetResourceResolverTest extends AbstractFileSystem simpleFileContentProvider = new SimpleFileContentProvider(root); resolverContext = - new FtpPathResolverContext(SESSION_TOKEN, service, generalInfoService, null, + new FtpPathResolverContext(SESSION_TOKEN, service, generalInfoService, v3api, null, new Cache(timeProvider)); context.checking(new Expectations() {