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