Package org.xlightweb

Source Code of org.xlightweb.BodyDataSink$NonBlockingStream$WriteCompletionHandlerAdapter

/*
*  Copyright (c) xlightweb.org, 2008 - 2009. All rights reserved.
*
*  This library is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public
*  License as published by the Free Software Foundation; either
*  version 2.1 of the License, or (at your option) any later version.
*
*  This library is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*  Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public
*  License along with this library; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* Please refer to the LGPL license at: http://www.gnu.org/copyleft/lesser.txt
* The latest copy of this software may be found on http://www.xlightweb.org/
*/
package org.xlightweb;

import java.io.Closeable;
import java.io.Flushable;
import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.FileChannel;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.xlightweb.AbstractHttpConnection.IMultimodeExecutor;
import org.xsocket.Execution;
import org.xsocket.IDataSink;
import org.xsocket.IDestroyable;
import org.xsocket.connection.AbstractNonBlockingStream;
import org.xsocket.connection.IConnection;
import org.xsocket.connection.IWriteCompletionHandler;
import org.xsocket.connection.IConnection.FlushMode;




/**
* I/O resource capable of receiving the body data.
*
* @author grro@xlightweb.org
*
*/
public final class BodyDataSink implements IDataSink, IDestroyable, Flushable, Closeable, WritableByteChannel, GatheringByteChannel {
 
  private static final Logger LOG = Logger.getLogger(BodyDataSink.class.getName());
 
  private final NonBlockingStream nonBlockingStream = new NonBlockingStream()
  private final ArrayList<IBodyCloseListener> closeListeners = new ArrayList<IBodyCloseListener>();
 
  private final IMultimodeExecutor executor;
  private final AbstractHttpConnection httpConnection;
  private final String connectionId;
 
  private IMessageWriter messageWriter;
  private AtomicBoolean isOpen = new AtomicBoolean(true);
 
 
  // write completion handler support
  private final WriteCompletionManager writeCompletionManager;
  private final Object writeCompletionGuard = new Object();
 
 

  /**
   * constructor
   *
   * @param httpConnection     the underlying http connection
   * @param executor           the executor
   * @param bodySerializer     the body serializer
   * @param characterEncoding  the character encoding
   * @throws IOException if an exception occurs
   */
  BodyDataSink(AbstractHttpConnection httpConnection, IMultimodeExecutor executor, IMessageWriter bodySerializer, String characterEncoding) throws IOException {
 
    this.httpConnection = httpConnection;
    this.executor = executor;
    this.messageWriter = bodySerializer;
   
    if (httpConnection != null) {
      this.httpConnection.setBodyDataSink(this);
      connectionId = httpConnection.getId();
     
    } else {
        connectionId = "<unset>";
    }
   
    writeCompletionManager = new WriteCompletionManager(connectionId);
   
    setFlushmode(FlushMode.SYNC);
    setEncoding(characterEncoding);
  }

 
 
  /**
   * return the id 
   * @return the id
   */
  String getId() {
    return connectionId;
  }
 
 
  /**
   * returns the body writer
   * @return the body writer
   */
  IMessageWriter getMessageWriter() {
    return messageWriter;
  }
 
 
  /**
   * sets the body writer
   * @param bodyWriter  the body writer
   */
  void setMessageWriter(IMessageWriter bodyWriter) {
    this.messageWriter = bodyWriter;
  }

 
  /**
   * adds a close listener
   * @param closeListener the close listener
   */
  void addCloseListener(IBodyCloseListener closeListener) {
    synchronized (closeListeners) {
      closeListeners.add(closeListener);
    }
  }

 
  boolean isNetworkEndpoint() {
    if (messageWriter == null) {
      return false;
    } else {
      return messageWriter.isNetworkEndpoint();
    }
  }
 
 
  /**
   * returns the size of pending write data
   *  
   * @return the size of pending write data
   */
  int getPendingWriteDataSize() {
    if (messageWriter == null) {
      return 0;
    } else {
      return messageWriter.getPendingWriteDataSize();
    }
  }
 
 
  /**
   * remove a close listener
   *
   * @param closeListener a close listener
   * @return true, if the listener is removed
   */
  boolean removeCloseListener(IBodyCloseListener closeListener) {
    synchronized (closeListeners) {
      return closeListeners.remove(closeListener);
    }
  }
 
 
  /**
   * {@inheritDoc}
   */
  public void flush() throws IOException {
    nonBlockingStream.flush();
  }
 
 
  /**
   * {@inheritDoc}
   */
  public void close() throws IOException {

    isOpen.set(false);
     
    if (httpConnection != null) {
      httpConnection.removeBodyDataSink(this);
    }
     
    try {
      flush();
         
    } catch (IOException ioe) {
      throw ioe;
     
    } catch (Exception e) {
        if (LOG.isLoggable(Level.FINE)) {
            LOG.fine("[" + connectionId + "] error occured by flushing BodyDataSink " + e.toString());
        }
      throw new IOException(e.toString());
       
    } finally {
      if (LOG.isLoggable(Level.FINE)) {
        LOG.fine("[" + connectionId + "] closing body serializer " + messageWriter);
      }
      messageWriter.close()
      callCloseListener();
    }
  }
 

 
  /**
   * writes a buffer array
   *
   * @param buffers                 the buffer array
   * @param writeCompletionHandler  the completion handler
   * @throws IOException if an exception occurs
   */
    public void write(ByteBuffer[] buffers, IWriteCompletionHandler writeCompletionHandler) throws IOException {
      nonBlockingStream.write(buffers, writeCompletionHandler);
    }


