Issue #2727 - Revisit JMX MBean lookup behavior.
Rewrote JMX implementation using caches to improve performance of JMX lookups. Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
parent
b225ebe756
commit
75b2c1d001
|
@ -19,18 +19,23 @@
|
||||||
package org.eclipse.jetty.jmx;
|
package org.eclipse.jetty.jmx;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.management.InstanceNotFoundException;
|
import javax.management.InstanceNotFoundException;
|
||||||
import javax.management.MBeanRegistrationException;
|
import javax.management.MBeanRegistrationException;
|
||||||
import javax.management.MBeanServer;
|
import javax.management.MBeanServer;
|
||||||
import javax.management.ObjectName;
|
import javax.management.ObjectName;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||||
import org.eclipse.jetty.util.component.Container;
|
import org.eclipse.jetty.util.component.Container;
|
||||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||||
|
@ -45,15 +50,151 @@ import org.eclipse.jetty.util.log.Logger;
|
||||||
@ManagedObject("The component that registers beans as MBeans")
|
@ManagedObject("The component that registers beans as MBeans")
|
||||||
public class MBeanContainer implements Container.InheritedListener, Dumpable, Destroyable
|
public class MBeanContainer implements Container.InheritedListener, Dumpable, Destroyable
|
||||||
{
|
{
|
||||||
private final static Logger LOG = Log.getLogger(MBeanContainer.class.getName());
|
private static final Logger LOG = Log.getLogger(MBeanContainer.class.getName());
|
||||||
private final static ConcurrentMap<String, AtomicInteger> __unique = new ConcurrentHashMap<>();
|
private static final ConcurrentMap<String, AtomicInteger> __unique = new ConcurrentHashMap<>();
|
||||||
private static final Container ROOT = new ContainerLifeCycle();
|
private static final Container ROOT = new ContainerLifeCycle();
|
||||||
|
|
||||||
private final MBeanServer _mbeanServer;
|
private final MBeanServer _mbeanServer;
|
||||||
|
private final boolean _useCacheForOtherClassLoaders;
|
||||||
|
private final ConcurrentMap<Class, MetaData> _metaData = new ConcurrentHashMap<>();
|
||||||
private final ConcurrentMap<Object, Container> _beans = new ConcurrentHashMap<>();
|
private final ConcurrentMap<Object, Container> _beans = new ConcurrentHashMap<>();
|
||||||
private final ConcurrentMap<Object, ObjectName> _mbeans = new ConcurrentHashMap<>();
|
private final ConcurrentMap<Object, ObjectName> _mbeans = new ConcurrentHashMap<>();
|
||||||
private String _domain = null;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates an ObjectMBean for the given object.</p>
|
||||||
|
* <p>Attempts to create an ObjectMBean for the object by searching the package
|
||||||
|
* and class name space. For example an object of the type:</p>
|
||||||
|
* <pre>
|
||||||
|
* class com.acme.MyClass extends com.acme.util.BaseClass implements com.acme.Iface
|
||||||
|
* </pre>
|
||||||
|
* <p>then this method would look for the following classes:</p>
|
||||||
|
* <ul>
|
||||||
|
* <li>com.acme.jmx.MyClassMBean</li>
|
||||||
|
* <li>com.acme.util.jmx.BaseClassMBean</li>
|
||||||
|
* <li>org.eclipse.jetty.jmx.ObjectMBean</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @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("mbeanFor {} is {}", o, mbean);
|
||||||
|
return mbean;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MetaData findMetaData(MBeanContainer container, Class<?> klass)
|
||||||
|
{
|
||||||
|
if (klass == null)
|
||||||
|
return null;
|
||||||
|
MetaData metaData = getMetaData(container, klass);
|
||||||
|
if (metaData != null)
|
||||||
|
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, Collections.emptyList());
|
||||||
|
|
||||||
|
List<MetaData> interfaces = Arrays.stream(klass.getInterfaces())
|
||||||
|
.map(iClass -> findMetaData(container, iClass))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
MetaData metaData = new MetaData(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return metaData;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lookup an object name by instance
|
* Lookup an object name by instance
|
||||||
*
|
*
|
||||||
|
@ -81,47 +222,6 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable, De
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs MBeanContainer
|
|
||||||
*
|
|
||||||
* @param server instance of MBeanServer for use by container
|
|
||||||
*/
|
|
||||||
public MBeanContainer(MBeanServer server)
|
|
||||||
{
|
|
||||||
_mbeanServer = server;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve instance of MBeanServer used by container
|
|
||||||
*
|
|
||||||
* @return instance of MBeanServer
|
|
||||||
*/
|
|
||||||
public MBeanServer getMBeanServer()
|
|
||||||
{
|
|
||||||
return _mbeanServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
public String getDomain()
|
|
||||||
{
|
|
||||||
return _domain;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beanAdded(Container parent, Object obj)
|
public void beanAdded(Container parent, Object obj)
|
||||||
{
|
{
|
||||||
|
@ -154,14 +254,13 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable, De
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Create an MBean for the object.
|
// Create an MBean for the object.
|
||||||
Object mbean = ObjectMBean.mbeanFor(obj);
|
Object mbean = mbeanFor(obj);
|
||||||
if (mbean == null)
|
if (mbean == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ObjectName objectName = null;
|
ObjectName objectName = null;
|
||||||
if (mbean instanceof ObjectMBean)
|
if (mbean instanceof ObjectMBean)
|
||||||
{
|
{
|
||||||
((ObjectMBean)mbean).setMBeanContainer(this);
|
|
||||||
objectName = ((ObjectMBean)mbean).getObjectName();
|
objectName = ((ObjectMBean)mbean).getObjectName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,6 +368,7 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable, De
|
||||||
@Override
|
@Override
|
||||||
public void destroy()
|
public void destroy()
|
||||||
{
|
{
|
||||||
|
_metaData.clear();
|
||||||
_mbeans.values().stream()
|
_mbeans.values().stream()
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.forEach(this::unregister);
|
.forEach(this::unregister);
|
||||||
|
|
|
@ -0,0 +1,515 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// 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.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.management.Attribute;
|
||||||
|
import javax.management.AttributeNotFoundException;
|
||||||
|
import javax.management.MBeanAttributeInfo;
|
||||||
|
import javax.management.MBeanConstructorInfo;
|
||||||
|
import javax.management.MBeanException;
|
||||||
|
import javax.management.MBeanInfo;
|
||||||
|
import javax.management.MBeanNotificationInfo;
|
||||||
|
import javax.management.MBeanOperationInfo;
|
||||||
|
import javax.management.MBeanParameterInfo;
|
||||||
|
import javax.management.ObjectName;
|
||||||
|
import javax.management.ReflectionException;
|
||||||
|
import javax.management.modelmbean.ModelMBean;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.Loader;
|
||||||
|
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||||
|
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||||
|
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
||||||
|
import org.eclipse.jetty.util.annotation.Name;
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
class MetaData
|
||||||
|
{
|
||||||
|
private static final Logger LOG = Log.getLogger(MetaData.class);
|
||||||
|
|
||||||
|
private final Map<String, AttributeInfo> _attributes = new HashMap<>();
|
||||||
|
private final Map<String, OperationInfo> _operations = new HashMap<>();
|
||||||
|
private final Class<?> _klass;
|
||||||
|
private final MetaData _parent;
|
||||||
|
private final List<MetaData> _interfaces;
|
||||||
|
private final Constructor<?> _constructor;
|
||||||
|
private final MBeanInfo _info;
|
||||||
|
|
||||||
|
MetaData(Class<?> klass, MetaData parent, List<MetaData> interfaces)
|
||||||
|
{
|
||||||
|
_klass = klass;
|
||||||
|
_parent = parent;
|
||||||
|
_interfaces = interfaces;
|
||||||
|
_constructor = findConstructor(klass);
|
||||||
|
if (_constructor != null)
|
||||||
|
parseMethods(klass, _constructor.getDeclaringClass());
|
||||||
|
else
|
||||||
|
parseMethods(klass);
|
||||||
|
_info = buildMBeanInfo(klass);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object newInstance(Object bean)
|
||||||
|
{
|
||||||
|
Object mbean;
|
||||||
|
if (_constructor != null)
|
||||||
|
mbean = newInstance(_constructor, bean);
|
||||||
|
else if (_parent != null)
|
||||||
|
mbean = _parent.newInstance(bean);
|
||||||
|
else
|
||||||
|
mbean = new ObjectMBean(bean);
|
||||||
|
return mbean;
|
||||||
|
}
|
||||||
|
|
||||||
|
MBeanInfo getMBeanInfo()
|
||||||
|
{
|
||||||
|
return _info;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object getAttribute(String name, ObjectMBean mbean) throws AttributeNotFoundException, ReflectionException
|
||||||
|
{
|
||||||
|
AttributeInfo info = findAttribute(name);
|
||||||
|
if (info == null)
|
||||||
|
throw new AttributeNotFoundException(name);
|
||||||
|
return info.getAttribute(mbean);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAttribute(Attribute attribute, ObjectMBean mbean) throws AttributeNotFoundException, ReflectionException
|
||||||
|
{
|
||||||
|
if (attribute == null)
|
||||||
|
return;
|
||||||
|
String name = attribute.getName();
|
||||||
|
AttributeInfo info = findAttribute(name);
|
||||||
|
if (info == null)
|
||||||
|
throw new AttributeNotFoundException(name);
|
||||||
|
info.setAttribute(attribute.getValue(), mbean);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AttributeInfo findAttribute(String name)
|
||||||
|
{
|
||||||
|
AttributeInfo result = _attributes.get(name);
|
||||||
|
if (result != null)
|
||||||
|
return result;
|
||||||
|
for (MetaData intf : _interfaces)
|
||||||
|
{
|
||||||
|
result = intf.findAttribute(name);
|
||||||
|
if (result != null)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (_parent != null)
|
||||||
|
return _parent.findAttribute(name);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object invoke(String name, String[] params, Object[] args, ObjectMBean mbean) throws ReflectionException, MBeanException
|
||||||
|
{
|
||||||
|
String signature = signature(name, params);
|
||||||
|
OperationInfo info = findOperation(signature);
|
||||||
|
if (info == null)
|
||||||
|
throw new ReflectionException(new NoSuchMethodException(signature));
|
||||||
|
return info.invoke(args, mbean);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OperationInfo findOperation(String signature)
|
||||||
|
{
|
||||||
|
OperationInfo result = _operations.get(signature);
|
||||||
|
if (result != null)
|
||||||
|
return result;
|
||||||
|
for (MetaData intf : _interfaces)
|
||||||
|
{
|
||||||
|
result = intf.findOperation(signature);
|
||||||
|
if (result != null)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (_parent != null)
|
||||||
|
return _parent.findOperation(signature);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Constructor<?> findConstructor(Class<?> klass)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String pName = klass.getPackage().getName();
|
||||||
|
String cName = klass.getName().substring(pName.length() + 1);
|
||||||
|
String mName = pName + ".jmx." + cName + "MBean";
|
||||||
|
Class<?> mbeanClass = Loader.loadClass(mName);
|
||||||
|
return ModelMBean.class.isAssignableFrom(mbeanClass)
|
||||||
|
? mbeanClass.getConstructor()
|
||||||
|
: mbeanClass.getConstructor(Object.class);
|
||||||
|
}
|
||||||
|
catch (Throwable x)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object newInstance(Constructor<?> constructor, Object bean)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Object mbean = constructor.getParameterCount() == 0 ? constructor.newInstance() : constructor.newInstance(bean);
|
||||||
|
if (mbean instanceof ModelMBean)
|
||||||
|
((ModelMBean)mbean).setManagedResource(bean, "objectReference");
|
||||||
|
return mbean;
|
||||||
|
}
|
||||||
|
catch (Throwable x)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseMethods(Class<?>... classes)
|
||||||
|
{
|
||||||
|
for (Class<?> klass : classes)
|
||||||
|
{
|
||||||
|
for (Method method : klass.getDeclaredMethods())
|
||||||
|
{
|
||||||
|
if (!Modifier.isPublic(method.getModifiers()))
|
||||||
|
continue;
|
||||||
|
ManagedAttribute attribute = method.getAnnotation(ManagedAttribute.class);
|
||||||
|
if (attribute != null)
|
||||||
|
{
|
||||||
|
AttributeInfo info = new AttributeInfo(attribute, method);
|
||||||
|
_attributes.put(info._name, info);
|
||||||
|
}
|
||||||
|
ManagedOperation operation = method.getAnnotation(ManagedOperation.class);
|
||||||
|
if (operation != null)
|
||||||
|
{
|
||||||
|
OperationInfo info = new OperationInfo(operation, method);
|
||||||
|
_operations.put(info._name, info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String toAttributeName(String methodName)
|
||||||
|
{
|
||||||
|
String attributeName = methodName;
|
||||||
|
if (methodName.startsWith("get") || methodName.startsWith("set"))
|
||||||
|
attributeName = attributeName.substring(3);
|
||||||
|
else if (methodName.startsWith("is"))
|
||||||
|
attributeName = attributeName.substring(2);
|
||||||
|
return attributeName.substring(0, 1).toLowerCase(Locale.ENGLISH) + attributeName.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isManagedObject(Class<?> klass)
|
||||||
|
{
|
||||||
|
if (klass.isArray())
|
||||||
|
klass = klass.getComponentType();
|
||||||
|
if (klass.isPrimitive())
|
||||||
|
return false;
|
||||||
|
while (klass != Object.class)
|
||||||
|
{
|
||||||
|
if (klass.isAnnotationPresent(ManagedObject.class))
|
||||||
|
return true;
|
||||||
|
klass = klass.getSuperclass();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String signature(String name, String[] params)
|
||||||
|
{
|
||||||
|
return String.format("%s(%s)", name, String.join(",", params));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String signature(Method method)
|
||||||
|
{
|
||||||
|
String signature = Arrays.stream(method.getParameterTypes())
|
||||||
|
.map(Class::getName)
|
||||||
|
.collect(Collectors.joining(","));
|
||||||
|
return String.format("%s(%s)", method.getName(), signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MBeanInfo buildMBeanInfo(Class<?> klass)
|
||||||
|
{
|
||||||
|
ManagedObject managedObject = klass.getAnnotation(ManagedObject.class);
|
||||||
|
String description = managedObject == null ? "" : managedObject.value();
|
||||||
|
|
||||||
|
List<MBeanAttributeInfo> attributeInfos = new ArrayList<>();
|
||||||
|
collectMBeanAttributeInfos(attributeInfos);
|
||||||
|
|
||||||
|
List<MBeanOperationInfo> operationInfos = new ArrayList<>();
|
||||||
|
collectMBeanOperationInfos(operationInfos);
|
||||||
|
|
||||||
|
return new MBeanInfo(klass.getName(), description, attributeInfos.toArray(new MBeanAttributeInfo[0]), new MBeanConstructorInfo[0], operationInfos.toArray(new MBeanOperationInfo[0]), new MBeanNotificationInfo[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collectMBeanAttributeInfos(List<MBeanAttributeInfo> attributeInfos)
|
||||||
|
{
|
||||||
|
_attributes.values().stream()
|
||||||
|
.map(info -> info._info)
|
||||||
|
.collect(Collectors.toCollection(() -> attributeInfos));
|
||||||
|
for (MetaData intf : _interfaces)
|
||||||
|
intf.collectMBeanAttributeInfos(attributeInfos);
|
||||||
|
if (_parent != null)
|
||||||
|
_parent.collectMBeanAttributeInfos(attributeInfos);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collectMBeanOperationInfos(List<MBeanOperationInfo> operationInfos)
|
||||||
|
{
|
||||||
|
_operations.values().stream()
|
||||||
|
.map(info -> info._info)
|
||||||
|
.collect(Collectors.toCollection(() -> operationInfos));
|
||||||
|
for (MetaData intf : _interfaces)
|
||||||
|
intf.collectMBeanOperationInfos(operationInfos);
|
||||||
|
if (_parent != null)
|
||||||
|
_parent.collectMBeanOperationInfos(operationInfos);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ObjectInfo
|
||||||
|
{
|
||||||
|
private final String _description;
|
||||||
|
|
||||||
|
private ObjectInfo(Class<?> klass)
|
||||||
|
{
|
||||||
|
ManagedObject managedObject = klass.getAnnotation(ManagedObject.class);
|
||||||
|
_description = managedObject == null ? "" : managedObject.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AttributeInfo
|
||||||
|
{
|
||||||
|
private final String _name;
|
||||||
|
private final Method _getter;
|
||||||
|
private final Method _setter;
|
||||||
|
private final boolean _proxied;
|
||||||
|
private final boolean _convert;
|
||||||
|
private final MBeanAttributeInfo _info;
|
||||||
|
|
||||||
|
private AttributeInfo(ManagedAttribute attribute, Method getter)
|
||||||
|
{
|
||||||
|
String name = attribute.name();
|
||||||
|
if ("".equals(name))
|
||||||
|
name = toAttributeName(getter.getName());
|
||||||
|
_name = name;
|
||||||
|
|
||||||
|
_getter = getter;
|
||||||
|
|
||||||
|
boolean readOnly = attribute.readonly();
|
||||||
|
_setter = readOnly ? null : findSetter(attribute, getter, name);
|
||||||
|
|
||||||
|
_proxied = attribute.proxied();
|
||||||
|
|
||||||
|
Class<?> returnType = getter.getReturnType();
|
||||||
|
_convert = isManagedObject(returnType);
|
||||||
|
String signature = _convert ?
|
||||||
|
returnType.isArray() ? ObjectName[].class.getName() : ObjectName.class.getName() :
|
||||||
|
returnType.getName();
|
||||||
|
|
||||||
|
String description = attribute.value();
|
||||||
|
_info = new MBeanAttributeInfo(name, signature, description, true,
|
||||||
|
_setter != null, getter.getName().startsWith("is"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Object getAttribute(ObjectMBean mbean) throws ReflectionException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Object target = mbean.getManagedObject();
|
||||||
|
if (_proxied || _getter.getDeclaringClass().isInstance(mbean))
|
||||||
|
target = mbean;
|
||||||
|
Object result = _getter.invoke(target);
|
||||||
|
if (result == null)
|
||||||
|
return null;
|
||||||
|
if (!_convert)
|
||||||
|
return result;
|
||||||
|
if (!_getter.getReturnType().isArray())
|
||||||
|
return mbean.findObjectName(result);
|
||||||
|
int length = Array.getLength(result);
|
||||||
|
ObjectName[] names = new ObjectName[length];
|
||||||
|
for (int i = 0; i < length; ++i)
|
||||||
|
names[i] = mbean.findObjectName(Array.get(result, i));
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
catch (Exception x)
|
||||||
|
{
|
||||||
|
throw new ReflectionException(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAttribute(Object value, ObjectMBean mbean) throws ReflectionException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_setter == null)
|
||||||
|
return;
|
||||||
|
Object target = mbean.getManagedObject();
|
||||||
|
if (_proxied || _setter.getDeclaringClass().isInstance(mbean))
|
||||||
|
target = mbean;
|
||||||
|
if (!_convert || value == null)
|
||||||
|
{
|
||||||
|
_setter.invoke(target, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!_getter.getReturnType().isArray())
|
||||||
|
{
|
||||||
|
value = mbean.findBean((ObjectName)value);
|
||||||
|
_setter.invoke(target, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ObjectName[] names = (ObjectName[])value;
|
||||||
|
Object result = new Object[names.length];
|
||||||
|
for (int i = 0; i < names.length; ++i)
|
||||||
|
Array.set(result, i, mbean.findBean(names[i]));
|
||||||
|
_setter.invoke(target, result);
|
||||||
|
}
|
||||||
|
catch (Exception x)
|
||||||
|
{
|
||||||
|
throw new ReflectionException(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Method findSetter(ManagedAttribute attribute, Method getter, String name)
|
||||||
|
{
|
||||||
|
String setterName = attribute.setter();
|
||||||
|
if ("".equals(setterName))
|
||||||
|
setterName = "set" + name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1);
|
||||||
|
|
||||||
|
Method setter = null;
|
||||||
|
Class<?> klass = getter.getDeclaringClass();
|
||||||
|
for (Method method : klass.getMethods())
|
||||||
|
{
|
||||||
|
if (method.getName().equals(setterName) && method.getParameterCount() == 1)
|
||||||
|
{
|
||||||
|
if (setter != null)
|
||||||
|
{
|
||||||
|
LOG.info("Multiple setters for mbean attribute {} in {}", name, klass);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!getter.getReturnType().equals(method.getParameterTypes()[0]))
|
||||||
|
{
|
||||||
|
LOG.info("Getter/setter type mismatch for mbean attribute {} in {}", name, klass);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
setter = method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return setter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class OperationInfo
|
||||||
|
{
|
||||||
|
private final String _name;
|
||||||
|
private final Method _method;
|
||||||
|
private final boolean _proxied;
|
||||||
|
private final boolean _convert;
|
||||||
|
private final MBeanOperationInfo _info;
|
||||||
|
|
||||||
|
private OperationInfo(ManagedOperation operation, Method method)
|
||||||
|
{
|
||||||
|
_name = signature(method);
|
||||||
|
|
||||||
|
_method = method;
|
||||||
|
|
||||||
|
_proxied = operation.proxied();
|
||||||
|
|
||||||
|
Class<?> returnType = method.getReturnType();
|
||||||
|
_convert = isManagedObject(returnType);
|
||||||
|
String returnSignature = _convert ?
|
||||||
|
returnType.isArray() ? ObjectName[].class.getName() : ObjectName.class.getName() :
|
||||||
|
returnType.getName();
|
||||||
|
|
||||||
|
String impactName = operation.impact();
|
||||||
|
int impact = MBeanOperationInfo.UNKNOWN;
|
||||||
|
if ("ACTION".equals(impactName))
|
||||||
|
impact = MBeanOperationInfo.ACTION;
|
||||||
|
else if ("INFO".equals(impactName))
|
||||||
|
impact = MBeanOperationInfo.INFO;
|
||||||
|
else if ("ACTION_INFO".equals(impactName))
|
||||||
|
impact = MBeanOperationInfo.ACTION_INFO;
|
||||||
|
|
||||||
|
String description = operation.value();
|
||||||
|
MBeanParameterInfo[] parameterInfos = parameters(method.getParameterTypes(), method.getParameterAnnotations());
|
||||||
|
_info = new MBeanOperationInfo(method.getName(), description, parameterInfos, returnSignature, impact);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object invoke(Object[] args, ObjectMBean mbean) throws ReflectionException, MBeanException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Object target = mbean.getManagedObject();
|
||||||
|
if (_proxied || _method.getDeclaringClass().isInstance(mbean))
|
||||||
|
target = mbean;
|
||||||
|
Object result = _method.invoke(target, args);
|
||||||
|
if (result == null)
|
||||||
|
return null;
|
||||||
|
if (!_convert)
|
||||||
|
return result;
|
||||||
|
if (!_method.getReturnType().isArray())
|
||||||
|
return mbean.findObjectName(result);
|
||||||
|
int length = Array.getLength(result);
|
||||||
|
ObjectName[] names = new ObjectName[length];
|
||||||
|
for (int i = 0; i < length; ++i)
|
||||||
|
names[i] = mbean.findObjectName(Array.get(result, i));
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
catch (InvocationTargetException x)
|
||||||
|
{
|
||||||
|
Throwable cause = x.getCause();
|
||||||
|
if (cause instanceof Exception)
|
||||||
|
throw new MBeanException((Exception)cause);
|
||||||
|
else
|
||||||
|
throw new MBeanException(new RuntimeException(cause));
|
||||||
|
}
|
||||||
|
catch (Exception x)
|
||||||
|
{
|
||||||
|
throw new ReflectionException(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MBeanParameterInfo[] parameters(Class<?>[] parameterTypes, Annotation[][] parametersAnnotations)
|
||||||
|
{
|
||||||
|
MBeanParameterInfo[] result = new MBeanParameterInfo[parameterTypes.length];
|
||||||
|
for (int i = 0; i < parametersAnnotations.length; i++)
|
||||||
|
{
|
||||||
|
MBeanParameterInfo info = null;
|
||||||
|
String typeName = parameterTypes[i].getName();
|
||||||
|
Annotation[] parameterAnnotations = parametersAnnotations[i];
|
||||||
|
for (Annotation parameterAnnotation : parameterAnnotations)
|
||||||
|
{
|
||||||
|
if (parameterAnnotation instanceof Name)
|
||||||
|
{
|
||||||
|
Name name = (Name)parameterAnnotation;
|
||||||
|
info = result[i] = new MBeanParameterInfo(name.value(), typeName, name.description());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (info == null)
|
||||||
|
result[i] = new MBeanParameterInfo("p" + i, typeName, "");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,42 +18,15 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.jmx;
|
package org.eclipse.jetty.jmx;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Array;
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.management.Attribute;
|
import javax.management.Attribute;
|
||||||
import javax.management.AttributeList;
|
import javax.management.AttributeList;
|
||||||
import javax.management.AttributeNotFoundException;
|
import javax.management.AttributeNotFoundException;
|
||||||
import javax.management.DynamicMBean;
|
import javax.management.DynamicMBean;
|
||||||
import javax.management.MBeanAttributeInfo;
|
|
||||||
import javax.management.MBeanConstructorInfo;
|
|
||||||
import javax.management.MBeanException;
|
import javax.management.MBeanException;
|
||||||
import javax.management.MBeanInfo;
|
import javax.management.MBeanInfo;
|
||||||
import javax.management.MBeanNotificationInfo;
|
|
||||||
import javax.management.MBeanOperationInfo;
|
|
||||||
import javax.management.MBeanParameterInfo;
|
|
||||||
import javax.management.ObjectName;
|
import javax.management.ObjectName;
|
||||||
import javax.management.ReflectionException;
|
import javax.management.ReflectionException;
|
||||||
import javax.management.modelmbean.ModelMBean;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.util.Loader;
|
|
||||||
import org.eclipse.jetty.util.TypeUtil;
|
|
||||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
|
||||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
|
||||||
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
|
||||||
import org.eclipse.jetty.util.annotation.Name;
|
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
@ -72,119 +45,11 @@ import org.eclipse.jetty.util.log.Logger;
|
||||||
public class ObjectMBean implements DynamicMBean
|
public class ObjectMBean implements DynamicMBean
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(ObjectMBean.class);
|
private static final Logger LOG = Log.getLogger(ObjectMBean.class);
|
||||||
private static final Class<?>[] OBJ_ARG = new Class[]{Object.class};
|
|
||||||
private static final String OBJECT_NAME_CLASS = ObjectName.class.getName();
|
|
||||||
private static final String OBJECT_NAME_ARRAY_CLASS = ObjectName[].class.getName();
|
|
||||||
|
|
||||||
protected Object _managed;
|
protected final Object _managed;
|
||||||
private MBeanInfo _info;
|
private MetaData _metaData;
|
||||||
private Map<String, Method> _getters = new HashMap<>();
|
|
||||||
private Map<String, Method> _setters = new HashMap<>();
|
|
||||||
private Map<String, Method> _methods = new HashMap<>();
|
|
||||||
// set of attributes mined from influence hierarchy
|
|
||||||
private Set<String> _attributes = new HashSet<>();
|
|
||||||
// set of attributes that are automatically converted to ObjectName
|
|
||||||
// as they represent other managed beans which can be linked to
|
|
||||||
private Set<String> _convert = new HashSet<>();
|
|
||||||
private ClassLoader _loader;
|
|
||||||
private MBeanContainer _mbeanContainer;
|
private MBeanContainer _mbeanContainer;
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Creates an ObjectMBean for the given object.</p>
|
|
||||||
* <p>Attempts to create an ObjectMBean for the object by searching the package
|
|
||||||
* and class name space. For example an object of the type:</p>
|
|
||||||
* <pre>
|
|
||||||
* class com.acme.MyClass extends com.acme.util.BaseClass implements com.acme.Iface
|
|
||||||
* </pre>
|
|
||||||
* <p>then this method would look for the following classes:</p>
|
|
||||||
* <ul>
|
|
||||||
* <li>com.acme.jmx.MyClassMBean</li>
|
|
||||||
* <li>com.acme.util.jmx.BaseClassMBean</li>
|
|
||||||
* <li>org.eclipse.jetty.jmx.ObjectMBean</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @param o The object
|
|
||||||
* @return A new instance of an MBean for the object or null.
|
|
||||||
*/
|
|
||||||
public static Object mbeanFor(Object o)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Class<?> oClass = o.getClass();
|
|
||||||
while (oClass != null)
|
|
||||||
{
|
|
||||||
String pName = oClass.getPackage().getName();
|
|
||||||
String cName = oClass.getName().substring(pName.length() + 1);
|
|
||||||
String mName = pName + ".jmx." + cName + "MBean";
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Class<?> mClass;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Look for an MBean class from the same loader that loaded the original class
|
|
||||||
mClass = (Object.class.equals(oClass)) ? oClass = ObjectMBean.class : Loader.loadClass(oClass, mName);
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException e)
|
|
||||||
{
|
|
||||||
// Not found, so if not the same as the thread context loader, try that.
|
|
||||||
if (Thread.currentThread().getContextClassLoader() == oClass.getClassLoader())
|
|
||||||
throw e;
|
|
||||||
LOG.ignore(e);
|
|
||||||
mClass = Loader.loadClass(oClass, mName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("ObjectMBean: mbeanFor {} mClass={}", o, mClass);
|
|
||||||
|
|
||||||
Object mbean = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Constructor<?> constructor = mClass.getConstructor(OBJ_ARG);
|
|
||||||
mbean = constructor.newInstance(o);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOG.ignore(e);
|
|
||||||
if (ModelMBean.class.isAssignableFrom(mClass))
|
|
||||||
{
|
|
||||||
mbean = mClass.getDeclaredConstructor().newInstance();
|
|
||||||
((ModelMBean)mbean).setManagedResource(o, "objectReference");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("mbeanFor {} is {}", o, mbean);
|
|
||||||
|
|
||||||
return mbean;
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException e)
|
|
||||||
{
|
|
||||||
// The code below was modified to fix bugs 332200 and JETTY-1416
|
|
||||||
// The issue was caused by additional information added to the
|
|
||||||
// message after the class name when running in Apache Felix,
|
|
||||||
// as well as before the class name when running in JBoss.
|
|
||||||
if (e.getMessage().contains(mName))
|
|
||||||
LOG.ignore(e);
|
|
||||||
else
|
|
||||||
LOG.warn(e);
|
|
||||||
}
|
|
||||||
catch (Throwable e)
|
|
||||||
{
|
|
||||||
LOG.warn(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
oClass = oClass.getSuperclass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOG.ignore(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new ObjectMBean wrapping the given {@code managedObject}.
|
* Creates a new ObjectMBean wrapping the given {@code managedObject}.
|
||||||
*
|
*
|
||||||
|
@ -193,7 +58,6 @@ public class ObjectMBean implements DynamicMBean
|
||||||
public ObjectMBean(Object managedObject)
|
public ObjectMBean(Object managedObject)
|
||||||
{
|
{
|
||||||
_managed = managedObject;
|
_managed = managedObject;
|
||||||
_loader = Thread.currentThread().getContextClassLoader();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -257,177 +121,34 @@ public class ObjectMBean implements DynamicMBean
|
||||||
return this._mbeanContainer;
|
return this._mbeanContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param o the object to wrap as MBean
|
||||||
|
* @return a new instance of an MBean for the object or null if the MBean cannot be created
|
||||||
|
* @deprecated Use {@link MBeanContainer#mbeanFor(Object)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static Object mbeanFor(Object o)
|
||||||
|
{
|
||||||
|
return MBeanContainer.mbeanFor(null, o);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MBeanInfo getMBeanInfo()
|
public MBeanInfo getMBeanInfo()
|
||||||
{
|
{
|
||||||
try
|
return metaData().getMBeanInfo();
|
||||||
{
|
|
||||||
if (_info == null)
|
|
||||||
{
|
|
||||||
String desc = null;
|
|
||||||
List<MBeanAttributeInfo> attributes = new ArrayList<>();
|
|
||||||
List<MBeanOperationInfo> operations = new ArrayList<>();
|
|
||||||
|
|
||||||
// Find list of classes that can influence the mbean
|
|
||||||
Class<?> o_class = _managed.getClass();
|
|
||||||
List<Class<?>> influences = new ArrayList<>();
|
|
||||||
influences.add(this.getClass()); // always add MBean itself
|
|
||||||
influences = findInfluences(influences, _managed.getClass());
|
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("Influence Count: {}", influences.size());
|
|
||||||
|
|
||||||
// Process Type Annotations
|
|
||||||
ManagedObject primary = o_class.getAnnotation(ManagedObject.class);
|
|
||||||
|
|
||||||
if (primary != null)
|
|
||||||
{
|
|
||||||
desc = primary.value();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("No @ManagedObject declared on {}", _managed.getClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each influence
|
|
||||||
for (Class<?> oClass : influences)
|
|
||||||
{
|
|
||||||
ManagedObject typeAnnotation = oClass.getAnnotation(ManagedObject.class);
|
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("Influenced by: " + oClass.getCanonicalName());
|
|
||||||
|
|
||||||
if (typeAnnotation == null)
|
|
||||||
{
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("Annotations not found for: {}", oClass.getCanonicalName());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process Method Annotations
|
|
||||||
|
|
||||||
for (Method method : oClass.getDeclaredMethods())
|
|
||||||
{
|
|
||||||
ManagedAttribute methodAttributeAnnotation = method.getAnnotation(ManagedAttribute.class);
|
|
||||||
|
|
||||||
if (methodAttributeAnnotation != null)
|
|
||||||
{
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("Attribute Annotation found for: {}", method.getName());
|
|
||||||
MBeanAttributeInfo mai = defineAttribute(method, methodAttributeAnnotation);
|
|
||||||
if (mai != null)
|
|
||||||
attributes.add(mai);
|
|
||||||
}
|
|
||||||
|
|
||||||
ManagedOperation methodOperationAnnotation = method.getAnnotation(ManagedOperation.class);
|
|
||||||
|
|
||||||
if (methodOperationAnnotation != null)
|
|
||||||
{
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("Method Annotation found for: {}", method.getName());
|
|
||||||
MBeanOperationInfo oi = defineOperation(method, methodOperationAnnotation);
|
|
||||||
if (oi != null)
|
|
||||||
operations.add(oi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_info = new MBeanInfo(o_class.getName(),
|
|
||||||
desc,
|
|
||||||
attributes.toArray(new MBeanAttributeInfo[attributes.size()]),
|
|
||||||
new MBeanConstructorInfo[0],
|
|
||||||
operations.toArray(new MBeanOperationInfo[operations.size()]),
|
|
||||||
new MBeanNotificationInfo[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (RuntimeException e)
|
|
||||||
{
|
|
||||||
LOG.warn(e);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
return _info;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getAttribute(String name) throws AttributeNotFoundException, ReflectionException
|
public Object getAttribute(String name) throws AttributeNotFoundException, ReflectionException
|
||||||
{
|
{
|
||||||
Method getter = _getters.get(name);
|
ClassLoader prevLoader = Thread.currentThread().getContextClassLoader();
|
||||||
if (getter == null)
|
|
||||||
throw new AttributeNotFoundException(name);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Object o = _managed;
|
return metaData().getAttribute(name, this);
|
||||||
if (getter.getDeclaringClass().isInstance(this))
|
|
||||||
o = this; // mbean method
|
|
||||||
|
|
||||||
// get the attribute
|
|
||||||
Object r = getter.invoke(o, (java.lang.Object[])null);
|
|
||||||
|
|
||||||
// convert to ObjectName if the type has the @ManagedObject annotation
|
|
||||||
if (r != null)
|
|
||||||
{
|
|
||||||
if (r.getClass().isArray())
|
|
||||||
{
|
|
||||||
if (r.getClass().getComponentType().isAnnotationPresent(ManagedObject.class))
|
|
||||||
{
|
|
||||||
ObjectName[] on = new ObjectName[Array.getLength(r)];
|
|
||||||
for (int i = 0; i < on.length; i++)
|
|
||||||
on[i] = _mbeanContainer.findMBean(Array.get(r, i));
|
|
||||||
r = on;
|
|
||||||
}
|
}
|
||||||
}
|
finally
|
||||||
else if (r instanceof Collection<?>)
|
|
||||||
{
|
{
|
||||||
@SuppressWarnings("unchecked")
|
Thread.currentThread().setContextClassLoader(prevLoader);
|
||||||
Collection<Object> c = (Collection<Object>)r;
|
|
||||||
if (!c.isEmpty() && c.iterator().next().getClass().isAnnotationPresent(ManagedObject.class))
|
|
||||||
{
|
|
||||||
// check the first thing out
|
|
||||||
ObjectName[] on = new ObjectName[c.size()];
|
|
||||||
int i = 0;
|
|
||||||
for (Object obj : c)
|
|
||||||
{
|
|
||||||
on[i++] = _mbeanContainer.findMBean(obj);
|
|
||||||
}
|
|
||||||
r = on;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Class<?> clazz = r.getClass();
|
|
||||||
while (clazz != null)
|
|
||||||
{
|
|
||||||
if (clazz.isAnnotationPresent(ManagedObject.class))
|
|
||||||
{
|
|
||||||
ObjectName mbean = _mbeanContainer.findMBean(r);
|
|
||||||
|
|
||||||
if (mbean != null)
|
|
||||||
{
|
|
||||||
return mbean;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clazz = clazz.getSuperclass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
catch (IllegalAccessException e)
|
|
||||||
{
|
|
||||||
LOG.warn(Log.EXCEPTION, e);
|
|
||||||
throw new AttributeNotFoundException(e.toString());
|
|
||||||
}
|
|
||||||
catch (InvocationTargetException e)
|
|
||||||
{
|
|
||||||
LOG.warn(Log.EXCEPTION, e);
|
|
||||||
throw new ReflectionException(new Exception(e.getCause()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,408 +162,75 @@ public class ObjectMBean implements DynamicMBean
|
||||||
{
|
{
|
||||||
results.add(new Attribute(name, getAttribute(name)));
|
results.add(new Attribute(name, getAttribute(name)));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Throwable x)
|
||||||
{
|
{
|
||||||
LOG.warn(Log.EXCEPTION, e);
|
LOG.info(x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAttribute(Attribute attr) throws AttributeNotFoundException, ReflectionException
|
public void setAttribute(Attribute attribute) throws AttributeNotFoundException, ReflectionException
|
||||||
{
|
{
|
||||||
if (attr == null)
|
ClassLoader prevLoader = Thread.currentThread().getContextClassLoader();
|
||||||
return;
|
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("setAttribute " + _managed + ":" + attr.getName() + "=" + attr.getValue());
|
|
||||||
Method setter = _setters.get(attr.getName());
|
|
||||||
if (setter == null)
|
|
||||||
throw new AttributeNotFoundException(attr.getName());
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Object o = _managed;
|
metaData().setAttribute(attribute, this);
|
||||||
if (setter.getDeclaringClass().isInstance(this))
|
|
||||||
o = this;
|
|
||||||
|
|
||||||
// get the value
|
|
||||||
Object value = attr.getValue();
|
|
||||||
|
|
||||||
// convert from ObjectName if need be
|
|
||||||
if (value != null && _convert.contains(attr.getName()))
|
|
||||||
{
|
|
||||||
if (value.getClass().isArray())
|
|
||||||
{
|
|
||||||
Class<?> t = setter.getParameterTypes()[0].getComponentType();
|
|
||||||
Object na = Array.newInstance(t, Array.getLength(value));
|
|
||||||
for (int i = Array.getLength(value); i-- > 0; )
|
|
||||||
Array.set(na, i, _mbeanContainer.findBean((ObjectName)Array.get(value, i)));
|
|
||||||
value = na;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
value = _mbeanContainer.findBean((ObjectName)value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// do the setting
|
|
||||||
setter.invoke(o, value);
|
|
||||||
}
|
|
||||||
catch (IllegalAccessException e)
|
|
||||||
{
|
|
||||||
LOG.warn(Log.EXCEPTION, e);
|
|
||||||
throw new AttributeNotFoundException(e.toString());
|
|
||||||
}
|
|
||||||
catch (InvocationTargetException e)
|
|
||||||
{
|
|
||||||
LOG.warn(Log.EXCEPTION, e);
|
|
||||||
throw new ReflectionException(new Exception(e.getCause()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AttributeList setAttributes(AttributeList attrs)
|
|
||||||
{
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("setAttributes");
|
|
||||||
|
|
||||||
AttributeList results = new AttributeList(attrs.size());
|
|
||||||
for (Object element : attrs)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Attribute attr = (Attribute)element;
|
|
||||||
setAttribute(attr);
|
|
||||||
results.add(new Attribute(attr.getName(), getAttribute(attr.getName())));
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOG.warn(Log.EXCEPTION, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object invoke(String name, Object[] params, String[] signature) throws MBeanException, ReflectionException
|
|
||||||
{
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("ObjectMBean:invoke " + name);
|
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder(name);
|
|
||||||
builder.append("(");
|
|
||||||
if (signature != null)
|
|
||||||
for (int i = 0; i < signature.length; i++)
|
|
||||||
builder.append(i > 0 ? "," : "").append(signature[i]);
|
|
||||||
builder.append(")");
|
|
||||||
String methodKey = builder.toString();
|
|
||||||
|
|
||||||
ClassLoader old_loader = Thread.currentThread().getContextClassLoader();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.currentThread().setContextClassLoader(_loader);
|
|
||||||
Method method = _methods.get(methodKey);
|
|
||||||
if (method == null)
|
|
||||||
throw new NoSuchMethodException(methodKey);
|
|
||||||
|
|
||||||
Object o = _managed;
|
|
||||||
|
|
||||||
if (method.getDeclaringClass().isInstance(this))
|
|
||||||
o = this;
|
|
||||||
|
|
||||||
return method.invoke(o, params);
|
|
||||||
}
|
|
||||||
catch (NoSuchMethodException e)
|
|
||||||
{
|
|
||||||
LOG.warn(Log.EXCEPTION, e);
|
|
||||||
throw new ReflectionException(e);
|
|
||||||
}
|
|
||||||
catch (IllegalAccessException e)
|
|
||||||
{
|
|
||||||
LOG.warn(Log.EXCEPTION, e);
|
|
||||||
throw new MBeanException(e);
|
|
||||||
}
|
|
||||||
catch (InvocationTargetException e)
|
|
||||||
{
|
|
||||||
LOG.warn(Log.EXCEPTION, e);
|
|
||||||
throw new ReflectionException(new Exception(e.getCause()));
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
Thread.currentThread().setContextClassLoader(old_loader);
|
Thread.currentThread().setContextClassLoader(prevLoader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Class<?>> findInfluences(List<Class<?>> influences, Class<?> aClass)
|
@Override
|
||||||
|
public AttributeList setAttributes(AttributeList attributes)
|
||||||
{
|
{
|
||||||
if (aClass != null)
|
AttributeList results = new AttributeList(attributes.size());
|
||||||
|
for (Attribute attribute : attributes.asList())
|
||||||
{
|
{
|
||||||
if (!influences.contains(aClass))
|
|
||||||
{
|
|
||||||
// This class is a new influence
|
|
||||||
influences.add(aClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
// So are the super classes
|
|
||||||
influences = findInfluences(influences, aClass.getSuperclass());
|
|
||||||
|
|
||||||
// So are the interfaces
|
|
||||||
Class<?>[] ifs = aClass.getInterfaces();
|
|
||||||
for (int i = 0; ifs != null && i < ifs.length; i++)
|
|
||||||
influences = findInfluences(influences, ifs[i]);
|
|
||||||
}
|
|
||||||
return influences;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Defines an attribute for the managed object using the annotation attributes.</p>
|
|
||||||
*
|
|
||||||
* @param method the method on the managed objec
|
|
||||||
* @param attributeAnnotation the annotation with the attribute metadata
|
|
||||||
* @return an MBeanAttributeInfo with the attribute metadata
|
|
||||||
*/
|
|
||||||
private MBeanAttributeInfo defineAttribute(Method method, ManagedAttribute attributeAnnotation)
|
|
||||||
{
|
|
||||||
// determine the name of the managed attribute
|
|
||||||
String name = attributeAnnotation.name();
|
|
||||||
|
|
||||||
if ("".equals(name))
|
|
||||||
name = toVariableName(method.getName());
|
|
||||||
|
|
||||||
if (_attributes.contains(name))
|
|
||||||
return null; // we have an attribute named this already
|
|
||||||
|
|
||||||
String description = attributeAnnotation.value();
|
|
||||||
boolean readonly = attributeAnnotation.readonly();
|
|
||||||
boolean onMBean = attributeAnnotation.proxied();
|
|
||||||
|
|
||||||
// determine if we should convert
|
|
||||||
Class<?> return_type = method.getReturnType();
|
|
||||||
|
|
||||||
// get the component type
|
|
||||||
Class<?> component_type = return_type;
|
|
||||||
while (component_type.isArray())
|
|
||||||
component_type = component_type.getComponentType();
|
|
||||||
|
|
||||||
// Test to see if the returnType or any of its super classes are managed objects
|
|
||||||
boolean convert = isAnnotationPresent(component_type, ManagedObject.class);
|
|
||||||
|
|
||||||
String uName = name.substring(0, 1).toUpperCase(Locale.ENGLISH) + name.substring(1);
|
|
||||||
Class<?> oClass = onMBean ? this.getClass() : _managed.getClass();
|
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("defineAttribute {} {}:{}:{}:{}", name, onMBean, readonly, oClass, description);
|
|
||||||
|
|
||||||
Method setter = null;
|
|
||||||
|
|
||||||
// dig out a setter if one exists
|
|
||||||
if (!readonly)
|
|
||||||
{
|
|
||||||
String declaredSetter = attributeAnnotation.setter();
|
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("DeclaredSetter: {}", declaredSetter);
|
|
||||||
|
|
||||||
for (Method method1 : oClass.getMethods())
|
|
||||||
{
|
|
||||||
if (!Modifier.isPublic(method1.getModifiers()))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!"".equals(declaredSetter))
|
|
||||||
{
|
|
||||||
// look for a declared setter
|
|
||||||
if (method1.getName().equals(declaredSetter) && method1.getParameterCount() == 1)
|
|
||||||
{
|
|
||||||
if (setter != null)
|
|
||||||
{
|
|
||||||
LOG.warn("Multiple setters for mbean attr {} in {}", name, oClass);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
setter = method1;
|
|
||||||
if (!component_type.equals(method1.getParameterTypes()[0]))
|
|
||||||
{
|
|
||||||
LOG.warn("Type conflict for mbean attr {} in {}", name, oClass);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("Declared Setter: " + declaredSetter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// look for a setter
|
|
||||||
if (method1.getName().equals("set" + uName) && method1.getParameterCount() == 1)
|
|
||||||
{
|
|
||||||
if (setter != null)
|
|
||||||
{
|
|
||||||
LOG.warn("Multiple setters for mbean attr {} in {}", name, oClass);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
setter = method1;
|
|
||||||
if (!return_type.equals(method1.getParameterTypes()[0]))
|
|
||||||
LOG.warn("Type conflict for mbean attr {} in {}", name, oClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (convert)
|
|
||||||
{
|
|
||||||
if (component_type.isPrimitive() && !component_type.isArray())
|
|
||||||
{
|
|
||||||
LOG.warn("Cannot convert mbean primitive {}", name);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("passed convert checks {} for type {}", name, component_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Remember the methods
|
setAttribute(attribute);
|
||||||
_getters.put(name, method);
|
results.add(new Attribute(attribute.getName(), getAttribute(attribute.getName())));
|
||||||
_setters.put(name, setter);
|
}
|
||||||
|
catch (Throwable x)
|
||||||
MBeanAttributeInfo info;
|
|
||||||
if (convert)
|
|
||||||
{
|
{
|
||||||
_convert.add(name);
|
LOG.info(x);
|
||||||
info = new MBeanAttributeInfo(name,
|
|
||||||
component_type.isArray() ? OBJECT_NAME_ARRAY_CLASS : OBJECT_NAME_CLASS,
|
|
||||||
description,
|
|
||||||
true,
|
|
||||||
setter != null,
|
|
||||||
method.getName().startsWith("is"));
|
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object invoke(String name, Object[] params, String[] signature) throws ReflectionException, MBeanException
|
||||||
{
|
{
|
||||||
info = new MBeanAttributeInfo(name, description, method, setter);
|
ClassLoader prevLoader = Thread.currentThread().getContextClassLoader();
|
||||||
}
|
|
||||||
|
|
||||||
_attributes.add(name);
|
|
||||||
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOG.warn(e);
|
|
||||||
throw new IllegalArgumentException(e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Defines an operation for the managed object using the annotation attributes.</p>
|
|
||||||
*
|
|
||||||
* @param method the method on the managed object
|
|
||||||
* @param methodAnnotation the annotation with the operation metadata
|
|
||||||
* @return an MBeanOperationInfo with the operation metadata
|
|
||||||
*/
|
|
||||||
private MBeanOperationInfo defineOperation(Method method, ManagedOperation methodAnnotation)
|
|
||||||
{
|
|
||||||
String description = methodAnnotation.value();
|
|
||||||
boolean onMBean = methodAnnotation.proxied();
|
|
||||||
|
|
||||||
// determine if we should convert
|
|
||||||
Class<?> returnType = method.getReturnType();
|
|
||||||
|
|
||||||
if (returnType.isArray())
|
|
||||||
{
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("returnType is array, get component type");
|
|
||||||
returnType = returnType.getComponentType();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean convert = false;
|
|
||||||
if (returnType.isAnnotationPresent(ManagedObject.class))
|
|
||||||
convert = true;
|
|
||||||
|
|
||||||
String impactName = methodAnnotation.impact();
|
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("defineOperation {} {}:{}:{}", method.getName(), onMBean, impactName, description);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Resolve the impact
|
return metaData().invoke(name, signature, params, this);
|
||||||
int impact = MBeanOperationInfo.UNKNOWN;
|
}
|
||||||
if ("UNKNOWN".equals(impactName))
|
finally
|
||||||
impact = MBeanOperationInfo.UNKNOWN;
|
|
||||||
else if ("ACTION".equals(impactName))
|
|
||||||
impact = MBeanOperationInfo.ACTION;
|
|
||||||
else if ("INFO".equals(impactName))
|
|
||||||
impact = MBeanOperationInfo.INFO;
|
|
||||||
else if ("ACTION_INFO".equals(impactName))
|
|
||||||
impact = MBeanOperationInfo.ACTION_INFO;
|
|
||||||
else
|
|
||||||
LOG.warn("Unknown impact '" + impactName + "' for " + method);
|
|
||||||
|
|
||||||
Annotation[][] allParameterAnnotations = method.getParameterAnnotations();
|
|
||||||
Class<?>[] methodTypes = method.getParameterTypes();
|
|
||||||
MBeanParameterInfo[] pInfo = new MBeanParameterInfo[allParameterAnnotations.length];
|
|
||||||
|
|
||||||
for (int i = 0; i < allParameterAnnotations.length; ++i)
|
|
||||||
{
|
{
|
||||||
Annotation[] parameterAnnotations = allParameterAnnotations[i];
|
Thread.currentThread().setContextClassLoader(prevLoader);
|
||||||
for (Annotation anno : parameterAnnotations)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectName findObjectName(Object bean)
|
||||||
{
|
{
|
||||||
if (anno instanceof Name)
|
return _mbeanContainer.findMBean(bean);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object findBean(ObjectName objectName)
|
||||||
{
|
{
|
||||||
Name nameAnnotation = (Name)anno;
|
return _mbeanContainer.findBean(objectName);
|
||||||
pInfo[i] = new MBeanParameterInfo(nameAnnotation.value(), methodTypes[i].getName(), nameAnnotation.description());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder(method.getName());
|
private MetaData metaData()
|
||||||
builder.append("(");
|
|
||||||
for (int i = 0; i < methodTypes.length; ++i)
|
|
||||||
{
|
{
|
||||||
builder.append(methodTypes[i].getName());
|
if (_metaData == null)
|
||||||
if (i != methodTypes.length - 1)
|
_metaData = MBeanContainer.findMetaData(_mbeanContainer, _managed.getClass());
|
||||||
builder.append(",");
|
return _metaData;
|
||||||
}
|
|
||||||
builder.append(")");
|
|
||||||
String signature = builder.toString();
|
|
||||||
|
|
||||||
Class<?> returnClass = method.getReturnType();
|
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
LOG.debug("Method Cache: " + signature);
|
|
||||||
|
|
||||||
if (_methods.containsKey(signature))
|
|
||||||
return null; // we have an operation for this already
|
|
||||||
|
|
||||||
_methods.put(signature, method);
|
|
||||||
if (convert)
|
|
||||||
_convert.add(signature);
|
|
||||||
|
|
||||||
return new MBeanOperationInfo(method.getName(), description, pInfo, returnClass.isPrimitive() ? TypeUtil.toName(returnClass) : (returnClass.getName()), impact);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOG.warn("Operation '" + method + "'", e);
|
|
||||||
throw new IllegalArgumentException(e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String toVariableName(String methodName)
|
|
||||||
{
|
|
||||||
String variableName = methodName;
|
|
||||||
if (methodName.startsWith("get") || methodName.startsWith("set"))
|
|
||||||
variableName = variableName.substring(3);
|
|
||||||
else if (methodName.startsWith("is"))
|
|
||||||
variableName = variableName.substring(2);
|
|
||||||
return variableName.substring(0, 1).toLowerCase(Locale.ENGLISH) + variableName.substring(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isAnnotationPresent(Class<?> clazz, Class<? extends Annotation> annotation)
|
|
||||||
{
|
|
||||||
Class<?> test = clazz;
|
|
||||||
while (test != null)
|
|
||||||
{
|
|
||||||
if (test.isAnnotationPresent(annotation))
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
test = test.getSuperclass();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,6 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.jmx;
|
package org.eclipse.jetty.jmx;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
|
|
||||||
import com.acme.Derived;
|
|
||||||
|
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
|
|
||||||
import javax.management.Attribute;
|
import javax.management.Attribute;
|
||||||
|
@ -31,6 +25,7 @@ import javax.management.MBeanInfo;
|
||||||
import javax.management.MBeanOperationInfo;
|
import javax.management.MBeanOperationInfo;
|
||||||
import javax.management.MBeanParameterInfo;
|
import javax.management.MBeanParameterInfo;
|
||||||
|
|
||||||
|
import com.acme.Derived;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||||
|
@ -39,6 +34,10 @@ import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
public class ObjectMBeanTest
|
public class ObjectMBeanTest
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(ObjectMBeanTest.class);
|
private static final Logger LOG = Log.getLogger(ObjectMBeanTest.class);
|
||||||
|
@ -215,14 +214,14 @@ public class ObjectMBeanTest
|
||||||
{
|
{
|
||||||
ObjectMBean mbean = new ObjectMBean(new Derived());
|
ObjectMBean mbean = new ObjectMBean(new Derived());
|
||||||
|
|
||||||
assertEquals("fullName",mbean.toVariableName("getFullName"));
|
assertEquals("fullName",MetaData.toAttributeName("getFullName"));
|
||||||
assertEquals("fullName",mbean.toVariableName("getfullName"));
|
assertEquals("fullName",MetaData.toAttributeName("getfullName"));
|
||||||
assertEquals("fullName",mbean.toVariableName("isFullName"));
|
assertEquals("fullName",MetaData.toAttributeName("isFullName"));
|
||||||
assertEquals("fullName",mbean.toVariableName("isfullName"));
|
assertEquals("fullName",MetaData.toAttributeName("isfullName"));
|
||||||
assertEquals("fullName",mbean.toVariableName("setFullName"));
|
assertEquals("fullName",MetaData.toAttributeName("setFullName"));
|
||||||
assertEquals("fullName",mbean.toVariableName("setfullName"));
|
assertEquals("fullName",MetaData.toAttributeName("setfullName"));
|
||||||
assertEquals("fullName",mbean.toVariableName("FullName"));
|
assertEquals("fullName",MetaData.toAttributeName("FullName"));
|
||||||
assertEquals("fullName",mbean.toVariableName("fullName"));
|
assertEquals("fullName",MetaData.toAttributeName("fullName"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,6 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.jmx;
|
package org.eclipse.jetty.jmx;
|
||||||
|
|
||||||
import com.acme.Derived;
|
|
||||||
import com.acme.DerivedExtended;
|
|
||||||
import com.acme.DerivedManaged;
|
|
||||||
|
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -33,6 +29,9 @@ import javax.management.MBeanException;
|
||||||
import javax.management.MBeanInfo;
|
import javax.management.MBeanInfo;
|
||||||
import javax.management.ReflectionException;
|
import javax.management.ReflectionException;
|
||||||
|
|
||||||
|
import com.acme.Derived;
|
||||||
|
import com.acme.DerivedExtended;
|
||||||
|
import com.acme.DerivedManaged;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
import org.eclipse.jetty.util.log.StdErrLog;
|
import org.eclipse.jetty.util.log.StdErrLog;
|
||||||
|
@ -41,7 +40,10 @@ import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
public class ObjectMBeanUtilTest
|
public class ObjectMBeanUtilTest
|
||||||
{
|
{
|
||||||
|
@ -294,12 +296,12 @@ public class ObjectMBeanUtilTest
|
||||||
setMBeanInfoForInvoke();
|
setMBeanInfoForInvoke();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
MBeanException e = assertThrows(MBeanException.class, ()->{
|
ReflectionException e = assertThrows(ReflectionException.class, ()->{
|
||||||
objectMBean.invoke("doodle2",new Object[] {},new String[] {});
|
objectMBean.invoke("doodle2",new Object[] {},new String[] {});
|
||||||
});
|
});
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertNotNull(e, "An MBeanException must have occurred by now as doodle2() in Derived bean throwing exception");
|
assertNotNull(e, "An ReflectionException must have occurred by now as doodle2() in Derived bean is private");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -309,12 +311,12 @@ public class ObjectMBeanUtilTest
|
||||||
setMBeanInfoForInvoke();
|
setMBeanInfoForInvoke();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
ReflectionException e = assertThrows(ReflectionException.class, ()->{
|
MBeanException e = assertThrows(MBeanException.class, ()->{
|
||||||
objectMBean.invoke("doodle1",new Object[] {},new String[] {});
|
objectMBean.invoke("doodle1",new Object[] {},new String[] {});
|
||||||
});
|
});
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertNotNull(e, "ReflectionException is null");
|
assertNotNull(e, "MBeanException is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -359,6 +361,6 @@ public class ObjectMBeanUtilTest
|
||||||
@Test
|
@Test
|
||||||
public void testToVariableName()
|
public void testToVariableName()
|
||||||
{
|
{
|
||||||
assertEquals("fullName",objectMBean.toVariableName("isfullName"));
|
assertEquals("fullName",MetaData.toAttributeName("isfullName"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue