/*
* A CCNx library test.
*
* Copyright (C) 2010, 2011, 2013 Palo Alto Research Center, Inc.
*
* This work is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License version 2 as published by the
* Free Software Foundation.
* This work 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 General Public License
* for more details. You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
package org.ccnx.ccn.test.io;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import java.util.logging.Level;
import org.ccnx.ccn.CCNHandle;
import org.ccnx.ccn.CCNInterestHandler;
import org.ccnx.ccn.impl.support.DataUtils;
import org.ccnx.ccn.impl.support.Log;
import org.ccnx.ccn.io.CCNAbstractOutputStream;
import org.ccnx.ccn.io.CCNVersionedInputStream;
import org.ccnx.ccn.io.CCNVersionedOutputStream;
import org.ccnx.ccn.protocol.ContentName;
import org.ccnx.ccn.protocol.Interest;
import org.ccnx.ccn.test.CCNTestHelper;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
public class CCNVersionedOutputStreamTest implements CCNInterestHandler {
static CCNTestHelper testHelper = new CCNTestHelper(CCNVersionedOutputStreamTest.class);
static CCNHandle readHandle;
static CCNHandle writeHandle;
static byte [] writeDigest;
static int BUF_SIZE = 2048;
static int FILE_SIZE = 65556;
static Writer writer;
public static class Writer extends Thread {
protected static Random random = new Random();
protected CCNAbstractOutputStream _stream;
protected int _fileLength;
protected boolean _done = false;
public Writer(CCNAbstractOutputStream stream, int fileLength) {
_stream = stream;
_fileLength = fileLength;
}
public synchronized boolean isDone() { return _done; }
/**
* @return The digest of the first segment of this stream
*/
public byte[] getFirstDigest() {
return _stream.getFirstDigest();
}
/**
* @return The index of the first segment of stream data.
*/
public Long firstSegmentNumber() {
return _stream.firstSegmentNumber();
}
@Override
public void run() {
try {
synchronized (this) {
writeDigest = writeRandomFile(_stream, _fileLength, random);
Log.info(Log.FAC_TEST, "Finished writing file of {0} bytes, digest {1}.", _fileLength, DataUtils.printHexBytes(writeDigest));
_done = true;
this.notifyAll();
}
} catch (IOException e) {
Log.severe(Log.FAC_TEST, "Exception writing random file: " + e.getClass().getName() + ": " + e.getMessage());
Log.logStackTrace(Log.FAC_TEST, Level.SEVERE, e);
Assert.fail("Exception in writeRandomFile: " + e);
}
}
}
@BeforeClass
public static void setUpBeforeClass() throws Exception {
try {
readHandle = CCNHandle.open();
writeHandle = CCNHandle.open();
} catch (Exception e) {
Log.severe("Exception in setUpBeforeClass: {0}: {1}", e.getClass().getName(), e);
throw e;
}
}
@AfterClass
public static void cleanupAfterClass() {
readHandle.close();
writeHandle.close();
}
@Test
public void testAddOutstandingInterest() throws Exception {
// Let's express an Interest in some data, and see if the network managers can
// handle the threading for us...
ContentName streamName = new ContentName(testHelper.getTestNamespace("testAddOutstandingInterest"), "testFile.bin");
writeHandle.registerFilter(streamName, this);
// Get the latest version when no versions exist.
CCNVersionedInputStream vis = new CCNVersionedInputStream(streamName, readHandle);
byte [] resultDigest = readFile(vis);
Log.info("Finished reading, read result {0}", DataUtils.printHexBytes(resultDigest));
synchronized (this) {
Assert.assertNotNull(writer);
if (!writer.isDone()) {
synchronized(writer) {
while (!writer.isDone()) {
writer.wait(500);
}
}
}
}
Log.info("Finished writing, read result {0}, write result {1}", DataUtils.printHexBytes(resultDigest), DataUtils.printHexBytes(writeDigest));
Assert.assertArrayEquals(resultDigest, writeDigest);
Assert.assertArrayEquals(writer.getFirstDigest(), vis.getFirstDigest());
Assert.assertEquals(writer.firstSegmentNumber(), (Long)vis.firstSegmentNumber());
}
public static byte [] readFile(InputStream inputStream) throws IOException {
DigestInputStream dis = null;
try {
dis = new DigestInputStream(inputStream, MessageDigest.getInstance("SHA1"));
} catch (NoSuchAlgorithmException e) {
Log.severe("No SHA1 available!");
Assert.fail("No SHA1 available!");
}
int elapsed = 0;
int read = 0;
byte [] bytes = new byte[BUF_SIZE];
while (true) {
read = dis.read(bytes);
if (read < 0) {
System.out.println("EOF read at " + elapsed + " bytes.");
break;
} else if (read == 0) {
System.out.println("0 bytes read at " + elapsed + " bytes.");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
}
elapsed += read;
System.out.println(" read " + elapsed + " bytes.");
}
return dis.getMessageDigest().digest();
}
public static byte [] writeRandomFile(OutputStream stream, int fileLength, Random randBytes) throws IOException {
DigestOutputStream digestStreamWrapper = null;
try {
digestStreamWrapper = new DigestOutputStream(stream, MessageDigest.getInstance("SHA1"));
} catch (NoSuchAlgorithmException e) {
Log.severe("No SHA1 available!");
Assert.fail("No SHA1 available!");
}
byte [] bytes = new byte[BUF_SIZE];
int elapsed = 0;
int nextBufSize = 0;
final double probFlush = .3;
while (elapsed < fileLength) {
nextBufSize = ((fileLength - elapsed) > BUF_SIZE) ? BUF_SIZE : (fileLength - elapsed);
randBytes.nextBytes(bytes);
digestStreamWrapper.write(bytes, 0, nextBufSize);
elapsed += nextBufSize;
if (randBytes.nextDouble() < probFlush) {
System.out.println("Flushing buffers, have written " + elapsed + " bytes out of " + fileLength);
digestStreamWrapper.flush();
}
}
digestStreamWrapper.close();
return digestStreamWrapper.getMessageDigest().digest();
}
public boolean handleInterest(Interest interest) {
if(interest.exclude()!=null && !interest.exclude().empty()) {
Log.info("this interest is probably a gLV interest, this is what we are looking for");
} else {
Log.info("this is not a gLV interest, dropping");
return false;
}
// we only deal with the first interest, at least for now
synchronized (this) {
if (null != writer) {
Log.info("handleInterests: already writing stream, ignoring interest {0}", interest);
return false;
}
}
Log.info("handleInterests got interest {0}", interest);
CCNVersionedOutputStream vos = null;
try {
vos = new CCNVersionedOutputStream(interest.name(), writeHandle);
} catch (IOException e) {
Log.severe("Exception in creating output stream: {0}", e);
Log.logStackTrace(Level.SEVERE, e);
Assert.fail("Exception creating output stream " + e);
}
vos.addOutstandingInterest(interest);
synchronized (this) {
writer = new Writer(vos, FILE_SIZE);
writer.start();
}
return true;
}
}