/* * JBoss, Home of Professional Open Source * Copyright 2008, Red Hat, Inc., and others contributors as indicated * by the @authors tag. All rights reserved. * See the copyright.txt in the distribution for a * full listing of individual contributors. * This copyrighted material is made available to anyone wishing to use, * modify, copy, or redistribute it subject to the terms and conditions * of the GNU Lesser General Public License, v. 2.1. * This program is distributed in the hope that it will be useful, but WITHOUT A * 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, * v.2.1 along with this distribution; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ package org.jboss.narayana.blacktie.administration; import static org.jboss.narayana.blacktie.administration.Authentication.getCallbackHandler; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.net.UnknownHostException; import java.util.Hashtable; import java.util.List; import java.util.Properties; import java.util.StringTokenizer; import javax.ejb.ActivationConfigProperty; import javax.ejb.MessageDriven; import javax.management.MBeanServerConnection; import javax.management.ObjectInstance; import javax.management.ObjectName; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.jboss.as.controller.client.ModelControllerClient; import org.jboss.as.controller.client.OperationBuilder; import org.jboss.dmr.ModelNode; import org.jboss.narayana.blacktie.administration.core.AdministrationProxy; import org.jboss.narayana.blacktie.jatmibroker.core.conf.ConfigurationException; import org.jboss.narayana.blacktie.jatmibroker.core.conf.XMLParser; import org.jboss.narayana.blacktie.jatmibroker.xatmi.Connection; import org.jboss.narayana.blacktie.jatmibroker.xatmi.ConnectionException; import org.jboss.narayana.blacktie.jatmibroker.xatmi.Response; import org.jboss.narayana.blacktie.jatmibroker.xatmi.TPSVCINFO; import org.jboss.narayana.blacktie.jatmibroker.xatmi.X_OCTET; import org.jboss.narayana.blacktie.jatmibroker.xatmi.mdb.MDBBlacktieService; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.InputSource; @MessageDriven(activationConfig = { @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"), @ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/BTR_BTStompAdmin") }) public class BlacktieStompAdministrationService extends MDBBlacktieService implements javax.jms.MessageListener { private static final Logger log = LogManager.getLogger(BlacktieStompAdministrationService.class); private static MBeanServerConnection beanServerConnection; private static Properties prop = new Properties(); private static Hashtable<String, Long> QUEUE_CREATION_TIMES = new Hashtable<String, Long>(); private static Hashtable<String, ServerInfo> SERVICE_OWNERS = new Hashtable<String, ServerInfo>(); private static ModelControllerClient client; private class ServerInfo { public final String name; public final boolean conversational; public final String type; public ServerInfo(String name, boolean conversational, String type) { this.name = name; this.conversational = conversational; this.type = type; } } public BlacktieStompAdministrationService() throws ConfigurationException { super("BlacktieStompAdministrationService"); } static void applyUpdate(ModelNode update, final ModelControllerClient client) throws IOException { ModelNode result = client.execute(new OperationBuilder(update).build()); if (result.hasDefined("outcome") && "success".equals(result.get("outcome").asString())) { if (result.hasDefined("result")) { System.out.println(result.get("result")); } } else if (result.hasDefined("failure-description")) { throw new RuntimeException(result.get("failure-description").toString()); } else { throw new RuntimeException("Operation not successful; outcome = " + result.get("outcome")); } } private static boolean isDeployQueue(String serviceName) throws Exception { boolean conversational = false; String type = "queue"; if (!serviceName.startsWith(".")) { ServerInfo info = SERVICE_OWNERS.get(serviceName); if(info != null) { conversational = info.conversational; type = info.type; } else { return false; } } String prefix = null; if (conversational) { prefix = "BTC_"; } else { prefix = "BTR_"; } ObjectName objName = new ObjectName("jboss.as:subsystem=messaging-activemq,server=default,jms-" + type + "=" + prefix + "*"); ObjectInstance[] dests = getMBeanServerConnection().queryMBeans(objName, null).toArray(new ObjectInstance[] {}); for (int i = 0; i < dests.length; i++) { String serviceComponentOfObjectName = dests[i].getObjectName().getCanonicalName(); serviceComponentOfObjectName = serviceComponentOfObjectName.substring( serviceComponentOfObjectName.indexOf('_') + 1, serviceComponentOfObjectName.indexOf(",", serviceComponentOfObjectName.indexOf('_'))); log.debug("Service name component of ObjectName is: " + serviceComponentOfObjectName); if (serviceComponentOfObjectName.equals(serviceName)) { log.debug("find serviceName " + serviceName + " in Queues"); return true; } } log.trace("did not find serviceName " + serviceName); return false; } private static MBeanServerConnection getMBeanServerConnection() throws ConfigurationException, UnknownHostException { initStatic(); return beanServerConnection; } private static Object getProperty(String string) throws ConfigurationException, UnknownHostException { initStatic(); return prop.get(string); } private static ModelControllerClient getClient() throws ConfigurationException, UnknownHostException { initStatic(); return client; } private static void initStatic() throws ConfigurationException, UnknownHostException { synchronized (prop) { if (prop.isEmpty()) { XMLParser.loadProperties("btconfig.xsd", "btconfig.xml", prop); beanServerConnection = java.lang.management.ManagementFactory.getPlatformMBeanServer(); String managementAddress = System.getProperty("jboss.bind.address.management", "localhost"); if (managementAddress.equals("0.0.0.0")) { managementAddress = "localhost"; } client = ModelControllerClient.Factory.create("http-remoting", managementAddress, 9990, getCallbackHandler(), null, 120 * 1000); } } } int consumerCount(String serviceName) throws Exception { log.trace("consCount" + serviceName); boolean conversational = false; String type = "queue"; if (!serviceName.startsWith(".")) { ServerInfo info = SERVICE_OWNERS.get(serviceName); conversational = info.conversational; type = info.type; } String prefix = null; if (conversational) { prefix = "BTC_"; } else { prefix = "BTR_"; } Integer count = null; ObjectName objName = new ObjectName("jboss.as:subsystem=messaging-activemq,server=default,jms-" + type + "=" + prefix + serviceName); if (type.toLowerCase().equals("queue")) { count = (Integer) getMBeanServerConnection().getAttribute(objName, "consumerCount"); } else { count = (Integer) getMBeanServerConnection().getAttribute(objName, "subscriptionCount"); } log.debug("consCount" + serviceName + " " + count.intValue()); return count.intValue(); } Element stringToElement(String s) throws Exception { StringReader sreader = new StringReader(s); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder parser = factory.newDocumentBuilder(); Document doc = parser.parse(new InputSource(sreader)); return doc.getDocumentElement(); } String printNode(Node node) { try { // Set up the output transformer TransformerFactory transfac = TransformerFactory.newInstance(); Transformer trans = transfac.newTransformer(); trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); trans.setOutputProperty(OutputKeys.INDENT, "yes"); // Print the DOM node StringWriter sw = new StringWriter(); StreamResult result = new StreamResult(sw); DOMSource source = new DOMSource(node); trans.transform(source, result); String xmlString = sw.toString(); return xmlString; } catch (TransformerException e) { log.error(e); } return null; } public static boolean isOlderThanReapCheck(String serviceName, long queueReapCheck) { // TODO THIS WILL NOT CLUSTER AS IT ASSUMES THE QUEUE WAS CREATED BY // THIS SERVER log.trace("Locking for isOlderThanReapCheck: " + serviceName); synchronized (QUEUE_CREATION_TIMES) { log.trace("Locked for isOlderThanReapCheck: " + serviceName); boolean toReturn = true; Long creationTime = QUEUE_CREATION_TIMES.get(serviceName); if (creationTime != null) { toReturn = creationTime < queueReapCheck; if (!toReturn) { log.debug("New queue will be ignored: " + serviceName); } } return toReturn; } } public int deployQueue(String serviceName, String serverName, boolean conversational, String type, String version) throws ConfigurationException, UnknownHostException { log.trace("deployQueue: " + serviceName + " version: " + version); if (version == null || !version.equals(getProperty("blacktie.domain.version"))) { log.warn("Blacktie Domain version " + getProperty("blacktie.domain.version") + " not match server " + version); return 4; } int result = 0; // Long currentTime = QUEUE_CREATION_TIMES.get(serviceName); try { boolean queue = false; log.debug("Locking for deployQueue: " + serviceName); synchronized (QUEUE_CREATION_TIMES) { log.debug("Locked for deployQueue: " + serviceName); queue = isDeployQueue(serviceName); log.debug("Queue " + serviceName + " was deployed?: " + queue); if (queue == false) { log.debug("Creating " + serviceName); log.trace("Lock acquired"); String prefix = null; if (conversational) { prefix = "BTC_"; } else { prefix = "BTR_"; } QUEUE_CREATION_TIMES.put(serviceName, System.currentTimeMillis()); SERVICE_OWNERS.put(serviceName, new ServerInfo(serverName, conversational, type)); log.trace(serviceName); log.debug("Invoking activemq to deploy queue"); ModelNode op = new ModelNode(); op.get("operation").set("add"); op.get("address").add("subsystem", "messaging-activemq"); op.get("address").add("server", "default"); op.get("address").add("jms-" + type, prefix + serviceName); op.get("entries").add("/" + type + "/" + prefix + serviceName); // op.get("jms-" + type + "-address").set("jms." + type + "." + prefix + serviceName); applyUpdate(op, getClient()); log.debug("Invoked activemq to deploy queue"); } log.debug("Created: " + serviceName); // QUEUE_CREATION_TIMES.put(serviceName, currentTime); if (!queue || !serviceName.contains(".")) { result = 1; if (AdministrationProxy.isDomainPause && serviceName.contains(".")) { log.debug("Domain is pause"); result = 3; } } else if (serviceName.contains(".") && queue && consumerCount(serviceName) > 0) { log.warn("can not advertise ADMIN with same id: " + serviceName); result = 2; } else if (AdministrationProxy.isDomainPause) { log.debug("Domain is pause"); result = 3; } else { result = 1; } } } catch (Throwable t) { log.error("Could not deploy queue of " + serviceName, t); } return result; } static int undeployQueue(String serviceName) { int result = 0; try { if (isDeployQueue(serviceName)) { log.trace(serviceName); boolean conversational = false; String type = "queue"; if (!serviceName.startsWith(".")) { ServerInfo info = SERVICE_OWNERS.get(serviceName); conversational = info.conversational; type = info.type; } String prefix = null; if (conversational) { prefix = "BTC_"; } else { prefix = "BTR_"; } ModelNode op = new ModelNode(); op.get("operation").set("remove"); op.get("address").add("subsystem", "messaging-activemq"); op.get("address").add("server", "default"); op.get("address").add("jms-" + type, prefix + serviceName); applyUpdate(op, getClient()); } result = 1; } catch (Throwable t) { log.error("Could not undeploy queue of " + serviceName, t); } return result; } public int decrementConsumer(String serviceName) { log.trace("decrement"); int consumerCounts; int result = 0; try { consumerCounts = consumerCount(serviceName); if (consumerCounts < 1) { result = undeployQueue(serviceName); log.debug(serviceName + " undeployed"); } else { // THERE ARE OTHER SERVERS STILL ALIVE result = 1; log.debug(serviceName + " still has " + consumerCounts + " consumers"); } } catch (Throwable t) { log.debug("Could not get consumer counts of " + serviceName, t); } return result; } public Response tpservice(TPSVCINFO svcinfo) { log.debug("Message received"); X_OCTET recv = (X_OCTET) svcinfo.getBuffer(); String string = new String(recv.getByteArray()); StringTokenizer st = new StringTokenizer(string, ",", false); String operation = st.nextToken(); String serverName = st.nextToken(); String serviceName = st.nextToken(); byte[] success = new byte[1]; String server = null; try { if (serviceName.indexOf(".") > -1) { server = serviceName.substring(1); server = server.replaceAll("[0-9]", ""); } else { ServerInfo info = SERVICE_OWNERS.get(serviceName); if(info == null) { server = serverName; } else { server = info.name; } } if (server != null && server.equals(serverName)) { log.trace("Service " + serviceName + " exists for server: " + server); if (operation.equals("tpadvertise")) { log.trace("Advertising: " + serviceName); boolean conversational = st.nextToken().equals("1"); String type = st.nextToken(); String version = st.nextToken(); success[0] = (byte) deployQueue(serviceName, serverName, conversational, type, version); log.trace("Advertised: " + serviceName); } else if (operation.equals("decrementconsumer")) { log.trace("Decrement consumer: " + serviceName); success[0] = (byte) decrementConsumer(serviceName); log.trace("Decremented consumer: " + serviceName); } else { log.error("Unknown operation " + operation); success[0] = 0; } } else { log.error("Service " + serviceName + " already exists for a different server (" + server + ")"); success[0] = 0; } X_OCTET buffer = (X_OCTET) svcinfo.getConnection().tpalloc("X_OCTET", null); buffer.setByteArray(success); log.debug("Responding"); return new Response(Connection.TPSUCCESS, 0, buffer, 0); } catch (ConnectionException e) { return new Response(Connection.TPFAIL, 0, null, 0); } catch (ConfigurationException e) { return new Response(Connection.TPFAIL, 0, null, 0); } catch (UnknownHostException e) { return new Response(Connection.TPFAIL, 0, null, 0); } } }