/*
* 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;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import java.util.StringTokenizer;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanServerConnection;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import net.timewalker.ffmq3.utils.Settings;
/**
* FFMQJMXConsole
*/
public final class FFMQJMXConsole implements Runnable
{
// Attributes
private Settings settings;
// Runtime
private JMXServiceURL jmxServiceURL;
private JMXConnector connector;
private MBeanServerConnection connection;
private boolean stopRequired;
// Output
private PrintStream out;
private PrintStream err;
private BufferedReader in;
/**
* Constructor
*/
public FFMQJMXConsole( Settings settings , InputStream in , PrintStream out , PrintStream err )
{
this.settings = settings;
this.in = new BufferedReader(new InputStreamReader(in));
this.out = out;
this.err = err;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run()
{
try
{
String serverHost = settings.getStringProperty(FFMQJMXConsoleSettings.SERVER_HOST, "localhost");
int serverPort = settings.getIntProperty(FFMQJMXConsoleSettings.SERVER_PORT, 10003);
String serviceUrl = "service:jmx:rmi://"+serverHost+"/jndi/rmi://"+serverHost+":"+serverPort+"/jmxconnector-FFMQ-server";
out.println("JMX Service URL : "+serviceUrl);
this.jmxServiceURL = new JMXServiceURL(serviceUrl);
printServerVersion();
boolean interactive = settings.getBooleanProperty(FFMQJMXConsoleSettings.INTERACTIVE, false);
if (interactive)
interactiveMode();
else
{
String command = settings.getStringProperty(FFMQJMXConsoleSettings.COMMAND, "help");
processCommand(command);
}
}
catch (Exception e)
{
closeJMXResources();
handleException(e);
}
}
private void interactiveMode() throws Exception
{
out.println("FFMQ JMX Console");
out.println("----------------");
while (!stopRequired)
{
out.print("> ");
out.flush();
String command = in.readLine();
if (command == null)
break;
command = command.trim();
try
{
if (command.length() > 0)
processCommand(command);
}
catch (Exception e)
{
closeJMXResources();
handleException(e);
}
}
}
private String[] splitCommand( String command )
{
StringTokenizer st = new StringTokenizer(command," \t");
String[] tokens = new String[st.countTokens()];
int pos = 0;
while (st.hasMoreTokens())
tokens[pos++] = st.nextToken();
return tokens;
}
private void processCommand( String command ) throws Exception
{
String[] commandTokens = splitCommand(command);
switch (commandTokens.length)
{
case 1 :
if (command.equals("help"))
printHelp();
else
if (command.equals("serverstatus"))
printServerStatus();
else
if (command.equals("enginesstatus"))
printEnginesStatus();
else
if (command.equals("listenersstatus"))
printListenersStatus();
else
if (command.equals("fullstatus"))
printFullStatus();
else
if (command.equals("quit") || command.equals("exit"))
{
stopRequired = true;
out.println("Exiting.");
}
else
err.println("Invalid command : "+command);
break;
case 2 :
if (commandTokens[0].equals("enginestatus"))
{
printEngineStatus(commandTokens[1]);
}
else
if (commandTokens[0].equals("listenerstatus"))
{
printListenerStatus(commandTokens[1]);
}
else
err.println("Invalid command : "+command);
break;
default:
err.println("Invalid command : "+command);
}
}
private void handleException( Exception e )
{
if (e instanceof InvocationTargetException)
{
Throwable cause = ((InvocationTargetException) e).getTargetException();
if (cause instanceof Exception)
handleException((Exception)cause);
else
throw (Error)cause;
}
else
{
err.println("ERROR: "+e.toString());
}
}
private void printHelp()
{
out.println("Available commands :");
out.println(" serverstatus : display server status");
out.println(" listenersstatus : display listeners status");
out.println(" enginesstatus : display engines status");
out.println(" fullstatus : display full server status");
out.println(" enginestatus <engineName> : display engine status");
out.println(" listenerstatus <listenerName> : display listener status");
out.println(" help : display console help");
out.println(" exit/quit : exit the console application");
}
private void printFullStatus() throws Exception
{
out.println("=============================");
out.println(" FULL SERVER STATUS");
out.println("=============================");
printServerStatus();
printListenersStatus();
printEnginesStatus();
}
private void printServerVersion() throws Exception
{
MBeanServerConnection conn = getMBeanServerConnection();
ObjectName serverON = new ObjectName("FFMQ:type=Server");
String version = (String)conn.getAttribute(serverON, "Version");
out.println("Connected to server version "+version);
}
private void printServerStatus() throws Exception
{
MBeanServerConnection conn = getMBeanServerConnection();
ObjectName serverON = new ObjectName("FFMQ:type=Server");
Boolean started = (Boolean)conn.getAttribute(serverON,"Started");
String version = (String)conn.getAttribute(serverON, "Version");
Long uptime = (Long)conn.getAttribute(serverON, "Uptime");
Boolean remoteAdmin = (Boolean)conn.getAttribute(serverON,"RemoteAdministrationEnabled");
out.println("Server (started="+started+")");
out.println(" Version : "+version);
out.println(" Uptime : "+formatDelay(uptime.longValue()));
out.println(" Remote admin. : "+remoteAdmin);
}
private void printListenersStatus() throws Exception
{
MBeanServerConnection conn = getMBeanServerConnection();
Iterator listenerInstances = conn.queryMBeans(new ObjectName("FFMQ:type=Listeners,listener=*"), null).iterator();
while (listenerInstances.hasNext())
{
ObjectInstance listenerInstance = (ObjectInstance)listenerInstances.next();
String listenerName = (String)conn.getAttribute(listenerInstance.getObjectName(),"Name");
printListenerStatus(listenerName);
}
}
private void printListenerStatus( String listenerName ) throws Exception
{
MBeanServerConnection conn = getMBeanServerConnection();
ObjectName listenerON = new ObjectName("FFMQ:type=Listeners,listener="+listenerName);
try
{
conn.getObjectInstance(listenerON);
}
catch (InstanceNotFoundException e)
{
err.println("No such listener : "+listenerName);
return;
}
Boolean started = (Boolean)conn.getAttribute(listenerON,"Started");
Integer activeClients = (Integer)conn.getAttribute(listenerON, "ActiveClients");
Integer acceptedTotal = (Integer)conn.getAttribute(listenerON, "AcceptedTotal");
Integer droppedTotal = (Integer)conn.getAttribute(listenerON, "DroppedTotal");
Integer maxActiveClients = (Integer)conn.getAttribute(listenerON, "MaxActiveClients");
Integer capacity = (Integer)conn.getAttribute(listenerON, "Capacity");
out.println("Listener ["+listenerName+"] (started="+started+")");
out.println(" Active clients : "+activeClients+" (peak="+maxActiveClients+",max="+capacity+")");
out.println(" Accepted total : "+acceptedTotal);
out.println(" Dropped total : "+droppedTotal);
}
private void printEnginesStatus() throws Exception
{
MBeanServerConnection conn = getMBeanServerConnection();
Iterator engineInstances = conn.queryMBeans(new ObjectName("FFMQ:type=Engines,engine=*"), null).iterator();
while (engineInstances.hasNext())
{
ObjectInstance engineInstance = (ObjectInstance)engineInstances.next();
String engineName = (String)conn.getAttribute(engineInstance.getObjectName(),"Name");
printEngineStatus(engineName);
}
}
private void printEngineStatus( String engineName ) throws Exception
{
MBeanServerConnection conn = getMBeanServerConnection();
ObjectName engineON = new ObjectName("FFMQ:type=Engines,engine="+engineName);
try
{
conn.getObjectInstance(engineON);
}
catch (InstanceNotFoundException e)
{
err.println("No such engine : "+engineName);
return;
}
Boolean deployed = (Boolean)conn.getAttribute(engineON,"Deployed");
out.println("Engine ["+engineName+"] (deployed="+deployed+")");
out.println(" Queues");
printEngineQueues(engineName," ");
out.println(" Topics");
printEngineTopics(engineName," ");
}
private void printEngineQueues(String engineName,String indent) throws Exception
{
MBeanServerConnection conn = getMBeanServerConnection();
Iterator queueInstances = conn.queryMBeans(new ObjectName("FFMQ:type=Engines,engine="+engineName+",children=queues,name=*"), null).iterator();
while (queueInstances.hasNext())
{
ObjectInstance queueInstance = (ObjectInstance)queueInstances.next();
String queueName = (String)conn.getAttribute(queueInstance.getObjectName(),"Name");
Long sentToQueueCount = (Long)connection.getAttribute(queueInstance.getObjectName(), "SentToQueueCount");
Long receivedFromQueueCount = (Long)connection.getAttribute(queueInstance.getObjectName(), "ReceivedFromQueueCount");
Long acknowledgedGetCount = (Long)connection.getAttribute(queueInstance.getObjectName(), "AcknowledgedGetCount");
Long rollbackedGetCount = (Long)connection.getAttribute(queueInstance.getObjectName(), "RollbackedGetCount");
Integer size = (Integer)connection.getAttribute(queueInstance.getObjectName(), "Size");
Integer registeredConsumersCount = (Integer)connection.getAttribute(queueInstance.getObjectName(), "RegisteredConsumersCount");
out.print(indent+queueName);
out.print(" - size="+size+" consumers="+registeredConsumersCount+" ack="+acknowledgedGetCount+" rollback="+rollbackedGetCount);
out.println(" sentTo="+sentToQueueCount+" receivedFrom="+receivedFromQueueCount);
}
}
private void printEngineTopics(String engineName,String indent) throws Exception
{
MBeanServerConnection conn = getMBeanServerConnection();
Iterator topicInstances = conn.queryMBeans(new ObjectName("FFMQ:type=Engines,engine="+engineName+",children=topics,name=*"), null).iterator();
while (topicInstances.hasNext())
{
ObjectInstance topicInstance = (ObjectInstance)topicInstances.next();
String topicName = (String)conn.getAttribute(topicInstance.getObjectName(),"Name");
Long sentToTopicCount = (Long)connection.getAttribute(topicInstance.getObjectName(), "SentToTopicCount");
Long dispatchedFromTopicCount = (Long)connection.getAttribute(topicInstance.getObjectName(), "DispatchedFromTopicCount");
Integer size = (Integer)connection.getAttribute(topicInstance.getObjectName(), "Size");
Integer registeredConsumersCount = (Integer)connection.getAttribute(topicInstance.getObjectName(), "RegisteredConsumersCount");
out.print(indent+topicName);
out.print(" - size="+size+" consumers="+registeredConsumersCount);
out.println(" sentTo="+sentToTopicCount+" dispatchedFrom="+dispatchedFromTopicCount);
}
}
private JMXConnector getConnector() throws Exception
{
if (connector == null)
connector = JMXConnectorFactory.connect(jmxServiceURL);
return connector;
}
private MBeanServerConnection getMBeanServerConnection() throws Exception
{
if (connection == null)
connection = getConnector().getMBeanServerConnection();
return connection;
}
private void closeJMXResources()
{
connection = null;
if (connector != null)
{
try
{
connector.close();
}
catch (Exception e)
{
err.println("Cannot close JMX connector");
e.printStackTrace(err);
}
finally
{
connector = null;
}
}
}
private String formatDelay( long delay )
{
StringBuffer sb = new StringBuffer();
if (delay < 0)
{
delay = -delay;
sb.append("-");
}
long days = delay / (1000*60*60*24);
if (days > 0)
{
sb.append(days);
sb.append("d");
delay = delay % (1000*60*60*24);
}
long hours = delay / (1000*60*60);
if (hours > 0)
{
if (sb.length() > 0)
sb.append(" ");
sb.append(hours);
sb.append("h");
delay = delay % (1000*60*60);
}
long minutes = delay / (1000*60);
if (minutes > 0)
{
if (sb.length() > 0)
sb.append(" ");
sb.append(minutes);
sb.append("min");
delay = delay % (1000*60);
}
long seconds = delay / 1000;
if (seconds > 0)
{
if (sb.length() > 0)
sb.append(" ");
sb.append(seconds);
sb.append("s");
delay = delay % 1000;
}
if (sb.length() > 0)
sb.append(" ");
sb.append(delay);
sb.append("ms");
return sb.toString();
}
}