/*
* Part of the CCNx Java Library.
*
* Copyright (C) 2008, 2009, 2010, 2011 Palo Alto Research Center, Inc.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
* This library 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 this library;
* if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
* Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.ccnx.ccn.io;
import java.io.IOException;
import java.io.OutputStream;
import org.ccnx.ccn.CCNHandle;
import org.ccnx.ccn.impl.CCNSegmenter;
import org.ccnx.ccn.impl.security.crypto.ContentKeys;
import org.ccnx.ccn.impl.support.Log;
import org.ccnx.ccn.profiles.SegmentationProfile;
import org.ccnx.ccn.profiles.VersioningProfile;
import org.ccnx.ccn.profiles.security.access.AccessControlManager;
import org.ccnx.ccn.protocol.CCNTime;
import org.ccnx.ccn.protocol.ContentName;
import org.ccnx.ccn.protocol.ContentObject;
import org.ccnx.ccn.protocol.Interest;
import org.ccnx.ccn.protocol.KeyLocator;
import org.ccnx.ccn.protocol.PublisherPublicKeyDigest;
import org.ccnx.ccn.protocol.SignedInfo.ContentType;
/**
* This abstract class is the superclass of all classes for writing an output stream of
* bytes segmented and stored in CCN.
*
* @see SegmentationProfile for description of CCN segmentation
*/
public abstract class CCNAbstractOutputStream extends OutputStream {
protected CCNHandle _handle = null;
protected CCNSegmenter _segmenter = null;
/**
* The name for the content fragments, up to just before the sequence number.
*/
protected ContentName _baseName = null;
/**
* type of content null == DATA (or ENCR if encrypted)
*/
protected ContentType _type;
protected ContentKeys _keys;
protected KeyLocator _locator;
protected PublisherPublicKeyDigest _publisher;
/**
* Base constructor for all output streams.
* The behavior of an output stream derived class is determined
* largely by its buffering, segmentation configuration (embodied
* in a CCNSegmenter) and flow control (embodied in a CCNFlowControl,
* contained within the segmenter), as well as the way it constructs is names.
* @param baseName specifies the base name to write. Can be null, and set later.
* @param type specifies the type of data to write.
* @param locator the key locator to be used in written segments. If null, default
* is used.
* @param publisher the key with which to sign the output. If null, default for user
* is used.
* @param segmenter The segmenter used to construct and sign output segments, specified
* by subclasses to provide various kinds of behavior.
*/
public CCNAbstractOutputStream(ContentName baseName,
KeyLocator locator,
PublisherPublicKeyDigest publisher,
ContentType type,
ContentKeys keys,
CCNSegmenter segmenter) {
super();
_baseName = baseName;
_type = type;
_segmenter = segmenter;
_handle = _segmenter.getLibrary();
if (null == _handle) {
_handle = CCNHandle.getHandle();
}
// If these are null, the handle defaults will be used.
_locator = locator;
if (null == locator)
_locator = _handle.keyManager().getKeyLocator(publisher);
_publisher = publisher;
// Initialize keys here now.
_keys = keys;
Log.info(Log.FAC_IO, "CCNAbstractOutputStream: {0} blocksize is {1}", baseName, _segmenter.getBlockSize());
}
/**
* Special purpose constructor used in tests.
*/
protected CCNAbstractOutputStream() {}
/**
* Override in subclasses that need to do something special with start writes
* (see CCNFlowControl#startWrite(ContentName, Shape)). They should call this
* superclass method, though, to initialize keys (may need to move this later).
* @throws IOException
*/
protected void startWrite() throws IOException {
if (null == _keys) {
Log.info(Log.FAC_IO, "CCNAbstractOutputStream: startWrite -- searching for keys.");
_keys = AccessControlManager.keysForOutput(_baseName, _publisher, getType(), _handle);
}
}
/**
* Method for streams used by CCNFilterListeners to output a block
* in response to an Interest callback.
* We've received an Interest prior to setting up this stream. Use
* a method to push this Interest, rather than passing it in in the
* constructor to make sure we have completed initializing the stream,
* and to limit the number of constructor types.
* (If the Interest doesn't match this stream's content,
* no initial block will be output; the stream will wait for matching Interests prior
* to writing its blocks.)
* @param outstandingInterest An interest received prior to constructing
* this stream, ideally on the same CCNHandle that the stream is using
* for output. Only one stream should attempt to put() a block in response
* to this Interest; it is up to the caller to make sure that is the case.
*/
public void addOutstandingInterest(Interest outstandingInterest) {
_segmenter.getFlowControl().handleInterest(outstandingInterest);
}
@Override
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
@Override
public void write(int b) throws IOException {
byte buf[] = {(byte)b};
write(buf, 0, 1);
}
/**
* @return The name used as a prefix for segments of this stream (not including the segment number).
*/
public ContentName getBaseName() {
return _baseName;
}
/**
* @return The version of the stream being written, if its name is versioned.
*/
public CCNTime getVersion() {
if (null == _baseName)
return null;
return VersioningProfile.getTerminalVersionAsTimestampIfVersioned(_baseName);
}
public ContentType getType() { return _type; }
/**
* Return the first segment of this stream.
*
* @return The first segment of this stream or null if no segments generated yet
*/
public ContentObject getFirstSegment() {
if (null != _segmenter) {
return _segmenter.getFirstSegment();
} else {
return null;
}
}
/**
*
* Returns the digest of the first segment of this stream.
*
* @return The digest of the first segment of this stream or null if no segments generated yet.
*/
public byte[] getFirstDigest() {
ContentObject firstSegment = _segmenter.getFirstSegment();
if (null != firstSegment) {
return firstSegment.digest();
} else {
return null;
}
}
/**
* Returns the first segment number for this stream.
* @return The index of the first segment of stream data or null if no segments generated yet.
*/
public Long firstSegmentNumber() {
ContentObject firstSegment = _segmenter.getFirstSegment();
if (null != firstSegment) {
return SegmentationProfile.getSegmentNumber(firstSegment.name());
} else {
return null;
}
}
/**
* @return The CCNSegmenter responsible for segmenting and signing stream content.
*/
protected CCNSegmenter getSegmenter() {
return _segmenter;
}
/**
* Set the timeout that will be used for all content writes on this stream.
* Default is 10 seconds
* @param timeout in milliseconds
*/
public void setTimeout(int timeout) {
getSegmenter().setTimeout(timeout);
}
}