mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-30 18:15:39 +00:00
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@46744 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
345 lines
12 KiB
Java
345 lines
12 KiB
Java
/*
|
|
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
|
*
|
|
* This file is part of Alfresco
|
|
*
|
|
* Alfresco is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Alfresco is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package org.alfresco.repo.content;
|
|
|
|
|
|
import static org.junit.Assert.assertTrue;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.nio.channels.Channels;
|
|
import java.nio.channels.ReadableByteChannel;
|
|
|
|
import org.alfresco.repo.content.transform.TransformerDebug;
|
|
import org.alfresco.service.cmr.repository.ContentIOException;
|
|
import org.alfresco.service.cmr.repository.ContentReader;
|
|
import org.alfresco.service.cmr.repository.TransformationOptionLimits;
|
|
import org.junit.Before;
|
|
import org.junit.Test;
|
|
import org.springframework.context.ApplicationContext;
|
|
|
|
/**
|
|
* Test class for time and byte limits on an AbstractContentReader
|
|
*
|
|
* @author Alan Davis
|
|
*/
|
|
public class AbstractContentReaderLimitTest
|
|
{
|
|
private static final int K = 1024;
|
|
private static final int M = K*K;
|
|
|
|
// Normal test file size 5K
|
|
private static final long SIZE = 5*K;
|
|
|
|
// Normal test delay between returning bytes, giving a run time of about 5 seconds if not interrupted.
|
|
private static final long MS_PER_BYTE = 1;
|
|
|
|
// Large test file size 100 MB with no delay takes about a second to read.
|
|
private static final long LARGE_SIZE = 100*M;
|
|
|
|
// Top speed to read 1 MB via the DummyAbstractContentReader
|
|
// Assume about 10ms normally per MB, so use half that and a high
|
|
// MARGIN_OF_ERROR_PERCENTAGE_FAST.
|
|
private static final long MS_PER_MB = 5;
|
|
|
|
// Margins of error when using a DummyAbstractContentReader
|
|
// with or without a delay. Used to make sure different runs
|
|
// don't result in failing tests but at the same time that
|
|
// they will if there is a real problem.
|
|
private static final int MARGIN_OF_ERROR_PERCENTAGE_SLOW = 100;
|
|
private static final int MARGIN_OF_ERROR_PERCENTAGE_FAST = 900;
|
|
|
|
private DummyAbstractContentReader reader;
|
|
private TransformationOptionLimits limits;
|
|
private TransformerDebug transformerDebug;
|
|
private long minTime;
|
|
private long maxTime;
|
|
private long minLength;
|
|
private long maxLength;
|
|
|
|
@Before
|
|
public void setUp() throws Exception
|
|
{
|
|
ApplicationContext ctx = ContentMinimalContextTestSuite.getContext();
|
|
transformerDebug = (TransformerDebug) ctx.getBean("transformerDebug");
|
|
|
|
limits = new TransformationOptionLimits();
|
|
reader = new DummyAbstractContentReader(SIZE, MS_PER_BYTE);
|
|
reader.setLimits(limits);
|
|
reader.setTransformerDebug(transformerDebug);
|
|
|
|
// Without the following, the bytes from the DummyAbstractContentReader are read in 4K blocks
|
|
// so the test to do with timeouts and read limits will not work, as they expect to read 1K per
|
|
// second. Not an issue in the real world as read rates are much higher, so a buffer makes no
|
|
// difference to limit checking. It does make a vast difference to performance when the InputStream
|
|
// is wrapped in a InputStreamReader as is done by a number of transformers.
|
|
reader.setUseBufferedInputStream(false);
|
|
}
|
|
|
|
@Test
|
|
public void noLimitTest() throws Exception
|
|
{
|
|
readAndCheck();
|
|
}
|
|
|
|
@Test(expected=ContentIOException.class)
|
|
public void maxKBytesTest() throws Exception
|
|
{
|
|
limits.setMaxSourceSizeKBytes(1);
|
|
readAndCheck();
|
|
}
|
|
|
|
@Test(expected=ContentIOException.class)
|
|
public void maxTimeTest() throws Exception
|
|
{
|
|
limits.setTimeoutMs(1000);
|
|
readAndCheck();
|
|
}
|
|
|
|
@Test(expected=ContentIOException.class)
|
|
public void maxTimeAndKBytesTest() throws Exception
|
|
{
|
|
limits.setTimeoutMs(1000);
|
|
limits.setMaxSourceSizeKBytes(1);
|
|
readAndCheck();
|
|
}
|
|
|
|
@Test
|
|
public void limitKBytesTest() throws Exception
|
|
{
|
|
limits.setReadLimitKBytes(1);
|
|
readAndCheck();
|
|
}
|
|
|
|
@Test
|
|
public void limitTimeTest() throws Exception
|
|
{
|
|
limits.setReadLimitTimeMs(1000);
|
|
readAndCheck();
|
|
}
|
|
|
|
@Test
|
|
public void limitTimeAndKBytesTest() throws Exception
|
|
{
|
|
limits.setReadLimitTimeMs(1000);
|
|
limits.setReadLimitKBytes(1);
|
|
readAndCheck();
|
|
}
|
|
|
|
@Test
|
|
public void fullSpeedReader() throws Exception
|
|
{
|
|
// Check that we have not slowed down reading of large files.
|
|
reader = new DummyAbstractContentReader(LARGE_SIZE, 0);
|
|
reader.setLimits(limits);
|
|
reader.setTransformerDebug(transformerDebug);
|
|
reader.setUseBufferedInputStream(true);
|
|
|
|
readAndCheck();
|
|
}
|
|
|
|
private void readAndCheck() throws Exception
|
|
{
|
|
Exception exception = null;
|
|
|
|
long length = 0;
|
|
long time = System.currentTimeMillis();
|
|
try
|
|
{
|
|
String content = reader.getContentString();
|
|
length = content.length();
|
|
}
|
|
catch(ContentIOException e)
|
|
{
|
|
exception = e;
|
|
}
|
|
time = System.currentTimeMillis() - time;
|
|
|
|
calcMaxMinValues();
|
|
|
|
System.out.printf("Time %04d %04d..%04d length %04d %04d..%04d %s\n",
|
|
time, minTime, maxTime,
|
|
length, minLength, maxLength,
|
|
(exception == null ? "" : exception.getClass().getSimpleName()));
|
|
|
|
assertTrue("Reader is too fast ("+time+"ms range is "+minTime+"..."+maxTime+"ms)", time >= minTime);
|
|
assertTrue("Reader is too slow ("+time+"ms range is "+minTime+"..."+maxTime+"ms)", time <= maxTime);
|
|
|
|
if (exception != null)
|
|
throw exception;
|
|
|
|
assertTrue("Content is too short ("+length+" bytes range is "+minLength+"..."+maxLength+")", length >= minLength);
|
|
assertTrue("Content is too long ("+length+" bytes range is "+minLength+"..."+maxLength+")", length <= maxLength);
|
|
}
|
|
|
|
private void calcMaxMinValues()
|
|
{
|
|
long size = reader.size;
|
|
|
|
long timeout = limits.getTimePair().getValue();
|
|
assertTrue("The test time value ("+timeout+
|
|
"ms) should be lowered given the file size ("+size+
|
|
") and the margin of error (of "+marginOfError(timeout)+"ms)",
|
|
timeout <= 0 || msToBytes(timeout+marginOfError(timeout)) <= size);
|
|
|
|
long readKBytes = limits.getKBytesPair().getValue();
|
|
long readBytes = readKBytes * K;
|
|
assertTrue("The test KByte value ("+readKBytes+
|
|
"K) should be lowered given the file size ("+size+
|
|
") and the margin of error (of "+marginOfError(readBytes)+"bytes)",
|
|
readBytes <= 0 || readBytes+marginOfError(readBytes) <= size);
|
|
|
|
long bytes = (readBytes > 0) ? readBytes : size;
|
|
long readTime = bytesToMs(bytes);
|
|
|
|
minTime = (timeout > 0) ? Math.min(timeout, readTime) : readTime;
|
|
maxTime = minTime + marginOfError(minTime);
|
|
minLength = (timeout > 0) ? msToBytes(minTime-marginOfError(minTime)) : bytes;
|
|
maxLength = (timeout > 0) ? Math.min(msToBytes(maxTime), size) : bytes;
|
|
}
|
|
|
|
private long msToBytes(long ms)
|
|
{
|
|
return (reader.msPerByte > 0)
|
|
? ms / reader.msPerByte
|
|
: ms / MS_PER_MB * M;
|
|
}
|
|
|
|
private long bytesToMs(long bytes)
|
|
{
|
|
return (reader.msPerByte > 0)
|
|
? bytes * reader.msPerByte
|
|
: bytes * MS_PER_MB / M;
|
|
}
|
|
|
|
private long marginOfError(long value)
|
|
{
|
|
return
|
|
value *
|
|
((reader.msPerByte > 0)
|
|
? MARGIN_OF_ERROR_PERCENTAGE_SLOW
|
|
: MARGIN_OF_ERROR_PERCENTAGE_FAST) /
|
|
100;
|
|
}
|
|
|
|
/**
|
|
* A dummy AbstractContentReader that returns a given number of bytes
|
|
* (all 'a') very slowly. There is a configurable delay returning each byte.
|
|
* Used to test timeouts and read limits.
|
|
*/
|
|
public static class DummyAbstractContentReader extends AbstractContentReader
|
|
{
|
|
final long size;
|
|
final long msPerByte;
|
|
|
|
/**
|
|
* @param size of the dummy data
|
|
* @param msPerByte milliseconds between byte reads
|
|
*/
|
|
public DummyAbstractContentReader(long size, long msPerByte)
|
|
{
|
|
super("a");
|
|
this.size = size;
|
|
this.msPerByte = msPerByte;
|
|
}
|
|
|
|
/**
|
|
* @return Returns an instance of the this class
|
|
*/
|
|
@Override
|
|
protected ContentReader createReader() throws ContentIOException
|
|
{
|
|
return new DummyAbstractContentReader(size, msPerByte);
|
|
}
|
|
|
|
@Override
|
|
protected ReadableByteChannel getDirectReadableChannel() throws ContentIOException
|
|
{
|
|
InputStream is = new InputStream()
|
|
{
|
|
long read = 0;
|
|
long start = 0;
|
|
|
|
@Override
|
|
public int read() throws IOException
|
|
{
|
|
if (read >= size)
|
|
return -1;
|
|
|
|
read++;
|
|
|
|
if (msPerByte > 0)
|
|
{
|
|
long elapse = System.currentTimeMillis() - start;
|
|
if (read == 1)
|
|
{
|
|
start = elapse;
|
|
}
|
|
else
|
|
{
|
|
// On Windows it is possible to just wait 1 ms per byte but this
|
|
// does not work on linux hence (end up with a full read taking
|
|
// 40 seconds rather than 5) the need to wait if elapse time
|
|
// is too fast.
|
|
long delay = (read * msPerByte) - elapse;
|
|
if (delay > 0)
|
|
{
|
|
try
|
|
{
|
|
Thread.sleep(delay);
|
|
}
|
|
catch (InterruptedException e)
|
|
{
|
|
// ignore
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 'a';
|
|
}
|
|
|
|
// Just a way to tell AbstractContentReader not to wrap the ChannelInputStream
|
|
// in a BufferedInputStream
|
|
@Override
|
|
public boolean markSupported()
|
|
{
|
|
return true;
|
|
}
|
|
};
|
|
return Channels.newChannel(is);
|
|
}
|
|
|
|
public boolean exists()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public long getLastModified()
|
|
{
|
|
return 0L;
|
|
}
|
|
|
|
public long getSize()
|
|
{
|
|
return size;
|
|
}
|
|
};
|
|
}
|