Package org.xlightweb.client

Source Code of org.xlightweb.client.DuplicatingBodyForwarder$ISink

/*
*  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.client;

import java.io.Closeable;
import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;


import org.xlightweb.BodyDataSink;
import org.xlightweb.IBodyDataHandler;
import org.xlightweb.IBodyDestroyListener;
import org.xlightweb.NonBlockingBodyDataSource;




/**
* A Forwarder which duplicates the data and forwards it to a primary and a secondary sink
*
* @author grro@xlightweb.org
*/
final class DuplicatingBodyForwarder implements IBodyDataHandler {
   
    private static final Logger LOG = Logger.getLogger(DuplicatingBodyForwarder.class.getName());
   
 
  private final NonBlockingBodyDataSource bodyDataSource;
  private final ISink primarySink;
  private final AtomicBoolean isPrimarySinkClosed = new AtomicBoolean(false);
  private final ISink secondarySink;
  private final AtomicBoolean isSecondarySinkClosed = new AtomicBoolean(false);
 
 
    /**
     * DuplicatingBodyForwarder is unsynchronized by config. See HttpUtils$getExecutionMode
     */

 

  public DuplicatingBodyForwarder(final NonBlockingBodyDataSource bodyDataSource, final ISink primarySink, final ISink secondarySink) {
    this.bodyDataSource = bodyDataSource;
    this.primarySink = primarySink;
    this.secondarySink = secondarySink;
   
   
    IBodyDestroyListener destroyListenerPrimary = new IBodyDestroyListener() {
        public void onDestroyed() {
            isPrimarySinkClosed.set(true);
            handlePeerDestroy();
        }
    };
   
    primarySink.setDestroyListener(destroyListenerPrimary);
   
   
    IBodyDestroyListener destroyListenerSecondary = new IBodyDestroyListener() {
        public void onDestroyed() {
            isSecondarySinkClosed.set(true);
            handlePeerDestroy();
        }
    };

    secondarySink.setDestroyListener(destroyListenerSecondary);
  }
 
 
  /**
   * {@inheritDoc}
   */
  public boolean onData(final NonBlockingBodyDataSource bodyDataSource) throws BufferUnderflowException {
     
      try {

          int available = 0;
          do {
              try {
                  available = bodyDataSource.available();
              } catch (IOException e) {
                  destroySinks();
                  return true;
              }

                if (available == -1) {
                    closeSinks();
                    return true;
                   
                } else if (available > 0) {
                    ByteBuffer[] buffers = bodyDataSource.readByteBufferByLength(available);
                   
                    for (ByteBuffer buffer : buffers) {
                       
                        if (isPrimarySinkClosed.get()) {
                            if (!isSecondarySinkClosed.get()) {
                                write(secondarySink, isSecondarySinkClosed, buffer);
                            } else {
                                throw new ClosedChannelException();
                            }
                        } else {
                            if (!isSecondarySinkClosed.get()) {
                                write(secondarySink, isSecondarySinkClosed, buffer.duplicate());
                            }
                            write(primarySink, isPrimarySinkClosed, buffer);
                        }
                       
                    }
                }

          } while (available > 0);
         
        } catch (IOException e) {
            destroySinks();
        }
       
    return true;
  }
 
 
  private void write(ISink sink, AtomicBoolean isClosed, ByteBuffer buffer) {
      try {
          sink.onData(buffer)
         
      } catch (IOException ioe) {
          if (LOG.isLoggable(Level.FINE)) {
              LOG.fine("[" + sink.getId() + "] error occured by writing data to " + sink + " " + ioe.toString());
          }
          isClosed.set(true);
          handlePeerDestroy();
      }
  }
 
 
  private void handlePeerDestroy() {
      if (isPrimarySinkClosed.get() && isSecondarySinkClosed.get()) {
            if (LOG.isLoggable(Level.FINE)) {
                LOG.fine("both data sink are closed. Destroying data source");
            }         
            bodyDataSource.destroy();
        }
  }
 
 
 
 
 
  private void destroySinks() {    
      if (!isPrimarySinkClosed.getAndSet(true)) {
          primarySink.destroy();
      }
         
      if (!isSecondarySinkClosed.getAndSet(true)) {
          secondarySink.destroy();
      }
  }
     
 

    private void closeSinks() throws IOException {
        if (!isPrimarySinkClosed.getAndSet(true)) {
            primarySink.close();
        }
           
        if (!isSecondarySinkClosed.getAndSet(true)) {
            secondarySink.close();
        }
    }
 
   
  
  public static interface ISink extends Closeable {
         
      void onData(ByteBuffer data) throws IOException;
     
      void setDestroyListener(IBodyDestroyListener destroyListener);
         
      void destroy();
     
      String getId();
  }
 
 
  static final class BodyDataSinkAdapter implements ISink {
       
        private final BodyDataSink dataSink;
           
        public BodyDataSinkAdapter(BodyDataSink dataSink) throws IOException {
            this.dataSink = dataSink;
        }
           
        public void onData(ByteBuffer data) throws IOException {
            dataSink.write(data);
        }
           
        public void close() throws IOException {
            dataSink.close();
        }
           
        public void destroy() {
            dataSink.destroy();
        }
           
        public void setDestroyListener(IBodyDestroyListener destroyListener) {
            dataSink.addDestroyListener(destroyListener);
        }
       
        public String getId() {
            return "wrapped" + dataSink.getId();
        }
    }
}
TOP

Related Classes of org.xlightweb.client.DuplicatingBodyForwarder$ISink

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.
ww.google-analytics.com/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');