/*
* This file is part of FFMQ.
*
* FFMQ 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; either version 2 of the License, or
* (at your option) any later version.
*
* FFMQ 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 FFMQ; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.timewalker.ffmq3.admin;
import java.util.Enumeration;
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.QueueConnection;
import javax.jms.QueueReceiver;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import net.timewalker.ffmq3.FFMQAdminConstants;
import net.timewalker.ffmq3.FFMQConstants;
import net.timewalker.ffmq3.FFMQException;
import net.timewalker.ffmq3.FFMQServer;
import net.timewalker.ffmq3.local.FFMQEngine;
import net.timewalker.ffmq3.local.connection.LocalQueueConnection;
import net.timewalker.ffmq3.local.destination.LocalQueue;
import net.timewalker.ffmq3.management.destination.definition.QueueDefinition;
import net.timewalker.ffmq3.management.destination.definition.TopicDefinition;
import net.timewalker.ffmq3.utils.ErrorTools;
import net.timewalker.ffmq3.utils.Settings;
import net.timewalker.ffmq3.utils.StringTools;
import net.timewalker.ffmq3.utils.concurrent.SynchronizableThread;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* AdministrationThread
*/
public final class RemoteAdministrationThread extends SynchronizableThread
{
private static final Log log = LogFactory.getLog(RemoteAdministrationThread.class);
protected FFMQServer server;
private FFMQEngine engine;
private QueueConnection conn = null;
private QueueSession session = null;
private QueueSender sender = null;
private QueueReceiver receiver = null;
private boolean stopRequired = false;
/**
* Constructor
*/
public RemoteAdministrationThread(FFMQServer server,FFMQEngine engine)
{
super("FFMQ-RemoteAdminThread");
this.server = server;
this.engine = engine;
}
/*
* (non-Javadoc)
* @see net.timewalker.ffmq3.utils.concurrent.SynchronizableThread#run()
*/
public void run()
{
log.info("Starting remote administration thread ...");
try
{
LocalQueue inputQueue = engine.getLocalQueue(FFMQConstants.ADM_REQUEST_QUEUE);
LocalQueue outputQueue = engine.getLocalQueue(FFMQConstants.ADM_REPLY_QUEUE);
conn = new LocalQueueConnection(engine, null, null);
session = conn.createQueueSession(true, Session.SESSION_TRANSACTED);
receiver = session.createReceiver(inputQueue);
sender = session.createSender(outputQueue);
conn.start();
// Flush input queue on startup
inputQueue.purge(null);
outputQueue.purge(null);
// Enter listening loop
notifyStartup();
while (!stopRequired)
{
Message message = receiver.receive();
if (message == null)
break; // Interrupted
log.debug("Received message " + message);
try
{
// Process the command
String errorMsg = process(message);
// Build response message
Message response = session.createMessage();
response.setJMSCorrelationID(message.getJMSMessageID());
if (errorMsg != null)
response.setStringProperty(FFMQAdminConstants.ADM_HEADER_ERRMSG, errorMsg);
sender.send(response,DeliveryMode.NON_PERSISTENT,Message.DEFAULT_PRIORITY,Message.DEFAULT_TIME_TO_LIVE);
}
catch (JMSException e)
{
log.error("Cannot process admin command",e);
}
finally
{
session.commit();
}
}
log.debug("Remote administration thread has stopped");
}
catch (Throwable e)
{
log.fatal("Administration thread failed", e);
notifyStartup();
}
finally
{
try
{
if (sender != null)
sender.close();
}
catch (JMSException e)
{
ErrorTools.log(e, log);
}
try
{
if (receiver != null)
receiver.close();
}
catch (JMSException e)
{
ErrorTools.log(e, log);
}
try
{
if (session != null)
session.close();
}
catch (JMSException e)
{
ErrorTools.log(e, log);
}
try
{
if (conn != null)
conn.close();
}
catch (JMSException e)
{
ErrorTools.log(e, log);
}
}
}
private String process(Message msg)
{
try
{
String command = msg.getStringProperty(FFMQAdminConstants.ADM_HEADER_COMMAND);
if (StringTools.isEmpty(command))
return "Administration command not set in message header";
// Dispatch
if (command.equals(FFMQAdminConstants.ADM_COMMAND_CREATE_QUEUE))
return processCreateQueue(msg);
if (command.equals(FFMQAdminConstants.ADM_COMMAND_CREATE_TOPIC))
return processCreateTopic(msg);
if (command.equals(FFMQAdminConstants.ADM_COMMAND_DELETE_QUEUE))
return processDeleteQueue(msg);
if (command.equals(FFMQAdminConstants.ADM_COMMAND_DELETE_TOPIC))
return processDeleteTopic(msg);
if (command.equals(FFMQAdminConstants.ADM_COMMAND_SHUTDOWN))
return processShutdown(msg);
return "Invalid administration command : "+command;
}
catch (JMSException e)
{
ErrorTools.log(e, log);
return "Error processing administration command : "+e.getMessage();
}
catch (Exception e)
{
ErrorTools.log(new FFMQException("Cannot process admin message","INVALID_ADMIN_MESSAGE",e), log);
return "Error processing administration command : "+e.getMessage();
}
}
private Settings createSettings( Message msg ) throws JMSException
{
// Fill settings from message headers
Settings queueSettings = new Settings();
Enumeration headers = msg.getPropertyNames();
while (headers.hasMoreElements())
{
String propName = (String)headers.nextElement();
if (propName.startsWith(FFMQAdminConstants.ADM_HEADER_PREFIX))
continue;
String propValue = msg.getStringProperty(propName);
queueSettings.setStringProperty(propName, propValue);
}
return queueSettings;
}
private String processCreateQueue( Message msg ) throws JMSException
{
Settings queueSettings = createSettings(msg);
QueueDefinition queueDef = new QueueDefinition(queueSettings);
log.debug("Creating queue : "+queueDef);
engine.createQueue(queueDef);
// Success
return null;
}
private String processCreateTopic( Message msg ) throws JMSException
{
Settings topicSettings = createSettings(msg);
TopicDefinition topicDef = new TopicDefinition(topicSettings);
log.debug("Creating topic : "+topicDef);
engine.createTopic(topicDef);
// Success
return null;
}
private String processDeleteQueue( Message msg ) throws JMSException
{
String destName = msg.getStringProperty("name");
if (StringTools.isEmpty(destName))
return "Destination name not specified";
if (!engine.localQueueExists(destName))
return "Queue "+destName+" does not exist";
engine.deleteQueue(destName);
return null;
}
private String processDeleteTopic( Message msg ) throws JMSException
{
String destName = msg.getStringProperty("name");
if (StringTools.isEmpty(destName))
return "Destination name not specified";
if (!engine.localTopicExists(destName))
return "Topic "+destName+" does not exist";
engine.deleteTopic(destName);
return null;
}
private String processShutdown( Message msg )
{
// Shutdown the server
if (server.isInRunnableMode())
server.pleaseStop();
else
{
// Run the shutdown sequence in a parallel thread to avoid deadlocks
new Thread() {
public void run() {
server.shutdown();
}
}.start();
}
return null;
}
/**
* Ask the thread to stop
*/
public void pleaseStop()
{
if (stopRequired)
return;
stopRequired = true;
try
{
if (receiver != null)
receiver.close();
}
catch (JMSException e)
{
ErrorTools.log(e, log);
}
}
}