/* 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; }