  /**
   * {@inheritDoc}
   */
  public long transferFrom(FileChannel source) throws IOException, BufferOverflowException {
    return nonBlockingStream.transferFrom(source);
  }

  /**
   * transfer the available data from the data source
   *
   * @param source   the data source
   * @return the transfered size
   *
   * @throws IOException if an exception occurs
   */
  public long transferFrom(NonBlockingBodyDataSource source) throws IOException {
    return source.transferTo(this);
  }
 
 
  /**
   * transfer the available data from the data source
   *
   * @param source   the data source
   * @param length   the length to transfer
   * @return the transfered size
   *
   * @throws IOException if an exception occurs
   */
  public long transferFrom(NonBlockingBodyDataSource source, int length) throws IOException {
    return source.transferTo(this, length);
  }

 
  /**
   * transfer all data from the data source
   *
   * @param source   the data source
   * @return the transfered size
   *
   * @throws IOException if an exception occurs
   */
  public long transferFrom(BlockingBodyDataSource source) throws IOException {
    return source.transferTo(this);
  }
 
 
  /**
   * transfer all data from the data source
   *
   * @param source   the data source
   * @param length   the length to transfer
   * @return the transfered size
   *
   * @throws IOException if an exception occurs
   */
  public long transferFrom(BlockingBodyDataSource source, int length) throws IOException {
    return source.transferTo(this);
  }

 
 
  /**
   * {@inheritDoc}
   */
  public long transferFrom(ReadableByteChannel source) throws IOException, BufferOverflowException {
    return nonBlockingStream.transferFrom(source);
  }


  /**
   * {@inheritDoc}
   */
  public long transferFrom(ReadableByteChannel source, int chunkSize) throws IOException, BufferOverflowException {
    return nonBlockingStream.transferFrom(source, chunkSize);
  }


  /**
   * {@inheritDoc}
   */
  public int write(byte b) throws IOException, BufferOverflowException {
    return nonBlockingStream.write(b);
  }


  /**
   * {@inheritDoc}
   */
  public int write(byte... bytes) throws IOException, BufferOverflowException {
    return nonBlockingStream.write(bytes);
  }


  /**
   * {@inheritDoc}
   */
  public int write(byte[] bytes, int offset, int length) throws IOException, BufferOverflowException {
    return nonBlockingStream.write(bytes, offset, length);
  }


  /**
   * {@inheritDoc}
   */
  public int write(ByteBuffer buffer) throws IOException, BufferOverflowException {
    return nonBlockingStream.write(buffer);
  }


  /**
   * {@inheritDoc}
   */
  public long write(ByteBuffer[] buffers) throws IOException, BufferOverflowException {
    return nonBlockingStream.write(buffers);
  }


