package uk.co.mmscomputing.device.capi; import java.io.*; import java.nio.channels.ClosedChannelException; import uk.co.mmscomputing.concurrent.*; import uk.co.mmscomputing.device.capi.ncc.DataB3Req; import uk.co.mmscomputing.device.capi.ncc.DataB3Conf; import uk.co.mmscomputing.device.capi.exception.CapiException; import uk.co.mmscomputing.device.capi.exception.CapiIOException; public class CapiOutputStream extends OutputStream implements CapiConstants{ final private static int maxBuffers = 8; private byte[] buf; private int count = 0; private CapiNCC ncc; private boolean isopen; private Semaphore blocker = new Semaphore(maxBuffers-1,true); private int current = 0; private byte[][] buffers = new byte[maxBuffers][DefaultB3DataBlockSize]; private DataB3Req[] dataB3Reqs = new DataB3Req[maxBuffers]; private int unconfirmed = 0; private boolean isdisconnected = false; private Semaphore ds = new Semaphore(0,true); // ---- Capi Thread Methods ---- CapiOutputStream(CapiNCC ncc){ this.ncc=ncc; this.isopen=true; this.buf=buffers[0]; } synchronized protected boolean checkIsOpen(){ if(isopen==false){return false;} isopen=false; return true; } private void releaseDataB3Reqs(int handle){ synchronized(dataB3Reqs){ if(dataB3Reqs[handle]!=null){ dataB3Reqs[handle].release(); // release native byte buffer dataB3Reqs[handle]=null; unconfirmed--; } } } private void releaseDataB3Reqs(){ for(int handle=0;handle<dataB3Reqs.length;handle++){ releaseDataB3Reqs(handle); // release native byte buffer } } void received(DataB3Conf msg)throws CapiException{ releaseDataB3Reqs(msg.getHandle()); // release native byte buffer blocker.release(); // release threads that wait on Outputstream Write methods if(!isopen&&(unconfirmed==0)){ // if stream is to be closed and all req have been confirmed isdisconnected=true; // we are truly disconnected ds.release(); // release threads that wait at close() } } boolean isDisconnected(){return isdisconnected;} synchronized void disconnect()throws CapiException{ // called from NCC -> close output stream isdisconnected=true; // passive disconnect; disconnected by peer isopen=false; // we cannot send any data and do not receive any DataB3Conf anymore blocker.release(); // release output stream thread if blocked releaseDataB3Reqs(); // release native byte buffers ds.release(); // release threads that wait at close() } // ---- OutputStream Thread Methods ---- boolean isOpen(){return isopen;} private void writeBuffer(int count)throws IOException{ try{ if(isdisconnected){return;} blocker.acquire(); // wait until capi can accept next DataB3Req if(isdisconnected){blocker.release();return;} synchronized(dataB3Reqs){ dataB3Reqs[current]=ncc.write(current,buf,count); // send DataB3Req current=(current+1)%maxBuffers; // use next buffer buf=buffers[current]; unconfirmed++; } }catch(InterruptedException ie){ disconnect(); } } public void write(int b)throws IOException{ if(!isopen){throw new ClosedChannelException();} if(count==DefaultB3DataBlockSize){ // collect bytes until buffer is full and then send. writeBuffer(count);count=0; } buf[count++]=(byte)b; } public void write(byte[] b, int off, int len)throws IOException{ // if(!isopen){throw new ClosedChannelException();} if(b==null){ throw new NullPointerException(getClass().getName()+".write(byte[] b, int off, int len):\n\tb is null"); } if((off<0)||(len<0)||(b.length<(off+len))){ throw new IndexOutOfBoundsException(getClass().getName()+".write(byte[] b, int off, int len):\n\tindex off or len out of bounds."); } if(count>0){ // flush data we might have accumulated with simple write writeBuffer(count);count=0; } while(len>DefaultB3DataBlockSize){ // chop data into chunks and send off System.arraycopy(b,off,buf,0,DefaultB3DataBlockSize); writeBuffer(DefaultB3DataBlockSize); len-=DefaultB3DataBlockSize; off+=DefaultB3DataBlockSize; } if(len>0){ System.arraycopy(b,off,buf,0,len); writeBuffer(len); } } public void flush()throws IOException{ if(!isopen){throw new ClosedChannelException();} if(count>0){ writeBuffer(count); // flush data in buf count=0; } } public void close()throws IOException{ // System.err.println("\n\n\nUnconfirmed DataB3Reqs = "+unconfirmed+"\n\n\n"); if(checkIsOpen()){ writeBuffer(count); // make sure there is at least one pending DataB3Req try{ds.acquire();}catch(InterruptedException ie){ // wait for DisconnectB3Ind ie.printStackTrace(); } ds.release(); // release other threads ncc.closedOutput(); // signal NCC output stream is closed }else{ try{ds.acquire();}catch(InterruptedException ie){ // wait for DisconnectB3Ind ie.printStackTrace(); } ds.release(); // release other threads } } protected void finalize()throws Throwable{ // System.err.println(getClass().getName()+".finalize:\n\t"+toString()); close();super.finalize(); } }