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

com.sun.grizzly.async.AbstractAsyncQueueWriter Maven / Gradle / Ivy

There is a newer version: 10.0-b28
Show 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.
 *
 */
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.sun.grizzly.async;

import com.sun.grizzly.Controller;
import com.sun.grizzly.SelectorHandler;
import com.sun.grizzly.async.AsyncQueue.AsyncQueueEntry;
import com.sun.grizzly.util.ByteBufferFactory;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.WritableByteChannel;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;

/**
 *
 * @author oleksiys
 */
public abstract class AbstractAsyncQueueWriter implements AsyncQueueWriter {
    private SelectorHandler selectorHandler;
    private AsyncQueue writeQueue;
    private ConcurrentLinkedQueue recordQueue;
    
    public AbstractAsyncQueueWriter(SelectorHandler selectorHandler) {
        this.selectorHandler = selectorHandler;
        writeQueue = new AsyncQueue();
        recordQueue = new ConcurrentLinkedQueue();
    }

    /**
     * {@inheritDoc}
     */
    public void write(SelectionKey key, ByteBuffer buffer) throws IOException {
        write(key, null, buffer, null);
    }
    
    /**
     * {@inheritDoc}
     */
    public void write(SelectionKey key, ByteBuffer buffer, 
            AsyncWriteCallbackHandler callbackHandler) throws IOException {
        write(key, null, buffer, callbackHandler, null);
    }

    /**
     * {@inheritDoc}
     */
    public void write(SelectionKey key, ByteBuffer buffer, 
            AsyncWriteCallbackHandler callbackHandler, 
            AsyncQueueDataProcessor writePreProcessor) throws IOException {
        write(key, null, buffer, callbackHandler, writePreProcessor, false);
    }

    /**
     * {@inheritDoc}
     */
    public void write(SelectionKey key, ByteBuffer buffer, 
            AsyncWriteCallbackHandler callbackHandler, 
            AsyncQueueDataProcessor writePreProcessor, boolean isCloneByteBuffer)
            throws IOException {
        write(key, null, buffer, callbackHandler, writePreProcessor, isCloneByteBuffer);
    }

    /**
     * {@inheritDoc}
     */
    public void write(SelectionKey key, SocketAddress dstAddress,
            ByteBuffer buffer) throws IOException {
        write(key, dstAddress, buffer, null);
    }
    
    /**
     * {@inheritDoc}
     */
    public void write(SelectionKey key, SocketAddress dstAddress, 
            ByteBuffer buffer, AsyncWriteCallbackHandler callbackHandler) 
            throws IOException {
        write(key, dstAddress, buffer, callbackHandler, null);
    }

    /**
     * {@inheritDoc}
     */
    public void write(SelectionKey key, SocketAddress dstAddress, 
            ByteBuffer buffer, AsyncWriteCallbackHandler callbackHandler,
            AsyncQueueDataProcessor writePreProcessor) throws IOException {
        write(key, dstAddress, buffer, callbackHandler, writePreProcessor, false);
    }

    /**
     * {@inheritDoc}
     */
    public void write(SelectionKey key, SocketAddress dstAddress, 
            ByteBuffer buffer, AsyncWriteCallbackHandler callbackHandler,
            AsyncQueueDataProcessor writePreProcessor, boolean isCloneByteBuffer)
            throws IOException {
        
        if (key == null) {
            throw new IOException("SelectionKey is null! " +
                    "Probably key was cancelled or connection was closed?");
        }

        SelectableChannel channel = key.channel();
        AsyncQueueEntry channelEntry = 
                writeQueue.obtainAsyncQueueEntry(channel);
        
        ConcurrentLinkedQueue queue = channelEntry.queue;
        AtomicReference currentElement = channelEntry.currentElement;
        ReentrantLock lock = channelEntry.queuedActionLock;
        
        // If AsyncQueue is empty - try to write ByteBuffer here
        try {
            AsyncWriteQueueRecord record = null;

            if (currentElement.get() == null && // Weak comparison for null
                    lock.tryLock()) {
                record = obtainRecord();
                // Strong comparison for null, because we're in locked region
                if (currentElement.compareAndSet(null, record)) {
                    doWrite((WritableByteChannel) channel, dstAddress, buffer, 
                            writePreProcessor);
                } else {
                    lock.unlock();
                }
            }

            if (buffer.hasRemaining() || 
                    (lock.isHeldByCurrentThread() && writePreProcessor != null && 
                    writePreProcessor.getInternalByteBuffer().hasRemaining())) {
                if (record == null) {
                    record = obtainRecord();
                }
                
                // clone ByteBuffer if required
                if (isCloneByteBuffer) {
                    int size = buffer.remaining();
                    ByteBuffer newBuffer = ByteBufferFactory.allocateView(
                            size, buffer.isDirect());

                    newBuffer.put(buffer);
                    newBuffer.position(0);
                    buffer = newBuffer;
                }

                record.set(buffer, callbackHandler, writePreProcessor, dstAddress);

                boolean isRegisterForWriting = false;
                
                // add new element to the queue, if it's not current
                if (currentElement.get() != record) {
                    queue.offer(record); // add to queue
                    if (!lock.isLocked()) {
                        isRegisterForWriting = true;
                    }
                } else {  // if element was written direct (not fully written)
                    isRegisterForWriting = true;
                    lock.unlock();
                }
                
                if (isRegisterForWriting) {
                    registerForWriting(key);
                }
            } else { // If there are no bytes available for writing
                
                // Notify callback handler
                if (callbackHandler != null) {
                    callbackHandler.onWriteCompleted(key, buffer);
                }
                
                // If buffer was written directly - set next queue element as current
                if (lock.isHeldByCurrentThread()) {
                    AsyncWriteQueueRecord nextRecord = queue.poll();
                    if (nextRecord != null) { // if there is something in queue
                        currentElement.set(nextRecord); 
                        lock.unlock();
                        registerForWriting(key);
                    } else { // if nothing in queue
                        currentElement.set(null);
                        lock.unlock();  // unlock
                        if (queue.peek() != null) {  // check one more time
                            registerForWriting(key);
                        }
                    }
                }
                
                // Release record element
                if (record != null) {
                    recordQueue.offer(record);
                }
            }
        } catch(IOException e) {
            onClose(channel);
            throw e;
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public boolean hasReadyAsyncWriteData(SelectionKey key) {
        AsyncQueueEntry channelEntry = 
                writeQueue.getAsyncQueueEntry(key.channel());
        
        return channelEntry != null && (channelEntry.currentElement.get() != null || 
                (channelEntry.queue != null && !channelEntry.queue.isEmpty()));
    }
    
    /**
     * {@inheritDoc}
     */
    public void onWrite(SelectionKey key) throws IOException {
        SelectableChannel channel = key.channel();
        
        AsyncQueueEntry channelEntry = 
                writeQueue.obtainAsyncQueueEntry(channel);
        
        ConcurrentLinkedQueue queue = channelEntry.queue;
        AtomicReference currentElement = channelEntry.currentElement;
        ReentrantLock lock = channelEntry.queuedActionLock;

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

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

                ByteBuffer byteBuffer = queueRecord.byteBuffer;
                AsyncQueueDataProcessor writePreProcessor = queueRecord.writePreProcessor;
                try {
                    doWrite((WritableByteChannel) channel, 
                            queueRecord.dstAddress, byteBuffer, writePreProcessor);
                } catch (IOException e) {
                    if (queueRecord.callbackHandler != null) {
                        queueRecord.callbackHandler.onIOException(e, key,
                                byteBuffer, queue);
                    } else {
                        Controller.logger().log(Level.SEVERE,
                                "Exception occured when executing " +
                                "asynchronous queue writing", e);
                    }

                    onClose(channel);
                }

                // check if buffer was completely written
                if (!byteBuffer.hasRemaining() &&
                        (writePreProcessor == null ||
                        !writePreProcessor.getInternalByteBuffer().hasRemaining())) {
                    if (queueRecord.callbackHandler != null) {
                        queueRecord.callbackHandler.onWriteCompleted(key, byteBuffer);
                    }

                    currentElement.set(queue.poll());
                    recordQueue.offer(queueRecord);

                    // If last element in queue is null - we have to be careful
                    if (currentElement.get() == null) {
                        lock.unlock();
                        AsyncWriteQueueRecord nextRecord = queue.peek();
                        if (nextRecord != null && lock.tryLock()) {
                            if (!queue.isEmpty() && 
                                    currentElement.compareAndSet(null, nextRecord)) {
                                queue.remove();
                            }
                            
                            continue;
                        } else {
                            break;
                        }
                    }
                } else { // if there is still some data in current buffer
                    lock.unlock();
                    registerForWriting(key);
                    break;
                }
            }
        } finally {
            if (lock.isHeldByCurrentThread()) {
                channelEntry.queuedActionLock.unlock();
            }
        }
    }
    
    /**
     * {@inheritDoc}
     */
    public void onClose(SelectableChannel channel) {
        writeQueue.removeEntry(channel);
    }
    
    /**
     * {@inheritDoc}
     */
    public void close() {
        writeQueue.clear();
    }
    
    protected void doWrite(WritableByteChannel channel, SocketAddress dstAddress,
            ByteBuffer byteBuffer, AsyncQueueDataProcessor writePreProcessor)
            throws IOException {
        if (writePreProcessor != null) {
            ByteBuffer resultByteBuffer = null;
            do {
                if (byteBuffer.hasRemaining()) {
                    writePreProcessor.process(byteBuffer);
                }
                
                resultByteBuffer = writePreProcessor.getInternalByteBuffer();
                doWrite(channel, dstAddress, resultByteBuffer);
            } while(byteBuffer.hasRemaining() && 
                    !resultByteBuffer.hasRemaining());
        } else {
            doWrite(channel, dstAddress, byteBuffer);
        }
    }

    protected abstract void doWrite(WritableByteChannel channel, SocketAddress dstAddress,
            ByteBuffer byteBuffer) throws IOException;
    
    protected void registerForWriting(SelectionKey key) {
        selectorHandler.register(key, SelectionKey.OP_WRITE);
    }
    
    private AsyncWriteQueueRecord obtainRecord() {
        AsyncWriteQueueRecord record = recordQueue.poll();
        if (record == null) {
            record = new AsyncWriteQueueRecord();
        }
        
        return record;
    }    
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy