/* * * 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.qpid.server.jmx; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.UUID; import javax.management.JMException; import org.apache.log4j.Logger; import org.apache.qpid.server.jmx.mbeans.LoggingManagementMBean; import org.apache.qpid.server.jmx.mbeans.UserManagementMBean; import org.apache.qpid.server.jmx.mbeans.ServerInformationMBean; import org.apache.qpid.server.jmx.mbeans.Shutdown; import org.apache.qpid.server.jmx.mbeans.VirtualHostMBean; import org.apache.qpid.server.logging.log4j.LoggingManagementFacade; import org.apache.qpid.server.model.AuthenticationProvider; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.ConfigurationChangeListener; import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.model.PasswordCredentialManagingAuthenticationProvider; import org.apache.qpid.server.model.Plugin; import org.apache.qpid.server.model.Port; import org.apache.qpid.server.model.Protocol; import org.apache.qpid.server.model.State; import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.model.adapter.AbstractPluginAdapter; import org.apache.qpid.server.plugin.PluginFactory; import org.apache.qpid.server.plugin.QpidServiceLoader; import org.apache.qpid.server.util.MapValueConverter; public class JMXManagement extends AbstractPluginAdapter implements ConfigurationChangeListener { private static final Logger LOGGER = Logger.getLogger(JMXManagement.class); public static final String PLUGIN_TYPE = "MANAGEMENT-JMX"; // attributes public static final String USE_PLATFORM_MBEAN_SERVER = "usePlatformMBeanServer"; public static final String NAME = "name"; // default values public static final String DEFAULT_NAME = "JMXManagement"; public static final boolean DEFAULT_USE_PLATFORM_MBEAN_SERVER = true; @SuppressWarnings("serial") private static final Collection AVAILABLE_ATTRIBUTES = Collections.unmodifiableCollection(new HashSet(Plugin.AVAILABLE_ATTRIBUTES){{ add(NAME); add(USE_PLATFORM_MBEAN_SERVER); add(PluginFactory.PLUGIN_TYPE); }}); @SuppressWarnings("serial") private static final Map DEFAULTS = new HashMap(){{ put(USE_PLATFORM_MBEAN_SERVER, DEFAULT_USE_PLATFORM_MBEAN_SERVER); put(NAME, DEFAULT_NAME); put(PluginFactory.PLUGIN_TYPE, PLUGIN_TYPE); }}; @SuppressWarnings("serial") private static final Map> ATTRIBUTE_TYPES = new HashMap>(){{ put(USE_PLATFORM_MBEAN_SERVER, Boolean.class); put(NAME, String.class); put(PluginFactory.PLUGIN_TYPE, String.class); }}; private final Broker _broker; private JMXManagedObjectRegistry _objectRegistry; private final Map _children = new HashMap(); public JMXManagement(UUID id, Broker broker, Map attributes) { super(id, DEFAULTS, MapValueConverter.convert(attributes, ATTRIBUTE_TYPES), broker.getTaskExecutor()); _broker = broker; addParent(Broker.class, broker); } @Override protected boolean setState(State currentState, State desiredState) { if(desiredState == State.ACTIVE) { try { start(); } catch (JMException e) { throw new RuntimeException("Couldn't start JMX management", e); } catch (IOException e) { throw new RuntimeException("Couldn't start JMX management", e); } return true; } else if(desiredState == State.STOPPED) { stop(); return true; } return false; } private void start() throws JMException, IOException { Port connectorPort = null; Port registryPort = null; Collection ports = _broker.getPorts(); for (Port port : ports) { if (State.QUIESCED.equals(port.getActualState())) { continue; } if(isRegistryPort(port)) { registryPort = port; } else if(isConnectorPort(port)) { connectorPort = port; } } if(connectorPort == null) { throw new IllegalStateException("No JMX connector port found supporting protocol " + Protocol.JMX_RMI); } if(registryPort == null) { throw new IllegalStateException("No JMX RMI port found supporting protocol " + Protocol.RMI); } _objectRegistry = new JMXManagedObjectRegistry(_broker, connectorPort, registryPort, this); _broker.addChangeListener(this); synchronized (_children) { for(VirtualHost virtualHost : _broker.getVirtualHosts()) { if(!_children.containsKey(virtualHost)) { LOGGER.debug("Create MBean for virtual host:" + virtualHost.getName()); VirtualHostMBean mbean = new VirtualHostMBean(virtualHost, _objectRegistry); LOGGER.debug("Check for additional MBeans for virtual host:" + virtualHost.getName()); createAdditionalMBeansFromProviders(virtualHost, mbean); } } Collection authenticationProviders = _broker.getAuthenticationProviders(); for (AuthenticationProvider authenticationProvider : authenticationProviders) { if(authenticationProvider instanceof PasswordCredentialManagingAuthenticationProvider) { UserManagementMBean mbean = new UserManagementMBean( (PasswordCredentialManagingAuthenticationProvider) authenticationProvider, _objectRegistry); _children.put(authenticationProvider, mbean); } } } new Shutdown(_objectRegistry); new ServerInformationMBean(_objectRegistry, _broker); if (LoggingManagementFacade.getCurrentInstance() != null) { new LoggingManagementMBean(LoggingManagementFacade.getCurrentInstance(), _objectRegistry); } _objectRegistry.start(); } private boolean isConnectorPort(Port port) { return port.getProtocols().contains(Protocol.JMX_RMI); } private boolean isRegistryPort(Port port) { return port.getProtocols().contains(Protocol.RMI); } private void stop() { synchronized (_children) { for(ConfiguredObject object : _children.keySet()) { AMQManagedObject mbean = _children.get(object); if (mbean instanceof ConfigurationChangeListener) { object.removeChangeListener((ConfigurationChangeListener)mbean); } try { mbean.unregister(); } catch (JMException e) { LOGGER.error("Error unregistering mbean", e); } } _children.clear(); } _broker.removeChangeListener(this); _objectRegistry.close(); } @Override public void stateChanged(ConfiguredObject object, State oldState, State newState) { // no-op } @Override public void childAdded(ConfiguredObject object, ConfiguredObject child) { synchronized (_children) { try { AMQManagedObject mbean; if(child instanceof VirtualHost) { VirtualHost vhostChild = (VirtualHost)child; mbean = new VirtualHostMBean(vhostChild, _objectRegistry); } else if(child instanceof PasswordCredentialManagingAuthenticationProvider) { mbean = new UserManagementMBean((PasswordCredentialManagingAuthenticationProvider) child, _objectRegistry); } else { mbean = null; } if (mbean != null) { createAdditionalMBeansFromProviders(child, mbean); } } catch(JMException e) { LOGGER.error("Error creating mbean", e); // TODO - Implement error reporting on mbean creation } } } @Override public void childRemoved(ConfiguredObject object, ConfiguredObject child) { // TODO - implement vhost removal (possibly just removing the instanceof check below) synchronized (_children) { if(child instanceof PasswordCredentialManagingAuthenticationProvider) { AMQManagedObject mbean = _children.remove(child); if(mbean != null) { try { mbean.unregister(); } catch(JMException e) { LOGGER.error("Error creating mbean", e); //TODO - report error on removing child MBean } } } } } @Override public void attributeSet(ConfiguredObject object, String attributeName, Object oldAttributeValue, Object newAttributeValue) { // no-op } private void createAdditionalMBeansFromProviders(ConfiguredObject child, AMQManagedObject mbean) throws JMException { _children.put(child, mbean); QpidServiceLoader qpidServiceLoader = new QpidServiceLoader(); for (MBeanProvider provider : qpidServiceLoader.instancesOf(MBeanProvider.class)) { LOGGER.debug("Consulting mbean provider : " + provider + " for child : " + child); if (provider.isChildManageableByMBean(child)) { LOGGER.debug("Provider will create mbean "); provider.createMBean(child, mbean); // TODO track the mbeans that have been created on behalf of a child in a map, then // if the child is ever removed, destroy these beans too. } } } /** Added for testing purposes */ Broker getBroker() { return _broker; } @Override public String getName() { return (String)getAttribute(NAME); } @Override public Collection getAttributeNames() { return AVAILABLE_ATTRIBUTES; } }