
org.simpleframework.transport.SocketFlusher Maven / Gradle / Ivy
/*
* SocketFlusher.java February 2007
*
* Copyright (C) 2007, Niall Gallagher
*
* 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.
*
* 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
*/
package org.simpleframework.transport;
import java.io.IOException;
import java.nio.channels.SocketChannel;
import org.simpleframework.transport.reactor.Operation;
import org.simpleframework.transport.reactor.Reactor;
/**
* The SocketFlusher
flushes bytes to the underlying
* socket channel. This allows asynchronous writes to the socket
* to be managed in such a way that there is order to the way data
* is delivered over the socket. This uses a selector to dispatch
* flush invocations to the underlying socket when the socket is
* write ready. This allows the writing thread to continue without
* having to wait for all the data to be written to the socket.
*
* @author Niall Gallagher
*
* @see org.simpleframework.transport.Controller
*/
class SocketFlusher implements Flusher {
/**
* This is the signaller used to determine when to flush.
*/
private Signaller signaller;
/**
* This is the scheduler used to block and signal the writer.
*/
private Scheduler scheduler;
/**
* This is the writer used to queue the packets written.
*/
private Writer writer;
/**
* This is used to determine if the socket flusher is closed.
*/
private boolean closed;
/**
* Constructor for the SocketFlusher
object. This is
* used to flush buffers to the underlying socket asynchronously.
* When finished flushing all of the buffered data this signals
* any threads that are blocking waiting for the write to finish.
*
* @param reactor this is used to perform asynchronous writes
* @param writer this is used to write the buffered packets
*/
public SocketFlusher(Reactor reactor, Writer writer) throws IOException {
this.signaller = new Signaller(writer);
this.scheduler = new Scheduler(reactor, signaller, this);
this.writer = writer;
}
/**
* Here in this method we schedule a flush when the underlying
* writer is write ready. This allows the writer thread to return
* without having to fully flush the content to the underlying
* transport. If there are references queued this will block.
*/
public synchronized void flush() throws IOException {
if(closed) {
throw new TransportException("Flusher is closed");
}
boolean block = writer.isBlocking();
if(!closed) {
scheduler.schedule(block);
}
}
/**
* This is executed when the flusher is to write all of the data to
* the underlying socket. In this situation the writes are attempted
* in a non blocking way, if the task does not complete then this
* will simply enqueue the writing task for OP_WRITE and leave the
* method. This returns true if all the buffers are written.
*/
private synchronized void execute() throws IOException {
boolean ready = writer.flush();
if(!ready) {
boolean block = writer.isBlocking();
if(!block && !closed) {
scheduler.release();
}
scheduler.repeat();
} else{
scheduler.ready();
}
}
/**
* This is used to abort the flushing process when the reactor has
* been stopped. An abort to the flusher typically happens when the
* server has been shutdown. It prevents threads lingering waiting
* for a I/O operation which prevents the server from shutting down.
*/
private synchronized void abort() throws IOException {
scheduler.close();
writer.close();
}
/**
* This is used to close the flusher ensuring that all of the
* data within the writer will be flushed regardless of the
* amount of data within the writer that needs to be written. If
* the writer does not block then this waits to be finished.
*/
public synchronized void close() throws IOException {
boolean ready = writer.flush();
if(!closed) {
closed = true;
}
if(!ready) {
scheduler.schedule(true);
}
}
/**
* The Signaller
is an operation that performs the
* write operation asynchronously. This will basically determine
* if the socket is write ready and drain each queued buffer to
* the socket until there are no more pending buffers.
*
* @author Niall Gallagher
*/
private class Signaller implements Operation {
/**
* This is the writer that is used to write the data.
*/
private final Writer writer;
/**
* Constructor for the Signaller
object. This will
* create an operation that is used to flush the packet queue
* to the underlying socket. This ensures that the data is
* written to the socket in the queued order.
*
* @param writer this is the writer to flush the data to
*/
public Signaller(Writer writer) {
this.writer = writer;
}
/**
* This returns the socket channel for the connected pipeline. It
* is this channel that is used to determine if there are bytes
* that can be written. When closed this is no longer selectable.
*
* @return this returns the connected channel for the pipeline
*/
public SocketChannel getChannel() {
return writer.getChannel();
}
/**
* This is used to perform the drain of the pending buffer
* queue. This will drain each pending queue if the socket is
* write ready. If the socket is not write ready the operation
* is enqueued for selection and this returns. This ensures
* that all the data will eventually be delivered.
*/
public void run() {
try {
execute();
} catch(Exception e) {
cancel();
}
}
/**
* This is used to cancel the operation if it has timed out.
* If the delegate is waiting too long to flush the contents
* of the buffers to the underlying transport then the socket
* is closed and the flusher times out to avoid deadlock.
*/
public void cancel() {
try {
abort();
}catch(Exception e){
return;
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy