/* * JBoss, Home of Professional Open Source. * * Copyright 2008, Red Hat Middleware LLC, and individual contributors by the * * @authors tag. See the copyright.txt in the distribution for a full listing of * individual contributors. * * This 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.1 of the License, or (at your option) * any later version. * * This software 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 this software; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF * site: http://www.fsf.org. */ package org.jboss.messaging.core.management.impl; import static javax.management.ObjectName.quote; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import javax.management.ListenerNotFoundException; import javax.management.MBeanNotificationInfo; import javax.management.MBeanServer; import javax.management.Notification; import javax.management.NotificationBroadcasterSupport; import javax.management.NotificationFilter; import javax.management.NotificationListener; import javax.management.ObjectName; import org.jboss.messaging.core.client.management.impl.ManagementHelper; import org.jboss.messaging.core.config.Configuration; import org.jboss.messaging.core.logging.Logger; import org.jboss.messaging.core.management.AddressControlMBean; import org.jboss.messaging.core.management.ManagementService; import org.jboss.messaging.core.management.MessagingServerControlMBean; import org.jboss.messaging.core.management.QueueControlMBean; import org.jboss.messaging.core.management.impl.MessagingServerControl.NotificationType; import org.jboss.messaging.core.messagecounter.MessageCounter; import org.jboss.messaging.core.messagecounter.MessageCounterManager; import org.jboss.messaging.core.messagecounter.impl.MessageCounterManagerImpl; import org.jboss.messaging.core.persistence.StorageManager; import org.jboss.messaging.core.postoffice.PostOffice; import org.jboss.messaging.core.security.Role; import org.jboss.messaging.core.server.MessagingServer; import org.jboss.messaging.core.server.Queue; import org.jboss.messaging.core.server.ServerMessage; import org.jboss.messaging.core.settings.HierarchicalRepository; import org.jboss.messaging.core.settings.impl.QueueSettings; import org.jboss.messaging.util.SimpleString; /* * @author <a href="mailto:jmesnil@redhat.com">Jeff Mesnil</a> * * @version <tt>$Revision$</tt> */ public class ManagementServiceImpl implements ManagementService { // Constants ----------------------------------------------------- private static final Logger log = Logger.getLogger(ManagementServiceImpl.class); public static final String DOMAIN = "org.jboss.messaging"; // Attributes ---------------------------------------------------- private final MBeanServer mbeanServer; private final boolean jmxManagementEnabled; private final Map<ObjectName, Object> registry; private final NotificationBroadcasterSupport broadcaster; private AtomicLong notifSeq = new AtomicLong(0); private PostOffice postOffice; private HierarchicalRepository<Set<Role>> securityRepository; private HierarchicalRepository<QueueSettings> queueSettingsRepository; private MessagingServerControlMBean managedServer; private final MessageCounterManager messageCounterManager = new MessageCounterManagerImpl(10000); // key is a Destination private final Map<SimpleString, NotificationListener> notifListeners = new HashMap<SimpleString, NotificationListener>(); // Static -------------------------------------------------------- public static ObjectName getMessagingServerObjectName() throws Exception { return ObjectName.getInstance(DOMAIN + ":module=Core,type=Server"); } public static ObjectName getAddressObjectName(final SimpleString address) throws Exception { return ObjectName.getInstance(String.format("%s:module=Core,type=Address,name=%s", DOMAIN, quote(address.toString()))); } public static ObjectName getQueueObjectName(final SimpleString address, final SimpleString name) throws Exception { return ObjectName.getInstance(String.format("%s:module=Core,type=Queue,address=%s,name=%s", DOMAIN, quote(address.toString()), quote(name.toString()))); } // Constructors -------------------------------------------------- public ManagementServiceImpl(final MBeanServer mbeanServer, final boolean jmxManagementEnabled) { this.mbeanServer = mbeanServer; this.jmxManagementEnabled = jmxManagementEnabled; registry = new HashMap<ObjectName, Object>(); broadcaster = new NotificationBroadcasterSupport(); notifSeq = new AtomicLong(0); } // Public -------------------------------------------------------- // ManagementService implementation ------------------------- public MessageCounterManager getMessageCounterManager() { return messageCounterManager; } public MessagingServerControlMBean registerServer(final PostOffice postOffice, final StorageManager storageManager, final Configuration configuration, final HierarchicalRepository<Set<Role>> securityRepository, final HierarchicalRepository<QueueSettings> queueSettingsRepository, final MessagingServer messagingServer) throws Exception { this.postOffice = postOffice; this.securityRepository = securityRepository; this.queueSettingsRepository = queueSettingsRepository; managedServer = new MessagingServerControl(postOffice, storageManager, configuration, securityRepository, queueSettingsRepository, messagingServer, messageCounterManager, broadcaster); ObjectName objectName = getMessagingServerObjectName(); registerResource(objectName, managedServer); return managedServer; } public void unregisterServer() throws Exception { ObjectName objectName = getMessagingServerObjectName(); unregisterResource(objectName); } public void registerAddress(final SimpleString address) throws Exception { ObjectName objectName = getAddressObjectName(address); AddressControlMBean addressControl = new AddressControl(address, postOffice, securityRepository); registerResource(objectName, addressControl); if (log.isDebugEnabled()) { log.debug("registered address " + objectName); } sendNotification(NotificationType.ADDRESS_ADDED, address.toString()); } public void unregisterAddress(final SimpleString address) throws Exception { ObjectName objectName = getAddressObjectName(address); unregisterResource(objectName); sendNotification(NotificationType.ADDRESS_REMOVED, address.toString()); } public void registerQueue(final Queue queue, final SimpleString address, final StorageManager storageManager) throws Exception { MessageCounter counter = new MessageCounter(queue.getName().toString(), null, queue, false, queue.isDurable(), messageCounterManager.getMaxDayCount()); messageCounterManager.registerMessageCounter(queue.getName().toString(), counter); ObjectName objectName = getQueueObjectName(address, queue.getName()); QueueControlMBean queueControl = new QueueControl(queue, storageManager, postOffice, queueSettingsRepository, counter); registerResource(objectName, queueControl); if (log.isDebugEnabled()) { log.debug("registered queue " + objectName); } sendNotification(NotificationType.QUEUE_CREATED, queue.getName().toString()); } public void unregisterQueue(final SimpleString name, final SimpleString address) throws Exception { ObjectName objectName = getQueueObjectName(address, name); unregisterResource(objectName); messageCounterManager.unregisterMessageCounter(name.toString()); sendNotification(NotificationType.QUEUE_DESTROYED, name.toString()); } public void handleMessage(final ServerMessage message) { SimpleString objectName = (SimpleString)message.getProperty(ManagementHelper.HDR_JMX_OBJECTNAME); if (log.isDebugEnabled()) { log.debug("handling management message for " + objectName); } Set<SimpleString> propertyNames = message.getPropertyNames(); // use an array with all the property names to avoid a // ConcurrentModificationException // when invoking an operation or retrieving attributes (since they add // properties to the message) List<SimpleString> propNames = new ArrayList<SimpleString>(propertyNames); if (propNames.contains(ManagementHelper.HDR_JMX_OPERATION_NAME)) { SimpleString operation = (SimpleString)message.getProperty(ManagementHelper.HDR_JMX_OPERATION_NAME); List<Object> operationParameters = retrieveOperationParameters(message); if (operation != null) { try { Object result = invokeOperation(objectName.toString(), operation.toString(), operationParameters); message.putBooleanProperty(ManagementHelper.HDR_JMX_OPERATION_SUCCEEDED, true); ManagementHelper.storeTypedProperty(message, operation, result); } catch (Exception e) { log.warn("exception while invoking " + operation + " on " + objectName, e); message.putBooleanProperty(ManagementHelper.HDR_JMX_OPERATION_SUCCEEDED, false); String exceptionMessage = e.getMessage(); if (e instanceof InvocationTargetException) { exceptionMessage = ((InvocationTargetException)e).getTargetException().getMessage(); } message.putStringProperty(ManagementHelper.HDR_JMX_OPERATION_EXCEPTION, new SimpleString(exceptionMessage)); } } } else { for (SimpleString propertyName : propNames) { if (propertyName.startsWith(ManagementHelper.HDR_JMX_ATTRIBUTE_PREFIX)) { SimpleString attribute = (SimpleString)message.getProperty(propertyName); Object result = getAttribute(objectName.toString(), attribute.toString()); ManagementHelper.storeTypedProperty(message, attribute, result); } } } } public void registerResource(final ObjectName objectName, final Object resource) throws Exception { registerInRegistry(objectName, resource); registerInJMX(objectName, resource); } public void unregisterResource(final ObjectName objectName) throws Exception { unregisterFromRegistry(objectName); unregisterFromJMX(objectName); } public Object getResource(final ObjectName objectName) { return registry.get(objectName); } // NotificatioBroadcaster implementation ----------------------------------- public void addNotificationListener(final NotificationListener listener, final NotificationFilter filter, final Object handback) throws IllegalArgumentException { broadcaster.addNotificationListener(listener, filter, handback); } public MBeanNotificationInfo[] getNotificationInfo() { return broadcaster.getNotificationInfo(); } public void removeNotificationListener(final NotificationListener listener) throws ListenerNotFoundException { broadcaster.removeNotificationListener(listener); } // Package protected --------------------------------------------- // Protected ----------------------------------------------------- // Private ------------------------------------------------------- public void registerInRegistry(final ObjectName objectName, final Object managedResource) { unregisterFromRegistry(objectName); registry.put(objectName, managedResource); } private void unregisterFromRegistry(final ObjectName objectName) { registry.remove(objectName); } private void registerInJMX(final ObjectName objectName, final Object managedResource) throws Exception { if (!jmxManagementEnabled) { return; } unregisterFromJMX(objectName); mbeanServer.registerMBean(managedResource, objectName); } private void unregisterFromJMX(final ObjectName objectName) throws Exception { if (!jmxManagementEnabled) { return; } if (mbeanServer.isRegistered(objectName)) { mbeanServer.unregisterMBean(objectName); } } private void sendNotification(final MessagingServerControl.NotificationType type, final String message) throws Exception { if (managedServer != null) { Notification notif = new Notification(type.toString(), getMessagingServerObjectName(), notifSeq.incrementAndGet(), message); broadcaster.sendNotification(notif); } } public Object getAttribute(final String objectNameStr, final String attribute) { try { ObjectName objectName = ObjectName.getInstance(objectNameStr); Object resource = registry.get(objectName); Method method = null; try { method = resource.getClass().getMethod("get" + attribute, new Class[0]); } catch (NoSuchMethodException nsme) { try { method = resource.getClass().getMethod("is" + attribute, new Class[0]); } catch (NoSuchMethodException nsme2) { throw new IllegalArgumentException("no getter method for " + attribute); } } return method.invoke(resource, new Object[0]); } catch (Throwable t) { throw new IllegalStateException("Problem while retrieving attribute " + attribute, t); } } private Object invokeOperation(final String objectNameStr, final String operation, final List<Object> params) throws Exception { ObjectName objectName = ObjectName.getInstance(objectNameStr); Object resource = registry.get(objectName); Method method = null; Method[] methods = resource.getClass().getMethods(); for (Method m : methods) { if (m.getName().equals(operation) && m.getParameterTypes().length == params.size()) { method = m; } } if (method == null) { throw new IllegalArgumentException("no operation " + operation + "/" + params.size()); } Object[] p = params.toArray(new Object[params.size()]); Object result = method.invoke(resource, p); return result; } private List<Object> retrieveOperationParameters(final ServerMessage message) { List<Object> params = new ArrayList<Object>(); Set<SimpleString> propertyNames = message.getPropertyNames(); // put the property names in a list to sort them and have the parameters // in the correct order List<SimpleString> propsNames = new ArrayList<SimpleString>(propertyNames); Collections.sort(propsNames); for (SimpleString propertyName : propsNames) { if (propertyName.startsWith(ManagementHelper.HDR_JMX_OPERATION_PREFIX)) { String s = propertyName.toString(); // split by the dot String[] ss = s.split("\\."); try { int index = Integer.parseInt(ss[ss.length - 1]); Object value = message.getProperty(propertyName); if (value instanceof SimpleString) { value = value.toString(); } params.add(index, value); } catch (NumberFormatException e) { // ignore the property (it is the operation name) } } } return params; } // Inner classes ------------------------------------------------- }