com.sun.grizzly.nio.AbstractNIOAsyncQueueWriter Maven / Gradle / Ivy
/*
*
* 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.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.Logger;
import com.sun.grizzly.AbstractWriter;
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.WriteResult;
import com.sun.grizzly.asyncqueue.AsyncQueue;
import com.sun.grizzly.asyncqueue.AsyncQueueWriter;
import com.sun.grizzly.asyncqueue.AsyncWriteQueueRecord;
import com.sun.grizzly.asyncqueue.MessageCloner;
import com.sun.grizzly.impl.FutureImpl;
import com.sun.grizzly.utils.LinkedTransferQueue;
import com.sun.grizzly.utils.ObjectPool;
/**
* The {@link AsyncQueueWriter} implementation, based on the Java NIO
*
* @author Alexey Stashok
*/
public abstract class AbstractNIOAsyncQueueWriter
extends AbstractWriter
implements AsyncQueueWriter {
protected NIOTransport transport;
private final static Logger logger = Grizzly.logger;
public AbstractNIOAsyncQueueWriter(NIOTransport transport) {
this.transport = transport;
}
/**
* {@inheritDoc}
*/
public Future> write(
Connection connection, SocketAddress dstAddress,
Buffer buffer,
CompletionHandler> completionHandler,
Interceptor interceptor) throws IOException {
return write(connection, dstAddress, buffer, completionHandler,
interceptor, null);
}
/**
* {@inheritDoc}
*/
public Future> write(
Connection connection, SocketAddress dstAddress,
Buffer buffer,
CompletionHandler> completionHandler,
Interceptor interceptor,
MessageCloner cloner) throws IOException {
if (connection == null) {
throw new IOException("Connection is null");
} else if (!connection.isOpen()) {
throw new IOException("Connection is closed");
}
// Create future
FutureImpl future = new FutureImpl();
WriteResult currentResult = new WriteResult(connection);
currentResult.setMessage(buffer);
currentResult.setDstAddress(dstAddress);
currentResult.setWrittenSize(0);
// Get connection async write queue
AsyncQueue connectionQueue =
((AbstractNIOConnection) connection).getAsyncWriteQueue();
LinkedTransferQueue queue =
connectionQueue.getQueue();
AtomicReference currentElement =
connectionQueue.getCurrentElement();
ReentrantLock lock = connectionQueue.getQueuedActionLock();
boolean isLockedByMe = false;
// create and initialize the write queue record
AsyncWriteQueueRecord queueRecord = new AsyncWriteQueueRecord();
queueRecord.set(buffer, future, currentResult, completionHandler,
interceptor, dstAddress);
// If AsyncQueue is empty - try to write 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)) {
doWrite(connection, currentResult, completionHandler,
(SocketAddress) dstAddress, buffer);
} else {
isLockedByMe = false;
lock.unlock();
}
}
if (isLockedByMe && isFinished(connection, buffer)) {
// If buffer was written directly - set next queue element as current
// Notify callback handler
onWriteCompleted(connection, queueRecord);
AsyncWriteQueueRecord nextRecord = queue.poll();
if (nextRecord != null) { // if there is something in queue
currentElement.set(nextRecord);
isLockedByMe = false;
lock.unlock();
onReadyToWrite(connection);
} else { // if nothing in queue
currentElement.set(null);
isLockedByMe = false;
lock.unlock(); // unlock
if (queue.peek() != null) { // check one more time
onReadyToWrite(connection);
}
}
} else { // If there are no bytes available for writing
if (cloner != null) {
// clone message
buffer = cloner.clone(connection, buffer);
queueRecord.setBuffer(buffer);
queueRecord.setCloned(true);
}
boolean isRegisterForWriting = 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()) {
isRegisterForWriting = true;
}
} else { // if element was written direct (not fully written)
isRegisterForWriting = true;
if (isLockedByMe) {
isLockedByMe = false;
lock.unlock();
}
}
if (isRegisterForWriting) {
onReadyToWrite(connection);
}
}
} catch (IOException e) {
onWriteFailure(connection, queueRecord, e);
throw e;
} finally {
if (isLockedByMe) {
lock.unlock();
}
}
return future;
}
/**
* {@inheritDoc}
*/
public boolean isReady(Connection connection) {
AsyncQueue connectionQueue =
((AbstractNIOConnection) connection).getAsyncWriteQueue();
return connectionQueue != null &&
(connectionQueue.getCurrentElement().get() != null ||
(connectionQueue.getQueue() != null &&
!connectionQueue.getQueue().isEmpty()));
}
/**
* {@inheritDoc}
*/
public void processAsync(Connection connection) throws IOException {
AsyncQueue connectionQueue =
((AbstractNIOConnection) connection).getAsyncWriteQueue();
LinkedTransferQueue queue =
connectionQueue.getQueue();
AtomicReference currentElement =
connectionQueue.getCurrentElement();
ReentrantLock lock = connectionQueue.getQueuedActionLock();
boolean isLockedByMe = false;
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;
}
isLockedByMe = true;
AsyncWriteQueueRecord queueRecord = null;
try {
while (currentElement.get() != null) {
queueRecord = currentElement.get();
WriteResult currentResult = queueRecord.getCurrentResult();
Buffer buffer = queueRecord.getBuffer();
doWrite(connection, currentResult, queueRecord.getCompletionHandler(),
(SocketAddress) queueRecord.getDstAddress(),
buffer);
// check if buffer was completely written
if (isFinished(connection, buffer)) {
currentElement.set(queue.poll());
onWriteCompleted(connection, queueRecord);
// If last element in queue is null - we have to be careful
if (currentElement.get() == null) {
if (isLockedByMe) {
isLockedByMe = false;
lock.unlock();
}
AsyncWriteQueueRecord 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 (isLockedByMe) {
isLockedByMe = false;
lock.unlock();
}
onReadyToWrite(connection);
break;
}
}
} catch (IOException e) {
onWriteFailure(connection, queueRecord, e);
} finally {
if (isLockedByMe) {
connectionQueue.getQueuedActionLock().unlock();
}
}
}
/**
* {@inheritDoc}
*/
public void onClose(Connection connection) {
AbstractNIOConnection nioConnection =
(AbstractNIOConnection) connection;
AsyncQueue writeQueue =
nioConnection.getAsyncWriteQueue();
if (writeQueue != null) {
writeQueue.getQueuedActionLock().lock();
try {
AsyncWriteQueueRecord record =
writeQueue.getCurrentElement().getAndSet(null);
Throwable error = new IOException("Connection closed");
failWriteRecord(connection, record, error);
LinkedTransferQueue recordsQueue =
writeQueue.getQueue();
if (recordsQueue != null) {
while (!recordsQueue.isEmpty()) {
failWriteRecord(connection, recordsQueue.poll(),
error);
}
}
} finally {
writeQueue.getQueuedActionLock().unlock();
}
}
}
/**
* {@inheritDoc}
*/
public ObjectPool getContextPool() {
return null;
}
/**
* {@inheritDoc}
*/
public boolean isInterested(IOEvent ioEvent) {
return ioEvent == IOEvent.WRITE;
}
/**
* {@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 write on the NIO channel
* @param connection the {@link Connection} to write to
* @param currentResult current result of the write operation
* @param dstAddress destination address
* @param message the message to write
* @param writePreProcessor write post-processor
* @throws java.io.IOException
*/
protected void doWrite(Connection connection, WriteResult currentResult,
CompletionHandler completionHandler,
SocketAddress dstAddress, Buffer buffer)
throws IOException {
write0(connection, dstAddress, buffer, currentResult);
}
protected void onWriteCompleted(Connection connection,
AsyncWriteQueueRecord> record)
throws IOException {
FutureImpl future = (FutureImpl) record.getFuture();
WriteResult currentResult = record.getCurrentResult();
future.setResult(currentResult);
CompletionHandler completionHandler =
record.getCompletionHandler();
if (completionHandler != null) {
completionHandler.completed(connection, currentResult);
}
}
protected void onWriteIncompleted(Connection connection,
AsyncWriteQueueRecord> record)
throws IOException {
WriteResult currentResult = record.getCurrentResult();
CompletionHandler completionHandler =
record.getCompletionHandler();
if (completionHandler != null) {
completionHandler.updated(connection, currentResult);
}
}
protected void onWriteFailure(Connection connection,
AsyncWriteQueueRecord failedRecord, IOException e) {
failWriteRecord(connection, failedRecord, e);
try {
connection.close();
} catch (IOException ioe) {
}
}
protected void failWriteRecord(Connection connection,
AsyncWriteQueueRecord record, Throwable 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 boolean isFinished(Connection connection, Buffer originalBuffer) {
return !originalBuffer.hasRemaining();
}
protected abstract int write0(Connection connection,
SocketAddress dstAddress, Buffer buffer,
WriteResult currentResult)
throws IOException;
protected abstract void onReadyToWrite(Connection connection)
throws IOException;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy