blob: b5e6a0a99b812777dc5696a71b0d60eb2be61318 [file] [log] [blame]
/*
* This file is part of the QuickServer library
* Copyright (C) QuickServer.org
*
* Use, modification, copying and distribution of this software is subject to
* the terms and conditions of the GNU Lesser General Public License.
* You should have received a copy of the GNU LGP License along with this
* library; if not, you can download a copy from <http://www.quickserver.org/>.
*
* For questions, suggestions, bug-reports, enhancement-requests etc.
* visit http://www.quickserver.org
*
*/
package org.quickserver.util.io;
import java.io.*;
import java.nio.*;
import java.util.*;
import org.apache.commons.pool.ObjectPool;
import org.quickserver.net.server.ClientHandler;
import org.quickserver.net.server.impl.NonBlockingClientHandler;
import java.util.logging.*;
/**
* This is an OutputStream constructed from list of ByteBuffers. This is
* used in non-blocking mode.
* @since 1.4.5
* @author Akshathkumar Shetty
*/
public class ByteBufferOutputStream extends OutputStream {
private static Logger logger = Logger.getLogger(ByteBufferOutputStream.class.getName());
static {
logger.setLevel(Level.INFO);
}
/**
* Sets the debug flag. When debug is set to <code>true</code>
* one can see number of bytes written.
*/
public static void setDebug(boolean flag) {
if(flag)
logger.setLevel(Level.FINEST);
else
logger.setLevel(Level.INFO);
}
/**
* @since 1.4.7
*/
public static boolean isLoggable(Level level) {
return logger.isLoggable(level);
}
private ArrayList bufferList;
private ByteBuffer lastByteBuffer = null;
private NonBlockingClientHandler handler;
private Object toNotify = null;
private ArrayList encryptedBufferList;
/**
* Creates a new ByteBufferOutputStream using the given list as its base
* and ClientHandler as the target channel.
*/
public ByteBufferOutputStream(ArrayList bufferList, ClientHandler handler) {
if(bufferList==null || handler==null)
throw new IllegalArgumentException("ArrayList or ClientHandler was null.");
this.bufferList = bufferList;
this.handler = (NonBlockingClientHandler) handler;
if(handler.isSecure()) {
encryptedBufferList = new ArrayList();
}
}
public synchronized void close() {
if(lastByteBuffer!=null) {
returnBufferBack(lastByteBuffer);
}
}
public void flush() throws IOException {
if(bufferList.size()!=0 || lastByteBuffer!=null) {
handler.registerWrite();
} else {
return;
}
while(bufferList.size()>=5) {
handler.waitTillFullyWritten();
}
}
public synchronized void write(int b) throws IOException {
handler.isConnected();
ByteBuffer byteBuffer = null;
if(bufferList.size()!=0) {
byteBuffer = (ByteBuffer) bufferList.remove(bufferList.size()-1);
if(byteBuffer.remaining()==0) {
bufferList.add(byteBuffer);
byteBuffer = null;
}
}
try {
if(byteBuffer==null) {
byteBuffer = (ByteBuffer) handler.getServer().getByteBufferPool().borrowObject();
}
} catch(Exception e) {
logger.warning("Could not borrow ByteBufer from pool: "+e);
throw new IOException(e.toString());
}
byteBuffer.put((byte)b);
bufferList.add(byteBuffer);
}
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
public synchronized void write(byte[] b, int off, int len) throws IOException {
if(len==0) {
return;
}
handler.isConnected();
ByteBuffer byteBuffer = null;
int remaining = 0;
int toWrite = len;
if(toWrite!=0 && bufferList.size()!=0) {
byteBuffer = (ByteBuffer) bufferList.remove(bufferList.size()-1);
if(byteBuffer.remaining()==0) {
bufferList.add(byteBuffer);
byteBuffer = null;
}
}
while(toWrite!=0) {
try {
if(byteBuffer==null) {
byteBuffer = (ByteBuffer)
handler.getServer().getByteBufferPool().borrowObject();
}
} catch(Exception e) {
logger.warning("Could not borrow ByteBufer from pool: "+e);
throw new IOException(e.toString());
}
remaining = byteBuffer.remaining();
if(remaining < toWrite) {
byteBuffer.put(b, off, remaining);
off = off + remaining;
toWrite = toWrite - remaining;
} else {
byteBuffer.put(b, off, toWrite);
toWrite=0;
}
bufferList.add(byteBuffer);
byteBuffer = null;
}
}
public synchronized boolean writeAllByteBuffer() throws IOException {
if(lastByteBuffer!=null) {
writeLastByteBuffer();
if(lastByteBuffer!=null) return false;
}
ByteBuffer dest = null;
while(bufferList.size()!=0) {
dest = (ByteBuffer) bufferList.remove(0);
if(handler.isSecure()==false) {
lastByteBuffer = dest;
lastByteBuffer.flip();
writeLastByteBuffer();
if(lastByteBuffer != null) return false;
} else {
lastByteBuffer = handler.encrypt(dest);
if(lastByteBuffer==null) { //coult not enc.. lets wait..
bufferList.add(0, dest);
return false;
}
addEncryptedByteBuffer(lastByteBuffer);
lastByteBuffer = null;
}
}
while(encryptedBufferList!=null && encryptedBufferList.size()!=0) {
lastByteBuffer = (ByteBuffer) encryptedBufferList.remove(0);
logger.fine("Sening to peer: "+lastByteBuffer.position());
lastByteBuffer.flip();
writeLastByteBuffer();
if(lastByteBuffer != null) return false;
}
if(toNotify!=null) {
synchronized(toNotify) {
toNotify.notify();
toNotify = null;
}
}
logger.fine("writeAllByteBuffer is true!");
return true;
}
private synchronized void writeLastByteBuffer() throws IOException {
int written = 0;
while(lastByteBuffer.remaining()!=0) {
java.nio.channels.SocketChannel sc = handler.getSocketChannel();
if(sc!=null && sc.isOpen()) {
written = sc.write(lastByteBuffer);
if(written==0) {
break;
}
if(logger.isLoggable(Level.FINEST)) {
logger.finest("Written "+written+" bytes");
}
} else {
throw new IOException("SocketChannel was closed.");
}
}
if(lastByteBuffer.remaining()==0) {
returnBufferBack(lastByteBuffer);
lastByteBuffer = null;
}
}
private void returnBufferBack(ByteBuffer byteBuffer) {
try {
handler.getServer().getByteBufferPool().returnObject(byteBuffer);
} catch(Exception er) {
logger.warning("Error while returning ByteBuffer to pool: "+er);
}
}
public void forceNotify() {
if(toNotify==null) return;
synchronized(toNotify) {
toNotify.notify();
toNotify = null;
}
}
public boolean isDataAvailableForWrite(Object toNotify) {
if(lastByteBuffer!=null) {
if(this.toNotify!=null) {
throw new IllegalStateException("toNotify object was already set!");
}
this.toNotify = toNotify;
return true;
}
if(bufferList.size()==0) {
return false;
} else {
if(this.toNotify!=null) {
throw new IllegalStateException("toNotify object was already set!");
}
this.toNotify = toNotify;
return true;
}
}
public void addEncryptedByteBuffer(ByteBuffer buff) {
encryptedBufferList.add(buff);
}
public boolean doShutdown() throws IOException {
if(handler.closeIfSSLOutboundDone()) return true;
ByteBuffer dummyByteBuffer = ByteBuffer.allocate(0);
lastByteBuffer = handler.encrypt(dummyByteBuffer);
writeLastByteBuffer();
if(lastByteBuffer != null) {
handler.registerWrite();
return false;
} else {
return handler.closeIfSSLOutboundDone();
}
}
}