  /**
   * {@inheritDoc}
   */
  public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
    return nonBlockingStream.write(srcs, offset, length);
  }


  /**
   * {@inheritDoc}
   */
  public long write(List<ByteBuffer> buffers) throws IOException, BufferOverflowException {
    return write(buffers.toArray(new ByteBuffer[buffers.size()]));
  }


  /**
   * {@inheritDoc}
   */
  public int write(int i) throws IOException, BufferOverflowException {
    return nonBlockingStream.write(i);
  }


  /**
   * {@inheritDoc}
   */
  public int write(short s) throws IOException, BufferOverflowException {
    return nonBlockingStream.write(s);
  }


  /**
   * {@inheritDoc}
   */
  public int write(long l) throws IOException, BufferOverflowException {
    return nonBlockingStream.write(l);
  }


  /**
   * {@inheritDoc}
   */
  public int write(double d) throws IOException, BufferOverflowException {
    return nonBlockingStream.write(d);
  }


  /**
   * {@inheritDoc}
   */
  public int write(String message) throws IOException, BufferOverflowException {
    return nonBlockingStream.write(message);
  }


  /**
   * sets the default encoding
   *
   * @param defaultEncoding  the default encoding
   */
  public final void setEncoding(String defaultEncoding) {
    nonBlockingStream.setEncoding(defaultEncoding);
  }


  /**
   * gets the default encoding
   *
   * @return  the default encoding
   */
  public final String getEncoding() {
    return nonBlockingStream.getEncoding();
  }


  /**
   * see {@link IConnection#setFlushmode(FlushMode)}
   */
  public void setFlushmode(FlushMode flushMode) {
    nonBlockingStream.setFlushmode(flushMode);
  }


  /**
   * see {@link IConnection#getFlushmode()}
   */
  public final FlushMode getFlushmode() {
    return nonBlockingStream.getFlushmode();
  }

 
  /**
   * set autoflush. If autoflush is activated, each write call
   * will cause a flush. <br><br>
   *
   * @param autoflush true if autoflush should be activated
   */
  public final void setAutoflush(boolean autoflush) {
    nonBlockingStream.setAutoflush(autoflush);
  }

 
  /**
   * get autoflush
   *
   * @return true, if autoflush is activated
   */
  public final boolean isAutoflush() {
    return nonBlockingStream.isAutoflush();
  }
 
 
 
  /**
   * Marks the write position in the connection.
   */
  public final void markWritePosition() {
    nonBlockingStream.markWritePosition();
  }


  /**
   * Resets to the marked write position. If the connection has been marked,
   * then attempt to reposition it at the mark.
   *
   * @return true, if reset was successful
   */
  public final boolean resetToWriteMark() {
    return nonBlockingStream.resetToWriteMark();
  }


 
  /**
   * remove the write mark
   */
  public final void removeWriteMark() {
    nonBlockingStream.removeWriteMark();
  }

 
  /**
   * Attaches the given object to this connection
   *
   * @param obj The object to be attached; may be null
   * @return The previously-attached object, if any, otherwise null
   */
  public final void setAttachment(Object obj) {
    nonBlockingStream.setAttachment(obj);
  }


  /**
   * Retrieves the current attachment.
   *
   * @return The object currently attached to this key, or null if there is no attachment
   */
  public final Object getAttachment() {
    return nonBlockingStream.getAttachment();
  }

 
 
  /**
   * returns true if the data sink is open
   *
   * @return true if the data sink is open
   */
  public boolean isOpen() {
    return isOpen.get();
  }
 

 
  /**
   * call back if the underlying connection is closed
   */
  void onUnderlyingHttpConnectionClosed() {
    if (isOpen.get()) {
      if (LOG.isLoggable(Level.FINE)) {
        LOG.fine("[" + connectionId + "] underlying connection is closed. closing data source");
      }
      isOpen.set(false);
     
      messageWriter.destroy();
      callCloseListener();
    }
  }
 
 
 
  @SuppressWarnings("unchecked")
  private void callCloseListener() {
    ArrayList<IBodyCloseListener> closeListenersCopy = null;
    synchronized (closeListeners) {
      closeListenersCopy = (ArrayList<IBodyCloseListener>) closeListeners.clone();
    }
   
   
    for (IBodyCloseListener bodyCloseListener : closeListenersCopy) {
      removeCloseListener(bodyCloseListener);
      callCloseListener(bodyCloseListener);
    }
  }
 
 
 
  private void callCloseListener(IBodyCloseListener listener) {
   
    Runnable task = new CloseListenerCaller(listener);
   
    if (HttpUtils.isBodyCloseListenerMutlithreaded(listener)) {
      executor.processMultithreaded(task);
    } else {
      executor.processNonthreaded(task);
    }
  }
 
 
  private static final class CloseListenerCaller implements Runnable {
 
    private IBodyCloseListener listener = null;
   
    public CloseListenerCaller(IBodyCloseListener listener) {
      this.listener = listener;
    }
   
    public void run() {
      try {
        listener.onClose();
      } catch (IOException ioe) {
        if (LOG.isLoggable(Level.FINE)) {
          LOG.fine("Error occured by calling close listener " + listener + " " + ioe.toString());
        }
      }     
    }
  }
 

  /**
   * destroys this data sink 
   */
  public void destroy() {
    isOpen.set(false);
   
    messageWriter.destroy();
    callCloseListener();
  }
 
 
  /**
   * {@inheritDoc}
   */
  public String toString() {
    return nonBlockingStream.toString();
  }   
 
 

  private final class NonBlockingStream extends AbstractNonBlockingStream {
   
    private boolean isContentImmutable = true;
   
    public boolean isOpen() {
      return BodyDataSink.this.isOpen();
    }
   
    public int write(ByteBuffer buffer) throws IOException, BufferOverflowException {
      if (super.getFlushmode() == FlushMode.SYNC) {
        isContentImmutable = false;
      }
      return super.write(buffer);
    }


    public long write(ByteBuffer[] buffers) throws IOException, BufferOverflowException {
      if (super.getFlushmode() == FlushMode.SYNC) {
        isContentImmutable = false;
      }
      return super.write(buffers);
    }


    public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
      if (super.getFlushmode() == FlushMode.SYNC) {
        isContentImmutable = false;
      }
      return super.write(srcs, offset, length);
    }


 

      void write(ByteBuffer[] buffers, IWriteCompletionHandler writeCompletionHandler) throws IOException {
          synchronized (writeCompletionGuard) {
              boolean isSuppressReuseBuffer = isSuppressReuseBufferWarning();
              setSuppressReuseBufferWarning(true);

              if (LOG.isLoggable(Level.FINE)) {
              LOG.fine("[" + connectionId + "] {" + buffers.hashCode() + "} Writing with completion handler. Register handler " + writeCompletionHandler);
            }     
              writeCompletionManager.registerCompletionHandler(writeCompletionHandler, executor, buffers);
              write(buffers);
             
              setSuppressReuseBufferWarning(isSuppressReuseBuffer);
          }
      }

  
   
    @Override
    protected boolean isDataWriteable() {
      return isOpen();
    }
   
    @Override
    protected boolean isMoreInputDataExpected() {
      return false;
    }
   

    public void flush() throws IOException {
     
      super.removeWriteMark();
     
      boolean isImmutable = isContentImmutable;
      ByteBuffer[] dataToWrite = drainWriteQueue();
      isContentImmutable = true;
         
      IWriteCompletionHandler completionHandler = null;
         
      if (!writeCompletionManager.isPendingCompletionConfirmationsEmtpy()) {
        if (LOG.isLoggable(Level.FINE)) {
          LOG.fine("[" + connectionId + "] write completion handlers are registered. perform write with WriteCompletionHandlerAdapter");
        }
                     
        completionHandler = new WriteCompletionHandlerAdapter(dataToWrite);
      }
         
      messageWriter.flush(dataToWrite, isImmutable, super.getFlushmode(), completionHandler);
    }

   
    @Execution(Execution.NONTHREADED)
    private final class WriteCompletionHandlerAdapter implements IWriteCompletionHandler {
           
        private final ByteBuffer[] dataToWrite;
       
        public WriteCompletionHandlerAdapter(ByteBuffer[] dataToWrite) {
            this.dataToWrite = dataToWrite;
          }
       
        public void onWritten(int written) throws IOException {
            if (LOG.isLoggable(Level.FINE)) {
                  LOG.fine("[" + connectionId + "]  {" + dataToWrite.hashCode() + "}  data (size=" + written + " bytes) has been written. notify registered WriteCompletionHandler (if exist)");
              }
                
            onDataWritten(dataToWrite);
        }
       
        public void onException(IOException ioe) {
            if (LOG.isLoggable(Level.FINE)) {
                LOG.fine("[" + connectionId + "]  {" + dataToWrite.hashCode() + "}   " + ioe.toString() + " error has been occured by writing data. notify registered WriteCompletionHandler (if exist)");
            }

            onWriteException(ioe, dataToWrite);
        }
    }
   

      private void onDataWritten(ByteBuffer[] data) {
          writeCompletionManager.onWritten(data, false);
     
   
     

      private void onWriteException(IOException ioException, ByteBuffer[] data) {
          destroy();
         
          writeCompletionManager.onWriteException(ioException, data);
      }


    /**
     * {@inheritDoc}
     */
    @Override
    protected void onWriteDataInserted() throws IOException, ClosedChannelException {
     
      if (super.isAutoflush()) {
        try {
          flush();
        } catch (IOException ioe) {
          throw ioe;
         
        } catch (Exception e) {
          IOException ioe = new IOException(e.getMessage());
          ioe.setStackTrace(e.getStackTrace());
          throw ioe;
        }
      }
    }

   
   
    public String toString() {
      return printWriteBuffer(super.getEncoding());
    }   
  }
}
TOP

Related Classes of org.xlightweb.BodyDataSink$NonBlockingStream$WriteCompletionHandlerAdapter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.