/*
BlockOutputStream.java
*
* Copyright (c) 2002, Benja Fallenstein
*
* This file is part of Storm.
*
* Storm 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 2 of the License, or
* (at your option) any later version.
*
* Storm 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 Storm; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
*
*/
/*
* Written by Benja Fallenstein
*/
package org.nongnu.storm;
import org.nongnu.storm.headers.*;
import java.io.*;
import java.security.*;
/** An OutputStream
for creating a new Storm block.
* The block is not actually created until close()
* is called on this stream. At that time, the block is added
* to the pool, and its data can be retrieved through the
* getBlock()
and getBlockId
methods.
*
* While data is written to the stream, it is also sent
* to a MessageDigest
object that is used
* to detemine the block's id.
*
* This class works in a way similar to what
* java.security.DigestOutputStream
does. However,
* we cannot subclass DigestOutputStream
here,
* because it provides public methods that could be used to
* change the id generated (it is possible to write data
* to the stream and not send it to the digest, for example).
*/
public abstract class BlockOutputStream extends FilterOutputStream {
boolean dbg = false;
private static void p(String s) { System.out.println(s); }
protected MessageDigest digest;
protected BlockOutputStream(OutputStream out) {
super(out);
digest = BlockId.makeMessageDigest();
digest.reset();
}
public void write(byte[] b, int off, int len) throws IOException {
digest.update(b, off, len);
if(dbg) p("<"+new String(b)+">");
out.write(b, off, len);
}
public void write(int b) throws IOException {
digest.update((byte)b);
if(dbg) p("<"+new String(new byte[] {(byte)b})+">");
out.write(b);
}
/** Construct a BlockId
from the current state
* of the internal message digest. This is usefully called
* from the close()
method.
*/
protected BlockId makeIdFromDigest() throws IOException {
byte[] bytes = new byte[21];
bytes[0] = 0x01;
try {
if(digest.digest(bytes, 1, 20) != 20)
throw new Error("SHA digest not 20 bytes long?!?");
} catch(DigestException e) {
throw new IOException("DigestException: "+e);
}
return new BlockId(bytes);
}
/** Close this stream and create the new block.
* The block is automatically added to the pool this stream
* came from. Additionally, the block and its id can be
* retrieved from this stream through the getBlock()
* and getBlockId()
methods.
*/
abstract public void close() throws IOException;
/** Get the Block
created by this stream.
* This may only be called after the stream has been closed.
*/
abstract public Block getBlock() throws IOException;
/** Get the id of the block created by this stream.
* This may only be called after the stream has been closed.
*/
public BlockId getBlockId() throws IOException {
return getBlock().getId();
}
/** Get the header of this block.
* The header is set when this object is created, because it must be
* the first thing written to the stream (obviously ;-)
).
*
* XXX not sure this is really needed... */ abstract public Header822 getHeader() throws IOException; }