From 2432ff678be30cf03f73f9849c96957bbfccbd99 Mon Sep 17 00:00:00 2001 From: cramakri <cramakri> Date: Tue, 19 Feb 2013 10:18:49 +0000 Subject: [PATCH] BIS-329 SP-500 : Introducing a user-defined validity duration to access streams via URLs SVN: 28389 --- .../generic/server/AbstractDssServiceRpc.java | 5 ++- .../dss/generic/server/IStreamRepository.java | 8 ++-- .../dss/generic/server/StreamRepository.java | 32 +++++++++------ .../server/api/v1/DssServiceRpcGeneric.java | 25 +++++++++-- .../api/v1/DssServiceRpcGenericLogger.java | 24 +++++++++++ .../shared/api/v1/IDssServiceRpcGeneric.java | 37 +++++++++++++++++ .../client/api/v1/impl/DssComponentTest.java | 22 ++++++++++ .../generic/server/StreamRepositoryTest.java | 41 +++++++++++++++---- 8 files changed, 165 insertions(+), 29 deletions(-) diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/AbstractDssServiceRpc.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/AbstractDssServiceRpc.java index 7200100dd06..7729ee1a278 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/AbstractDssServiceRpc.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/AbstractDssServiceRpc.java @@ -141,11 +141,12 @@ public abstract class AbstractDssServiceRpc<T> extends AbstractServiceWithLogger return openBISService.tryGetDataSet(sessionToken, dataSetCode); } - protected String addToRepositoryAndReturnDownloadUrl(InputStream stream, String path) + protected String addToRepositoryAndReturnDownloadUrl(InputStream stream, String path, + long validityDurationInSeconds) { return downloadUrl + "/" + IdentifiedStreamHandlingServlet.SERVLET_NAME + "?" + IdentifiedStreamHandlingServlet.STREAM_ID_PARAMETER_KEY + "=" - + streamRepository.addStream(stream, path); + + streamRepository.addStream(stream, path, validityDurationInSeconds); } } \ No newline at end of file diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IStreamRepository.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IStreamRepository.java index 7ae604ce2f3..9f2a6c98a37 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IStreamRepository.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/IStreamRepository.java @@ -20,7 +20,7 @@ import java.io.InputStream; /** * Repositories for {@link InputStream} objects. - * + * * @author Franz-Josef Elmer */ public interface IStreamRepository @@ -28,8 +28,8 @@ public interface IStreamRepository /** * Adds specified stream and returns a unique id. */ - public String addStream(InputStream inputStream, String path); - + public String addStream(InputStream inputStream, String path, long validityDurationInSeconds); + /** * Retrieves stream by specified id. A stream can be retrieved only once. * @@ -37,5 +37,5 @@ public interface IStreamRepository * because the time between adding and retrieving was to long. */ public InputStreamWithPath getStream(String inputStreamID); - + } diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/StreamRepository.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/StreamRepository.java index 8ecaa64ec82..1e7594c3298 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/StreamRepository.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/StreamRepository.java @@ -37,16 +37,20 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.IConfigProvider; */ public class StreamRepository implements IStreamRepository { - private static final class InputStreamWithTimeStamp + private static final class InputStreamWithValidityDuration { + final long validityInMs; + final Date timestamp; final InputStreamWithPath inputStreamWithPath; - InputStreamWithTimeStamp(InputStreamWithPath inputStreamWithPath, Date timestamp) + InputStreamWithValidityDuration(InputStreamWithPath inputStreamWithPath, Date timestamp, + long validityDuration) { this.inputStreamWithPath = inputStreamWithPath; this.timestamp = timestamp; + this.validityInMs = validityDuration; } } @@ -66,9 +70,9 @@ public class StreamRepository implements IStreamRepository } } - private final Map<String, InputStreamWithTimeStamp> streams = + private final Map<String, InputStreamWithValidityDuration> streams = - new HashMap<String, InputStreamWithTimeStamp>(); + new HashMap<String, InputStreamWithValidityDuration>(); private final IUniqueIdGenerator inputStreamIDGenerator; @@ -98,13 +102,16 @@ public class StreamRepository implements IStreamRepository } @Override - public synchronized String addStream(InputStream inputStream, String path) + public synchronized String addStream(InputStream inputStream, String path, long validityInSeconds) { removeStaleInputStreams(); String id = inputStreamIDGenerator.createUniqueID(); Date timestamp = new Date(timeProvider.getTimeInMilliseconds()); - streams.put(id, new InputStreamWithTimeStamp(new InputStreamWithPath(inputStream, path), - timestamp)); + + long validityInMs = validityInSeconds * 1000L; + long validity = (validityInMs < minimumTime) ? minimumTime : validityInMs; + streams.put(id, new InputStreamWithValidityDuration(new InputStreamWithPath(inputStream, + path), timestamp, validity)); return id; } @@ -112,7 +119,7 @@ public class StreamRepository implements IStreamRepository public synchronized InputStreamWithPath getStream(String inputStreamID) { removeStaleInputStreams(); - InputStreamWithTimeStamp inputStreamWithTimeStamp = streams.remove(inputStreamID); + InputStreamWithValidityDuration inputStreamWithTimeStamp = streams.remove(inputStreamID); if (inputStreamWithTimeStamp == null) { throw new IllegalArgumentException("Stream " + inputStreamID @@ -124,11 +131,12 @@ public class StreamRepository implements IStreamRepository private void removeStaleInputStreams() { long currentTime = timeProvider.getTimeInMilliseconds(); - Set<Entry<String, InputStreamWithTimeStamp>> entrySet = streams.entrySet(); - for (Iterator<Entry<String, InputStreamWithTimeStamp>> iterator = entrySet.iterator(); iterator - .hasNext();) + Set<Entry<String, InputStreamWithValidityDuration>> entrySet = streams.entrySet(); + for (Iterator<Entry<String, InputStreamWithValidityDuration>> iterator = + entrySet.iterator(); iterator.hasNext();) { - if (iterator.next().getValue().timestamp.getTime() < currentTime - minimumTime) + InputStreamWithValidityDuration stream = iterator.next().getValue(); + if (stream.timestamp.getTime() < currentTime - stream.validityInMs) { iterator.remove(); } diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/api/v1/DssServiceRpcGeneric.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/api/v1/DssServiceRpcGeneric.java index 9f00a3d8fe7..ea06a9f443d 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/api/v1/DssServiceRpcGeneric.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/api/v1/DssServiceRpcGeneric.java @@ -193,7 +193,16 @@ public class DssServiceRpcGeneric extends AbstractDssServiceRpc<IDssServiceRpcGe String path) throws IOExceptionUnchecked, IllegalArgumentException { InputStream stream = getFileForDataSet(sessionToken, dataSetCode, path); - return addToRepositoryAndReturnDownloadUrl(stream, path); + return addToRepositoryAndReturnDownloadUrl(stream, path, 0); + } + + @Override + public String getDownloadUrlForFileForDataSetWithTimeout(String sessionToken, + String dataSetCode, String path, long validityDurationInSeconds) + throws IOExceptionUnchecked, IllegalArgumentException + { + InputStream stream = getFileForDataSet(sessionToken, dataSetCode, path); + return addToRepositoryAndReturnDownloadUrl(stream, path, validityDurationInSeconds); } private IHierarchicalContentNode getContentNode(IHierarchicalContent content, String startPath) @@ -311,7 +320,7 @@ public class DssServiceRpcGeneric extends AbstractDssServiceRpc<IDssServiceRpcGe @Override public int getMinorVersion() { - return 6; + return 7; } /** @@ -347,7 +356,17 @@ public class DssServiceRpcGeneric extends AbstractDssServiceRpc<IDssServiceRpcGe throws IOExceptionUnchecked, IllegalArgumentException { InputStream stream = getFileForDataSet(sessionToken, fileOrFolder); - return addToRepositoryAndReturnDownloadUrl(stream, fileOrFolder.getPath()); + return addToRepositoryAndReturnDownloadUrl(stream, fileOrFolder.getPath(), 0); + } + + @Override + public String getDownloadUrlForFileForDataSetWithTimeout(String sessionToken, + DataSetFileDTO fileOrFolder, long validityDurationInSeconds) + throws IOExceptionUnchecked, IllegalArgumentException + { + InputStream stream = getFileForDataSet(sessionToken, fileOrFolder); + return addToRepositoryAndReturnDownloadUrl(stream, fileOrFolder.getPath(), + validityDurationInSeconds); } @Override diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/api/v1/DssServiceRpcGenericLogger.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/api/v1/DssServiceRpcGenericLogger.java index e74b35860dc..6d4fb716cf3 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/api/v1/DssServiceRpcGenericLogger.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/api/v1/DssServiceRpcGenericLogger.java @@ -23,7 +23,9 @@ import java.util.Map; import ch.systemsx.cisd.base.exceptions.IOExceptionUnchecked; import ch.systemsx.cisd.openbis.common.spring.IInvocationLoggerContext; +import ch.systemsx.cisd.openbis.dss.generic.shared.api.internal.authorization.AuthorizationGuard; import ch.systemsx.cisd.openbis.dss.generic.shared.api.internal.authorization.DataSetAccessGuard; +import ch.systemsx.cisd.openbis.dss.generic.shared.api.internal.authorization.DataSetFileDTOPredicate; import ch.systemsx.cisd.openbis.dss.generic.shared.api.internal.authorization.IDssServiceRpcGenericInternal; import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.DataSetFileDTO; import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.FileInfoDssDTO; @@ -206,4 +208,26 @@ public class DssServiceRpcGenericLogger extends AbstractServerLogger implements logAccess(sessionToken, "list-table-report-descriptions"); return null; } + + @Override + public String getDownloadUrlForFileForDataSetWithTimeout(String sessionToken, + String dataSetCode, String path, long validityDurationInSeconds) + throws IOExceptionUnchecked, IllegalArgumentException + { + logAccess(sessionToken, "get_download_url_for_file_for_data_set", + "DATA_SET(%s) PATH(%s) VALIDITY(%i)", dataSetCode, path, validityDurationInSeconds); + return null; + } + + @Override + @DataSetAccessGuard + public String getDownloadUrlForFileForDataSetWithTimeout( + String sessionToken, + @AuthorizationGuard(guardClass = DataSetFileDTOPredicate.class) DataSetFileDTO fileOrFolder, + long validityDurationInSeconds) throws IOExceptionUnchecked, IllegalArgumentException + { + logAccess(sessionToken, "get_download_url_for_file_for_data_set", + "DATA_SET(%s) VALIDITY(%i)", fileOrFolder, validityDurationInSeconds); + return null; + } } diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/api/v1/IDssServiceRpcGeneric.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/api/v1/IDssServiceRpcGeneric.java index b21542d396a..745f02c7070 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/api/v1/IDssServiceRpcGeneric.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/api/v1/IDssServiceRpcGeneric.java @@ -86,6 +86,24 @@ public interface IDssServiceRpcGeneric extends IRpcService @AuthorizationGuard(guardClass = DataSetFileDTOPredicate.class) DataSetFileDTO fileOrFolder) throws IOExceptionUnchecked, IllegalArgumentException; + /** + * Returns an URL from which the requested file. The URL is valid for a caller-specified amount + * of time. + * + * @param sessionToken The session token + * @param fileOrFolder The file or folder to retrieve + * @param validityDurationInSeconds The number of seconds for which the download URL should be + * valid. + * @throws IOExceptionUnchecked Thrown if an IOException occurs when listing the files + * @throws IllegalArgumentException Thrown if the dataSetCode or startPath are not valid + * @since 1.7 + */ + @DataSetAccessGuard + public String getDownloadUrlForFileForDataSetWithTimeout( + String sessionToken, + @AuthorizationGuard(guardClass = DataSetFileDTOPredicate.class) DataSetFileDTO fileOrFolder, + long validityDurationInSeconds) throws IOExceptionUnchecked, IllegalArgumentException; + /** * Get an array of FileInfoDss objects that describe the file-system structure of the data set. * @@ -133,6 +151,25 @@ public interface IDssServiceRpcGeneric extends IRpcService @AuthorizationGuard(guardClass = DataSetCodeStringPredicate.class) String dataSetCode, String path) throws IOExceptionUnchecked, IllegalArgumentException; + /** + * Returns an URL from which the requested file of the specified data set can be downloaded. The + * URL is valid for a caller-specified amount of time. + * + * @param sessionToken The session token + * @param dataSetCode The data set to retrieve file from + * @param path The path within the data set to retrieve file information about + * @param validityDurationInSeconds The number of seconds for which the download URL should be + * valid. + * @throws IOExceptionUnchecked Thrown if an IOException occurs when listing the files + * @throws IllegalArgumentException Thrown if the dataSetCode or startPath are not valid + * @since 1.7 + */ + @DataSetAccessGuard + public String getDownloadUrlForFileForDataSetWithTimeout(String sessionToken, + @AuthorizationGuard(guardClass = DataSetCodeStringPredicate.class) String dataSetCode, + String path, long validityDurationInSeconds) throws IOExceptionUnchecked, + IllegalArgumentException; + /** * Upload a new data set to the DSS. * diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/DssComponentTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/DssComponentTest.java index 957e295b877..9c8f222c43a 100644 --- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/DssComponentTest.java +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/DssComponentTest.java @@ -61,6 +61,9 @@ import ch.systemsx.cisd.openbis.dss.generic.server.api.v1.DssServiceRpcGeneric; import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService; import ch.systemsx.cisd.openbis.dss.generic.shared.IShareIdManager; import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProviderTestWrapper; +import ch.systemsx.cisd.openbis.dss.generic.shared.api.internal.authorization.AuthorizationGuard; +import ch.systemsx.cisd.openbis.dss.generic.shared.api.internal.authorization.DataSetAccessGuard; +import ch.systemsx.cisd.openbis.dss.generic.shared.api.internal.authorization.DataSetFileDTOPredicate; import ch.systemsx.cisd.openbis.dss.generic.shared.api.internal.authorization.DssSessionAuthorizationHolder; import ch.systemsx.cisd.openbis.dss.generic.shared.api.internal.authorization.IDssServiceRpcGenericInternal; import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.DataSetFileDTO; @@ -713,6 +716,25 @@ public class DssComponentTest extends AbstractFileSystemTestCase { return null; } + + @Override + public String getDownloadUrlForFileForDataSetWithTimeout(String sessionToken, + String dataSetCode, String path, long validityDuration) + throws IOExceptionUnchecked, IllegalArgumentException + { + return url.toString(); + } + + @Override + @DataSetAccessGuard + public String getDownloadUrlForFileForDataSetWithTimeout( + String sessionToken, + @AuthorizationGuard(guardClass = DataSetFileDTOPredicate.class) DataSetFileDTO fileOrFolder, + long validityDurationInSeconds) throws IOExceptionUnchecked, + IllegalArgumentException + { + return url.toString(); + } } private class MockDssServiceRpcV1_1 extends MockDssServiceRpcV1_0 diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/StreamRepositoryTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/StreamRepositoryTest.java index 8bf1cd7ae5b..f8b033c4a95 100644 --- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/StreamRepositoryTest.java +++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/StreamRepositoryTest.java @@ -66,9 +66,9 @@ public class StreamRepositoryTest extends AssertJUnit { StreamRepository repository = new StreamRepository(2, idGenerator, timeProvider); ByteArrayInputStream stream1 = new ByteArrayInputStream("s1".getBytes()); - String id1 = repository.addStream(stream1, "f1.txt"); + String id1 = repository.addStream(stream1, "f1.txt", 0); ByteArrayInputStream stream2 = new ByteArrayInputStream("s2".getBytes()); - String id2 = repository.addStream(stream2, "f2.txt"); + String id2 = repository.addStream(stream2, "f2.txt", 0); assertEquals("0", id1); assertEquals("1", id2); @@ -86,10 +86,10 @@ public class StreamRepositoryTest extends AssertJUnit { StreamRepository repository = new StreamRepository(2, idGenerator, timeProvider); ByteArrayInputStream stream1 = new ByteArrayInputStream("s1".getBytes()); - String id1 = repository.addStream(stream1, "f1.txt"); - + String id1 = repository.addStream(stream1, "f1.txt", 0); + assertEquals("0", id1); - + assertSame(stream1, repository.getStream("0").getInputStream()); try { @@ -100,15 +100,15 @@ public class StreamRepositoryTest extends AssertJUnit assertEquals("Stream 0 is no longer available.", ex.getMessage()); } } - + @Test public void testAddingAndRetrievingTwoStreamsButSecondStreamNoLongerExists() { StreamRepository repository = new StreamRepository(2, idGenerator, timeProvider); ByteArrayInputStream stream1 = new ByteArrayInputStream("s1".getBytes()); - String id1 = repository.addStream(stream1, "f1.txt"); + String id1 = repository.addStream(stream1, "f1.txt", 0); ByteArrayInputStream stream2 = new ByteArrayInputStream("s2".getBytes()); - String id2 = repository.addStream(stream2, "f2.txt"); + String id2 = repository.addStream(stream2, "f2.txt", 0); assertEquals("0", id1); assertEquals("1", id2); @@ -127,4 +127,29 @@ public class StreamRepositoryTest extends AssertJUnit } } + @Test + public void testValidityDuration() + { + StreamRepository repository = new StreamRepository(2, idGenerator, timeProvider); + ByteArrayInputStream stream0 = new ByteArrayInputStream("s1".getBytes()); + repository.addStream(stream0, "f1.txt", 5); + ByteArrayInputStream stream1 = new ByteArrayInputStream("s2".getBytes()); + repository.addStream(stream1, "f2.txt", 3); + + // Advance to a time when stream 1 is still available but not stream2 + timeProvider.getTimeInMilliseconds(); + timeProvider.getTimeInMilliseconds(); + timeProvider.getTimeInMilliseconds(); + timeProvider.getTimeInMilliseconds(); + timeProvider.getTimeInMilliseconds(); + assertSame(stream0, repository.getStream("0").getInputStream()); + try + { + repository.getStream("1"); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException ex) + { + assertEquals("Stream 1 is no longer available.", ex.getMessage()); + } + } } -- GitLab