/* * DataChannelOutputStream.java - An output stream using a channel * * Copyright (c) 1996 Chuck McManis, All Rights Reserved. * * Permission to use, copy, modify, and distribute this software * and its documentation for NON-COMMERCIAL purposes and without * fee is hereby granted provided that this copyright notice * appears in all copies. * * CHUCK MCMANIS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. CHUCK MCMANIS * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT * OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. */ package util.comm; import java.io.OutputStream; import java.io.IOException; import util.Recyclable; /** * This class implements a standard Java OutputStream on top of a * DataChannel. This allows applets and applications that are designed * to talk to streams to talk to another class via a DataChannel. * Unlike PipedOutputStream which connects one input to one output, * this class connects a DataChannelOutputStream to zero or more * DataChannelInputStreams. This gives many to one, and one to many * linkage paths for objects that wish to communicate locally using * shared memory buffers. * * The most complex portion of the code deals with managing reusable * StreamBuffer objects whose references move from the * output stream into one or more input streams and then get recycled * back to the output stream when all of the input streams have read * the data. */ public class DataChannelOutputStream extends OutputStream implements Recyclable { private DataChannel dc; private StreamBufferList freeList = new StreamBufferList(); private StreamBuffer current; private int bufSize = 2048; /** * Open an output stream on top of the DataChannel * named chanID */ public DataChannelOutputStream(String chanID) { dc = DataChannel.getChannel(chanID); } /** * Open an output stream on top of the DataChannel * named chanID with intermediate buffer * size of bsize. */ public DataChannelOutputStream(String chanID, int bsize) { dc = DataChannel.getChannel(chanID); bufSize = bsize; } /** * Buffer management. We return a stream buffer object * from the free queue if one is there, otherwise we * allocate a new one. The idea here is that the channel * allocates a few buffers and then recirculates them * rather than allocating and deallocating them. */ synchronized StreamBuffer getBuffer() { StreamBuffer result = freeList.getBuffer(); if (result == null) result = new StreamBuffer(bufSize, this); result.next = null; return result; } /** * Recycle StreamBuffer objects that are * no longer needed by the receiving DataChannelInputStreams. * Note that given the way the recycle method works in a StreamBuffer * this method will only be called by the StreamBuffer when its reference * count is zero. */ public void recycle(Object x) { if (x instanceof StreamBuffer) { freeList.putBuffer((StreamBuffer) x); } } public synchronized void write(int c) throws IOException { if (dc == null) throw new IOException("No Data Channel is open."); if (current == null) current = getBuffer(); // get a new buffer if (current.write((byte) c)) { dc.putValue(current); // it is full so flush it. current = null; } } public synchronized void flush() throws IOException { if (dc == null) throw new IOException("Missing DataChannel."); if (current == null) return; dc.putValue(current); current = null; } public void close() throws IOException { dc = null; } }