All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.grizzly.nio.AbstractNIOAsyncQueueReader Maven / Gradle / Ivy

The newest version!
/*
 * 
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 *
 */

package com.sun.grizzly.nio;

import java.io.EOFException;
import java.io.IOException;
import java.net.SocketAddress;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.grizzly.AbstractReader;
import com.sun.grizzly.Buffer;
import com.sun.grizzly.CompletionHandler;
import com.sun.grizzly.Connection;
import com.sun.grizzly.IOEvent;
import com.sun.grizzly.Context;
import com.sun.grizzly.Grizzly;
import com.sun.grizzly.Interceptor;
import com.sun.grizzly.ProcessorResult;
import com.sun.grizzly.ReadResult;
import com.sun.grizzly.Reader;
import com.sun.grizzly.asyncqueue.AsyncQueue;
import com.sun.grizzly.asyncqueue.AsyncQueueProcessor;
import com.sun.grizzly.asyncqueue.AsyncQueueReader;
import com.sun.grizzly.asyncqueue.AsyncReadQueueRecord;
import com.sun.grizzly.impl.FutureImpl;
import com.sun.grizzly.utils.LinkedTransferQueue;
import com.sun.grizzly.utils.ObjectPool;

/**
 * The {@link AsyncQueueReader} implementation, based on the Java NIO
 * 
 * @author Alexey Stashok
 */
