package net.sf.colossus.webserver;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Date;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Logger;
class QueuedSocketWriter extends Thread
{
static final Logger LOGGER = Logger.getLogger(QueuedSocketWriter.class
.getName());
private final static String MSG_EXIT_LOOP = "_EXIT_LOOP";
private final static String MSG_FLUSH_MSGS = "_FLUSH_MESSAGES";
/**
* The actual queue holding all messages that need to be sent.
* This is a concurrent-safe queue.
*/
private final LinkedBlockingQueue<String> queue;
/**
* The actual writer object which will send printed data over the socket.
*/
PrintWriter out;
/**
* Sending thread that requests the flushing, waits on this mutex until
* notified that the flush was completed (boolean 'flushed' below set to true).
*/
private final Object flushMutex = new Object();
/**
* Set to true when flushing is completed.
*/
private boolean flushed;
private boolean done = false;
private static int instanceIdCounter = 0;
private final int instanceId;
public QueuedSocketWriter(Socket socket) throws IOException
{
this.out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream())), true);
this.queue = new LinkedBlockingQueue<String>();
this.instanceId = ++instanceIdCounter;
LOGGER.info("QueuedSocketWriter #" + instanceId + " created.");
}
@Override
public void run()
{
while (!done)
{
String message = readNextFromQueue();
if (message == null)
{
LOGGER.warning("Got null message from queue?");
continue;
}
if (message.equals(MSG_EXIT_LOOP))
{
LOGGER.info("QueuedSocketWriter #" + instanceId
+ ": Got MSG_EXIT_LOOP.");
break;
}
if (message.equals(MSG_FLUSH_MSGS))
{
LOGGER.info("QueuedSocketWriter #" + instanceId
+ ": reached the FLUSH marker.");
synchronized (flushMutex)
{
flushed = true;
LOGGER.info("QueuedSocketWriter #" + instanceId
+ ": notifying flusher.");
flushMutex.notify();
}
}
else
{
long sentTime = new Date().getTime();
out.println(message);
long spentTime = new Date().getTime() - sentTime;
String logMsg = "QueuedSocketWriter #" + instanceId
+ ": actual writing took " + spentTime
+ " ms for message: " + message;
if (spentTime > 500)
{
LOGGER.info("NOTE: " + logMsg);
}
else
{
// LOGGER.fine(logMsg);
}
}
}
LOGGER.info("QueuedSocketWriter #" + instanceId + " after main loop.");
}
/**
* Enqueues a flush marker and waits on the mutex until the flushing of all
* messages enqueued prior to the marker have been sent.
* (this does at the moment not imply that the client has received them
* (not to mention even has processed them).
*/
public void flushMessages()
{
synchronized (flushMutex)
{
sendMessage(MSG_FLUSH_MSGS);
try
{
flushed = false;
while (!flushed)
{
flushMutex.wait();
}
}
catch (InterruptedException e)
{
LOGGER.warning("flushMessages waiting interrupted?");
}
}
}
public void stopWriter()
{
done = true;
sendMessage(MSG_EXIT_LOOP);
}
public void sendMessage(String message)
{
queue.add(message);
}
/**
* We use no timeout while waiting for next message in the queue.
* To get it out of the loop, we enqueue a special marker (MSG_EXIT_LOOP).
* @return String containing the next message to write.
*/
private String readNextFromQueue()
{
try
{
String message = queue.take();
return message;
}
catch (InterruptedException e)
{
LOGGER.warning("queue.poll interrupted!");
}
return null;
}
}