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