public abstract class AbstractNIOAsyncQueueReader
        extends AbstractReader
        implements AsyncQueueReader {

    public static final int DEFAULT_BUFFER_SIZE = 8192;

    protected int defaultBufferSize = DEFAULT_BUFFER_SIZE;

    protected NIOTransport transport;
    private Logger logger = Grizzly.logger;
    
    public AbstractNIOAsyncQueueReader(NIOTransport transport) {
        this.transport = transport;

    }

    /**
     * {@inheritDoc}
     */
    public Future> read(
            final Connection connection,
            final Buffer buffer,
            final CompletionHandler> completionHandler,
            final Interceptor interceptor)
            throws IOException {
        
        if (connection == null) {
            throw new IOException("Connection is null");
        } else if (!connection.isOpen()) {
            throw new IOException("Connection is closed");
        }

        int finalInterceptorEvent;

        // Create future
        final FutureImpl> future =
                new FutureImpl>();
        final ReadResult currentResult = new ReadResult(connection);
        currentResult.setMessage(null);
        currentResult.setReadSize(0);

        // Get connection async read queue
        final AsyncQueue connectionQueue =
                ((AbstractNIOConnection) connection).getAsyncReadQueue();

        final LinkedTransferQueue queue =
                connectionQueue.getQueue();
        final AtomicReference currentElement =
                connectionQueue.getCurrentElement();
        final ReentrantLock lock = connectionQueue.getQueuedActionLock();
        boolean isLockedByMe = false;

        // create and initialize the read queue record
        final AsyncReadQueueRecord queueRecord = new AsyncReadQueueRecord();
        queueRecord.set(buffer, future, currentResult, completionHandler,
                interceptor);

        // If AsyncQueue is empty - try to read Buffer here
        try {

            if (currentElement.get() == null && // Weak comparison for null
                    lock.tryLock()) {
                isLockedByMe = true;
                // Strong comparison for null, because we're in locked region
                if (currentElement.compareAndSet(null, queueRecord)) {
                    doRead(connection, currentResult, buffer);
                } else {
                    isLockedByMe = false;
                    lock.unlock();
                }
            }

            final int interceptInstructions = intercept(connection,
                    Reader.READ_EVENT, queueRecord,
                    currentResult);

            final boolean registerForReadingInstr = interceptor == null ||
                    (interceptInstructions & AsyncQueueProcessor.NOT_REGISTER_KEY) == 0;

            if ((interceptInstructions & Interceptor.COMPLETED) != 0 ||
                    (interceptor == null && isFinished(currentResult))) {
                // If message was written directly - set next queue element as current
                if (isLockedByMe) {
                    AsyncReadQueueRecord nextRecord = queue.poll();
                    if (nextRecord != null) { // if there is something in queue
                        currentElement.set(nextRecord);
                        lock.unlock();
                        isLockedByMe = false;
                        if (registerForReadingInstr) {
                            onReadyToRead(connection);
                        }
                    } else { // if nothing in queue
                        currentElement.set(null);
                        lock.unlock();  // unlock
                        isLockedByMe = false;
                        if (registerForReadingInstr &&
                                queue.peek() != null) {  // check one more time
                            onReadyToRead(connection);
                        }
                    }
                }

                // Notify callback handler
                onReadCompleted(connection, queueRecord);

                finalInterceptorEvent = Reader.COMPLETE_EVENT;
            } else { // If there are no bytes available for writing
                if ((interceptInstructions & Interceptor.RESET) != 0) {
                    queueRecord.setCurrentResult(new ReadResult(connection));
                    queueRecord.setBuffer(null);
                }

                boolean isRegisterForReading = false;

                // add new element to the queue, if it's not current
                if (currentElement.get() != queueRecord) {
                    queue.offer(queueRecord); // add to queue
                    if (!lock.isLocked()) {
                        isRegisterForReading = true;
                    }
                } else {  // if element was written direct (not fully written)
                    onReadIncompleted(connection, queueRecord);
                    isRegisterForReading = true;
                    if (isLockedByMe) {
                        isLockedByMe = false;
                        lock.unlock();
                    }
                }

                if (registerForReadingInstr && isRegisterForReading) {
                    onReadyToRead(connection);
                }

                finalInterceptorEvent = INCOMPLETE_EVENT;
            }
        } catch(IOException e) {
            onReadFailure(connection, queueRecord, e);
            throw e;
        } finally {
            if (isLockedByMe) {
                lock.unlock();
            }
        }

        intercept(connection, finalInterceptorEvent, queueRecord, null);
        return future;
    }
    
    /**
     * {@inheritDoc}
     */
    public boolean isReady(final Connection connection) {
        AsyncQueue connectionQueue =
                ((AbstractNIOConnection) connection).getAsyncReadQueue();

        return connectionQueue != null &&
                (connectionQueue.getCurrentElement().get() != null ||
                (connectionQueue.getQueue() != null &&
                !connectionQueue.getQueue().isEmpty()));
    }

    /**
     * {@inheritDoc}
     */
    public void processAsync(final Connection connection) throws IOException {
        final AsyncQueue connectionQueue =
                ((AbstractNIOConnection) connection).getAsyncReadQueue();

        final LinkedTransferQueue queue =
                connectionQueue.getQueue();
        final AtomicReference currentElement =
                connectionQueue.getCurrentElement();
        final ReentrantLock lock = connectionQueue.getQueuedActionLock();
        boolean isLockedByMe = false;

        if (currentElement.get() == null) {
            AsyncReadQueueRecord nextRecord = queue.peek();
            if (nextRecord != null && lock.tryLock()) {
                if (!queue.isEmpty() &&
                        currentElement.compareAndSet(null, nextRecord)) {
                    queue.remove();
                }
            } else {
                return;
            }
        } else if (!lock.tryLock()) {
            return;
        }
        isLockedByMe = true;

        int finalInterceptorEvent = Reader.COMPLETE_EVENT;

        AsyncReadQueueRecord queueRecord = null;
        try {
            while (currentElement.get() != null) {
                queueRecord = currentElement.get();

                final ReadResult currentResult = queueRecord.getCurrentResult();
                final Buffer message = queueRecord.getBuffer();
                doRead(connection, currentResult, message);

                final Interceptor interceptor =
                        queueRecord.getInterceptor();
                // check if message was completely read
                final int interceptInstructions = intercept(connection,
                        Reader.READ_EVENT, queueRecord,
                        currentResult);
                
                final boolean registerForReadingInstr = interceptor == null ||
                        (interceptInstructions & AsyncQueueProcessor.NOT_REGISTER_KEY) == 0;

                if ((interceptInstructions & Interceptor.COMPLETED) != 0 ||
                        (interceptor == null && isFinished(currentResult))) {
                    currentElement.set(queue.poll());
                    onReadCompleted(connection, queueRecord);

                    intercept(connection, Reader.COMPLETE_EVENT,
                            queueRecord, null);

                    // If last element in queue is null - we have to be careful
                    if (currentElement.get() == null) {
                        if (isLockedByMe) {
                            isLockedByMe = false;
                            lock.unlock();
                        }
                        AsyncReadQueueRecord nextRecord = queue.peek();
                        if (nextRecord != null && lock.tryLock()) {
                            isLockedByMe = true;
                            if (!queue.isEmpty() &&
                                    currentElement.compareAndSet(null, nextRecord)) {
                                queue.remove();
                            }
                            
                            continue;
                        } else {
                            break;
                        }
                    }
                } else { // if there is still some data in current message
                    if ((interceptInstructions & Interceptor.RESET) != 0) {
                        queueRecord.setCurrentResult(new ReadResult(connection));
                        queueRecord.setBuffer(null);
                    }

                    onReadIncompleted(connection, queueRecord);

                    if (isLockedByMe) {
                        isLockedByMe = false;
                        lock.unlock();
                    }
                    
                    if (registerForReadingInstr) {
                        onReadyToRead(connection);
                    }

                    finalInterceptorEvent = Reader.INCOMPLETE_EVENT;
                    break;
                }
            }
        } catch (IOException e) {
            onReadFailure(connection, queueRecord, e);
        } catch (Exception e) {
            String message = "Unexpected exception occurred in AsyncQueueReader";
            logger.log(Level.SEVERE, message, e);
            IOException ioe = new IOException(e.getClass() + ": " + message);
            onReadFailure(connection, queueRecord, ioe);
        } finally {
            if (isLockedByMe) {
                connectionQueue.getQueuedActionLock().unlock();
            }
        }

        if (finalInterceptorEvent == Reader.INCOMPLETE_EVENT) {
            intercept(connection, finalInterceptorEvent, queueRecord, null);
        }
    }

    /**
     * {@inheritDoc}
     */
    public void onClose(Connection connection) {
        AbstractNIOConnection nioConnection = (AbstractNIOConnection) connection;
        AsyncQueue readQueue =
                nioConnection.getAsyncReadQueue();
        if (readQueue != null) {
            readQueue.getQueuedActionLock().lock();
            try {
                AsyncReadQueueRecord record =
                        readQueue.getCurrentElement().getAndSet(null);

                failReadRecord(connection, record,
                        new IOException("Connection closed"));

                LinkedTransferQueue recordsQueue =
                        readQueue.getQueue();
                if (recordsQueue != null) {
                    while(!recordsQueue.isEmpty()) {
                        failReadRecord(connection, recordsQueue.poll(),
                                new IOException("Connection closed"));
                    }
                }
            } finally {
                readQueue.getQueuedActionLock().unlock();
            }
        }
    }
    
    /**
     * {@inheritDoc}
     */
    public ObjectPool getContextPool() {
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public boolean isInterested(IOEvent ioEvent) {
        return ioEvent == IOEvent.READ;
    }

    /**
     * {@inheritDoc}
     */
    public ProcessorResult process(Context context)
            throws IOException {
        processAsync(context.getConnection());
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public void setInterested(IOEvent ioEvent, boolean isInterested) {
    }

    /**
     * {@inheritDoc}
     */
    public void close() {
    }

    /**
     * Performs real read on the NIO channel
     * 
     * @param connection the {@link Connection} to read from
     * @param readFuture the asynchronous operation result holder
     * @param message the message to read to
     * @throws java.io.IOException
     */
    protected int doRead(final Connection connection,
            final ReadResult currentResult, final Buffer message)
            throws IOException {
        
        final Buffer buffer = (Buffer) message;
        final int readBytes = read0(connection, buffer, currentResult);
        if (readBytes == -1) {
            throw new EOFException();
        }

        return readBytes;
    }


    protected void onReadCompleted(Connection connection,
            AsyncReadQueueRecord record)
            throws IOException {

        FutureImpl future = (FutureImpl) record.getFuture();
        ReadResult currentResult = record.getCurrentResult();
        future.setResult(currentResult);
        CompletionHandler completionHandler =
                record.getCompletionHandler();

        if (completionHandler != null) {
            completionHandler.completed(connection, currentResult);
        }
    }

    protected void onReadIncompleted(Connection connection,
            AsyncReadQueueRecord record)
            throws IOException {

        ReadResult currentResult = record.getCurrentResult();
        CompletionHandler completionHandler =
                record.getCompletionHandler();

        if (completionHandler != null) {
            completionHandler.updated(connection, currentResult);
        }
    }

    protected void onReadFailure(Connection connection,
            AsyncReadQueueRecord failedRecord, IOException e) {

        failReadRecord(connection, failedRecord, e);
        try {
            connection.close();
        } catch (IOException ioe) {
        }
    }

    protected void failReadRecord(Connection connection,
            AsyncReadQueueRecord record, IOException e) {
        if (record == null) return;

        FutureImpl future = (FutureImpl) record.getFuture();
        if (!future.isDone()) {
            CompletionHandler completionHandler =
                    record.getCompletionHandler();

            if (completionHandler != null) {
                completionHandler.failed(connection, e);
            }

            future.failure(e);
        }
    }

    private int intercept(Connection connection, int event,
            AsyncReadQueueRecord asyncQueueRecord, ReadResult currentResult) {
        Interceptor interceptor = asyncQueueRecord.getInterceptor();
        if (interceptor != null) {
            return interceptor.intercept(event, asyncQueueRecord, currentResult);
        }

        return Interceptor.DEFAULT;
    }

    private  boolean isFinished(ReadResult readResult) {
        E message = readResult.getMessage();
        return readResult.getReadSize() > 0 ||
                !((Buffer) message).hasRemaining();
    }

    protected abstract int read0(Connection connection, Buffer buffer,
            ReadResult currentResult)throws IOException;

    protected abstract void onReadyToRead(Connection connection)
            throws IOException;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy