blob: 8f087ba50c0e105a6963a040c9709f9ea8c96666 [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.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<String> AVAILABLE_ATTRIBUTES = Collections.unmodifiableCollection(new HashSet<String>(Plugin.AVAILABLE_ATTRIBUTES){{
add(NAME);
add(USE_PLATFORM_MBEAN_SERVER);
add(PluginFactory.PLUGIN_TYPE);
}});
@SuppressWarnings("serial")
private static final Map<String, Object> DEFAULTS = new HashMap<String, Object>(){{
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<String, Class<?>> ATTRIBUTE_TYPES = new HashMap<String, Class<?>>(){{
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<ConfiguredObject, AMQManagedObject> _children = new HashMap<ConfiguredObject, AMQManagedObject>();
public JMXManagement(UUID id, Broker broker, Map<String, Object> 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<Port> 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<AuthenticationProvider> 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<MBeanProvider> qpidServiceLoader = new QpidServiceLoader<MBeanProvider>();
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<String> getAttributeNames()
{
return AVAILABLE_ATTRIBUTES;
}
}