diff --git a/datastore_server/.classpath b/datastore_server/.classpath index 105dc732c45ae51449a0f9690e05c8fd70ce1f39..1247b650f9c67ddc8cdd4a914e9c3e848dd26684 100644 --- a/datastore_server/.classpath +++ b/datastore_server/.classpath @@ -19,5 +19,8 @@ <classpathentry kind="lib" path="/libraries/jmock/objenesis/objenesis-1.0.jar"/> <classpathentry combineaccessrules="false" kind="src" path="/openbis"/> <classpathentry combineaccessrules="false" kind="src" path="/server-common"/> + <classpathentry kind="lib" path="/libraries/jetty/jetty.jar" sourcepath="/libraries/jetty/src/jetty.zip"/> + <classpathentry kind="lib" path="/libraries/jetty/jetty-util.jar" sourcepath="/libraries/jetty/src/jetty-util.zip"/> + <classpathentry kind="lib" path="/libraries/jetty/servlet-api-2.5.jar" sourcepath="/libraries/jetty/src/servlet-api-2.5.zip"/> <classpathentry kind="output" path="targets/classes"/> </classpath> diff --git a/datastore_server/resource/eclipse/Dataset Download Service.launch b/datastore_server/resource/eclipse/Dataset Download Service.launch new file mode 100644 index 0000000000000000000000000000000000000000..2cb0da27a61bec52db6b179a06cfa37e0ff82973 --- /dev/null +++ b/datastore_server/resource/eclipse/Dataset Download Service.launch @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication"> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> +<listEntry value="/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DatasetDownloadService.java"/> +</listAttribute> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> +<listEntry value="1"/> +</listAttribute> +<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/> +<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="ch.systemsx.cisd.openbis.dss.generic.server.DatasetDownloadService"/> +<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="datastore_server"/> +<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea"/> +</launchConfiguration> diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ApplicationContext.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ApplicationContext.java new file mode 100644 index 0000000000000000000000000000000000000000..2e5f7ea1e4cf972e8b8cb9614c6620893d571082 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ApplicationContext.java @@ -0,0 +1,58 @@ +/* + * Copyright 2008 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; + +import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService; + +/** + * Application context. It contains the object accessing the openBIS for retrieving the data set, + * configuration parameters, and the name of the application which will be a part of its URL. + * + * @author Franz-Josef Elmer + */ +class ApplicationContext +{ + private final IETLLIMSService dataSetService; + + private final ConfigParameters configParameters; + + private final String applicationName; + + ApplicationContext(IETLLIMSService service, ConfigParameters configParameters, + String applicationName) + { + this.dataSetService = service; + this.configParameters = configParameters; + this.applicationName = applicationName; + } + + public final IETLLIMSService getDataSetService() + { + return dataSetService; + } + + public final ConfigParameters getConfigParameters() + { + return configParameters; + } + + public final String getApplicationName() + { + return applicationName; + } + +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..362d70a70eb1aa35eb47fe6fa2b8563cf58309f8 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ConfigParameters.java @@ -0,0 +1,146 @@ +/* + * Copyright 2008 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; + +import java.util.Properties; + +import org.apache.log4j.Logger; + +import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException; +import ch.systemsx.cisd.common.logging.LogCategory; +import ch.systemsx.cisd.common.logging.LogFactory; +import ch.systemsx.cisd.common.utilities.PropertyUtils; + +/** + * Configuration parameters for the Data Set Download Server. + * + * @author Franz-Josef Elmer + */ +final class ConfigParameters +{ + + private static final Logger operationLog = + LogFactory.getLogger(LogCategory.OPERATION, ConfigParameters.class); + + static final String SERVER_URL_KEY = "server-url"; + + static final String PORT_KEY = "port"; + + static final String STOREROOT_DIR_KEY = "storeroot-dir"; + + static final String SESSION_TIMEOUT_KEY = "session-timeout"; + + private static final String KEYSTORE = "keystore."; + + static final String KEYSTORE_PATH_KEY = KEYSTORE + "path"; + + static final String KEYSTORE_PASSWORD_KEY = KEYSTORE + "password"; + + static final String KEYSTORE_KEY_PASSWORD_KEY = KEYSTORE + "key-password"; + + private final String storePath; + + private final int port; + + private final String serverURL; + + private final int sessionTimeout; + + private final String keystorePath; + + private final String keystorePassword; + + private final String keystoreKeyPassword; + + /** + * Creates an instance based on the specified properties. + * + * @throws ConfigurationFailureException if a property is missed or has an invalid value. + */ + public ConfigParameters(final Properties properties) + { + storePath = PropertyUtils.getMandatoryProperty(properties, STOREROOT_DIR_KEY); + port = getMandatoryIntegerProperty(properties, PORT_KEY); + serverURL = PropertyUtils.getMandatoryProperty(properties, SERVER_URL_KEY); + sessionTimeout = getMandatoryIntegerProperty(properties, SESSION_TIMEOUT_KEY) * 60; + keystorePath = PropertyUtils.getMandatoryProperty(properties, KEYSTORE_PATH_KEY); + keystorePassword = PropertyUtils.getMandatoryProperty(properties, KEYSTORE_PASSWORD_KEY); + keystoreKeyPassword = + PropertyUtils.getMandatoryProperty(properties, KEYSTORE_KEY_PASSWORD_KEY); + } + + private final static int getMandatoryIntegerProperty(final Properties properties, + final String key) + { + final String property = PropertyUtils.getMandatoryProperty(properties, key); + try + { + return Integer.parseInt(property); + } catch (final NumberFormatException ex) + { + throw new ConfigurationFailureException("Configuration parameter '" + key + + "' is not an integer number: " + property); + } + } + + public final String getStorePath() + { + return storePath; + } + + public final int getPort() + { + return port; + } + + public final String getServerURL() + { + return serverURL; + } + + public final int getSessionTimeout() + { + return sessionTimeout; + } + + public final String getKeystorePath() + { + return keystorePath; + } + + public final String getKeystorePassword() + { + return keystorePassword; + } + + public final String getKeystoreKeyPassword() + { + return keystoreKeyPassword; + } + + public final void log() + { + if (operationLog.isInfoEnabled()) + { + operationLog.info(String.format("Store root directory: '%s'.", storePath)); + operationLog.info(String.format("Port number: %d.", port)); + operationLog.info(String.format("URL of openBIS server: '%s'.", serverURL)); + operationLog.info(String.format("Session timeout (minutes): %d.", sessionTimeout)); + operationLog.info(String.format("Keystore path: '%s'.", keystorePath)); + } + } +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataSetService.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataSetService.java new file mode 100644 index 0000000000000000000000000000000000000000..37ff1eb6b025c119967468e72705141b739108e2 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataSetService.java @@ -0,0 +1,111 @@ +/* + * Copyright 2008 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; + +import ch.systemsx.cisd.common.exceptions.UserFailureException; +import ch.systemsx.cisd.common.spring.HttpInvokerUtils; +import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService; +import ch.systemsx.cisd.openbis.generic.shared.dto.DataStorePE; +import ch.systemsx.cisd.openbis.generic.shared.dto.DatabaseInstancePE; +import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE; +import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalData; +import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE; +import ch.systemsx.cisd.openbis.generic.shared.dto.IAuthSession; +import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePropertyPE; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifier; +import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.SampleIdentifier; + +/** + * A <code>IDataSetService</code> implementation. + * + * @author Franz-Josef Elmer + */ +final class DataSetService implements IETLLIMSService +{ + private IETLLIMSService service; + + DataSetService(final ConfigParameters configParameters) + { + String url = configParameters.getServerURL() + "/rmi-etl"; + service = HttpInvokerUtils.createServiceStub(IETLLIMSService.class, url, 5); + } + + // + // IETLLIMSService + // + + public final ExternalDataPE tryGetDataSet(final String sessionToken, final String dataSetCode) + throws UserFailureException + { + return service.tryGetDataSet(sessionToken, dataSetCode); + } + + public final String authenticate(final String user, final String password) + throws UserFailureException + { + return service.authenticate(user, password); + } + + public final void closeSession(final String sessionToken) throws UserFailureException + { + service.closeSession(sessionToken); + } + + public final int getVersion() + { + return service.getVersion(); + } + + public String createDataSetCode(String sessionToken) throws UserFailureException + { + return null; + } + + public DatabaseInstancePE getHomeDatabaseInstance(String sessionToken) + { + return null; + } + + public void registerDataSet(String sessionToken, SampleIdentifier sampleIdentifier, + String procedureTypeCode, ExternalData externalData) throws UserFailureException + { + } + + public ExperimentPE tryToGetBaseExperiment(String sessionToken, + SampleIdentifier sampleIdentifier) throws UserFailureException + { + return null; + } + + public SamplePropertyPE[] tryToGetPropertiesOfTopSampleRegisteredFor(String sessionToken, + SampleIdentifier sampleIdentifier) throws UserFailureException + { + return null; + } + + public DataStorePE getDataStore(String sessionToken, ExperimentIdentifier experimentIdentifier, + String dataSetTypeCode) throws UserFailureException + { + return null; + } + + public IAuthSession getSession(String sessionToken) throws UserFailureException + { + return null; + } + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DatasetDownloadService.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DatasetDownloadService.java new file mode 100644 index 0000000000000000000000000000000000000000..99dd5f59f5974b113e767708116a0a97f744da33 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DatasetDownloadService.java @@ -0,0 +1,166 @@ +/* + * Copyright 2008 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; + +import java.io.File; +import java.util.Enumeration; +import java.util.Properties; + +import org.apache.log4j.Logger; +import org.mortbay.jetty.Server; +import org.mortbay.jetty.security.SslSocketConnector; +import org.mortbay.jetty.servlet.Context; + +import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException; +import ch.systemsx.cisd.common.logging.LogCategory; +import ch.systemsx.cisd.common.logging.LogFactory; +import ch.systemsx.cisd.common.logging.LogInitializer; +import ch.systemsx.cisd.common.utilities.PropertyUtils; +import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService; +import ch.systemsx.cisd.openbis.generic.shared.IWebService; + +/** + * Main class of the service. Starts up jetty with {@link DatasetDownloadServlet}. + * + * @author Franz-Josef Elmer + */ +public class DatasetDownloadService +{ + static final String APPLICATION_CONTEXT_KEY = "application-context"; + + private static final String PREFIX = "data-set-download."; + + private static final int PREFIX_LENGTH = PREFIX.length(); + + private static final String SERVICE_PROPERTIES_FILE = "etc/service.properties"; + + private static final Logger operationLog = + LogFactory.getLogger(LogCategory.OPERATION, DatasetDownloadService.class); + + private static Server server; + + public static final void start() + { + assert server == null : "Server already started"; + final ApplicationContext applicationContext = createApplicationContext(); + server = createServer(applicationContext); + try + { + server.start(); + selfTest(applicationContext); + if (operationLog.isInfoEnabled()) + { + operationLog.info("Data set download server ready on port " + + applicationContext.getConfigParameters().getPort()); + } + } catch (final Exception ex) + { + operationLog.error("Failed to start server.", ex); + } + } + + public static final void stop() + { + assert server != null : "Server has not been started."; + if (server.isRunning()) + { + try + { + server.stop(); + } catch (final Exception ex) + { + operationLog.error("Failed to stop server.", ex); + } + } + server = null; + } + + public static void main(final String[] args) + { + LogInitializer.init(); + start(); + } + + private final static Server createServer(final ApplicationContext applicationContext) + { + final ConfigParameters configParameters = applicationContext.getConfigParameters(); + final int port = configParameters.getPort(); + final Server thisServer = new Server(); + final SslSocketConnector socketConnector = new SslSocketConnector(); + socketConnector.setPort(port); + socketConnector.setMaxIdleTime(30000); + socketConnector.setKeystore(configParameters.getKeystorePath()); + socketConnector.setPassword(configParameters.getKeystorePassword()); + socketConnector.setKeyPassword(configParameters.getKeystoreKeyPassword()); + thisServer.addConnector(socketConnector); + final Context context = new Context(thisServer, "/", Context.SESSIONS); + context.setAttribute(APPLICATION_CONTEXT_KEY, applicationContext); + context.addServlet(DatasetDownloadServlet.class, "/" + + applicationContext.getApplicationName() + "/*"); + return thisServer; + } + + private final static void selfTest(final ApplicationContext applicationContext) + { + final int version = applicationContext.getDataSetService().getVersion(); + if (IWebService.VERSION != version) + { + throw new ConfigurationFailureException( + "This client has the wrong service version for the server (client: " + + IWebService.VERSION + ", server: " + version + ")."); + } + if (operationLog.isInfoEnabled()) + { + operationLog.info("openBIS service (interface version " + version + ") is reachable"); + } + } + + private final static ApplicationContext createApplicationContext() + { + final ConfigParameters configParameters = getConfigParameters(); + final IETLLIMSService dataSetService = new DataSetService(configParameters); + final ApplicationContext applicationContext = + new ApplicationContext(dataSetService, configParameters, "dataset-download"); + return applicationContext; + } + + private final static ConfigParameters getConfigParameters() + { + final Properties properties; + if (new File(SERVICE_PROPERTIES_FILE).exists() == false) + { + properties = new Properties(); + } else + { + properties = PropertyUtils.loadProperties(SERVICE_PROPERTIES_FILE); + } + final Properties systemProperties = System.getProperties(); + final Enumeration<?> propertyNames = systemProperties.propertyNames(); + while (propertyNames.hasMoreElements()) + { + final String name = (String) propertyNames.nextElement(); + if (name.startsWith(PREFIX)) + { + final String value = systemProperties.getProperty(name); + properties.setProperty(name.substring(PREFIX_LENGTH), value); + } + } + final ConfigParameters configParameters = new ConfigParameters(properties); + configParameters.log(); + return configParameters; + } +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DatasetDownloadServlet.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DatasetDownloadServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..c864640e07c10d72e371c58d7e3b0835cf305e7d --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DatasetDownloadServlet.java @@ -0,0 +1,453 @@ +/* + * Copyright 2008 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; + +import static ch.systemsx.cisd.openbis.dss.generic.server.DatasetDownloadService.APPLICATION_CONTEXT_KEY; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; + +import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException; +import ch.systemsx.cisd.common.exceptions.UserFailureException; +import ch.systemsx.cisd.common.filesystem.FileUtilities; +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.ExternalDataPE; +import ch.systemsx.cisd.openbis.generic.shared.dto.LocatorType; +import ch.systemsx.cisd.openbis.generic.shared.dto.LocatorTypePE; + +/** + * @author Franz-Josef Elmer + */ +public class DatasetDownloadServlet extends HttpServlet +{ + private static final String TEXT_MODE_DISPLAY = "txt"; + + static final String DATA_SET_KEY = "data-set"; + + static final String DATASET_CODE_KEY = "dataSetCode"; + + static final String SESSION_ID_KEY = "sessionID"; + + static final String DISPLAY_MODE_KEY = "mode"; + + static final String BINARY_CONTENT_TYPE = "binary"; + + private static final long serialVersionUID = 1L; + + protected static final Logger operationLog = + LogFactory.getLogger(LogCategory.OPERATION, DatasetDownloadServlet.class); + + protected static final Logger notificationLog = + LogFactory.getLogger(LogCategory.NOTIFY, DatasetDownloadServlet.class); + + private static final Comparator<File> FILE_COMPARATOR = new Comparator<File>() + { + public int compare(File file1, File file2) + { + return createSortableName(file1).compareTo(createSortableName(file2)); + } + + private String createSortableName(File file) + { + return (file.isDirectory() ? "D" : "F") + file.getName().toUpperCase(); + } + }; + + private ApplicationContext applicationContext; + + public DatasetDownloadServlet() + { + } + + DatasetDownloadServlet(ApplicationContext applicationContext) + { + this.applicationContext = applicationContext; + } + + @Override + public final void init(final ServletConfig servletConfig) throws ServletException + { + super.init(servletConfig); + try + { + ServletContext context = servletConfig.getServletContext(); + applicationContext = (ApplicationContext) context.getAttribute(APPLICATION_CONTEXT_KEY); + } catch (Exception ex) + { + notificationLog.fatal("Failure during '" + servletConfig.getServletName() + + "' servlet initialization.", ex); + throw new ServletException(ex); + } + } + + // helper class to store parsed URL request + private static class RequestParams + { + private final String dataSetCode; + + private final String pathInfo; + + private final String sessionIdOrNull; + + private final boolean isPlainTextMode; + + private final String urlPrefixWithDataset; + + public RequestParams(String dataSetCode, String pathInfo, String sessionIdOrNull, + String urlPrefixWithDataset, boolean isPlainTextMode) + { + this.dataSetCode = dataSetCode; + this.pathInfo = pathInfo; + this.sessionIdOrNull = sessionIdOrNull; + this.urlPrefixWithDataset = urlPrefixWithDataset; + this.isPlainTextMode = isPlainTextMode; + } + + public String getDataSetCode() + { + return dataSetCode; + } + + public String getPathInfo() + { + return pathInfo; + } + + public String tryGetSessionId() + { + return sessionIdOrNull; + } + + public boolean isPlainTextMode() + { + return isPlainTextMode; + } + + public String getURLPrefix() + { + return urlPrefixWithDataset; + } + } + + @Override + protected final void doGet(final HttpServletRequest request, final HttpServletResponse response) + throws ServletException, IOException + { + IRendererFactory rendererFactory = null; + try + { + RequestParams requestParams = + parseRequestURL(request, applicationContext.getApplicationName()); + rendererFactory = createRendererFactory(requestParams.isPlainTextMode()); + + obtainDataSetFromServer(requestParams.getDataSetCode(), + requestParams.tryGetSessionId(), request); + + HttpSession session = request.getSession(false); + if (session == null) + { + printSessionExpired(response); + } else + { + printResponse(response, rendererFactory, requestParams, session); + } + } catch (Exception e) + { + if (rendererFactory == null) + { + rendererFactory = new PlainTextRendererFactory(); + } + printError(rendererFactory, request, response, e); + } + } + + private void printResponse(final HttpServletResponse response, + IRendererFactory rendererFactory, RequestParams requestParams, HttpSession session) + throws UnsupportedEncodingException, IOException + { + String dataSetCode = requestParams.getDataSetCode(); + ExternalDataPE dataSet = tryToGetDataSet(session, dataSetCode); + if (dataSet == null) + { + throw new UserFailureException("Unknown data set '" + dataSetCode + "'."); + } + File rootDir = createDataSetRootDirectory(dataSet); + RenderingContext context = + new RenderingContext(rootDir, requestParams.getURLPrefix(), requestParams + .getPathInfo()); + renderPage(rendererFactory, response, dataSet, context); + } + + private IRendererFactory createRendererFactory(boolean plainTextMode) + { + if (plainTextMode) + { + return new PlainTextRendererFactory(); + } else + { + return new HTMLRendererFactory(); + } + } + + private static RequestParams parseRequestURL(HttpServletRequest request, String applicationName) + throws UnsupportedEncodingException + { + final String urlPrefix = "/" + applicationName + "/"; + final String requestURI = URLDecoder.decode(request.getRequestURI(), "UTF-8"); + if (requestURI.startsWith(urlPrefix) == false) + { + throw new EnvironmentFailureException("Request URI '" + requestURI + + "' expected to start with '" + urlPrefix + "'."); + } + final String fullPathInfo = requestURI.substring(urlPrefix.length()); + final int indexOfFirstSeparator = fullPathInfo.indexOf('/'); + final String dataSetCode; + final String pathInfo; + if (indexOfFirstSeparator < 0) + { + dataSetCode = fullPathInfo; + pathInfo = ""; + } else + { + dataSetCode = fullPathInfo.substring(0, indexOfFirstSeparator); + pathInfo = fullPathInfo.substring(indexOfFirstSeparator + 1); + } + final String urlPrefixWithDataset = + requestURI.substring(0, requestURI.length() - pathInfo.length()); + + final String sessionIDOrNull = request.getParameter(SESSION_ID_KEY); + final String displayMode = request.getParameter(DISPLAY_MODE_KEY); + final boolean isTextMode = (displayMode != null && displayMode.equals(TEXT_MODE_DISPLAY)); + + return new RequestParams(dataSetCode, pathInfo, sessionIDOrNull, urlPrefixWithDataset, + isTextMode); + } + + private void printError(IRendererFactory rendererFactory, final HttpServletRequest request, + final HttpServletResponse response, Exception exception) throws IOException + { + if (exception instanceof UserFailureException == false) + { + StringBuffer url = request.getRequestURL(); + String queryString = request.getQueryString(); + if (StringUtils.isNotBlank(queryString)) + { + url.append("?").append(queryString); + } + operationLog.error("Request " + url + " caused an exception: ", exception); + } else if (operationLog.isInfoEnabled()) + { + operationLog.info("User failure: " + exception.getMessage()); + } + String message = exception.getMessage(); + String errorText = StringUtils.isBlank(message) ? exception.toString() : message; + IErrorRenderer errorRenderer = rendererFactory.createErrorRenderer(); + response.setContentType(rendererFactory.getContentType()); + PrintWriter writer = response.getWriter(); + errorRenderer.setWriter(writer); + errorRenderer.printErrorMessage(errorText); + writer.flush(); + writer.close(); + } + + private void renderPage(IRendererFactory rendererFactory, HttpServletResponse response, + ExternalDataPE dataSet, RenderingContext renderingContext) throws IOException + { + File file = renderingContext.getFile(); + if (file.exists() == false) + { + throw new EnvironmentFailureException("File '" + file.getName() + "' does not exist."); + } + if (file.isDirectory()) + { + createPage(rendererFactory, response, dataSet, renderingContext, file); + } else + { + deliverFile(response, dataSet, file); + } + } + + private void createPage(IRendererFactory rendererFactory, HttpServletResponse response, + ExternalDataPE dataSet, RenderingContext renderingContext, File file) + throws IOException + { + if (operationLog.isInfoEnabled()) + { + operationLog.info("For data set '" + dataSet.getCode() + "' show directory " + + file.getAbsolutePath()); + } + IDirectoryRenderer directoryRenderer = + rendererFactory.createDirectoryRenderer(renderingContext); + response.setContentType(rendererFactory.getContentType()); + PrintWriter writer = null; + try + { + writer = response.getWriter(); + directoryRenderer.setWriter(writer); + directoryRenderer.printHeader(dataSet); + String relativeParentPath = renderingContext.getRelativeParentPath(); + if (relativeParentPath != null) + { + directoryRenderer.printLinkToParentDirectory(relativeParentPath); + } + File[] children = file.listFiles(); + Arrays.sort(children, FILE_COMPARATOR); + for (File child : children) + { + String name = child.getName(); + File rootDir = renderingContext.getRootDir(); + String relativePath = FileUtilities.getRelativeFile(rootDir, child); + String normalizedRelativePath = relativePath.replace('\\', '/'); + if (child.isDirectory()) + { + directoryRenderer.printDirectory(name, normalizedRelativePath); + } else + { + directoryRenderer.printFile(name, normalizedRelativePath, child.length()); + } + } + directoryRenderer.printFooter(); + writer.flush(); + + } finally + { + IOUtils.closeQuietly(writer); + } + } + + private void deliverFile(final HttpServletResponse response, ExternalDataPE dataSet, File file) + throws IOException, FileNotFoundException + { + long size = file.length(); + if (operationLog.isInfoEnabled()) + { + operationLog.info("For data set '" + dataSet.getCode() + "' deliver file " + + file.getAbsolutePath() + " (" + size + " bytes)."); + } + response.setContentLength((int) size); + response.setHeader("Content-Disposition", "inline; filename=" + file.getName()); + ServletOutputStream outputStream = null; + FileInputStream fileInputStream = null; + response.setContentType(BINARY_CONTENT_TYPE); + try + { + outputStream = response.getOutputStream(); + fileInputStream = new FileInputStream(file); + IOUtils.copy(fileInputStream, outputStream); + } finally + { + IOUtils.closeQuietly(fileInputStream); + IOUtils.closeQuietly(outputStream); + } + } + + private void printSessionExpired(final HttpServletResponse response) throws IOException + { + PrintWriter writer = response.getWriter(); + writer.write("<html><body>Download session expired.</body></html>"); + writer.flush(); + writer.close(); + } + + private void obtainDataSetFromServer(String dataSetCode, String sessionIdOrNull, + final HttpServletRequest request) + { + if (sessionIdOrNull != null) + { + IETLLIMSService dataSetService = applicationContext.getDataSetService(); + ExternalDataPE dataSet = dataSetService.tryGetDataSet(sessionIdOrNull, dataSetCode); + if (operationLog.isInfoEnabled()) + { + String actionDesc = (dataSet != null) ? "obtained from" : "not found in"; + operationLog.info(String.format("Data set '%s' %s openBIS server.", dataSetCode, + actionDesc)); + } + HttpSession session = request.getSession(true); + ConfigParameters configParameters = applicationContext.getConfigParameters(); + session.setMaxInactiveInterval(configParameters.getSessionTimeout()); + if (dataSet != null) + { + putDataSetToMap(session, dataSetCode, dataSet); + } + } + } + + private File createDataSetRootDirectory(ExternalDataPE dataSet) + { + String path = dataSet.getLocation(); + LocatorTypePE locatorType = dataSet.getLocatorType(); + if (locatorType.getCode().equals(LocatorType.DEFAULT_LOCATOR_TYPE_CODE)) + { + path = applicationContext.getConfigParameters().getStorePath() + "/" + path; + } + File dataSetRootDirectory = new File(path); + if (dataSetRootDirectory.exists() == false) + { + throw new UserFailureException("Data set '" + dataSet.getCode() + + "' not found in store at '" + dataSetRootDirectory.getAbsolutePath() + "'."); + } + return dataSetRootDirectory; + } + + private void putDataSetToMap(HttpSession session, String dataSetCode, ExternalDataPE dataSet) + { + getDataSets(session).put(dataSetCode, dataSet); + } + + private ExternalDataPE tryToGetDataSet(HttpSession session, String dataSetCode) + { + return getDataSets(session).get(dataSetCode); + } + + @SuppressWarnings("unchecked") + private Map<String, ExternalDataPE> getDataSets(HttpSession session) + { + Map<String, ExternalDataPE> map = + (Map<String, ExternalDataPE>) session.getAttribute(DATA_SET_KEY); + if (map == null) + { + map = new HashMap<String, ExternalDataPE>(); + session.setAttribute(DATA_SET_KEY, map); + } + return map; + } + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/HTMLDirectoryRenderer.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/HTMLDirectoryRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..c06f6cb672a420a01907bffb61534f2ad136886d --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/HTMLDirectoryRenderer.java @@ -0,0 +1,176 @@ +/* + * Copyright 2008 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; + +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.StringUtils; + +import ch.systemsx.cisd.common.exceptions.CheckedExceptionTunnel; +import ch.systemsx.cisd.common.utilities.Template; +import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE; +import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE; +import ch.systemsx.cisd.openbis.generic.shared.dto.GroupPE; +import ch.systemsx.cisd.openbis.generic.shared.dto.ProcedurePE; +import ch.systemsx.cisd.openbis.generic.shared.dto.ProjectPE; + +/** + * An <code>IDirectoryRenderer</code> implementation which renders on HTML pages. + * + * @author Franz-Josef Elmer + */ +final class HTMLDirectoryRenderer implements IDirectoryRenderer +{ + private static final String DATASET_DESCRIPTION = + "${group}/${project}/${experiment}/${sample}/${dataset}"; + + private static final String DATASET_DOWNLOAD_SERVICE = "Data Set Download Service"; + + private static final String TITLE = + "<title> " + DATASET_DOWNLOAD_SERVICE + ": " + DATASET_DESCRIPTION + "</title>"; + + private static final String CSS = + "<style type='text/css'> " + + "* { margin: 3px; }" + + "html { height: 100%; }" + + "body { height: 100%; font-family: verdana, tahoma, helvetica; font-size: 11px; text-align:left; }" + + "h1 { text-align: center; padding: 1em; color: #1E4E8F;}" + + ".td_hd { border: 1px solid #FFFFFF; padding 3px; background-color: #DDDDDD; height: 1.5em; }" + + ".div_hd { background-color: #1E4E8F; color: white; font-weight: bold; padding: 3px; }" + + "table { border-collapse: collapse; padding: 1em; }" + + "tr, td { font-family: verdana, tahoma, helvetica; font-size: 11px; }" + + ".td_file { font-family: verdana, tahoma, helvetica; font-size: 11px; height: 1.5em }" + + ".wrapper { min-height: 100%; height: auto !important; height: 100%; margin: 0em auto -4em; }" + + ".footer { height: 4em; text-align: center; }" + "</style>"; + + private static final Template ROW_TEMPLATE = + new Template( + "<tr><td class='td_file'><a href='${path}'>${name}</td><td>${size}</td></tr>"); + + private static final Template HEADER_TEMPLATE = + new Template("<html><head>" + TITLE + CSS + "</head>" + + "<body><div class='wrapper'><h1>" + DATASET_DOWNLOAD_SERVICE + "</h1>" + + "<div class='div_hd'>Information about data set</div>" + "<table>" + + "<tr><td class='td_hd'>Group:</td><td>${group}</td></tr>" + + "<tr><td class='td_hd'>Project:</td><td>${project}</td></tr>" + + "<tr><td class='td_hd'>Experiment:</td><td>${experiment}</td></tr>" + + "<tr><td class='td_hd'>Sample:</td><td>${sample}</td></tr>" + + "<tr><td class='td_hd'>Data Set Code:</td><td>${dataset}</td></tr></table> " + + "<div class='div_hd'>Files</div>" + "<table> " + "${folder}" + ""); + + private static final Template FOOTER_TEMPLATE = + new Template("</table> </div> <div class='footer'>${footer} </div> </body></html>"); + + private PrintWriter writer; + + private final String urlPrefix; + + private final String relativePathOrNull; + + HTMLDirectoryRenderer(final RenderingContext context) + { + this.relativePathOrNull = context.getRelativePathOrNull(); + final String prefix = context.getUrlPrefix(); + this.urlPrefix = prefix.endsWith("/") ? prefix : prefix + "/"; + } + + public void setWriter(final PrintWriter writer) + { + this.writer = writer; + } + + public void printHeader(final ExternalDataPE dataSet) + { + final String datasetCode = dataSet.getCode(); + final String sampleCode = dataSet.getAssociatedSampleCode(); + final ProcedurePE procedure = dataSet.getProcedure(); + final ExperimentPE experiment = procedure.getExperiment(); + final String experimentCode = experiment.getCode(); + final ProjectPE project = experiment.getProject(); + final String projectCode = project.getCode(); + final GroupPE group = project.getGroup(); + final String groupCode = group.getCode(); + final Template template = HEADER_TEMPLATE.createFreshCopy(); + template.bind("group", groupCode); + template.bind("project", projectCode); + template.bind("experiment", experimentCode); + template.bind("sample", sampleCode); + template.bind("dataset", datasetCode); + if (StringUtils.isNotBlank(relativePathOrNull)) + { + template.bind("folder", "<tr><td class='td_hd'>Folder:</td><td>" + relativePathOrNull + + "</td></tr>"); + } else + { + template.bind("folder", ""); + } + writer.println(template.createText()); + } + + public void printLinkToParentDirectory(final String relativePath) + { + printRow("..", relativePath, ""); + } + + public void printDirectory(final String name, final String relativePath) + { + printRow(name, relativePath, ""); + } + + public void printFile(final String name, final String relativePath, final long size) + { + printRow(name, relativePath, renderFileSize(size)); + } + + private void printRow(final String name, final String relativePath, final String fileSize) + { + final Template template = ROW_TEMPLATE.createFreshCopy(); + template.bind("path", urlPrefix + encodeURL(relativePath)); + template.bind("name", name); + template.bind("size", fileSize); + writer.println(template.createText()); + } + + private String encodeURL(final String url) + { + try + { + return URLEncoder.encode(url, "UTF-8"); + } catch (final UnsupportedEncodingException ex) + { + throw CheckedExceptionTunnel.wrapIfNecessary(ex); + } + } + + private final static String renderFileSize(final long size) + { + return FileUtils.byteCountToDisplaySize(size); + } + + public void printFooter() + { + final Template template = FOOTER_TEMPLATE.createFreshCopy(); + template + .bind("footer", + "Copyright © 2008 ETHZ - <a href='http://www.cisd.systemsx.ethz.ch/'>CISD</a>"); + writer.println(template.createText()); + } + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/HTMLRendererFactory.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/HTMLRendererFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..a211fbbff5107a13734c40fd18164cd5fe669335 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/HTMLRendererFactory.java @@ -0,0 +1,61 @@ +/* + * Copyright 2008 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; + +import java.io.PrintWriter; + +/** + * Factory for rendering file system view in HTML. + * + * @author Franz-Josef Elmer + */ +public class HTMLRendererFactory implements IRendererFactory +{ + public String getContentType() + { + return "text/html"; + } + + + public IDirectoryRenderer createDirectoryRenderer(RenderingContext context) + { + return new HTMLDirectoryRenderer(context); + } + + public IErrorRenderer createErrorRenderer() + { + return new HTMLErrorRenderer(); + } + + private static class HTMLErrorRenderer implements IErrorRenderer + { + private PrintWriter writer; + + public void setWriter(PrintWriter writer) + { + this.writer = writer; + } + + public void printErrorMessage(String errorMessage) + { + writer.println("<html><body><h1>Error</h1>"); + writer.println(errorMessage); + writer.println("</body></html>"); + } + + } +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IDirectoryRenderer.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IDirectoryRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..2d7ed4338f13a1b311f75efddaa87a893185653b --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IDirectoryRenderer.java @@ -0,0 +1,39 @@ +/* + * Copyright 2008 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; + + +import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE; + +/** + * Interface of a renderer of a directory. + * + * @author Franz-Josef Elmer + */ +public interface IDirectoryRenderer extends IWriterInjector +{ + public void printHeader(ExternalDataPE dataSet); + + public void printLinkToParentDirectory(String relativePath); + + public void printDirectory(String name, String relativePath); + + public void printFile(String name, String relativePath, long size); + + public void printFooter(); + +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IErrorRenderer.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IErrorRenderer.java new file mode 100644 index 0000000000000000000000000000000000000000..d44d88501d7e6833b8058e0f0ca8f3f9de7f92a6 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IErrorRenderer.java @@ -0,0 +1,31 @@ +/* + * Copyright 2008 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; + +/** + * Interface for a renderer of an error messages. + * + * @author Franz-Josef Elmer + */ +public interface IErrorRenderer extends IWriterInjector +{ + /** + * Prints the error message set by + * {@link IWriterInjector#setWriter(java.io.PrintWriter)}. + */ + public void printErrorMessage(String errorMessage); +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IRendererFactory.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IRendererFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..4ab85e7b74aaece984d8a172138a09a661dc5074 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IRendererFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright 2008 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; + +/** + * Interface of a factory of renderers. + * + * @author Franz-Josef Elmer + */ +public interface IRendererFactory +{ + public String getContentType(); + + public IDirectoryRenderer createDirectoryRenderer(RenderingContext context); + + public IErrorRenderer createErrorRenderer(); +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IWriterInjector.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IWriterInjector.java new file mode 100644 index 0000000000000000000000000000000000000000..4cecc0782440e89d901284891c9e8be5180a57bc --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IWriterInjector.java @@ -0,0 +1,33 @@ +/* + * Copyright 2008 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; + +import java.io.PrintWriter; + +/** + * Interface of objects which allows injection of a {@link PrintWriter}. + * + * @author Franz-Josef Elmer + */ +public interface IWriterInjector +{ + /** + * Sets the writer. + */ + public void setWriter(PrintWriter writer); + +} \ No newline at end of file diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/PlainTextRendererFactory.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/PlainTextRendererFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..6bf45fe05224e86be3ff15e73ed2a5dfcca880c2 --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/PlainTextRendererFactory.java @@ -0,0 +1,100 @@ +/* + * Copyright 2008 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; + +import java.io.PrintWriter; + +import org.apache.commons.io.FileUtils; + +import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE; + +/** + * Factory for rendering file system view in plain text. + * + * @author Tomasz Pylak + */ +public class PlainTextRendererFactory implements IRendererFactory +{ + + public IDirectoryRenderer createDirectoryRenderer(RenderingContext context) + { + return new PlainTextDirectoryRenderer(); + } + + public IErrorRenderer createErrorRenderer() + { + return new IErrorRenderer() + { + private PrintWriter writer; + + public void printErrorMessage(String errorMessage) + { + writer.println("Error:"); + writer.println(errorMessage); + } + + public void setWriter(PrintWriter writer) + { + this.writer = writer; + } + }; + } + + public String getContentType() + { + return "text"; + } + + private static class PlainTextDirectoryRenderer implements IDirectoryRenderer + { + private PrintWriter writer; + + public String getContentType() + { + return "text"; + } + + public void printDirectory(String name, String relativePath) + { + writer.print(name + "\n"); + } + + public void printFile(String name, String relativePath, long size) + { + writer.format("%s\t%s\n", name, FileUtils.byteCountToDisplaySize(size)); + } + + public void printFooter() + { + } + + public void printHeader(ExternalDataPE dataSet) + { + writer.println("Directory content:"); + } + + public void printLinkToParentDirectory(String relativePath) + { + } + + public void setWriter(PrintWriter writer) + { + this.writer = writer; + } + + } +} diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/RenderingContext.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/RenderingContext.java new file mode 100644 index 0000000000000000000000000000000000000000..46309f1a4fdc76131251a0aa30f8e90f5bf7e62b --- /dev/null +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/RenderingContext.java @@ -0,0 +1,77 @@ +/* + * Copyright 2008 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; + +import java.io.File; + +import ch.systemsx.cisd.common.filesystem.FileUtilities; + +final class RenderingContext +{ + private final File rootDir; + + private final String relativePathOrNull; + + private File file; + + private final String urlPrefix; + + private String relativeParentPath; + + RenderingContext(File rootDir, String urlPrefix, String relativePathOrNull) + { + this.rootDir = rootDir; + this.relativePathOrNull = relativePathOrNull; + this.file = rootDir; + this.urlPrefix = urlPrefix; + if (relativePathOrNull != null && relativePathOrNull.length() > 0) + { + file = new File(rootDir, relativePathOrNull); + relativeParentPath = FileUtilities.getRelativeFile(rootDir, file.getParentFile()); + if (relativeParentPath == null) + { + relativeParentPath = ""; + } + } + } + + public final File getRootDir() + { + return rootDir; + } + + public final String getRelativePathOrNull() + { + return relativePathOrNull; + } + + public final File getFile() + { + return file; + } + + public final String getUrlPrefix() + { + return urlPrefix; + } + + public final String getRelativeParentPath() + { + return relativeParentPath; + } + +} \ No newline at end of file diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/.gitignore b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/DatasetDownloadServletTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/DatasetDownloadServletTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8a565747ea6231e64511a127e033fceffc9c74c0 --- /dev/null +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/DatasetDownloadServletTest.java @@ -0,0 +1,583 @@ +/* + * Copyright 2008 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; + +import static org.testng.AssertJUnit.assertEquals; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.log4j.Level; +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import ch.systemsx.cisd.common.exceptions.CheckedExceptionTunnel; +import ch.systemsx.cisd.common.filesystem.FileUtilities; +import ch.systemsx.cisd.common.logging.BufferedAppender; +import ch.systemsx.cisd.common.utilities.OSUtilities; +import ch.systemsx.cisd.openbis.dss.generic.server.ApplicationContext; +import ch.systemsx.cisd.openbis.dss.generic.server.ConfigParameters; +import ch.systemsx.cisd.openbis.dss.generic.server.DatasetDownloadServlet; +import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService; +import ch.systemsx.cisd.openbis.generic.shared.dto.ExperimentPE; +import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalData; +import ch.systemsx.cisd.openbis.generic.shared.dto.ExternalDataPE; +import ch.systemsx.cisd.openbis.generic.shared.dto.GroupPE; +import ch.systemsx.cisd.openbis.generic.shared.dto.LocatorType; +import ch.systemsx.cisd.openbis.generic.shared.dto.LocatorTypePE; +import ch.systemsx.cisd.openbis.generic.shared.dto.ProcedurePE; +import ch.systemsx.cisd.openbis.generic.shared.dto.ProjectPE; +import ch.systemsx.cisd.openbis.generic.shared.dto.SamplePE; + +/** + * @author Franz-Josef Elmer + */ +public class DatasetDownloadServletTest +{ + private static final String APPLICATION_NAME = "download"; + + private static final String REQUEST_URI_PREFIX = "/" + APPLICATION_NAME + "/"; + + private static final String EXPIRATION_MESSAGE = + "<html><body>Download session expired.</body></html>"; + + private static final String LOGGER_NAME = "OPERATION.DatasetDownloadServlet"; + + private static final String LOG_INFO = "INFO " + LOGGER_NAME + " - "; + + private static final String LOG_ERROR = "ERROR " + LOGGER_NAME + " - "; + + private static final File TEST_FOLDER = new File("targets/unit-test/store"); + + private static final String EXAMPLE_DATA_SET_FOLDER_NAME = "data set #123"; + + private static final File EXAMPLE_DATA_SET_FOLDER = + new File(TEST_FOLDER, EXAMPLE_DATA_SET_FOLDER_NAME); + + private static final String EXAMPLE_FILE_NAME = "read me @home.txt"; + + private static final File EXAMPLE_FILE = new File(EXAMPLE_DATA_SET_FOLDER, EXAMPLE_FILE_NAME); + + private static final String EXAMPLE_FILE_CONTENT = "Hello world!"; + + private static final String EXAMPLE_DATA_SET_SUB_FOLDER_NAME = "+ s % ! # @"; + + private static final String ESCAPED_EXAMPLE_DATA_SET_SUB_FOLDER_NAME = + encode(EXAMPLE_DATA_SET_SUB_FOLDER_NAME); + + private static final File EXAMPLE_DATA_SET_SUB_FOLDER = + new File(EXAMPLE_DATA_SET_FOLDER, EXAMPLE_DATA_SET_SUB_FOLDER_NAME); + + private static final String EXAMPLE_SESSION_ID = "AV76CF"; + + private static final String EXAMPLE_DATA_SET_CODE = "1234-1"; + + private static final String SAMPLE_CODE = "SAMPLE-S"; + + private static final String EXPERIMENT_CODE = "EPERIMENT-E"; + + private static final String GROUP_CODE = "GROUP-G"; + + private static final String PROJECT_CODE = "PROJECT-P"; + + private static String encode(String url) + { + try + { + return URLEncoder.encode(url, "UTF-8"); + } catch (UnsupportedEncodingException ex) + { + throw CheckedExceptionTunnel.wrapIfNecessary(ex); + } + } + + private BufferedAppender logRecorder; + + private Mockery context; + + private HttpServletRequest request; + + private HttpServletResponse response; + + private IETLLIMSService dataSetService; + + private HttpSession httpSession; + + @BeforeMethod + public void setUp() + { + logRecorder = new BufferedAppender("%-5p %c - %m%n", Level.DEBUG); + context = new Mockery(); + request = context.mock(HttpServletRequest.class); + response = context.mock(HttpServletResponse.class); + dataSetService = context.mock(IETLLIMSService.class); + httpSession = context.mock(HttpSession.class); + TEST_FOLDER.mkdirs(); + EXAMPLE_DATA_SET_FOLDER.mkdir(); + FileUtilities.writeToFile(EXAMPLE_FILE, EXAMPLE_FILE_CONTENT); + EXAMPLE_DATA_SET_SUB_FOLDER.mkdir(); + } + + @AfterMethod + public void tearDown() + { + logRecorder.reset(); + FileUtilities.deleteRecursively(TEST_FOLDER); + // To following line of code should also be called at the end of each test method. + // Otherwise one do not known which test failed. + context.assertIsSatisfied(); + } + + @Test + public void testInitialDoGet() throws Exception + { + final StringWriter writer = new StringWriter(); + final ExternalDataPE externalData = createExternalData(); + prepareParseRequestURL(); + prepareForObtainingDataSetFromServer(externalData); + prepareForGettingDataSetFromSession(externalData, ""); + prepareForCreatingHTML(writer); + + DatasetDownloadServlet servlet = createServlet(); + servlet.doGet(request, response); + assertEquals( + "<html><head><title> Data Set Download Service: GROUP-G/PROJECT-P/EPERIMENT-E/SAMPLE-S/1234-1</title><style type=\'text/css\'> * { margin: 3px; }html { height: 100%; }body { height: 100%; font-family: verdana, tahoma, helvetica; font-size: 11px; text-align:left; }h1 { text-align: center; padding: 1em; color: #1E4E8F;}.td_hd { border: 1px solid #FFFFFF; padding 3px; background-color: #DDDDDD; height: 1.5em; }.div_hd { background-color: #1E4E8F; color: white; font-weight: bold; padding: 3px; }table { border-collapse: collapse; padding: 1em; }tr, td { font-family: verdana, tahoma, helvetica; font-size: 11px; }.td_file { font-family: verdana, tahoma, helvetica; font-size: 11px; height: 1.5em }.wrapper { min-height: 100%; height: auto !important; height: 100%; margin: 0em auto -4em; }.footer { height: 4em; text-align: center; }</style></head><body><div class=\'wrapper\'><h1>Data Set Download Service</h1><div class=\'div_hd\'>Information about data set</div><table><tr><td class=\'td_hd\'>Group:</td><td>GROUP-G</td></tr><tr><td class=\'td_hd\'>Project:</td><td>PROJECT-P</td></tr><tr><td class=\'td_hd\'>Experiment:</td><td>EPERIMENT-E</td></tr><tr><td class=\'td_hd\'>Sample:</td><td>SAMPLE-S</td></tr><tr><td class=\'td_hd\'>Data Set Code:</td><td>1234-1</td></tr></table> <div class=\'div_hd\'>Files</div><table> " + + OSUtilities.LINE_SEPARATOR + + "<tr><td class=\'td_file\'><a href=\'/download/1234-1/%2B+s+%25+%21+%23+%40\'>+ s % ! # @</td><td></td></tr>" + + OSUtilities.LINE_SEPARATOR + + "<tr><td class=\'td_file\'><a href=\'/download/1234-1/read+me+%40home.txt\'>read me @home.txt</td><td>12 bytes</td></tr>" + + OSUtilities.LINE_SEPARATOR + + "</table> </div> <div class=\'footer\'>Copyright © 2008 ETHZ - <a href=\'http://www.cisd.systemsx.ethz.ch/\'>CISD</a> </div> </body></html>" + + OSUtilities.LINE_SEPARATOR + "", writer.toString()); + assertEquals(LOG_INFO + "Data set '1234-1' obtained from openBIS server." + + OSUtilities.LINE_SEPARATOR + LOG_INFO + + "For data set '1234-1' show directory <wd>/data set #123", + getNormalizedLogContent()); + + context.assertIsSatisfied(); + } + + private void prepareParseRequestURL() + { + context.checking(new Expectations() + { + { + one(request).getParameter(DatasetDownloadServlet.SESSION_ID_KEY); + will(returnValue(EXAMPLE_SESSION_ID)); + + one(request).getParameter(DatasetDownloadServlet.DISPLAY_MODE_KEY); + will(returnValue(null)); + } + }); + } + + @Test + public void testInitialDoGetButDataSetNotFoundInStore() throws Exception + { + final StringWriter writer = new StringWriter(); + final ExternalDataPE externalData = createExternalData(); + LocatorTypePE locatorType = new LocatorTypePE(); + locatorType.setCode("unknown"); + externalData.setLocatorType(locatorType); + prepareParseRequestURL(); + prepareForObtainingDataSetFromServer(externalData); + prepareForGettingDataSetFromSession(externalData, "blabla"); + context.checking(new Expectations() + { + { + one(response).setContentType("text/html"); + one(response).getWriter(); + will(returnValue(new PrintWriter(writer))); + } + }); + + DatasetDownloadServlet servlet = createServlet(); + servlet.doGet(request, response); + String pageContent = writer.toString(); + String snippet = "Data set '1234-1' not found in store"; + assertEquals("Text snippet >" + snippet + "< not found in following page content: " + + pageContent, true, pageContent.indexOf(snippet) > 0); + String logContent = logRecorder.getLogContent(); + assertEquals("Text snippet >" + snippet + "< not found in following page content: " + + logContent, true, logContent.indexOf(snippet) > 0); + + context.assertIsSatisfied(); + } + + @Test + public void testDoGetButUnknownDataSetCode() throws Exception + { + final StringWriter writer = new StringWriter(); + final ExternalDataPE externalData = createExternalData(); + prepareParseRequestURL(); + prepareForObtainingDataSetFromServer(externalData); + context.checking(new Expectations() + { + { + one(request).getSession(false); + will(returnValue(httpSession)); + + one(httpSession).getAttribute(DatasetDownloadServlet.DATA_SET_KEY); + Map<String, ExternalData> map = new HashMap<String, ExternalData>(); + will(returnValue(map)); + + one(request).getRequestURI(); + String codeAndPath = externalData.getCode(); + will(returnValue(REQUEST_URI_PREFIX + codeAndPath)); + + one(response).setContentType("text/html"); + one(response).getWriter(); + will(returnValue(new PrintWriter(writer))); + } + }); + + DatasetDownloadServlet servlet = createServlet(); + servlet.doGet(request, response); + assertEquals("<html><body><h1>Error</h1>" + OSUtilities.LINE_SEPARATOR + + "Unknown data set '1234-1'." + OSUtilities.LINE_SEPARATOR + "</body></html>" + + OSUtilities.LINE_SEPARATOR, writer.toString()); + String logContent = logRecorder.getLogContent(); + assertEquals(LOG_INFO + "Data set '1234-1' obtained from openBIS server." + + OSUtilities.LINE_SEPARATOR + LOG_INFO + + "User failure: Unknown data set '1234-1'.", logContent); + + context.assertIsSatisfied(); + } + + @Test + public void testDoGetSubFolder() throws Exception + { + final StringWriter writer = new StringWriter(); + final ExternalDataPE externalData = createExternalData(); + prepareParseRequestURLNoSession(); + prepareForGettingDataSetFromSession(externalData, ESCAPED_EXAMPLE_DATA_SET_SUB_FOLDER_NAME); + prepareForCreatingHTML(writer); + + DatasetDownloadServlet servlet = createServlet(); + servlet.doGet(request, response); + assertEquals( + "<html><head><title> Data Set Download Service: GROUP-G/PROJECT-P/EPERIMENT-E/SAMPLE-S/1234-1</title><style type=\'text/css\'> * { margin: 3px; }html { height: 100%; }body { height: 100%; font-family: verdana, tahoma, helvetica; font-size: 11px; text-align:left; }h1 { text-align: center; padding: 1em; color: #1E4E8F;}.td_hd { border: 1px solid #FFFFFF; padding 3px; background-color: #DDDDDD; height: 1.5em; }.div_hd { background-color: #1E4E8F; color: white; font-weight: bold; padding: 3px; }table { border-collapse: collapse; padding: 1em; }tr, td { font-family: verdana, tahoma, helvetica; font-size: 11px; }.td_file { font-family: verdana, tahoma, helvetica; font-size: 11px; height: 1.5em }.wrapper { min-height: 100%; height: auto !important; height: 100%; margin: 0em auto -4em; }.footer { height: 4em; text-align: center; }</style></head><body><div class=\'wrapper\'><h1>Data Set Download Service</h1><div class=\'div_hd\'>Information about data set</div><table><tr><td class=\'td_hd\'>Group:</td><td>GROUP-G</td></tr><tr><td class=\'td_hd\'>Project:</td><td>PROJECT-P</td></tr><tr><td class=\'td_hd\'>Experiment:</td><td>EPERIMENT-E</td></tr><tr><td class=\'td_hd\'>Sample:</td><td>SAMPLE-S</td></tr><tr><td class=\'td_hd\'>Data Set Code:</td><td>1234-1</td></tr></table> <div class=\'div_hd\'>Files</div><table> <tr><td class=\'td_hd\'>Folder:</td><td>+ s % ! # @</td></tr>" + + OSUtilities.LINE_SEPARATOR + + "<tr><td class=\'td_file\'><a href=\'/download/1234-1/\'>..</td><td></td></tr>" + + OSUtilities.LINE_SEPARATOR + + "</table> </div> <div class=\'footer\'>Copyright © 2008 ETHZ - <a href=\'http://www.cisd.systemsx.ethz.ch/\'>CISD</a> </div> </body></html>" + + OSUtilities.LINE_SEPARATOR, writer.toString()); + assertEquals(LOG_INFO + "For data set '1234-1' show directory <wd>/data set #123/" + + EXAMPLE_DATA_SET_SUB_FOLDER_NAME, getNormalizedLogContent()); + + context.assertIsSatisfied(); + } + + @Test + public void testDoGetFile() throws Exception + { + final ExternalDataPE externalData = createExternalData(); + prepareParseRequestURLNoSession(); + prepareForGettingDataSetFromSession(externalData, EXAMPLE_FILE_NAME); + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + context.checking(new Expectations() + { + { + one(response).setContentType(DatasetDownloadServlet.BINARY_CONTENT_TYPE); + one(response).setContentLength(EXAMPLE_FILE_CONTENT.length()); + one(response).setHeader("Content-Disposition", + "inline; filename=" + EXAMPLE_FILE_NAME); + one(response).getOutputStream(); + will(returnValue(new ServletOutputStream() + { + @Override + public void write(int b) throws IOException + { + outputStream.write(b); + } + })); + } + }); + + DatasetDownloadServlet servlet = createServlet(); + servlet.doGet(request, response); + assertEquals("Hello world!", outputStream.toString()); + assertEquals(LOG_INFO + "For data set '1234-1' deliver file " + + "<wd>/data set #123/read me @home.txt (12 bytes).", getNormalizedLogContent()); + + context.assertIsSatisfied(); + } + + @Test + public void testDoGetNonExistingFile() throws Exception + { + final StringWriter writer = new StringWriter(); + final ExternalDataPE externalData = createExternalData(); + prepareParseRequestURLNoSession(); + prepareForGettingDataSetFromSession(externalData, "blabla"); + context.checking(new Expectations() + { + { + one(request).getRequestURL(); + will(returnValue(new StringBuffer("requestURL"))); + + one(request).getQueryString(); + will(returnValue("queryString")); + + one(response).setContentType("text/html"); + one(response).getWriter(); + will(returnValue(new PrintWriter(writer))); + } + }); + + DatasetDownloadServlet servlet = createServlet(); + servlet.doGet(request, response); + assertEquals("<html><body><h1>Error</h1>" + OSUtilities.LINE_SEPARATOR + + "File 'blabla' does not exist." + OSUtilities.LINE_SEPARATOR + "</body></html>" + + OSUtilities.LINE_SEPARATOR, writer.toString()); + String logContent = getNormalizedLogContent(); + assertEquals("The following string does not start as expected: " + logContent, true, + logContent.startsWith(LOG_ERROR + + "Request requestURL?queryString caused an exception:")); + + context.assertIsSatisfied(); + } + + @Test + public void testDoGetForExpiredSession() throws Exception + { + final StringWriter writer = new StringWriter(); + prepareParseRequestURLNoSession(); + context.checking(new Expectations() + { + { + one(request).getRequestURI(); + will(returnValue(REQUEST_URI_PREFIX + EXAMPLE_DATA_SET_CODE)); + + one(request).getSession(false); + will(returnValue(null)); + + one(response).getWriter(); + will(returnValue(new PrintWriter(writer))); + } + }); + + DatasetDownloadServlet servlet = createServlet(); + servlet.doGet(request, response); + assertEquals(EXPIRATION_MESSAGE, writer.toString()); + assertEquals("", getNormalizedLogContent()); + + context.assertIsSatisfied(); + } + + @Test + public void testDoGetRequestURINotStartingWithApplicationName() throws Exception + { + final StringWriter writer = new StringWriter(); + context.checking(new Expectations() + { + { + one(request).getRequestURI(); + will(returnValue("blabla")); + + one(request).getRequestURL(); + will(returnValue(new StringBuffer("requestURL"))); + + one(request).getQueryString(); + will(returnValue("query")); + + one(response).setContentType("text"); + one(response).getWriter(); + will(returnValue(new PrintWriter(writer))); + } + }); + + DatasetDownloadServlet servlet = createServlet(); + servlet.doGet(request, response); + assertEquals("Error:" + OSUtilities.LINE_SEPARATOR + + "Request URI 'blabla' expected to start with '/download/'." + + OSUtilities.LINE_SEPARATOR, writer.toString()); + String logContent = getNormalizedLogContent(); + assertEquals("The following string does not start as expected: " + logContent, true, + logContent.startsWith(LOG_ERROR + "Request requestURL?query caused an exception:")); + + context.assertIsSatisfied(); + } + + @Test + public void testDoGetForPathInfoStartingWithSeparator() throws Exception + { + final StringWriter writer = new StringWriter(); + final ExternalDataPE externalData = createExternalData(); + prepareParseRequestURL(); + prepareForObtainingDataSetFromServer(externalData); + context.checking(new Expectations() + { + { + one(request).getRequestURI(); + will(returnValue(REQUEST_URI_PREFIX + EXAMPLE_DATA_SET_CODE)); + + one(request).getSession(false); + will(returnValue(null)); + + one(response).getWriter(); + will(returnValue(new PrintWriter(writer))); + } + }); + + DatasetDownloadServlet servlet = createServlet(); + servlet.doGet(request, response); + assertEquals(EXPIRATION_MESSAGE, writer.toString()); + assertEquals(LOG_INFO + "Data set '1234-1' obtained from openBIS server.", + getNormalizedLogContent()); + + context.assertIsSatisfied(); + } + + private void prepareForGettingDataSetFromSession(final ExternalDataPE externalData, + final String path) + { + context.checking(new Expectations() + { + { + one(request).getSession(false); + will(returnValue(httpSession)); + + one(httpSession).getAttribute(DatasetDownloadServlet.DATA_SET_KEY); + Map<String, ExternalDataPE> map = new HashMap<String, ExternalDataPE>(); + map.put(externalData.getCode(), externalData); + will(returnValue(map)); + + one(request).getRequestURI(); + String codeAndPath = REQUEST_URI_PREFIX + externalData.getCode() + "/" + path; + will(returnValue(codeAndPath)); + + allowing(request).getRequestURI(); + will(returnValue(APPLICATION_NAME + "/" + codeAndPath)); + + } + }); + } + + private void prepareParseRequestURLNoSession() + { + context.checking(new Expectations() + { + { + one(request).getParameter(DatasetDownloadServlet.SESSION_ID_KEY); + will(returnValue(null)); + + one(request).getParameter(DatasetDownloadServlet.DISPLAY_MODE_KEY); + will(returnValue("html")); + } + }); + } + + private void prepareForObtainingDataSetFromServer(final ExternalDataPE externalData) + { + context.checking(new Expectations() + { + { + one(dataSetService).tryGetDataSet(EXAMPLE_SESSION_ID, EXAMPLE_DATA_SET_CODE); + will(returnValue(externalData)); + + one(request).getSession(true); + will(returnValue(httpSession)); + + one(httpSession).setMaxInactiveInterval(120); + one(httpSession).getAttribute(DatasetDownloadServlet.DATA_SET_KEY); + will(returnValue(null)); + + one(httpSession).setAttribute(DatasetDownloadServlet.DATA_SET_KEY, + new HashMap<String, ExternalDataPE>()); + } + }); + } + + private void prepareForCreatingHTML(final StringWriter writer) throws IOException + { + context.checking(new Expectations() + { + { + one(response).getWriter(); + will(returnValue(new PrintWriter(writer))); + + one(response).setContentType("text/html"); + } + }); + } + + private ExternalDataPE createExternalData() + { + GroupPE group = new GroupPE(); + group.setCode(GROUP_CODE); + ProjectPE project = new ProjectPE(); + project.setCode(PROJECT_CODE); + project.setGroup(group); + ProcedurePE procedure = new ProcedurePE(); + ExperimentPE experiment = new ExperimentPE(); + experiment.setCode(EXPERIMENT_CODE); + experiment.setProject(project); + procedure.setExperiment(experiment); + final ExternalDataPE externalData = new ExternalDataPE(); + externalData.setProcedure(procedure); + externalData.setCode(EXAMPLE_DATA_SET_CODE); + SamplePE samplePE = new SamplePE(); + samplePE.setCode(SAMPLE_CODE); + externalData.setSampleAcquiredFrom(samplePE); + LocatorTypePE locatorTypePE = new LocatorTypePE(); + locatorTypePE.setCode(LocatorType.DEFAULT_LOCATOR_TYPE_CODE); + externalData.setLocatorType(locatorTypePE); + externalData.setLocation(EXAMPLE_DATA_SET_FOLDER_NAME); + return externalData; + } + + private DatasetDownloadServlet createServlet() + { + Properties properties = new Properties(); + properties.setProperty(ConfigParameters.STOREROOT_DIR_KEY, TEST_FOLDER.toString()); + properties.setProperty(ConfigParameters.PORT_KEY, "8080"); + properties.setProperty(ConfigParameters.SERVER_URL_KEY, "http://localhost"); + properties.setProperty(ConfigParameters.SESSION_TIMEOUT_KEY, "2"); + properties.setProperty(ConfigParameters.KEYSTORE_PATH_KEY, "/"); + properties.setProperty(ConfigParameters.KEYSTORE_PASSWORD_KEY, "x"); + properties.setProperty(ConfigParameters.KEYSTORE_KEY_PASSWORD_KEY, "y"); + ConfigParameters configParameters = new ConfigParameters(properties); + return new DatasetDownloadServlet(new ApplicationContext(dataSetService, configParameters, + APPLICATION_NAME)); + } + + private String getNormalizedLogContent() + { + String logContent = logRecorder.getLogContent(); + logContent = logContent.replace(TEST_FOLDER.getAbsolutePath(), "<wd>"); + logContent = logContent.replace('\\', '/'); + return logContent; + } + +} diff --git a/datastore_server/sourceTest/java/tests.xml b/datastore_server/sourceTest/java/tests.xml index e8c6f023e2b6fc1afa085f085c31e77836e50519..cdd9b5631f699efede5f768192417bf157c7b70d 100644 --- a/datastore_server/sourceTest/java/tests.xml +++ b/datastore_server/sourceTest/java/tests.xml @@ -9,6 +9,7 @@ </groups> <packages> <package name="ch.systemsx.cisd.etlserver.*" /> + <package name="ch.systemsx.cisd.openbis.dss.*" /> </packages> </test> </suite>