org.glassfish.admin.amx.impl.util.MBeanInfoSupport Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of payara-micro Show documentation
Show all versions of payara-micro Show documentation
Micro Distribution of the Payara Project
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2009-2012 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
// Portions Copyright [2019] Payara Foundation and/or affiliates
package org.glassfish.admin.amx.impl.util;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import javax.management.Descriptor;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.ObjectName;
import javax.management.modelmbean.DescriptorSupport;
import org.glassfish.admin.amx.annotation.ManagedAttribute;
import org.glassfish.admin.amx.annotation.ManagedOperation;
import org.glassfish.admin.amx.annotation.Description;
import org.glassfish.admin.amx.annotation.Param;
import org.glassfish.admin.amx.base.Singleton;
import org.glassfish.admin.amx.core.AMXProxy;
import org.glassfish.admin.amx.core.AMX_SPI;
import org.glassfish.admin.amx.util.jmx.JMXUtil;
import org.glassfish.admin.amx.core.AMXMBeanMetadata;
import org.glassfish.admin.amx.util.AMXLoggerInfo;
import static org.glassfish.external.amx.AMX.*;
/**
*
* @author llc
*/
public final class MBeanInfoSupport {
private MBeanInfoSupport() {
}
private static MBeanInfo amxspiMBeanInfo = null;
public static synchronized MBeanInfo getAMX_SPIMBeanInfo() {
if (amxspiMBeanInfo == null) {
amxspiMBeanInfo = MBeanInfoSupport.getMBeanInfo(AMX_SPI.class);
}
return amxspiMBeanInfo;
}
public static MBeanInfo getMBeanInfo(final Class intf) {
final Map getters = new HashMap();
final Map setters = new HashMap();
final Map getterSetters = new HashMap();
final Set operations = new HashSet();
findInterfaceMethods(intf, getters, setters, getterSetters, operations);
if (!AMX_SPI.class.isAssignableFrom(intf)) {
findInterfaceMethods(AMX_SPI.class, getters, setters, getterSetters, operations);
}
final List attrsList =
generateMBeanAttributeInfos(getterSetters.values(), getters.values(), setters.values());
final MBeanOperationInfo[] operationInfos = generateMBeanOperationInfos(operations);
// might or might not have metadata
final AMXMBeanMetadata meta = intf.getAnnotation(AMXMBeanMetadata.class);
final boolean globalSingleton = meta != null && meta.globalSingleton();
final boolean singleton = Singleton.class.isAssignableFrom(intf) || globalSingleton ||
(meta != null && meta.singleton());
final String group = GROUP_OTHER;
final boolean isLeaf = meta != null && meta.leaf();
final boolean supportsAdoption = !isLeaf;
if (isLeaf) {
JMXUtil.remove(attrsList, ATTR_CHILDREN);
}
final Descriptor d = mbeanDescriptor(
true,
intf,
singleton,
globalSingleton,
group,
supportsAdoption,
null);
final MBeanAttributeInfo[] attrInfos = new MBeanAttributeInfo[attrsList.size()];
attrsList.toArray(attrInfos);
final MBeanInfo mbeanInfo = new MBeanInfo(
intf.getName(),
intf.getName(),
attrInfos,
null,
operationInfos,
null,
d);
return (mbeanInfo);
}
public static void findInterfaceMethods(final Class> intf,
final Map getters,
final Map setters,
final Map getterSetters,
final Set operations) {
final Method[] methods = intf.getMethods();
for (final Method method : methods) {
final ManagedAttribute managedAttr = method.getAnnotation(ManagedAttribute.class);
final ManagedOperation managedOp = method.getAnnotation(ManagedOperation.class);
if (managedAttr != null) {
String attrName = null;
final int numArgs = method.getParameterTypes().length;
if (managedOp != null) {
AMXLoggerInfo.getLogger().log(Level.WARNING, AMXLoggerInfo.attributeCantBeOperation,
new Object[]{intf.getName(), method.getName()});
} else if (numArgs == 0 && JMXUtil.isIsOrGetter(method)) {
attrName = JMXUtil.getAttributeName(method);
getters.put(attrName, method);
} else if (numArgs == 1 && JMXUtil.isSetter(method)) {
attrName = JMXUtil.getAttributeName(method);
setters.put(attrName, method);
} else {
AMXLoggerInfo.getLogger().log(Level.WARNING, AMXLoggerInfo.attributeNotGetterSetter,
new Object[]{intf.getName(), method.getName()});
// ignore
}
if ((attrName != null) &&
getters.containsKey(attrName) &&
setters.containsKey(attrName)) {
final Method getter = getters.get(attrName);
final Class> getterType = getter.getReturnType();
final Class> setterType = setters.get(attrName).getParameterTypes()[ 0];
if (getterType == setterType) {
getters.remove(attrName);
setters.remove(attrName);
getterSetters.put(attrName, getter);
//debug( "findInterfaceMethods: getter/setter: " + attrName );
} else {
throw new IllegalArgumentException("Attribute " + attrName +
"has type " + getterType.getName() + " as getter but type " +
setterType.getName() + " as setter");
}
}
} else if (managedOp != null) {
operations.add(method);
}
}
}
/**
* Return MBeanAttributeInfo for the method. If it's a getter,
* it's marked as read-only, if it's a setter, it's marked as read/write.
* @param m
* @return
*/
public static MBeanAttributeInfo attributeInfo(final Method m) {
final ManagedAttribute managed = m.getAnnotation(ManagedAttribute.class);
if (managed == null) {
return null;
}
final Description d = m.getAnnotation(Description.class);
final String description = d == null ? "" : d.value();
String attrName = JMXUtil.getAttributeName(m);
final boolean isGetter = JMXUtil.isGetter(m);
final boolean isSetter = JMXUtil.isSetter(m);
final boolean isIs = JMXUtil.isIs(m);
final MBeanAttributeInfo info =
new MBeanAttributeInfo(attrName, m.getReturnType().getName(),
description, isGetter, isSetter, isIs, null);
return info;
}
public static Class> translatedType(final Class> clazz) {
Class> type = clazz;
if (AMXProxy.class.isAssignableFrom(clazz)) {
type = ObjectName.class;
} else if (clazz.isArray() && AMXProxy.class.isAssignableFrom(clazz.getComponentType())) {
type = ObjectName[].class;
}
return type;
}
private static List generateAttributeInfos(
final Collection methods,
final boolean read,
final boolean write) {
final List infos = new ArrayList();
for (final Method m : methods) {
final String description = getDescription(m);
String attrName = JMXUtil.getAttributeName(m);
final MBeanAttributeInfo info = new MBeanAttributeInfo(
attrName,
translatedType(m.getReturnType()).getName(),
description,
read,
write,
JMXUtil.isIs(m));
infos.add(info);
}
return (infos);
}
public static List generateMBeanAttributeInfos(
final Collection getterSetters,
final Collection getters,
final Collection setters) {
final List attrsList = new ArrayList();
attrsList.addAll(generateAttributeInfos(getterSetters, true, true));
attrsList.addAll(generateAttributeInfos(getters, true, false));
attrsList.addAll(generateAttributeInfos(setters, false, true));
return (attrsList);
}
public static T getAnnotation(final Annotation[] annotations, final Class clazz) {
T result = null;
for (final Annotation a : annotations) {
if (a.annotationType() == clazz) {
result = (T) a;
break;
}
}
return result;
}
private static String getDescription(final AnnotatedElement o) {
final Description d = o.getAnnotation(Description.class);
return d == null ? "" : d.value();
}
public static MBeanParameterInfo[] parameterInfos(final Method method) {
final Class>[] sig = method.getParameterTypes();
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
final MBeanParameterInfo[] infos = new MBeanParameterInfo[sig.length];
for (int i = 0; i < sig.length; ++i) {
final Class> paramClass = translatedType(sig[i]);
final Annotation[] annotations = paramAnnotations[i];
final Param p = getAnnotation(annotations, Param.class);
final String paramName = (p == null || p.name().length() == 0) ? ("p" + i) : p.name();
final Description d = getAnnotation(annotations, Description.class);
String description = "";
if (d != null && d.value().length() != 0) {
description = d.value();
}
final String type = paramClass.getName();
final MBeanParameterInfo info = new MBeanParameterInfo(paramName, type, description);
infos[i] = info;
}
return (infos);
}
public static MBeanOperationInfo[] generateMBeanOperationInfos(final Collection methods) {
final MBeanOperationInfo[] infos = new MBeanOperationInfo[methods.size()];
int i = 0;
for (final Method m : methods) {
final ManagedOperation managed = m.getAnnotation(ManagedOperation.class);
final String methodName = m.getName();
final MBeanParameterInfo[] parameterInfos = parameterInfos(m);
final int impact = managed == null ? MBeanOperationInfo.UNKNOWN : managed.impact();
final String description = getDescription(m);
final MBeanOperationInfo info = new MBeanOperationInfo(
methodName,
description,
parameterInfos,
translatedType(m.getReturnType()).getName(),
impact,
null);
infos[i] = info;
++i;
}
return (infos);
}
public static DescriptorSupport mbeanDescriptor(
final boolean immutable,
final Class> intf,
final boolean singleton,
final boolean globalSingleton,
final String group,
final boolean supportsAdoption,
final String[] subTypes) {
final DescriptorSupport desc = new DescriptorSupport();
if (intf == null || !intf.isInterface()) {
throw new IllegalArgumentException("interface class must be an interface");
}
desc.setField(DESC_STD_IMMUTABLE_INFO, immutable);
desc.setField(DESC_STD_INTERFACE_NAME, intf.getName());
desc.setField(DESC_IS_SINGLETON, singleton);
desc.setField(DESC_IS_GLOBAL_SINGLETON, globalSingleton);
desc.setField(DESC_GROUP, group);
desc.setField(DESC_SUPPORTS_ADOPTION, supportsAdoption);
if (subTypes != null) {
desc.setField(DESC_SUB_TYPES, subTypes);
}
return desc;
}
}