diff --git a/dataset_download/source/java/ch/systemsx/cisd/openbis/datasetdownload/ApplicationContext.java b/dataset_download/source/java/ch/systemsx/cisd/openbis/datasetdownload/ApplicationContext.java index 431e9bf137cc27439451ee2fbe8fba4cb6da4e37..d4edd002a54bcc5b6f5efd30a7ade34487325ea6 100644 --- a/dataset_download/source/java/ch/systemsx/cisd/openbis/datasetdownload/ApplicationContext.java +++ b/dataset_download/source/java/ch/systemsx/cisd/openbis/datasetdownload/ApplicationContext.java @@ -19,7 +19,8 @@ package ch.systemsx.cisd.openbis.datasetdownload; import ch.systemsx.cisd.lims.base.IDataSetService; /** - * + * 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 */ @@ -28,11 +29,15 @@ class ApplicationContext private final IDataSetService dataSetService; private final ConfigParameters configParameters; + + private final String applicationName; - ApplicationContext(IDataSetService service, ConfigParameters configParameters) + ApplicationContext(IDataSetService service, ConfigParameters configParameters, + String applicationName) { this.dataSetService = service; this.configParameters = configParameters; + this.applicationName = applicationName; } public final IDataSetService getDataSetService() @@ -44,6 +49,10 @@ class ApplicationContext { return configParameters; } - + + public final String getApplicationName() + { + return applicationName; + } } diff --git a/dataset_download/source/java/ch/systemsx/cisd/openbis/datasetdownload/DatasetDownloadService.java b/dataset_download/source/java/ch/systemsx/cisd/openbis/datasetdownload/DatasetDownloadService.java index d4eb5c9afec1851dfba50668413dea1400257548..6bc0a8a7709208ba3614891561c5322002a26773 100644 --- a/dataset_download/source/java/ch/systemsx/cisd/openbis/datasetdownload/DatasetDownloadService.java +++ b/dataset_download/source/java/ch/systemsx/cisd/openbis/datasetdownload/DatasetDownloadService.java @@ -70,7 +70,8 @@ public class DatasetDownloadService server.addConnector(socketConnector); Context context = new Context(server, "/", Context.SESSIONS); context.setAttribute(APPLICATION_CONTEXT_KEY, applicationContext); - context.addServlet(DatasetDownloadServlet.class, "/dataset-download/*"); + context.addServlet(DatasetDownloadServlet.class, "/" + + applicationContext.getApplicationName() + "/*"); server.start(); selfTest(applicationContext); @@ -99,7 +100,8 @@ public class DatasetDownloadService { ConfigParameters configParameters = getConfigParameters(); IDataSetService dataSetService = new DataSetService(configParameters); - ApplicationContext applicationContext = new ApplicationContext(dataSetService, configParameters); + ApplicationContext applicationContext = + new ApplicationContext(dataSetService, configParameters, "dataset-download"); return applicationContext; } diff --git a/dataset_download/source/java/ch/systemsx/cisd/openbis/datasetdownload/DatasetDownloadServlet.java b/dataset_download/source/java/ch/systemsx/cisd/openbis/datasetdownload/DatasetDownloadServlet.java index c988c546489c8ec4db0f9a618619876099f6a578..4d12bdaa86667865454f17fee4b4724d9019d10b 100644 --- a/dataset_download/source/java/ch/systemsx/cisd/openbis/datasetdownload/DatasetDownloadServlet.java +++ b/dataset_download/source/java/ch/systemsx/cisd/openbis/datasetdownload/DatasetDownloadServlet.java @@ -23,6 +23,7 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; +import java.net.URLDecoder; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; @@ -115,15 +116,14 @@ public class DatasetDownloadServlet extends HttpServlet { try { - String pathInfo = request.getPathInfo(); - if (pathInfo == null) + String requestURI = URLDecoder.decode(request.getRequestURI(), "UTF-8"); + String prefix = "/" + applicationContext.getApplicationName() + "/"; + if (requestURI.startsWith(prefix) == false) { - throw new UserFailureException("Path not specified in URL."); - } - if (pathInfo.startsWith("/")) - { - pathInfo = pathInfo.substring(1); + throw new EnvironmentFailureException("Request URI '" + requestURI + + "' expected to start with '" + prefix + "'."); } + String pathInfo = requestURI.substring(prefix.length()); int indexOfFirstSeparator = pathInfo.indexOf('/'); String dataSetCode; if (indexOfFirstSeparator < 0) @@ -151,7 +151,6 @@ public class DatasetDownloadServlet extends HttpServlet throw new UserFailureException("Unknown data set '" + dataSetCode + "'."); } File rootDir = createDataSetRootDirectory(dataSet); - String requestURI = request.getRequestURI(); RenderingContext context = new RenderingContext(rootDir, requestURI, pathInfo); renderPage(response, dataSet, context); } diff --git a/dataset_download/source/java/ch/systemsx/cisd/openbis/datasetdownload/HTMLDirectoryRenderer.java b/dataset_download/source/java/ch/systemsx/cisd/openbis/datasetdownload/HTMLDirectoryRenderer.java index d0e8ab3c929771a4aabd974321b9e95bcb619c1d..b27a5bb0856559baca0b920ab7e15acf70f5c227 100644 --- a/dataset_download/source/java/ch/systemsx/cisd/openbis/datasetdownload/HTMLDirectoryRenderer.java +++ b/dataset_download/source/java/ch/systemsx/cisd/openbis/datasetdownload/HTMLDirectoryRenderer.java @@ -17,10 +17,13 @@ package ch.systemsx.cisd.openbis.datasetdownload; import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; import java.text.DecimalFormat; import org.apache.commons.lang.StringUtils; +import ch.systemsx.cisd.common.exceptions.CheckedExceptionTunnel; import ch.systemsx.cisd.common.utilities.Template; import ch.systemsx.cisd.lims.base.ExternalData; @@ -96,12 +99,24 @@ class HTMLDirectoryRenderer implements IDirectoryRenderer private void printRow(String name, String relativePath, String fileSize) { Template template = ROW_TEMPLATE.createFreshCopy(); - template.bind("path", urlPrefix + relativePath); + System.out.println("url prefix:"+urlPrefix+" unescaped path:"+relativePath); + template.bind("path", urlPrefix + encodeURL(relativePath)); template.bind("name", name); template.bind("size", fileSize); writer.println(template.createText()); } + private String encodeURL(String url) + { + try + { + return URLEncoder.encode(url, "UTF-8"); + } catch (UnsupportedEncodingException ex) + { + throw CheckedExceptionTunnel.wrapIfNecessary(ex); + } + } + private String renderFileSize(long size) { if (size < 10 * UNIT_KB) diff --git a/dataset_download/sourceTest/java/ch/systemsx/cisd/openbis/datasetdownload/DatasetDownloadServletTest.java b/dataset_download/sourceTest/java/ch/systemsx/cisd/openbis/datasetdownload/DatasetDownloadServletTest.java index 2e4e0eabc580a5ac8f569a6e9d5703a3f56b6ae1..03f8efe9611b92618986b75ea4df9733f06322dc 100644 --- a/dataset_download/sourceTest/java/ch/systemsx/cisd/openbis/datasetdownload/DatasetDownloadServletTest.java +++ b/dataset_download/sourceTest/java/ch/systemsx/cisd/openbis/datasetdownload/DatasetDownloadServletTest.java @@ -23,6 +23,8 @@ 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; @@ -39,6 +41,7 @@ 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.logging.BufferedAppender; import ch.systemsx.cisd.common.utilities.FileUtilities; import ch.systemsx.cisd.common.utilities.OSUtilities; @@ -54,6 +57,10 @@ import ch.systemsx.cisd.lims.base.LocatorType; */ 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>"; @@ -66,18 +73,22 @@ public class DatasetDownloadServletTest 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 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 = "readme.txt"; + 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 = "sub"; + 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); @@ -86,6 +97,16 @@ public class DatasetDownloadServletTest private static final String EXAMPLE_DATA_SET_CODE = "1234-1"; + 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; @@ -137,14 +158,16 @@ public class DatasetDownloadServletTest assertEquals("<html><body>" + OSUtilities.LINE_SEPARATOR + "<h1>Data Set 1234-1</h1>" + OSUtilities.LINE_SEPARATOR + "<table border=\'0\' cellpadding=\'5\' cellspacing=\'0\'>" - + OSUtilities.LINE_SEPARATOR + "<tr><td><a href='download/1234-1/sub'>sub</td><td></td></tr>" + + OSUtilities.LINE_SEPARATOR + "<tr><td><a href='/download/1234-1/" + + ESCAPED_EXAMPLE_DATA_SET_SUB_FOLDER_NAME + "'>" + + EXAMPLE_DATA_SET_SUB_FOLDER_NAME + "</td><td></td></tr>" + OSUtilities.LINE_SEPARATOR - + "<tr><td><a href='download/1234-1/readme.txt'>readme.txt</td><td>12 Bytes</td></tr>" - + OSUtilities.LINE_SEPARATOR + "</table></body></html>" + + "<tr><td><a href='/download/1234-1/read+me+%40home.txt'>read me @home.txt</td>" + + "<td>12 Bytes</td></tr>" + OSUtilities.LINE_SEPARATOR + "</table></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", + + "For data set '1234-1' show directory <wd>/data set #123", getNormalizedLogContent()); context.assertIsSatisfied(); @@ -196,9 +219,9 @@ public class DatasetDownloadServletTest Map<String, ExternalData> map = new HashMap<String, ExternalData>(); will(returnValue(map)); - one(request).getPathInfo(); + one(request).getRequestURI(); String codeAndPath = externalData.getCode(); - will(returnValue(codeAndPath)); + will(returnValue(REQUEST_URI_PREFIX + codeAndPath)); one(response).getWriter(); will(returnValue(new PrintWriter(writer))); @@ -224,20 +247,20 @@ public class DatasetDownloadServletTest final StringWriter writer = new StringWriter(); final ExternalData externalData = createExternalData(); prepareForNotObtainingDataSetFromServer(); - prepareForGettingDataSetFromSession(externalData, EXAMPLE_DATA_SET_SUB_FOLDER_NAME); + prepareForGettingDataSetFromSession(externalData, ESCAPED_EXAMPLE_DATA_SET_SUB_FOLDER_NAME); prepareForCreatingHTML(writer); DatasetDownloadServlet servlet = createServlet(); servlet.doGet(request, response); assertEquals("<html><body>" + OSUtilities.LINE_SEPARATOR + "<h1>Data Set 1234-1</h1>" - + OSUtilities.LINE_SEPARATOR + "Folder: sub" + + OSUtilities.LINE_SEPARATOR + "Folder: " + EXAMPLE_DATA_SET_SUB_FOLDER_NAME + OSUtilities.LINE_SEPARATOR + "<table border=\'0\' cellpadding=\'5\' cellspacing=\'0\'>" - + OSUtilities.LINE_SEPARATOR + "<tr><td><a href='download/1234-1/'>..</td><td></td></tr>" + + OSUtilities.LINE_SEPARATOR + "<tr><td><a href='/download/1234-1/'>..</td><td></td></tr>" + OSUtilities.LINE_SEPARATOR + "</table></body></html>" + OSUtilities.LINE_SEPARATOR, writer.toString()); - assertEquals(LOG_INFO + "For data set '1234-1' show directory <wd>/data-set-123/sub", - getNormalizedLogContent()); + assertEquals(LOG_INFO + "For data set '1234-1' show directory <wd>/data set #123/" + + EXAMPLE_DATA_SET_SUB_FOLDER_NAME, getNormalizedLogContent()); context.assertIsSatisfied(); } @@ -270,9 +293,8 @@ public class DatasetDownloadServletTest 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/readme.txt (12 bytes).", - getNormalizedLogContent()); + assertEquals(LOG_INFO + "For data set '1234-1' deliver file " + + "<wd>/data set #123/read me @home.txt (12 bytes).", getNormalizedLogContent()); context.assertIsSatisfied(); } @@ -319,8 +341,8 @@ public class DatasetDownloadServletTest context.checking(new Expectations() { { - one(request).getPathInfo(); - will(returnValue(EXAMPLE_DATA_SET_CODE)); + one(request).getRequestURI(); + will(returnValue(REQUEST_URI_PREFIX + EXAMPLE_DATA_SET_CODE)); one(request).getSession(false); will(returnValue(null)); @@ -339,14 +361,20 @@ public class DatasetDownloadServletTest } @Test - public void testDoGetForNullPathInfo() throws Exception + public void testDoGetRquestURINotStartingWithApplicationName() throws Exception { final StringWriter writer = new StringWriter(); context.checking(new Expectations() { { - one(request).getPathInfo(); - will(returnValue(null)); + one(request).getRequestURI(); + will(returnValue("blabla")); + + one(request).getRequestURL(); + will(returnValue(new StringBuffer("requestURL"))); + + one(request).getQueryString(); + will(returnValue("query")); one(response).getWriter(); will(returnValue(new PrintWriter(writer))); @@ -356,10 +384,13 @@ public class DatasetDownloadServletTest DatasetDownloadServlet servlet = createServlet(); servlet.doGet(request, response); assertEquals("<html><body><h1>Error</h1>" + OSUtilities.LINE_SEPARATOR - + "Path not specified in URL." + OSUtilities.LINE_SEPARATOR + "</body></html>" - + OSUtilities.LINE_SEPARATOR, writer.toString()); - assertEquals(LOG_INFO + "User failure: Path not specified in URL.", - getNormalizedLogContent()); + + "Request URI 'blabla' expected to start with '/download/'." + + 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?query caused an exception:")); context.assertIsSatisfied(); } @@ -373,8 +404,8 @@ public class DatasetDownloadServletTest context.checking(new Expectations() { { - one(request).getPathInfo(); - will(returnValue("/" + EXAMPLE_DATA_SET_CODE)); + one(request).getRequestURI(); + will(returnValue(REQUEST_URI_PREFIX + EXAMPLE_DATA_SET_CODE)); one(request).getSession(false); will(returnValue(null)); @@ -407,12 +438,12 @@ public class DatasetDownloadServletTest map.put(externalData.getCode(), externalData); will(returnValue(map)); - one(request).getPathInfo(); - String codeAndPath = externalData.getCode() + "/" + path; + one(request).getRequestURI(); + String codeAndPath = REQUEST_URI_PREFIX + externalData.getCode() + "/" + path; will(returnValue(codeAndPath)); allowing(request).getRequestURI(); - will(returnValue("download/" + codeAndPath)); + will(returnValue(APPLICATION_NAME + "/" + codeAndPath)); } }); @@ -486,7 +517,8 @@ public class DatasetDownloadServletTest properties.setProperty(ConfigParameters.KEYSTORE_PASSWORD_KEY, ""); properties.setProperty(ConfigParameters.KEYSTORE_KEY_PASSWORD_KEY, ""); ConfigParameters configParameters = new ConfigParameters(properties); - return new DatasetDownloadServlet(new ApplicationContext(dataSetService, configParameters)); + return new DatasetDownloadServlet(new ApplicationContext(dataSetService, configParameters, + APPLICATION_NAME)); } private String getNormalizedLogContent()