blob: 9dd649d8380bc20108959ce49b00c1942f5fa480 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.uima.examples.as;
import java.net.URI;
import java.util.HashSet;
import java.util.Set;
import javax.jms.Connection;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TemporaryQueue;
import javax.jms.TextMessage;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.broker.jmx.QueueViewMBean;
import org.apache.activemq.command.ActiveMQTextMessage;
import org.apache.uima.aae.message.AsynchAEMessage;
import org.apache.uima.aae.message.UIMAMessage;
import org.springframework.jmx.JmxException;
/**
* This is a stand-alone utility class with a "main".
* It is used to retrieve the meta information for a UIMA service, which is attached to a JMS Broker
*
* It is passed the broker URI and the service name, and optionally, a -verbose flag.
*/
public class GetMetaRequest {
private static MBeanServerConnection brokerMBeanServer = null;
private static String brokerName;
private static JMXConnector jmxc = null;
private static boolean initialized = false;
private static enum QueueState {exists, existsnot, jmxnot};
private static String jmxPort;
/**
* retrieve meta information for a UIMA-AS Service attached to a broker
* It uses the port 1099 as the JMX port on the broker, unless overridden
* by defining the system property activemq.broker.jmx.port with a value of another port number
* It uses the default JMX ActiveMQ Domain "org.apache.activemq", unless overridden
* by defining the system property activemq.broker.jmx.domain with a value of the domain to use
* This normally never needs to be done unless multiple brokers are run on the same node
* as is sometimes done for unit tests.
* @param args - brokerUri serviceName [-verbose]
*/
public static void main(String[] args) {
if (args.length < 2) {
System.err.println("Need arguments: brokerURI serviceName [-verbose]");
System.exit(1);
}
String brokerURI = args[0];
String queueName = args[1];
boolean printReply = false;
if(args.length > 2) {
if (args[2].equalsIgnoreCase("-verbose")) {
printReply = true;
}
else {
System.err.println("Unknown argument: " + args[2]);
System.exit(1);
}
}
final Connection connection;
Session producerSession = null;
Queue producerQueue = null;
MessageProducer producer;
MessageConsumer consumer;
Session consumerSession = null;
TemporaryQueue consumerDestination = null;
long startTime = 0;
// Check if JMX server port number was specified
jmxPort = System.getProperty("activemq.broker.jmx.port");
if ( jmxPort == null || jmxPort.trim().length() == 0 ) {
jmxPort = "1099"; // default
}
try {
// First create connection to a broker
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(brokerURI);
connection = factory.createConnection();
connection.start();
Runtime.getRuntime().addShutdownHook(
new Thread(
new Runnable() {
public void run() {
try {
if ( connection != null ) {
connection.close();
}
if ( jmxc != null ) {
jmxc.close();
}
} catch( Exception ex) {
}
}
}
)
);
URI target = new URI(brokerURI);
String brokerHost = target.getHost();
attachToRemoteBrokerJMXServer(brokerURI);
if (isQueueAvailable(queueName) == QueueState.exists) {
System.out.println("Queue "+queueName+" found on "+ brokerURI);
System.out.println("Sending getMeta...");
}
else if (isQueueAvailable(queueName) == QueueState.existsnot) {
System.err.println("Queue "+queueName+" does not exist on "+ brokerURI);
System.exit(1);
}
else {
System.out.println("Cannot see queues on JMX port "+brokerHost+":"+jmxPort);
System.out.println("Sending getMeta anyway...");
}
producerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producerQueue = producerSession.createQueue(queueName);
producer = producerSession.createProducer(producerQueue);
consumerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
consumerDestination = consumerSession.createTemporaryQueue();
// -----------------------------------------------------------------------------
// Create message consumer. The consumer uses a selector to filter out messages other
// then GetMeta replies. Currently UIMA AS service returns two messages for each request:
// ServiceInfo message and GetMeta message. The ServiceInfo message is returned by the
// service immediately upon receiving a message from a client. This serves dual purpose,
// 1) to make sure the client reply destination exists
// 2) informs the client which service is processing its request
// -----------------------------------------------------------------------------
consumer = consumerSession.createConsumer(consumerDestination, "Command=2001");
TextMessage msg = producerSession.createTextMessage();
msg.setStringProperty(AsynchAEMessage.MessageFrom, consumerDestination.getQueueName());
msg.setStringProperty(UIMAMessage.ServerURI, brokerURI);
msg.setIntProperty(AsynchAEMessage.MessageType, AsynchAEMessage.Request);
msg.setIntProperty(AsynchAEMessage.Command, AsynchAEMessage.GetMeta);
msg.setJMSReplyTo(consumerDestination);
msg.setText("");
producer.send(msg);
startTime = System.nanoTime();
System.out.println("Sent getMeta request to " + queueName + " at " + brokerURI);
System.out.println("Waiting for getMeta reply...");
ActiveMQTextMessage reply = (ActiveMQTextMessage) consumer.receive();
long waitTime = (System.nanoTime() - startTime)/1000000;
System.out.println("Reply from "+reply.getStringProperty("ServerIP")+" received in " + waitTime + " ms");
if (printReply) {
System.out.println("Reply MessageText: " + reply.getText());
}
} catch (Exception e) {
System.err.println(e.toString());
}
System.exit(0);
}
/**
* Connects to this service Broker's JMX Server. If unable to connect, this method
* fails silently. The method uses default JMX Port number 1099 to create a connection
* to the Broker's JMX MBean server. The default can be overridden via System
* property 'activemq.broker.jmx.port'. If connection cannot be established the
* method silently fails.
*
*/
private static void attachToRemoteBrokerJMXServer(String brokerUri) throws Exception {
// Fetch AMQ jmx domain from system properties. This property is not required
// and the default AMQ jmx is used. The only exception is when the service is
// deployed in a jvm with multiple brokers deployed as it is the case with jUnit
// tests. In such a case, each broker will register self with JMX using a different
// domain.
String jmxAMQDomain = System.getProperty("activemq.broker.jmx.domain");
if ( jmxAMQDomain == null ) {
jmxAMQDomain = "org.apache.activemq"; // default
}
String brokerHostname="";
URI target = new URI(brokerUri);
brokerHostname = target.getHost();
initialize(jmxAMQDomain, brokerHostname,jmxPort);
}
/**
* Creates a connection to an MBean Server identified by
* <code>remoteJMXServerHostName and remoteJMXServerPort</code>
*
* @param remoteJMXServerHostName
* - MBeanServer host name
* @param remoteJMXServerPort
* - MBeanServer port
* @return - none
*
* @throws Exception
*/
private static void initialize(String jmxDomain, String remoteJMXServerHostname,
String remoteJMXServerPort) throws Exception {
// Construct connect string to the JMX MBeanServer
String remoteJmxUrl = "service:jmx:rmi:///jndi/rmi://" + remoteJMXServerHostname + ":"
+ remoteJMXServerPort + "/jmxrmi";
try {
JMXServiceURL url = new JMXServiceURL(remoteJmxUrl);
jmxc = JMXConnectorFactory.connect(url, null);
brokerMBeanServer = jmxc.getMBeanServerConnection();
// Its possible that the above code succeeds even though the broker runs
// with no JMX Connector. At least on windows the above does not throw an
// exception as expected. It appears that the broker registers self JVMs MBeanServer
// but it does *not* register any Connections, nor Queues. The code below
// checks if the MBean server we are connected to has any QueueMBeans registered.
// A broker with jmx connector should have queue mbeans registered and thus
// the code below should always succeed. Conversely, a broker with no jmx connector
// reports no queue mbeans.
// Query broker MBeanServer for QueueMBeans
Set queueSet = brokerMBeanServer.queryNames(new ObjectName(jmxDomain
+ ":*,Type=Queue"), (QueryExp) null);
if ( queueSet.isEmpty() ) { // No QueueMBeans, meaning no JMX support
throw new JmxException("ActiveMQ Broker Not Configured With JMX Support");
}
} catch (Exception e) {
return;
}
// Query JMX Server for Broker MBean. We need the broker's name from an MBean to construct
// queue query string.
for (Object nameObject : brokerMBeanServer.queryNames(new ObjectName(jmxDomain
+ ":*,Type=Broker"), (QueryExp) null)) {
ObjectName brokerObjectName = (ObjectName) nameObject;
if (brokerObjectName.getCanonicalName().endsWith("Type=Broker")) {
// Extract just the name from the canonical name
brokerName = brokerObjectName.getCanonicalName().substring(0,
brokerObjectName.getCanonicalName().indexOf(","));
initialized = true;
break; // got the broker name
}
}
}
private static boolean isServerAvailable() {
try {
brokerMBeanServer.getMBeanCount();
return true;
} catch (Exception e) {
return false;
}
}
private static QueueState isQueueAvailable(String queueName) throws Exception {
if (!initialized) {
return QueueState.jmxnot;
}
ObjectName uimaServiceQueuePattern = new ObjectName(brokerName + ",Type=Queue,Destination=" + queueName);
// Tests if queue exists. If a client terminates, the reply queue will be removed and we
// expect null from getQueueMBean()
if (isServerAvailable() && getQueueMBean(queueName, uimaServiceQueuePattern) == null) {
return QueueState.existsnot;
}
return QueueState.exists;
}
private static QueueViewMBean getQueueMBean(String key, ObjectName matchPattern) throws Exception {
// Fetch queue names matching a given pattern.
Set<ObjectName> queues = new HashSet<ObjectName>(brokerMBeanServer.queryNames(matchPattern,
null));
for (ObjectName name : queues) {
// Create and return a proxy to the queue's MBean
return (QueueViewMBean) MBeanServerInvocationHandler.newProxyInstance(brokerMBeanServer,
name, QueueViewMBean.class, true);
}
return null;
}
}