Skip to content
Snippets Groups Projects
Commit 2432ff67 authored by cramakri's avatar cramakri
Browse files

BIS-329 SP-500 : Introducing a user-defined validity duration to access streams via URLs

SVN: 28389
parent 175804e6
No related branches found
No related tags found
No related merge requests found
Showing
with 165 additions and 29 deletions
......@@ -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
......@@ -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);
}
......@@ -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();
}
......
......@@ -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
......
......@@ -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;
}
}
......@@ -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.
*
......
......@@ -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
......
......@@ -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());
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment