Package org.xlightweb

Source Code of org.xlightweb.AbstractNetworkBodyDataSource

/*
*  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.IOException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.xsocket.DataConverter;
import org.xsocket.MaxReadSizeExceededException;



/**
* data source network based base implementation
* @author grro@xlightweb.org
*/
abstract class AbstractNetworkBodyDataSource extends NonBlockingBodyDataSource {
   
    private static final Logger LOG = Logger.getLogger(AbstractNetworkBodyDataSource.class.getName());

    private final AbstractHttpConnection httpConnection;
   
    private final AtomicBoolean isSuspendedRef = new AtomicBoolean(false);
    private final AtomicBoolean isConnectedRef = new AtomicBoolean(true);
   
    private final HttpHeader header;

    private static final boolean DEFAULT_IS_AUTODETECTEDING_ENCODING = Boolean.parseBoolean(System.getProperty("org.xlightweb.autodetectedingEncoding", "true"));
    private boolean isDetectEncoding = !DEFAULT_IS_AUTODETECTEDING_ENCODING;
    private final AtomicReference<Runnable> autoEncodingCallbackRef = new AtomicReference<Runnable>(null);
    private byte[] encodingBuffer = null;

   
    public AbstractNetworkBodyDataSource(HttpHeader header, AbstractHttpConnection httpConnection) {
        super(header.getCharacterEncoding(), httpConnection.getExecutor());
        this.header = header;
        this.httpConnection = httpConnection;
       
        if ((header.getContentType()) != null && HttpUtils.isTextMimeType(header.getContentType()) && HttpUtils.parseEncoding(header.getContentType()) == null) {
            isDetectEncoding = true;
        } else {
            isDetectEncoding = false;
        }
    }
   
   
    private void setDetectEncoding(boolean isDetectEncoding) {
       
        synchronized (autoEncodingCallbackRef) {
            this.isDetectEncoding = isDetectEncoding;
           
            if (isDetectEncoding == false) {
                Runnable task = autoEncodingCallbackRef.getAndSet(null);
                if (task != null) {
                    task.run();
                }
            }
        }
    }
   
    final void registerAutoEncondingDetectCallback(Runnable task) {
        synchronized (autoEncodingCallbackRef) {
            if (!isDetectEncoding) {
                task.run();
            } else {
                autoEncodingCallbackRef.set(task);
            }
        }
    }
   
  
    protected final String getId() {
        return httpConnection.getId();
    }
   
   
    protected final void setNonPersistent() {
        httpConnection.setPersistent(false);
    }
   
   
    /**
     * {@inheritDoc}
     */
    @Override
    final void suspend() throws IOException {
       
        // has not already suspended?
        if (!isSuspendedRef.getAndSet(true)) {
            if (LOG.isLoggable(Level.FINE)) {
                LOG.fine("suspend receiving data");
            }
           
            Runnable suspendTask = new Runnable() {
               
                public void run() {
                    try {
                        httpConnection.suspendReceiving();
                    } catch (IOException ioe) {
                        if (LOG.isLoggable(Level.FINE)) {
                            LOG.fine("[" + getId() + "] error occured by suspending " + ioe.toString());
                        }
                    }
                }
            };
           
            httpConnection.getExecutor().processNonthreaded(suspendTask);
        }
    }
   
   
    /**
     * {@inheritDoc}
     */
    @Override
    boolean isSuspended() {
        return isSuspendedRef.get();
    }
   

   
    /**
     * {@inheritDoc}
     */
    @Override
    void resume() throws IOException {

        // is suspended?
        if (isSuspendedRef.getAndSet(false)) {
           
            Runnable resumeTask = new Runnable() {
                           
                public void run() {
                    try {
                        if (httpConnection.isReceivingSuspended()) {
                            if (LOG.isLoggable(Level.FINE)) {
                                LOG.fine("[" + getId() + "] resume receiving data");
                            }
                            httpConnection.resumeReceiving();
                            callBodyDataHandler(true);
                        }
                    } catch (IOException ioe) {
                        if (LOG.isLoggable(Level.FINE)) {
                            LOG.fine("[" + getId() + "] error occured by resuming " + ioe.toString());
                        }
                    }
                }              
            };
            httpConnection.getExecutor().processNonthreaded(resumeTask);
        }
    }

   
   
    final void onDisconnect() throws IOException {
        setDetectEncoding(false);
       
        if (isConnectedRef.getAndSet(false)) {
            try {
                performOnDisconnect();
            } catch (ProtocolException pe) {
                setException(pe);
                throw pe;
            }
        }
    }

   
    abstract void performOnDisconnect() throws IOException;
   
   
    @Override
    final void onDestroy(String reason) {
        setDetectEncoding(false);
        httpConnection.destroy(reason);
    }


   
    final void setComplete() throws IOException {
        if (LOG.isLoggable(Level.FINE)) {
            LOG.fine("[" + getId() "] completed reveived");
        }
       
        setDetectEncoding(false);
       
        httpConnection.onMessageBodyReceived(header);
        super.setComplete();
    }
   

   
    final int readByteBufferByLength(ByteBuffer[] buffers, int length) throws IOException {

        if (buffers == null) {
            return 0;
        }
       
       
        if (isDetectEncoding) {
            byte[] data = DataConverter.toBytes(HttpUtils.copy(buffers));
            if (encodingBuffer != null) {
                byte[] newData = new byte[encodingBuffer.length + data.length];
                System.arraycopy(encodingBuffer, 0, newData, 0, encodingBuffer.length);
                System.arraycopy(data, 0, newData, encodingBuffer.length, data.length);
                data = newData;
            }
           
            try {
                String encoding = HttpUtils.detectEncoding(data);
                if (encoding != null) {
                    setEncoding(encoding);
                    header.setCharacterEncoding(encoding);
                }
                encodingBuffer = null;
                setDetectEncoding(false);
            } catch (BufferUnderflowException bue) {
                encodingBuffer = data;
            }
        }
       
       
        int remaining = length;
       
        for (int i = 0; i < buffers.length; i++) {
            ByteBuffer buffer = buffers[i];
            if (buffer == null) {
                continue;
            }
           
            int available = buffer.remaining();
               
            if (available == 0) {
                continue;
            }
           
                // not enough data
            if (available < remaining) {
                append(buffer);
                buffers[i] = null;
                remaining -= available;
                continue;
               
            } else if (available == remaining) {
                append(buffer);
                buffers[i] = null;
                return 0;
   
            // available > remaining
            } else {
                int limit = buffer.limit();
                buffer.limit(buffer.position() + remaining);
                ByteBuffer buf = buffer.slice();
                append(buf);
                   
                buffer.position(buffer.limit());
                buffer.limit(limit);
               
                return 0;
            }
        }
       
        return remaining;
    }

   
   
    /**
     * parses the the body  
     * 
     * @param rawData   the raw read network data buffer
     * @return true, if the body is complete
     * @throws IOException  if an exception occurs
     * @throws BufferUnderflowException if more data is required
     * @throws MaxReadSizeExceededException if the max read size is exceeded
     */
    final void parse(ByteBuffer[] rawData) throws IOException, BufferUnderflowException, MaxReadSizeExceededException {         
       
        if (!isComplete()) {
            try {
                doParse(rawData);
               
            } catch (BufferUnderflowException bue) {
                throw bue;
                   
            } catch (IOException ioe) {
                if (LOG.isLoggable(Level.FINE)) {
                    LOG.fine("[" + httpConnection.getId() + "] (protocol?) error occured by reading body " + ioe.toString());
                }
   
                if (!isComplete()) {
                    setException(ioe);
                }
                throw ioe;
            }
        } else {
            if (LOG.isLoggable(Level.FINE)) {
                LOG.fine("do not parse, because body is already complete");
            }
        }
    }
   
   
    /**
     * parse the body
     * @param rawData       the raw read network data buffer
     * @throws IOException if an exception occurs
     * @return true, if body is complete 
     */
    abstract boolean doParse(ByteBuffer[] rawData) throws IOException;

   
    void onException(IOException ioe, ByteBuffer[] rawData) {
        setException(ioe);
    }
   
   
    final void add(ByteBuffer buffer) throws IOException {
        append(buffer);
    }  
}
TOP

Related Classes of org.xlightweb.AbstractNetworkBodyDataSource

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.