package freenet.support.io;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Security;
import java.util.Arrays;
import java.util.Random;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import freenet.crypt.EncryptedRandomAccessBucket;
import freenet.crypt.MasterSecret;
import freenet.support.Executor;
import freenet.support.SerialExecutor;
import freenet.support.api.RandomAccessBucket;
import freenet.support.api.RandomAccessBuffer;
import freenet.support.io.TempBucketFactory.TempBucket;
import freenet.support.io.TempBucketFactory.TempRandomAccessBuffer;
public abstract class TempBucketFactoryRAFBase extends RandomAccessBufferTestBase {
public TempBucketFactoryRAFBase() {
super(TEST_LIST);
}
public abstract boolean enableCrypto();
private static final int[] TEST_LIST = new int[] { 0, 1, 32, 64, 32768, 1024*1024, 1024*1024+1 };
private static final int[] TEST_LIST_NOT_MIGRATED = new int[] { 1, 32, 64, 1024, 2048, 4095 };
private Random weakPRNG = new Random(12340);
private Executor exec = new SerialExecutor(NativeThread.NORM_PRIORITY);
private File f = new File("temp-bucket-raf-test");
private FilenameGenerator fg;
private TempBucketFactory factory;
static final MasterSecret secret = new MasterSecret();
static{
Security.addProvider(new BouncyCastleProvider());
}
@Override
public void setUp() throws IOException {
fg = new FilenameGenerator(weakPRNG, true, f, "temp-raf-test-");
factory = new TempBucketFactory(exec, fg, 4096, 65536, weakPRNG, false, 1024*1024*2, secret);
factory.setEncryption(enableCrypto());
assertEquals(factory.getRamUsed(), 0);
FileUtil.removeAll(f);
f.mkdir();
assertTrue(f.exists() && f.isDirectory());
}
@Override
public void tearDown() {
assertEquals(factory.getRamUsed(), 0);
// Everything should have been free()'ed.
assertEquals(f.listFiles().length, 0);
FileUtil.removeAll(f);
}
@Override
protected RandomAccessBuffer construct(long size) throws IOException {
return factory.makeRAF(size);
}
public void testArrayMigration() throws IOException {
Random r = new Random(21162506);
for(int size : TEST_LIST_NOT_MIGRATED)
innerTestArrayMigration(size, r);
}
/** Create an array, fill it with random numbers, write it sequentially to the
* RandomAccessBuffer, then read randomly and compare. */
protected void innerTestArrayMigration(int len, Random r) throws IOException {
if(len == 0) return;
byte[] buf = new byte[len];
r.nextBytes(buf);
RandomAccessBuffer raf = construct(len);
TempRandomAccessBuffer t = (TempRandomAccessBuffer) raf;
assertFalse(t.hasMigrated());
assertEquals(factory.getRamUsed(), len);
t.migrateToDisk();
assertTrue(t.hasMigrated());
assertEquals(factory.getRamUsed(), 0);
raf.pwrite(0L, buf, 0, buf.length);
checkArrayInner(buf, raf, len, r);
raf.close();
raf.free();
}
public void testBucketToRAFWhileArray() throws IOException {
int len = 4095;
Random r = new Random(21162101);
TempBucket bucket = (TempBucket) factory.makeBucket(1024);
byte[] buf = new byte[len];
r.nextBytes(buf);
OutputStream os = bucket.getOutputStream();
os.write(buf.clone());
os.close();
assertTrue(bucket.isRAMBucket());
assertEquals(len, bucket.size());
TempRandomAccessBuffer raf = (TempRandomAccessBuffer) bucket.toRandomAccessBuffer();
bucket.getInputStream().close(); // Can read.
try {
bucket.getOutputStream(); // Cannot write.
fail();
} catch (IOException e) {
// Ok.
}
assertEquals(len, raf.size());
assertFalse(raf.hasMigrated());
checkArrayInner(buf, raf, len, r);
// Now migrate to disk.
raf.migrateToDisk();
File f = ((PooledFileRandomAccessBuffer) raf.getUnderlying()).file;
assertTrue(f.exists());
assertEquals(len, f.length());
assertTrue(raf.hasMigrated());
assertEquals(factory.getRamUsed(), 0);
checkArrayInner(buf, raf, len, r);
checkBucket(bucket, buf);
raf.close();
raf.free();
assertFalse(f.exists());
}
public void testBucketToRAFCallTwiceArray() throws IOException {
int len = 4095;
Random r = new Random(21162101);
TempBucket bucket = (TempBucket) factory.makeBucket(1024);
byte[] buf = new byte[len];
r.nextBytes(buf);
OutputStream os = bucket.getOutputStream();
os.write(buf.clone());
os.close();
assertTrue(bucket.isRAMBucket());
assertEquals(len, bucket.size());
TempRandomAccessBuffer raf = (TempRandomAccessBuffer) bucket.toRandomAccessBuffer();
assertTrue(raf != null);
raf = (TempRandomAccessBuffer) bucket.toRandomAccessBuffer();
assertTrue(raf != null);
raf.close();
raf.free();
}
public void testBucketToRAFCallTwiceFile() throws IOException {
int len = 4095;
Random r = new Random(21162101);
TempBucket bucket = (TempBucket) factory.makeBucket(1024);
byte[] buf = new byte[len];
r.nextBytes(buf);
OutputStream os = bucket.getOutputStream();
os.write(buf.clone());
os.close();
assertTrue(bucket.isRAMBucket());
assertEquals(len, bucket.size());
assertTrue(bucket.migrateToDisk());
TempRandomAccessBuffer raf = (TempRandomAccessBuffer) bucket.toRandomAccessBuffer();
assertTrue(raf != null);
raf = (TempRandomAccessBuffer) bucket.toRandomAccessBuffer();
assertTrue(raf != null);
raf.close();
raf.free();
}
public void testBucketToRAFFreeBucketWhileArray() throws IOException {
int len = 4095;
Random r = new Random(21162101);
TempBucket bucket = (TempBucket) factory.makeBucket(1024);
byte[] buf = new byte[len];
r.nextBytes(buf);
OutputStream os = bucket.getOutputStream();
os.write(buf.clone());
os.close();
assertTrue(bucket.isRAMBucket());
assertEquals(len, bucket.size());
bucket.getInputStream().close();
bucket.free();
try {
TempRandomAccessBuffer raf = (TempRandomAccessBuffer) bucket.toRandomAccessBuffer();
fail();
} catch (IOException e) {
// Ok.
}
}
public void testBucketToRAFFreeWhileArray() throws IOException {
int len = 4095;
Random r = new Random(21162101);
TempBucket bucket = (TempBucket) factory.makeBucket(1024);
byte[] buf = new byte[len];
r.nextBytes(buf);
OutputStream os = bucket.getOutputStream();
os.write(buf.clone());
os.close();
assertTrue(bucket.isRAMBucket());
assertEquals(len, bucket.size());
bucket.getInputStream().close();
TempRandomAccessBuffer raf = (TempRandomAccessBuffer) bucket.toRandomAccessBuffer();
bucket.free();
try {
raf.pread(0, new byte[len], 0, buf.length);
fail();
} catch (IOException e) {
// Ok.
}
try {
bucket.getInputStream();
fail();
} catch(IOException e) {
// Ok.
}
}
public void testBucketToRAFFreeWhileFile() throws IOException {
int len = 4095;
Random r = new Random(21162101);
TempBucket bucket = (TempBucket) factory.makeBucket(1024);
byte[] buf = new byte[len];
r.nextBytes(buf);
OutputStream os = bucket.getOutputStream();
os.write(buf.clone());
os.close();
assertTrue(bucket.isRAMBucket());
assertEquals(len, bucket.size());
bucket.getInputStream().close();
TempRandomAccessBuffer raf = (TempRandomAccessBuffer) bucket.toRandomAccessBuffer();
assertTrue(raf.migrateToDisk());
assertFalse(raf.migrateToDisk());
assertFalse(bucket.migrateToDisk());
assertTrue(raf.hasMigrated());
File f = ((PooledFileRandomAccessBuffer) raf.getUnderlying()).file;
assertTrue(f.exists());
bucket.free();
assertFalse(f.exists());
try {
raf.pread(0, new byte[len], 0, buf.length);
fail();
} catch (IOException e) {
// Ok.
}
try {
bucket.getInputStream();
fail();
} catch(IOException e) {
// Ok.
}
}
public void testBucketToRAFFreeWhileFileFreeRAF() throws IOException {
int len = 4095;
Random r = new Random(21162101);
TempBucket bucket = (TempBucket) factory.makeBucket(1024);
byte[] buf = new byte[len];
r.nextBytes(buf);
OutputStream os = bucket.getOutputStream();
os.write(buf.clone());
os.close();
assertTrue(bucket.isRAMBucket());
assertEquals(len, bucket.size());
bucket.getInputStream().close();
TempRandomAccessBuffer raf = (TempRandomAccessBuffer) bucket.toRandomAccessBuffer();
raf.migrateToDisk();
assertTrue(raf.hasMigrated());
File f = ((PooledFileRandomAccessBuffer) raf.getUnderlying()).file;
assertTrue(f.exists());
raf.free();
assertFalse(f.exists());
try {
raf.pread(0, new byte[len], 0, buf.length);
fail();
} catch (IOException e) {
// Ok.
}
try {
InputStream is = bucket.getInputStream();
is.read(); // Tricky to make it fail on getInputStream(). FIXME.
fail();
} catch(IOException e) {
// Ok.
}
}
public void testBucketToRAFFreeWhileFileMigrateFirst() throws IOException {
int len = 4095;
Random r = new Random(21162101);
TempBucket bucket = (TempBucket) factory.makeBucket(1024);
byte[] buf = new byte[len];
r.nextBytes(buf);
OutputStream os = bucket.getOutputStream();
os.write(buf.clone());
os.close();
assertTrue(bucket.isRAMBucket());
assertEquals(len, bucket.size());
bucket.getInputStream().close();
bucket.migrateToDisk();
File f = getFile(bucket);
assertTrue(f.exists());
TempRandomAccessBuffer raf = (TempRandomAccessBuffer) bucket.toRandomAccessBuffer();
assertTrue(raf.hasMigrated());
bucket.free();
assertFalse(f.exists());
try {
raf.pread(0, new byte[len], 0, buf.length);
fail();
} catch (IOException e) {
// Ok.
}
try {
bucket.getInputStream();
fail();
} catch(IOException e) {
// Ok.
}
}
private File getFile(TempBucket bucket) {
if(!this.enableCrypto())
return ((TempFileBucket)(((TempBucket) bucket).getUnderlying())).getFile();
else {
EncryptedRandomAccessBucket erab = (EncryptedRandomAccessBucket) bucket.getUnderlying();
RandomAccessBucket b = erab.getUnderlying();
if(b instanceof PaddedRandomAccessBucket) {
b = ((PaddedRandomAccessBucket)b).getUnderlying();
}
return ((TempFileBucket) b).getFile();
}
}
private void checkBucket(TempBucket bucket, byte[] buf) throws IOException {
DataInputStream dis = new DataInputStream(bucket.getInputStream());
byte[] cbuf = new byte[buf.length];
dis.readFully(cbuf);
assertTrue(Arrays.equals(buf, cbuf));
}
public void testBucketToRAFWhileFile() throws IOException {
int len = 4095;
Random r = new Random(21162101);
TempBucket bucket = (TempBucket) factory.makeBucket(1024);
byte[] buf = new byte[len];
r.nextBytes(buf);
OutputStream os = bucket.getOutputStream();
os.write(buf.clone());
os.close();
assertTrue(bucket.isRAMBucket());
assertEquals(len, bucket.size());
// Migrate to disk
bucket.migrateToDisk();
assertFalse(bucket.isRAMBucket());
File f = getFile(bucket);
assertTrue(f.exists());
if(enableCrypto())
assertEquals(f.length(), 8192);
else
assertEquals(f.length(), 4095);
TempRandomAccessBuffer raf = (TempRandomAccessBuffer) bucket.toRandomAccessBuffer();
assertTrue(f.exists());
if(enableCrypto())
assertEquals(f.length(), 8192);
else
assertEquals(f.length(), 4095);
assertEquals(len, raf.size());
checkArrayInner(buf, raf, len, r);
assertEquals(factory.getRamUsed(), 0);
checkArrayInner(buf, raf, len, r);
raf.close();
raf.free();
assertFalse(f.exists());
}
public void testBucketToRAFFailure() throws IOException {
int len = 4095;
Random r = new Random(21162101);
TempBucket bucket = (TempBucket) factory.makeBucket(1024);
byte[] buf = new byte[len];
r.nextBytes(buf);
OutputStream os = bucket.getOutputStream();
os.write(buf.clone());
assertTrue(bucket.isRAMBucket());
try {
bucket.toRandomAccessBuffer();
fail();
} catch (IOException e) {
// Ok.
}
os.close();
InputStream is = bucket.getInputStream();
try {
bucket.toRandomAccessBuffer();
fail();
} catch (IOException e) {
// Ok.
}
is.close();
TempRandomAccessBuffer raf = (TempRandomAccessBuffer) bucket.toRandomAccessBuffer();
try {
bucket.getOutputStream(); // Cannot write.
fail();
} catch (IOException e) {
// Ok.
}
checkBucket(bucket, buf);
raf.free();
}
private void checkArrayInner(byte[] buf, RandomAccessBuffer raf, int len, Random r) throws IOException {
for(int i=0;i<100;i++) {
int end = len == 1 ? 1 : r.nextInt(len)+1;
int start = r.nextInt(end);
checkArraySectionEqualsReadData(buf, raf, start, end, true);
}
checkArraySectionEqualsReadData(buf, raf, 0, len, true);
if(len > 1)
checkArraySectionEqualsReadData(buf, raf, 1, len-1, true);
}
}