Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.eclipse.jetty.jmx.MBeanContainer Maven / Gradle / Ivy
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.jmx;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.modelmbean.ModelMBean;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.Container;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.component.Destroyable;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* Container class for the MBean instances
*/
@ManagedObject("The component that registers beans as MBeans")
public class MBeanContainer implements Container.InheritedListener, Dumpable, Destroyable
{
private static final Logger LOG = Log.getLogger(MBeanContainer.class.getName());
private static final ConcurrentMap __unique = new ConcurrentHashMap<>();
private static final Container ROOT = new ContainerLifeCycle();
private final MBeanServer _mbeanServer;
private final boolean _useCacheForOtherClassLoaders;
private final ConcurrentMap _metaData = new ConcurrentHashMap<>();
private final ConcurrentMap _beans = new ConcurrentHashMap<>();
private final ConcurrentMap _mbeans = new ConcurrentHashMap<>();
private String _domain = null;
/**
* Constructs MBeanContainer
*
* @param server instance of MBeanServer for use by container
*/
public MBeanContainer(MBeanServer server)
{
this(server, true);
}
/**
* Constructs MBeanContainer
*
* @param server instance of MBeanServer for use by container
* @param cacheOtherClassLoaders If true, MBeans from other classloaders (eg WebAppClassLoader) will be cached.
* The cache is never flushed, so this should be false if some classloaders do not live forever.
*/
public MBeanContainer(MBeanServer server, boolean cacheOtherClassLoaders)
{
_mbeanServer = server;
_useCacheForOtherClassLoaders = cacheOtherClassLoaders;
}
/**
* Retrieve instance of MBeanServer used by container
*
* @return instance of MBeanServer
*/
public MBeanServer getMBeanServer()
{
return _mbeanServer;
}
@ManagedAttribute(value = "Whether to use the cache for MBeans loaded by other ClassLoaders", readonly = true)
public boolean isUseCacheForOtherClassLoaders()
{
return _useCacheForOtherClassLoaders;
}
/**
* Set domain to be used to add MBeans
*
* @param domain domain name
*/
public void setDomain(String domain)
{
_domain = domain;
}
/**
* Retrieve domain name used to add MBeans
*
* @return domain name
*/
@ManagedAttribute("The default ObjectName domain")
public String getDomain()
{
return _domain;
}
/**
* Creates an ObjectMBean for the given object.
* Attempts to create an ObjectMBean for the object by searching the package
* and class name space. For example an object of the type:
*
* class com.acme.MyClass extends com.acme.util.BaseClass implements com.acme.Iface
*
* then this method would look for the following classes:
*
* com.acme.jmx.MyClassMBean
* com.acme.util.jmx.BaseClassMBean
* org.eclipse.jetty.jmx.ObjectMBean
*
*
* @param o The object
* @return A new instance of an MBean for the object or null.
*/
public Object mbeanFor(Object o)
{
return mbeanFor(this, o);
}
static Object mbeanFor(MBeanContainer container, Object o)
{
if (o == null)
return null;
Object mbean = findMetaData(container, o.getClass()).newInstance(o);
if (mbean instanceof ObjectMBean)
((ObjectMBean)mbean).setMBeanContainer(container);
if (LOG.isDebugEnabled())
{
LOG.debug("MBean for {} is {}", o, mbean);
if (mbean instanceof ObjectMBean)
{
MBeanInfo info = ((ObjectMBean)mbean).getMBeanInfo();
for (Object a : info.getAttributes())
{
LOG.debug(" {}", a);
}
for (Object a : info.getOperations())
{
LOG.debug(" {}", a);
}
}
}
return mbean;
}
static MetaData findMetaData(MBeanContainer container, Class> klass)
{
if (klass == null)
return null;
MetaData metaData = getMetaData(container, klass);
if (metaData != null)
{
if (LOG.isDebugEnabled())
LOG.debug("Found cached {}", metaData);
return metaData;
}
return newMetaData(container, klass);
}
private static MetaData getMetaData(MBeanContainer container, Class> klass)
{
return container == null ? null : container._metaData.get(klass);
}
private static MetaData newMetaData(MBeanContainer container, Class> klass)
{
if (klass == null)
return null;
if (klass == Object.class)
return new MetaData(klass, null, null, Collections.emptyList());
List interfaces = Arrays.stream(klass.getInterfaces())
.map(intf -> findMetaData(container, intf))
.collect(Collectors.toList());
MetaData metaData = new MetaData(klass, findConstructor(klass), findMetaData(container, klass.getSuperclass()), interfaces);
if (container != null)
{
if (container.isUseCacheForOtherClassLoaders() || klass.getClassLoader() == container.getClass().getClassLoader())
{
MetaData existing = container._metaData.putIfAbsent(klass, metaData);
if (existing != null)
metaData = existing;
if (LOG.isDebugEnabled())
LOG.debug("Cached {}", metaData);
}
}
return metaData;
}
private static Constructor> findConstructor(Class> klass)
{
Package pkg = klass.getPackage();
if (pkg == null)
return null;
String pName = pkg.getName();
String cName = klass.getName().substring(pName.isEmpty() ? 0 : pName.length() + 1);
String mName = pName + ".jmx." + cName + "MBean";
try
{
Class> mbeanClass = Loader.loadClass(klass, mName);
Constructor> constructor = ModelMBean.class.isAssignableFrom(mbeanClass)
? mbeanClass.getConstructor()
: mbeanClass.getConstructor(Object.class);
if (LOG.isDebugEnabled())
LOG.debug("Found MBean wrapper: {} for {}", mName, klass.getName());
return constructor;
}
catch (Throwable x)
{
if (LOG.isDebugEnabled())
LOG.debug("MBean wrapper not found: {} for {}", mName, klass.getName());
return null;
}
}
/**
* Lookup an object name by instance
*
* @param object instance for which object name is looked up
* @return object name associated with specified instance, or null if not found
*/
public ObjectName findMBean(Object object)
{
return _mbeans.get(object);
}
/**
* Lookup an instance by object name
*
* @param objectName object name of instance
* @return instance associated with specified object name, or null if not found
*/
public Object findBean(ObjectName objectName)
{
for (Map.Entry entry : _mbeans.entrySet())
{
if (entry.getValue().equals(objectName))
return entry.getKey();
}
return null;
}
@Override
public void beanAdded(Container parent, Object obj)
{
if (LOG.isDebugEnabled())
LOG.debug("beanAdded {}->{}", parent, obj);
if (obj == null)
return;
if (parent == null)
parent = ROOT;
// Is the bean already tracked ?
if (_beans.putIfAbsent(obj, parent) != null)
return;
// Is there an object name for the parent ?
ObjectName parentObjectName = null;
if (parent != ROOT)
{
parentObjectName = findMBean(parent);
if (parentObjectName == null)
{
// Create the parent bean.
beanAdded(null, parent);
parentObjectName = findMBean(parent);
}
}
try
{
// Create an MBean for the object.
Object mbean = mbeanFor(obj);
if (mbean == null)
return;
ObjectName objectName = null;
if (mbean instanceof ObjectMBean)
{
objectName = ((ObjectMBean)mbean).getObjectName();
}
// No override of the mbean's ObjectName, so make a generic one.
if (objectName == null)
{
Class> klass = obj.getClass();
while (klass.isArray())
{
klass = klass.getComponentType();
}
// If no explicit domain, create one.
String domain = _domain;
if (domain == null)
{
Package pkg = klass.getPackage();
domain = pkg == null ? "" : pkg.getName();
}
String type = klass.getName().toLowerCase(Locale.ENGLISH);
int dot = type.lastIndexOf('.');
if (dot >= 0)
type = type.substring(dot + 1);
StringBuilder buf = new StringBuilder();
String context = (mbean instanceof ObjectMBean) ? makeName(((ObjectMBean)mbean).getObjectContextBasis()) : null;
if (context == null && parentObjectName != null)
context = parentObjectName.getKeyProperty("context");
if (context != null && context.length() > 1)
buf.append("context=").append(context).append(",");
buf.append("type=").append(type);
String name = (mbean instanceof ObjectMBean) ? makeName(((ObjectMBean)mbean).getObjectNameBasis()) : context;
if (name != null && name.length() > 1)
buf.append(",").append("name=").append(name);
String basis = buf.toString();
AtomicInteger count = __unique.get(basis);
if (count == null)
{
count = new AtomicInteger();
AtomicInteger existing = __unique.putIfAbsent(basis, count);
if (existing != null)
count = existing;
}
objectName = ObjectName.getInstance(domain + ":" + basis + ",id=" + count.getAndIncrement());
}
_mbeanServer.registerMBean(mbean, objectName);
if (LOG.isDebugEnabled())
LOG.debug("Registered {}", objectName);
_mbeans.put(obj, objectName);
}
catch (Throwable x)
{
LOG.warn("bean: " + obj, x);
}
}
@Override
public void beanRemoved(Container parent, Object obj)
{
if (LOG.isDebugEnabled())
LOG.debug("beanRemoved {}->{}", parent, obj);
if (parent == null)
parent = ROOT;
if (_beans.remove(obj, parent))
{
ObjectName objectName = _mbeans.remove(obj);
if (objectName != null)
unregister(objectName);
}
}
/**
* @param basis name to strip of special characters.
* @return normalized name
*/
public String makeName(String basis)
{
return StringUtil.sanitizeFileSystemName(basis);
}
@Override
public void dump(Appendable out, String indent) throws IOException
{
Dumpable.dumpObjects(out, indent, this, _mbeans.entrySet());
}
@Override
public String dump()
{
return Dumpable.dump(this);
}
@Override
public void destroy()
{
_metaData.clear();
_mbeans.values().stream()
.filter(Objects::nonNull)
.forEach(this::unregister);
_mbeans.clear();
_beans.clear();
}
private void unregister(ObjectName objectName)
{
try
{
getMBeanServer().unregisterMBean(objectName);
if (LOG.isDebugEnabled())
LOG.debug("Unregistered {}", objectName);
}
catch (MBeanRegistrationException | InstanceNotFoundException x)
{
LOG.ignore(x);
}
catch (Throwable x)
{
LOG.warn(x);
}
}
}