From 7c8f30dbe860a8abd0de3e7e140953774d3f06c2 Mon Sep 17 00:00:00 2001 From: kaloyane <kaloyane> Date: Tue, 26 Apr 2011 11:16:50 +0000 Subject: [PATCH] LMS-2197: create a simple FTP server showing space/projects/experiments/datasets (initial version). SVN: 21038 --- datastore_server/.classpath | 3 + .../dss/generic/server/ConfigParameters.java | 12 +- .../generic/server/ftp/DSSFileSystemView.java | 124 ++++++++ .../dss/generic/server/ftp/FtpConstants.java | 33 +++ .../server/ftp/FtpPathResolverContext.java | 58 ++++ .../server/ftp/FtpPathResolverRegistry.java | 90 ++++++ .../dss/generic/server/ftp/FtpServer.java | 125 ++++++++ .../generic/server/ftp/FtpServerConfig.java | 139 +++++++++ .../dss/generic/server/ftp/FtpUser.java | 92 ++++++ .../generic/server/ftp/FtpUserManager.java | 106 +++++++ .../generic/server/ftp/IFtpPathResolver.java | 38 +++ .../server/ftp/IFtpPathResolverRegistry.java | 29 ++ .../server/ftp/resolver/AbstractFtpFile.java | 120 ++++++++ .../ftp/resolver/AbstractFtpFolder.java | 60 ++++ .../resolver/ExperimentFolderResolver.java | 100 +++++++ .../HierarchicalContentToFtpFileAdapter.java | 109 +++++++ .../resolver/IExperimentChildrenLister.java | 31 ++ .../ftp/resolver/ProjectFolderResolver.java | 85 ++++++ .../ftp/resolver/RootFolderResolver.java | 88 ++++++ .../ftp/resolver/SpaceFolderResolver.java | 102 +++++++ .../TemplateBasedDataSetResourceResolver.java | 267 ++++++++++++++++++ .../dss/generic/server/webdav/.gitignore | 0 .../dss/generic/shared/ServiceProvider.java | 10 +- .../source/java/dssApplicationContext.xml | 22 ++ .../openbis/generic/server/ETLService.java | 22 ++ .../generic/server/ETLServiceLogger.java | 12 + .../generic/shared/IETLLIMSService.java | 22 +- 27 files changed, 1889 insertions(+), 10 deletions(-) create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/DSSFileSystemView.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpConstants.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpPathResolverContext.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpPathResolverRegistry.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServer.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServerConfig.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpUser.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpUserManager.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/IFtpPathResolver.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/IFtpPathResolverRegistry.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/AbstractFtpFile.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/AbstractFtpFolder.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/ExperimentFolderResolver.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/HierarchicalContentToFtpFileAdapter.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/IExperimentChildrenLister.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/ProjectFolderResolver.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/RootFolderResolver.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/SpaceFolderResolver.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/TemplateBasedDataSetResourceResolver.java create mode 100644 datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/webdav/.gitignore diff --git a/datastore_server/.classpath b/datastore_server/.classpath index 61b8c11668f..5b7e6596399 100644 --- a/datastore_server/.classpath +++ b/datastore_server/.classpath @@ -62,5 +62,8 @@ <classpathentry kind="lib" path="/libraries/cisd-image_readers/cisd-image_readers-jai.jar"/> <classpathentry kind="lib" path="/libraries/cisd-image_readers/cisd-image_readers.jar"/> <classpathentry kind="lib" path="/libraries/cisd-image_readers/cisd-image_readers-imagej.jar"/> + <classpathentry kind="lib" path="/libraries/ftpserver/ftplet-api.jar"/> + <classpathentry kind="lib" path="/libraries/ftpserver/ftpserver-core.jar"/> + <classpathentry kind="lib" path="/libraries/mina/mina-core.jar"/> <classpathentry kind="output" path="targets/classes"/> </classpath> diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ConfigParameters.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ConfigParameters.java index 13cdf8f4412..3077eb0d03b 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ConfigParameters.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ConfigParameters.java @@ -35,7 +35,7 @@ import ch.systemsx.cisd.common.utilities.PropertyUtils; * * @author Franz-Josef Elmer */ -final class ConfigParameters +public final class ConfigParameters { private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, @@ -68,11 +68,11 @@ final class ConfigParameters private static final int DEFAULT_AUTH_CACHE_CLEANUP_TIMER_PERIOD_MINS = 3 * 60; - static final String KEYSTORE_PATH_KEY = KEYSTORE + "path"; + public static final String KEYSTORE_PATH_KEY = KEYSTORE + "path"; - static final String KEYSTORE_PASSWORD_KEY = KEYSTORE + "password"; + public static final String KEYSTORE_PASSWORD_KEY = KEYSTORE + "password"; - static final String KEYSTORE_KEY_PASSWORD_KEY = KEYSTORE + "key-password"; + public static final String KEYSTORE_KEY_PASSWORD_KEY = KEYSTORE + "key-password"; static final String PLUGIN_SERVICES_LIST_KEY = "plugin-services"; @@ -124,7 +124,7 @@ final class ConfigParameters private final String webstartJarPath; - public static final class PluginServlet + static final class PluginServlet { private final String servletClass; @@ -169,7 +169,7 @@ final class ConfigParameters * * @throws ConfigurationFailureException if a property is missed or has an invalid value. */ - public ConfigParameters(final Properties properties) + ConfigParameters(final Properties properties) { this.properties = properties; storePath = new File(PropertyUtils.getMandatoryProperty(properties, STOREROOT_DIR_KEY)); 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 new file mode 100644 index 00000000000..6672b0ec781 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/DSSFileSystemView.java @@ -0,0 +1,124 @@ +/* + * 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; + +import org.apache.ftpserver.ftplet.FileSystemView; +import org.apache.ftpserver.ftplet.FtpException; +import org.apache.ftpserver.ftplet.FtpFile; + +import ch.systemsx.cisd.cifex.client.application.utils.StringUtils; +import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService; + +/** + * A central class that manages the movement of a user up and down the exposed hierarchical + * structure. + * + * @author Kaloyan Enimanev + */ +public class DSSFileSystemView implements FileSystemView +{ + private final String sessionToken; + + private final IETLLIMSService service; + + private FtpFile workingDirectory; + + private final IFtpPathResolverRegistry pathResolverRegistry; + + DSSFileSystemView(String sessionToken, IETLLIMSService service, + IFtpPathResolverRegistry pathResolverRegistry) throws FtpException + { + this.sessionToken = sessionToken; + this.service = service; + this.pathResolverRegistry = pathResolverRegistry; + this.workingDirectory = getHomeDirectory(); + } + + public boolean changeWorkingDirectory(String path) throws FtpException + { + FtpFile ftpFile = getFile(path); + if (ftpFile != null && ftpFile.isDirectory()) + { + workingDirectory = ftpFile; + return true; + } + return false; + } + + public void dispose() + { + } + + public FtpFile getFile(String path) throws FtpException + { + String normalizedPath = normalizePath(path); + + // this check speeds directory listings in the LFTP console client + if (workingDirectory != null && workingDirectory.getAbsolutePath().equals(normalizedPath)) + { + return workingDirectory; + } + + FtpPathResolverContext context = + new FtpPathResolverContext(sessionToken, service, pathResolverRegistry); + return pathResolverRegistry.tryResolve(normalizedPath, context); + } + + private String normalizePath(String path) throws FtpException + { + String result = path.trim(); + if (result.startsWith("..")) + { + String currentPath = workingDirectory.getAbsolutePath(); + int idx = currentPath.lastIndexOf(FtpConstants.FILE_SEPARATOR); + result = currentPath.substring(0, idx) + result.substring(2); + } else if (result.startsWith(".")) + { + result = workingDirectory.getAbsolutePath() + result.substring(1); + } else if (false == result.startsWith(FtpConstants.ROOT_DIRECTORY)) + { + result = workingDirectory.getAbsolutePath() + FtpConstants.FILE_SEPARATOR + result; + } + // remove trailing slashes + result = result.replaceAll("/*$", ""); + // replace multiple adjacent slashes with a single slash + result = result.replaceAll("/+", "/"); + + if (StringUtils.isBlank(result)) + { + return FtpConstants.ROOT_DIRECTORY; + } + + return result; + } + + public FtpFile getHomeDirectory() throws FtpException + { + return getFile(FtpConstants.ROOT_DIRECTORY); + } + + public FtpFile getWorkingDirectory() throws FtpException + { + return workingDirectory; + } + + public boolean isRandomAccessible() throws FtpException + { + return true; + } + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpConstants.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpConstants.java new file mode 100644 index 00000000000..8616dc1be02 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpConstants.java @@ -0,0 +1,33 @@ +/* + * 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; + +/** + * @author Kaloyan Enimanev + */ +public class FtpConstants +{ + + public static final String FTP_USER_GROUP_NAME = "dss"; + + public static final String FTP_USER_NAME = "dss"; + + public static final String ROOT_DIRECTORY = "/"; + + public static final String FILE_SEPARATOR = "/"; + +} 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 new file mode 100644 index 00000000000..465e4cce345 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpPathResolverContext.java @@ -0,0 +1,58 @@ +/* + * 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; + +import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService; + +/** + * An object holding all necessary context information for ftp path resolution. + * + * @author Kaloyan Enimanev + */ +public class FtpPathResolverContext +{ + + private final String sessionToken; + + private final IETLLIMSService service; + + private final IFtpPathResolverRegistry resolverRegistry; + + public FtpPathResolverContext(String sessionToken, IETLLIMSService service, + IFtpPathResolverRegistry resolverRegistry) + { + this.sessionToken = sessionToken; + this.service = service; + this.resolverRegistry = resolverRegistry; + } + + public String getSessionToken() + { + return sessionToken; + } + + public IETLLIMSService getService() + { + return service; + } + + public IFtpPathResolverRegistry getResolverRegistry() + { + return resolverRegistry; + } + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpPathResolverRegistry.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpPathResolverRegistry.java new file mode 100644 index 00000000000..31b862e0fd4 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpPathResolverRegistry.java @@ -0,0 +1,90 @@ +/* + * 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; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.ftpserver.ftplet.FtpFile; +import org.apache.log4j.Logger; + +import ch.systemsx.cisd.common.logging.LogCategory; +import ch.systemsx.cisd.common.logging.LogFactory; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.resolver.ExperimentFolderResolver; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.resolver.ProjectFolderResolver; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.resolver.RootFolderResolver; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.resolver.SpaceFolderResolver; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.resolver.TemplateBasedDataSetResourceResolver; + +/** + * A registry class keeping references to all known {@link IFtpPathResolver} instances. + * + * @author Kaloyan Enimanev + */ +public class FtpPathResolverRegistry implements IFtpPathResolverRegistry +{ + + private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, + FtpPathResolverRegistry.class); + + private List<IFtpPathResolver> pathResolvers = new ArrayList<IFtpPathResolver>(); + + /** + * initializes the registry with all known {@link IFtpPathResolver}-s. + */ + public FtpPathResolverRegistry(FtpServerConfig ftpServerConfig) + { + pathResolvers.add(new RootFolderResolver()); + pathResolvers.add(new SpaceFolderResolver()); + pathResolvers.add(new ProjectFolderResolver()); + TemplateBasedDataSetResourceResolver dataSetResolver = + new TemplateBasedDataSetResourceResolver(ftpServerConfig); + pathResolvers.add(new ExperimentFolderResolver(dataSetResolver)); + pathResolvers.add(dataSetResolver); + } + + /** + * tries for find an {@link IFtpPathResolver} instance that can resolve a given path. + */ + IFtpPathResolver tryFindResolver(String path) + { + for (IFtpPathResolver ftpFileCreator : pathResolvers) + { + if (ftpFileCreator.canResolve(path)) + { + return ftpFileCreator; + } + } + return null; + } + + public FtpFile tryResolve(String path, FtpPathResolverContext resolverContext) + { + IFtpPathResolver resolver = tryFindResolver(path); + if (resolver != null) + { + return resolver.resolve(path, resolverContext); + } else + { + String message = + String.format("Cannot find resolver for path '%s'. Wrong user input ?", path); + operationLog.warn(message); + return null; + } + } + +} 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 new file mode 100644 index 00000000000..1687baa36e4 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServer.java @@ -0,0 +1,125 @@ +/* + * 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; + +import java.util.Properties; + +import org.apache.ftpserver.ConnectionConfigFactory; +import org.apache.ftpserver.FtpServerFactory; +import org.apache.ftpserver.ftplet.FileSystemFactory; +import org.apache.ftpserver.ftplet.FileSystemView; +import org.apache.ftpserver.ftplet.FtpException; +import org.apache.ftpserver.ftplet.User; +import org.apache.ftpserver.ftplet.UserManager; +import org.apache.ftpserver.listener.ListenerFactory; +import org.apache.ftpserver.ssl.SslConfigurationFactory; +import org.apache.log4j.Logger; + +import ch.systemsx.cisd.common.logging.LogCategory; +import ch.systemsx.cisd.common.logging.LogFactory; +import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService; + +/** + * Controls the lifecycle of an FTP server built into DSS. + * + * @author Kaloyan Enimanev + */ +public class FtpServer implements FileSystemFactory +{ + private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, + FtpServer.class); + + private final IETLLIMSService openBisService; + + private final UserManager userManager; + + private final FtpServerConfig config; + + private final IFtpPathResolverRegistry pathResolverRegistry; + + private org.apache.ftpserver.FtpServer server; + + public FtpServer(IETLLIMSService openBisService, UserManager userManager, Properties configProps) + throws Exception + { + this.openBisService = openBisService; + this.userManager = userManager; + this.config = new FtpServerConfig(configProps); + this.pathResolverRegistry = new FtpPathResolverRegistry(config); + + if (config.isStartServer()) + { + start(); + } + } + + private void start() throws Exception + { + FtpServerFactory serverFactory = new FtpServerFactory(); + + ListenerFactory factory = new ListenerFactory(); + factory.setPort(config.getPort()); + if (config.isUseSSL()) + { + SslConfigurationFactory sslConfigFactory = new SslConfigurationFactory(); + sslConfigFactory.setKeystoreFile(config.getKeyStore()); + sslConfigFactory.setKeystorePassword(config.getKeyStorePassword()); + sslConfigFactory.setKeyPassword(config.getKeyPassword()); + factory.setSslConfiguration(sslConfigFactory.createSslConfiguration()); + } + serverFactory.addListener("default", factory.createListener()); + + ConnectionConfigFactory connectionConfigFactory = new ConnectionConfigFactory(); + connectionConfigFactory.setMaxThreads(config.getMaxThreads()); + serverFactory.setConnectionConfig(connectionConfigFactory.createConnectionConfig()); + + serverFactory.setFileSystem(this); + serverFactory.setUserManager(userManager); + + server = serverFactory.createServer(); + + String startingMessage = + String.format("Starting FTP server on port %d ...", config.getPort()); + operationLog.info(startingMessage); + server.start(); + + } + + /** + * called by spring IoC container when the application shuts down. + */ + public void stop() + { + if (server != null) + { + server.stop(); + } + } + + public FileSystemView createFileSystemView(User user) throws FtpException + { + if (user instanceof FtpUser) + { + String sessionToken = ((FtpUser) user).getSessionToken(); + return new DSSFileSystemView(sessionToken, openBisService, pathResolverRegistry); + } else + { + throw new FtpException("Unsupported user type."); + } + } + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServerConfig.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServerConfig.java new file mode 100644 index 00000000000..9f3ebd8c765 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServerConfig.java @@ -0,0 +1,139 @@ +/* + * 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; + +import java.io.File; +import java.util.Properties; + +import ch.systemsx.cisd.common.utilities.PropertyUtils; +import ch.systemsx.cisd.openbis.dss.generic.server.ConfigParameters; + +/** + * @author Kaloyan Enimanev + */ +public class FtpServerConfig +{ + private final static String PREFIX = "ftp.server."; + + private final static String ENABLE_KEY = PREFIX + "enable"; + + private final static String PORT_KEY = PREFIX + "port"; + + private final static String USE_SSL_KEY = PREFIX + "use-ssl"; + + private final static String MAX_THREADS_KEY = PREFIX + "maxThreads"; + + private final static String DATASET_DISPLAY_TEMPLATE_KEY = PREFIX + "dataset.display.template"; + + private static final int DEFAULT_PORT = 2121; + + private static final boolean DEFAULT_USE_SSL = false; + + private static final int DEFAULT_MAX_THREADS = 25; + + private static final String DEFAULT_DATASET_TEMPLATE = "${dataSetCode}"; + + private boolean startServer; + + private int port; + + private boolean useSSL; + + private File keyStore; + + private String keyPassword; + + private String keyStorePassword; + + private String dataSetDisplayTemplate; + + private int maxThreads; + + public FtpServerConfig(Properties props) { + this.startServer = PropertyUtils.getBoolean(props, ENABLE_KEY, false); + if (startServer) + { + initializeProperties(props); + } + } + + private void initializeProperties(Properties props) + { + this.port = PropertyUtils.getPosInt(props, PORT_KEY, DEFAULT_PORT); + this.useSSL = PropertyUtils.getBoolean(props, USE_SSL_KEY, DEFAULT_USE_SSL); + if (useSSL) + { + initializeSSLProperties(props); + } + maxThreads = PropertyUtils.getPosInt(props, MAX_THREADS_KEY, DEFAULT_MAX_THREADS); + dataSetDisplayTemplate = + PropertyUtils.getProperty(props, DATASET_DISPLAY_TEMPLATE_KEY, DEFAULT_DATASET_TEMPLATE); + } + + private void initializeSSLProperties(Properties props) + { + String keyStoreFileName = + PropertyUtils.getMandatoryProperty(props, ConfigParameters.KEYSTORE_PATH_KEY); + keyStore = new File(keyStoreFileName); + keyStorePassword = + PropertyUtils.getMandatoryProperty(props, ConfigParameters.KEYSTORE_PASSWORD_KEY); + keyPassword = + PropertyUtils.getMandatoryProperty(props, + ConfigParameters.KEYSTORE_KEY_PASSWORD_KEY); + } + + public boolean isStartServer() + { + return startServer; + } + + public int getPort() + { + return port; + } + + public boolean isUseSSL() + { + return useSSL; + } + + public File getKeyStore() + { + return keyStore; + } + + public String getKeyPassword() + { + return keyPassword; + } + + public String getKeyStorePassword() + { + return keyStorePassword; + } + + public Integer getMaxThreads() + { + return maxThreads; + } + + public String getDataSetDisplayTemplate() + { + return dataSetDisplayTemplate; + } + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpUser.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpUser.java new file mode 100644 index 00000000000..d3fe821cf75 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpUser.java @@ -0,0 +1,92 @@ +/* + * 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; + +import java.util.Collections; +import java.util.List; + +import org.apache.ftpserver.ftplet.Authority; +import org.apache.ftpserver.ftplet.AuthorizationRequest; +import org.apache.ftpserver.ftplet.User; + +/** + * An implementation of the Apache {@link User} interface, additionally holding an authenticated + * openBIS session token. + * + * @author Kaloyan Enimanev + */ +public class FtpUser implements User +{ + private static final int SECONDS_PER_HOUR = 60 * 60; + + private final String name; + + private final String sessionToken; + + public FtpUser(String userName, String sessionToken) + { + this.name = userName; + this.sessionToken = sessionToken; + } + + public boolean getEnabled() + { + return true; + } + + public String getHomeDirectory() + { + return "/"; + } + + public int getMaxIdleTime() + { + return SECONDS_PER_HOUR; + } + + public String getName() + { + return name; + } + + public String getPassword() + { + throw new UnsupportedOperationException(); + } + + public String getSessionToken() + { + return sessionToken; + } + + public AuthorizationRequest authorize(AuthorizationRequest request) + { + // authorization is implemented by not showing the users datasets they cannot see. + return request; + } + + public List<Authority> getAuthorities() + { + return Collections.emptyList(); + } + + public List<Authority> getAuthorities(Class<? extends Authority> arg0) + { + return Collections.emptyList(); + } + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpUserManager.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpUserManager.java new file mode 100644 index 00000000000..40671c09966 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpUserManager.java @@ -0,0 +1,106 @@ +/* + * 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; + +import org.apache.ftpserver.ftplet.Authentication; +import org.apache.ftpserver.ftplet.AuthenticationFailedException; +import org.apache.ftpserver.ftplet.FtpException; +import org.apache.ftpserver.ftplet.User; +import org.apache.ftpserver.ftplet.UserManager; +import org.apache.ftpserver.usermanager.UsernamePasswordAuthentication; +import org.apache.log4j.Logger; + +import ch.systemsx.cisd.common.logging.LogCategory; +import ch.systemsx.cisd.common.logging.LogFactory; +import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService; +import ch.systemsx.cisd.openbis.generic.shared.dto.SessionContextDTO; + +/** + * An implementation of the Apache {@link UserManager} interface, adapting openBIS users to FTP + * users. + * + * @author Kaloyan Enimanev + */ +public class FtpUserManager implements UserManager +{ + + private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, + FtpUserManager.class); + + + private final IETLLIMSService service; + + public FtpUserManager(IETLLIMSService service) + { + this.service = service; + } + + public User authenticate(Authentication authentication) throws AuthenticationFailedException + { + if (authentication instanceof UsernamePasswordAuthentication) + { + UsernamePasswordAuthentication upa = (UsernamePasswordAuthentication) authentication; + String user = upa.getUsername(); + String password = upa.getPassword(); + SessionContextDTO session = service.tryToAuthenticate(user, password); + if (session != null && session.getSessionToken() != null) + { + return new FtpUser(user, session.getSessionToken()); + } + } else { + operationLog.warn("Unsupported authentication type :" + authentication.getClass()); + } + + throw new AuthenticationFailedException(); + } + + public void delete(String arg0) throws FtpException + { + throw new UnsupportedOperationException(); + } + + public boolean doesExist(String arg0) throws FtpException + { + return false; + } + + public String getAdminName() throws FtpException + { + throw new UnsupportedOperationException(); + } + + public String[] getAllUserNames() throws FtpException + { + throw new UnsupportedOperationException(); + } + + public User getUserByName(String userName) throws FtpException + { + return new FtpUser(userName, null); + } + + public boolean isAdmin(String arg0) throws FtpException + { + return false; + } + + public void save(User arg0) throws FtpException + { + throw new UnsupportedOperationException(); + } + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/IFtpPathResolver.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/IFtpPathResolver.java new file mode 100644 index 00000000000..d771ada9e38 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/IFtpPathResolver.java @@ -0,0 +1,38 @@ +/* + * 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; + +import org.apache.ftpserver.ftplet.FtpFile; + +/** + * {@link IFtpPathResolver}-s can translate String paths to {@link FtpFile} objects. + * + * @author Kaloyan Enimanev + */ +public interface IFtpPathResolver +{ + /** + * @param path a normalized path, containing no trailing slashes. + */ + boolean canResolve(String path); + + /** + * @param path a normalized path, containing no trailing slashes. + */ + FtpFile resolve(String path, FtpPathResolverContext resolverContext); + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/IFtpPathResolverRegistry.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/IFtpPathResolverRegistry.java new file mode 100644 index 00000000000..ab79a831eeb --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/IFtpPathResolverRegistry.java @@ -0,0 +1,29 @@ +/* + * 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; + +import org.apache.ftpserver.ftplet.FtpFile; + +/** + * @author Kaloyan Enimanev + */ +public interface IFtpPathResolverRegistry +{ + + FtpFile tryResolve(String path, FtpPathResolverContext resolverContext); + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/AbstractFtpFile.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/AbstractFtpFile.java new file mode 100644 index 00000000000..a02ece45c54 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/AbstractFtpFile.java @@ -0,0 +1,120 @@ +/* + * 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.resolver; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.ftpserver.ftplet.FtpFile; + +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpConstants; + +/** + * A convenience abstract implementation for an ftp folder. + * + * @author Kaloyan Enimanev + */ +public abstract class AbstractFtpFile implements FtpFile +{ + protected final String absolutePath; + + public AbstractFtpFile(String absolutePath) + { + this.absolutePath = absolutePath; + } + + public boolean doesExist() + { + return true; + } + + public String getAbsolutePath() + { + return absolutePath; + } + + public String getName() + { + return new File(absolutePath).getName(); + } + + public String getOwnerName() + { + return FtpConstants.FTP_USER_NAME; + } + + public String getGroupName() + { + return FtpConstants.FTP_USER_GROUP_NAME; + } + + public boolean isHidden() + { + return false; + } + + public boolean isReadable() + { + return true; + } + + public boolean isRemovable() + { + return false; + } + + public boolean isWritable() + { + return false; + } + + // ================================= + // Unsupported operations + // ================================= + + public OutputStream createOutputStream(long arg0) throws IOException + { + return null; + } + + public boolean delete() + { + return false; + } + + public boolean mkdir() + { + return false; + } + + public boolean move(FtpFile arg0) + { + return false; + } + + public boolean setLastModified(long arg0) + { + return false; + } + + public int getLinkCount() + { + return 0; + } + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/AbstractFtpFolder.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/AbstractFtpFolder.java new file mode 100644 index 00000000000..3b567621009 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/AbstractFtpFolder.java @@ -0,0 +1,60 @@ +/* + * 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.resolver; + +import java.io.IOException; +import java.io.InputStream; + +/** + * A convenience abstract implementation for an ftp folder. + * + * @author Kaloyan Enimanev + */ +public abstract class AbstractFtpFolder extends AbstractFtpFile +{ + + public AbstractFtpFolder(String absolutePath) + { + super(absolutePath); + } + + public boolean isDirectory() + { + return true; + } + + public boolean isFile() + { + return false; + } + + public InputStream createInputStream(long arg0) throws IOException + { + return null; + } + + public long getSize() + { + return 0; + } + + public long getLastModified() + { + return System.currentTimeMillis(); + } + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/ExperimentFolderResolver.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/ExperimentFolderResolver.java new file mode 100644 index 00000000000..4741ea8c53b --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/ExperimentFolderResolver.java @@ -0,0 +1,100 @@ +/* + * 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.resolver; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.apache.ftpserver.ftplet.FtpFile; + +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpConstants; +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.generic.shared.IETLLIMSService; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifier; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifierFactory; + +/** + * Resolves experiment folders with path "/<space-code>/<project-code>/<experiment-code>" to + * {@link FtpFile}-s. + * + * @author Kaloyan Enimanev + */ +public class ExperimentFolderResolver implements IFtpPathResolver +{ + private final IExperimentChildrenLister childLister; + + public ExperimentFolderResolver(IExperimentChildrenLister childLister) + { + this.childLister = childLister; + } + + /** + * @return <code>true</code> for all paths containing 3 levels of nested folders, + * <code>false</code> for all other paths. + */ + public boolean canResolve(String path) + { + int nestedLevels = StringUtils.countMatches(path, FtpConstants.FILE_SEPARATOR); + return nestedLevels == 3; + } + + public FtpFile resolve(final String path, final FtpPathResolverContext resolverContext) + { + return new AbstractFtpFolder(path) + { + + public List<FtpFile> listFiles() + { + List<FtpFile> result = new ArrayList<FtpFile>(); + + for (String childName : listChildrenNames(path, resolverContext)) + { + String childPath = path + FtpConstants.FILE_SEPARATOR + childName; + FtpFile childFile = + resolverContext.getResolverRegistry().tryResolve(childPath, + resolverContext); + result.add(childFile); + } + + return result; + } + + }; + } + + private List<String> listChildrenNames(String expIdentifier, FtpPathResolverContext context) + { + IETLLIMSService service = context.getService(); + String sessionToken = context.getSessionToken(); + + ExperimentIdentifier identifier = + new ExperimentIdentifierFactory(expIdentifier).createIdentifier(); + + Experiment exp = service.tryToGetExperiment(sessionToken, identifier); + if (exp == null) + { + return Collections.emptyList(); + } else + { + return childLister.listExperimentChildrenPaths(exp, context); + } + } +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/HierarchicalContentToFtpFileAdapter.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/HierarchicalContentToFtpFileAdapter.java new file mode 100644 index 00000000000..9f3c3e6c346 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/HierarchicalContentToFtpFileAdapter.java @@ -0,0 +1,109 @@ +/* + * 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.resolver; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import org.apache.ftpserver.ftplet.FtpFile; + +import ch.systemsx.cisd.common.io.IHierarchicalContentNode; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpConstants; + +/** + * An {@link FtpFile} implementation adapting an underlying {@link IHierarchicalContentNode}. + * <p> + * The resources represented by {@link HierarchicalContentToFtpFileAdapter} exist in the data store. + * + * @author Kaloyan Enimanev + */ +public class HierarchicalContentToFtpFileAdapter extends AbstractFtpFile +{ + private final IHierarchicalContentNode contentNode; + + public HierarchicalContentToFtpFileAdapter(String path, IHierarchicalContentNode contentNode) + { + super(path); + this.contentNode = contentNode; + } + + public InputStream createInputStream(long offset) throws IOException + { + InputStream result = contentNode.getInputStream(); + if (offset > 0) + { + result.skip(offset); + } + return result; + } + + public long getLastModified() + { + try + { + return contentNode.getFile().lastModified(); + } catch (UnsupportedOperationException uoe) + { + return 0; + } + } + + + public long getSize() + { + if (isFile()) + { + return contentNode.getFileLength(); + } + return 0; + } + + public boolean isDirectory() + { + return contentNode.isDirectory(); + } + + public boolean isFile() + { + return isDirectory() == false; + } + + public List<org.apache.ftpserver.ftplet.FtpFile> listFiles() + { + if (isDirectory()) + { + List<IHierarchicalContentNode> children = contentNode.getChildNodes(); + List<org.apache.ftpserver.ftplet.FtpFile> result = + new ArrayList<org.apache.ftpserver.ftplet.FtpFile>(); + for (IHierarchicalContentNode childNode : children) + { + String childPath = absolutePath + FtpConstants.FILE_SEPARATOR + childNode.getName(); + HierarchicalContentToFtpFileAdapter childFile = + new HierarchicalContentToFtpFileAdapter(childPath, childNode); + result.add(childFile); + } + return result; + + } else + { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/IExperimentChildrenLister.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/IExperimentChildrenLister.java new file mode 100644 index 00000000000..f0df672104a --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/IExperimentChildrenLister.java @@ -0,0 +1,31 @@ +/* + * 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.resolver; + +import java.util.List; + +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpPathResolverContext; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment; + +/** + * @author Kaloyan Enimanev + */ +public interface IExperimentChildrenLister +{ + List<String> listExperimentChildrenPaths(Experiment experiment, FtpPathResolverContext context); + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/ProjectFolderResolver.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/ProjectFolderResolver.java new file mode 100644 index 00000000000..846dc597107 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/ProjectFolderResolver.java @@ -0,0 +1,85 @@ +/* + * 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.resolver; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.apache.ftpserver.ftplet.FtpFile; + +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpConstants; +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.generic.shared.IETLLIMSService; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ProjectIdentifier; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ProjectIdentifierFactory; + +/** + * Resolves project folders with path "/<space-code>/<project-code>" to {@link FtpFile}-s. + * + * @author Kaloyan Enimanev + */ +public class ProjectFolderResolver implements IFtpPathResolver +{ + + /** + * @return <code>true</code> for all paths containing 2 levels of nested folders, + * <code>false</code> for all other paths. + */ + public boolean canResolve(String path) + { + int nestedLevels = StringUtils.countMatches(path, FtpConstants.FILE_SEPARATOR); + return nestedLevels == 2; + } + + public FtpFile resolve(final String path, final FtpPathResolverContext resolverContext) + { + return new AbstractFtpFolder(path) + { + + public List<FtpFile> listFiles() + { + List<Experiment> experiments = listExperiments(path, resolverContext); + List<FtpFile> result = new ArrayList<FtpFile>(); + + for (Experiment experiment : experiments) + { + String childPath = + path + FtpConstants.FILE_SEPARATOR + experiment.getCode(); + FtpFile childFile = + resolverContext.getResolverRegistry().tryResolve(childPath, + resolverContext); + result.add(childFile); + } + + return result; + } + }; + } + + private List<Experiment> listExperiments(String projectIdentifier, + FtpPathResolverContext context) + { + IETLLIMSService service = context.getService(); + String sessionToken = context.getSessionToken(); + ProjectIdentifier identifier = + new ProjectIdentifierFactory(projectIdentifier).createIdentifier(); + return service.listExperiments(sessionToken, identifier); + } +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/RootFolderResolver.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/RootFolderResolver.java new file mode 100644 index 00000000000..90697ee047c --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/RootFolderResolver.java @@ -0,0 +1,88 @@ +/* + * 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.resolver; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.ftpserver.ftplet.FtpFile; + +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpConstants; +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.generic.shared.IETLLIMSService; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Project; + +/** + * Creates a root folder {@link FtpFile} listing all existing spaces as its children. + * + * @author Kaloyan Enimanev + */ +public class RootFolderResolver implements IFtpPathResolver +{ + + public boolean canResolve(String path) + { + return FtpConstants.ROOT_DIRECTORY.equals(path); + } + + public FtpFile resolve(String path, final FtpPathResolverContext resolverContext) + { + return new AbstractFtpFolder(path) + { + + public List<FtpFile> listFiles() + { + + List<Project> projects = listProjects(resolverContext); + List<FtpFile> result = new ArrayList<FtpFile>(); + Set<String> spacesSeen = new TreeSet<String>(); + + for (Project project : projects) + { + String spaceCode = project.getSpace().getCode(); + spacesSeen.add(spaceCode); + } + + for (String spaceCode : spacesSeen) + { + String childPath = FtpConstants.ROOT_DIRECTORY + spaceCode; + FtpFile child = + resolverContext.getResolverRegistry().tryResolve(childPath, + resolverContext); + if (child != null) + { + result.add(child); + } + } + + return result; + } + + private List<Project> listProjects(FtpPathResolverContext context) + { + IETLLIMSService service = context.getService(); + String sessionToken = context.getSessionToken(); + List<Project> projects = service.listProjects(sessionToken); + return projects; + } + }; + } + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/SpaceFolderResolver.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/SpaceFolderResolver.java new file mode 100644 index 00000000000..edbefcbdaa1 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/SpaceFolderResolver.java @@ -0,0 +1,102 @@ +/* + * 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.resolver; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.ftpserver.ftplet.FtpFile; + +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpConstants; +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.generic.shared.IETLLIMSService; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Project; + +/** + * Resolves experiment folders with path "/<space-code>" to {@link FtpFile}-s. + * + * @author Kaloyan Enimanev + */ +public class SpaceFolderResolver implements IFtpPathResolver +{ + + /** + * @return <code>true</code> for all paths containing single folder, <code>false</code> for all + * other paths. + */ + public boolean canResolve(String path) + { + return false == removeStartingSlash(path).contains(FtpConstants.FILE_SEPARATOR); + } + + public FtpFile resolve(final String path, final FtpPathResolverContext resolverContext) + { + return new AbstractFtpFolder(path) + { + + public List<FtpFile> listFiles() + { + List<Project> projects = listProjects(resolverContext); + + String pathSpaceCode = removeStartingSlash(path); + List<FtpFile> result = new ArrayList<FtpFile>(); + List<String> childProjects = new ArrayList<String>(); + + for (Project project : projects) + { + String projectSpaceCode = project.getSpace().getCode(); + if (projectSpaceCode.equals(pathSpaceCode)) + { + childProjects.add(project.getCode()); + } + } + + for (String childProject : childProjects) + { + String childPath = path + FtpConstants.FILE_SEPARATOR + childProject; + FtpFile childFile = + resolverContext.getResolverRegistry().tryResolve(childPath, + resolverContext); + result.add(childFile); + } + + return result; + } + + }; + } + + private List<Project> listProjects(FtpPathResolverContext context) + { + IETLLIMSService service = context.getService(); + String sessionToken = context.getSessionToken(); + List<Project> projects = service.listProjects(sessionToken); + return projects; + } + + private String removeStartingSlash(String path) + { + if (path.startsWith(FtpConstants.FILE_SEPARATOR)) + { + return path.substring(1); + } else + { + return path; + } + } +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/TemplateBasedDataSetResourceResolver.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/TemplateBasedDataSetResourceResolver.java new file mode 100644 index 00000000000..3cd9de5630b --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/TemplateBasedDataSetResourceResolver.java @@ -0,0 +1,267 @@ +/* + * 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.resolver; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.time.DateFormatUtils; +import org.apache.ftpserver.ftplet.FtpFile; + +import ch.systemsx.cisd.common.io.IHierarchicalContent; +import ch.systemsx.cisd.common.io.IHierarchicalContentNode; +import ch.systemsx.cisd.common.utilities.ExtendedProperties; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpConstants; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpPathResolverContext; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpServerConfig; +import ch.systemsx.cisd.openbis.dss.generic.server.ftp.IFtpPathResolver; +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.IETLLIMSService; +import ch.systemsx.cisd.openbis.generic.shared.basic.TechId; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifier; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifierFactory; + +/** + * Resolves paths like + * /<space-code>/<project-code>/<experiment-code>/<dataset-template>[/<sub-path>]* + * <p> + * Subpaths are resolved as a relative paths starting from the root of a dataset. + * <p> + * TODO KE: add disambiguation checks - append an uniqueness factor to (?another variable) to the + * path if no "dataSetCode" variable is used in the template + * + * @author Kaloyan Enimanev + */ +public class TemplateBasedDataSetResourceResolver implements IFtpPathResolver, IExperimentChildrenLister +{ + private static final String DATA_SET_CODE_VARNAME = "dataSetCode"; + + private static final String DATA_SET_TYPE_VARNAME = "dataSetType"; + + private static final String DATA_SET_DATE_VARNAME = "dataSetDate"; + + private static final String FILE_NAME_VARNAME = "fileName"; + + private static final String DATA_SET_DATE_FORMAT = "yyyy-MM-dd-HH-mm"; + + /** + * a template, that can contain special variables. + * + * @see #evaluateTemplate(ExternalData, String) to find out what variables are understood and + * interpreted. + */ + private final String template; + + private static class DataSetAndFileName + { + ExternalData dataSet; + + // will only be filled when the ${fileName} variable + // is used in the template + String fileName = StringUtils.EMPTY; + } + + public TemplateBasedDataSetResourceResolver(FtpServerConfig ftpServerConfig) + { + this.template = ftpServerConfig.getDataSetDisplayTemplate(); + } + + /** + * @return <code>true</code> for paths containing at least 4 nested directory levels. + */ + public boolean canResolve(String path) + { + int nestedLevels = StringUtils.countMatches(path, FtpConstants.FILE_SEPARATOR); + return nestedLevels >= 4; + } + + public FtpFile resolve(String path, final FtpPathResolverContext resolverContext) + { + IETLLIMSService service = resolverContext.getService(); + String sessionToken = resolverContext.getSessionToken(); + + DataSetAndFileName dataSetAndFileName = + extractDataSetAndFileName(path, service, sessionToken); + if (dataSetAndFileName == null) + { + return null; + } + + String nestedSubPath = extractNestedSubPath(path); + String relativePath = + dataSetAndFileName.fileName + FtpConstants.FILE_SEPARATOR + nestedSubPath; + + IHierarchicalContentProvider provider = ServiceProvider.getHierarchicalContentProvider(); + IHierarchicalContent content = + provider.asContent(dataSetAndFileName.dataSet.getDataSetCode()); + IHierarchicalContentNode contentNode = + StringUtils.isBlank(relativePath) ? content.getRootNode() : content + .getNode(relativePath); + return new HierarchicalContentToFtpFileAdapter(path, contentNode); + } + + private DataSetAndFileName extractDataSetAndFileName(String path, IETLLIMSService service, + String sessionToken) + { + String experimentId = extractExperimentIdentifier(path); + Experiment experiment = extractExperiment(experimentId, service, sessionToken); + if (experiment == null) + { + // cannot resolve an existing experiment from the specified path + return null; + } + List<ExternalData> dataSets = + service.listDataSetsByExperimentID(sessionToken, new TechId(experiment)); + String pathWithEndSlash = path + FtpConstants.FILE_SEPARATOR; + for (ExternalData dataSet : dataSets) + { + Map<String /* fileName */, String /* evaluated */> evaluatedPaths = + getEvaluatedDataSetPaths(dataSet); + for (Entry<String, String> entry : evaluatedPaths.entrySet()) + { + String fullEvaluatedPath = + experimentId + FtpConstants.FILE_SEPARATOR + entry.getValue() + + FtpConstants.FILE_SEPARATOR; + if (pathWithEndSlash.startsWith(fullEvaluatedPath)) + { + DataSetAndFileName result = new DataSetAndFileName(); + result.dataSet = dataSet; + result.fileName = entry.getKey(); + return result; + } + } + + } + + return null; + } + + /** + * @return the nested path within the data set (i.e. under the dataset directory level) + */ + private String extractNestedSubPath(String path) + { + String[] levels = StringUtils.split(path, FtpConstants.FILE_SEPARATOR); + if (levels.length > 4) { + return StringUtils.join(levels, FtpConstants.FILE_SEPARATOR, 4, levels.length); + } else { + return StringUtils.EMPTY; + } + } + + private Experiment extractExperiment(String experimentId, IETLLIMSService service, + String sessionToken) + { + ExperimentIdentifier experimentIdentifier = + new ExperimentIdentifierFactory(experimentId).createIdentifier(); + + return service.tryToGetExperiment(sessionToken, experimentIdentifier); + } + + private String extractExperimentIdentifier(String path) + { + String[] levels = StringUtils.split(path, FtpConstants.FILE_SEPARATOR); + String experimentId = + FtpConstants.ROOT_DIRECTORY + + StringUtils.join(levels, FtpConstants.FILE_SEPARATOR, 0, 3); + return experimentId; + } + + public List<String> listExperimentChildrenPaths(Experiment experiment, FtpPathResolverContext context) + { + IETLLIMSService service = context.getService(); + String sessionToken = context.getSessionToken(); + + List<ExternalData> dataSets = service.listDataSetsByExperimentID(sessionToken, new TechId(experiment)); + List<String> result = new ArrayList<String>(); + for (ExternalData dataSet : dataSets) + { + Map<String, String> pathsForDataSet = getEvaluatedDataSetPaths(dataSet); + result.addAll(pathsForDataSet.values()); + } + return result; + } + + private Map<String /* fileName */, String /* evaluated template */> getEvaluatedDataSetPaths( + ExternalData dataSet) + { + Map<String, String> result = new HashMap<String, String>(); + + IHierarchicalContentProvider provider = ServiceProvider.getHierarchicalContentProvider(); + IHierarchicalContent content = provider.asContent(dataSet.getCode()); + IHierarchicalContentNode rootNode = content.getRootNode(); + + for (String fileName : extractFileNames(rootNode)) + { + String path = evaluateTemplate(dataSet, fileName); + result.put(fileName, path); + } + + return result; + } + + /** + * @return all values to be used when evaluating the template "${fileName}" variable. + */ + private List<String> extractFileNames(IHierarchicalContentNode rootNode) + { + if (rootNode.isDirectory()) + { + List<String> childrenFileNames = new ArrayList<String>(); + for (IHierarchicalContentNode child : rootNode.getChildNodes()) + { + childrenFileNames.add(child.getName()); + } + return childrenFileNames; + } else + { + return Collections.singletonList(StringUtils.EMPTY); + } + } + + public String evaluateTemplate(ExternalData dataSet, + String fileName) + { + ExtendedProperties properties = new ExtendedProperties(); + properties.put(DATA_SET_CODE_VARNAME, dataSet.getCode()); + properties.put(DATA_SET_TYPE_VARNAME, dataSet.getDataSetType().getCode()); + String dataSetDate = extractDateValue(dataSet.getRegistrationDate()); + properties.put(DATA_SET_DATE_VARNAME, dataSetDate); + properties.put(FILE_NAME_VARNAME, fileName); + + String templatePropName = "template"; + properties.put(templatePropName, template); + return properties.getProperty(templatePropName); + } + + + private String extractDateValue(Date dataSetDate) + { + return DateFormatUtils.format(dataSetDate, DATA_SET_DATE_FORMAT); + } + + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/webdav/.gitignore b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/webdav/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/ServiceProvider.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/ServiceProvider.java index 002916febdb..8acc1bc783d 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/ServiceProvider.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/ServiceProvider.java @@ -60,8 +60,14 @@ public class ServiceProvider { if (create && applicationContext == null) { - applicationContext = new ClassPathXmlApplicationContext(new String[] - { "dssApplicationContext.xml" }, true); + synchronized (ServiceProvider.class) + { + if (applicationContext == null) + { + applicationContext = new ClassPathXmlApplicationContext(new String[] + { "dssApplicationContext.xml" }, true); + } + } } return applicationContext; } diff --git a/datastore_server/source/java/dssApplicationContext.xml b/datastore_server/source/java/dssApplicationContext.xml index 370deea1160..258a04722f9 100644 --- a/datastore_server/source/java/dssApplicationContext.xml +++ b/datastore_server/source/java/dssApplicationContext.xml @@ -18,6 +18,10 @@ <property name="ignoreUnresolvablePlaceholders" value="true" /> </bean> + <bean id="configProperties" factory-bean="propertyConfigurer" + factory-method="getResolvedProps"> + </bean> + <bean id="data-set-path-infos-provider" class="ch.systemsx.cisd.openbis.dss.generic.server.DatabaseBasedDataSetPathInfoProvider"/> <bean id="plugin-tasks" class="ch.systemsx.cisd.openbis.dss.generic.server.plugins.tasks.PluginTaskProviders" @@ -142,5 +146,23 @@ --> <bean class="ch.systemsx.cisd.common.spring.LogAdvisor" /> + + <!-- + // FTP server + --> + + <!-- Adapts the openBIS users for the FTP server --> + <bean id="adapted-ftp-user-manager" class="ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpUserManager"> + <constructor-arg ref="etl-lims-service"/> + </bean> + + <!-- + Optionally starts an FTP server. + --> + <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="adapted-ftp-user-manager"/> + <constructor-arg ref="configProperties" /> + </bean> </beans> \ No newline at end of file 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 c8657c640d4..43877b3dcb2 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 @@ -35,6 +35,7 @@ import ch.systemsx.cisd.openbis.generic.server.business.IDataStoreServiceFactory import ch.systemsx.cisd.openbis.generic.server.business.IPropertiesBatchManager; import ch.systemsx.cisd.openbis.generic.server.business.bo.ICommonBusinessObjectFactory; import ch.systemsx.cisd.openbis.generic.server.business.bo.IExperimentBO; +import ch.systemsx.cisd.openbis.generic.server.business.bo.IExperimentTable; import ch.systemsx.cisd.openbis.generic.server.business.bo.IExternalDataBO; import ch.systemsx.cisd.openbis.generic.server.business.bo.IExternalDataTable; import ch.systemsx.cisd.openbis.generic.server.business.bo.IGroupBO; @@ -62,6 +63,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataStoreServiceKind; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseInstance; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatastoreServiceDescription; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DeletedDataSet; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityType; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentType; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData; @@ -528,6 +530,26 @@ public class ETLService extends AbstractCommonServer<IETLService> implements IET return datasetLister.listByDatasetCode(dataSetCodes); } + public List<Project> listProjects(String sessionToken) + { + checkSession(sessionToken); + final List<ProjectPE> projects = getDAOFactory().getProjectDAO().listProjects(); + Collections.sort(projects); + return ProjectTranslator.translate(projects); + } + + public List<Experiment> listExperiments(String sessionToken, ProjectIdentifier projectIdentifier) + { + final Session session = getSession(sessionToken); + final IExperimentTable experimentTable = + businessObjectFactory.createExperimentTable(session); + experimentTable.load(EntityType.ALL_TYPES_CODE, projectIdentifier); + final List<ExperimentPE> experiments = experimentTable.getExperiments(); + Collections.sort(experiments); + return ExperimentTranslator.translate(experiments, session.getBaseIndexURL(), + ExperimentTranslator.LoadableFields.PROPERTIES); + } + public IEntityProperty[] tryToGetPropertiesOfTopSampleRegisteredFor(String sessionToken, SampleIdentifier sampleIdentifier) throws UserFailureException { diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceLogger.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceLogger.java index fcbcbb36ca8..31c61ec419a 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceLogger.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/ETLServiceLogger.java @@ -463,4 +463,16 @@ public class ETLServiceLogger extends AbstractServerLogger implements IETLServic return null; } + public List<Experiment> listExperiments(String sessionToken, ProjectIdentifier projectIdentifier) + { + logAccess(sessionToken, "listExperiments", "%s", projectIdentifier); + return null; + } + + public List<Project> listProjects(String sessionToken) + { + logAccess(sessionToken, "listProjects"); + return null; + } + } diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java index 1c8539c0c2d..5c887f26405 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/IETLLIMSService.java @@ -389,7 +389,8 @@ public interface IETLLIMSService extends IServer, ISessionProvider throws UserFailureException; /** - * Creates and returns a unique code for a new data set. + * Creates and returns a unique code for a new data set. TODO KE: 2011-04-19 remove this method. + * It is equivalent to "createPermId()" */ @Transactional @RolesAllowed(RoleWithHierarchy.SPACE_ETL_SERVER) @@ -459,6 +460,24 @@ public interface IETLLIMSService extends IServer, ISessionProvider @Transactional(readOnly = true) @RolesAllowed(RoleWithHierarchy.SPACE_ETL_SERVER) public List<ExternalData> listDataSets(String sessionToken, String dataStoreCode, TrackingDataSetCriteria criteria); + + /** + * List all experiments for a given project identifier. + */ + @Transactional(readOnly = true) + @RolesAllowed(value = + { RoleWithHierarchy.SPACE_OBSERVER }) + public List<Experiment> listExperiments( + String sessionToken, + @AuthorizationGuard(guardClass = ExistingSpaceIdentifierPredicate.class) ProjectIdentifier projectIdentifier); + + /** + * List all projects that the user can see. + */ + @Transactional(readOnly = true) + @RolesAllowed(value = + { RoleWithHierarchy.SPACE_OBSERVER }) + public List<Project> listProjects(String sessionToken); /** * Adds specified properties of given data set. Properties defined before will not be updated. @@ -656,5 +675,4 @@ public interface IETLLIMSService extends IServer, ISessionProvider public Project tryGetProject( String sessionToken, @AuthorizationGuard(guardClass = ExistingSpaceIdentifierPredicate.class) ProjectIdentifier projectIdentifier); - } -- GitLab