diff --git a/src/java.management/share/classes/com/sun/jmx/annotations/AnnotatedMBean.java b/src/java.management/share/classes/com/sun/jmx/annotations/AnnotatedMBean.java new file mode 100644 index 0000000000000..cf6a79ff35d62 --- /dev/null +++ b/src/java.management/share/classes/com/sun/jmx/annotations/AnnotatedMBean.java @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.jmx.annotations; + +import com.sun.jmx.annotations.model.AttributeModel; +import com.sun.jmx.annotations.model.MXBeanModel; +import com.sun.jmx.annotations.model.OperationModel; +import com.sun.jmx.annotations.model.InjectableFieldModel; +import com.sun.jmx.mbeanserver.DynamicMBean2; +import com.sun.jmx.mbeanserver.MXBeanMapping; +import com.sun.jmx.mbeanserver.MXBeanMappingFactory; + +import java.io.InvalidObjectException; +import java.lang.invoke.MethodHandle; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.IntrospectionException; +import javax.management.InvalidAttributeValueException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanNotificationInfo; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ReflectionException; +import javax.management.annotations.NotificationSender; +import javax.management.annotations.NotificationInfos; +import javax.management.modelmbean.ModelMBeanNotificationInfo; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.CompositeType; +import javax.management.openmbean.OpenDataException; + + +/** + * A wrapper for the annotated MBeans. For the purposes of cooperation with + * the rest of the JMX system the annotated MBean will behave like a + * {@linkplain DynamicMBean2} and {@linkplain NotificationEmitter} instance. + * + */ +abstract class AnnotatedMBean implements DynamicMBean2, NotificationEmitter, NotificationSender { + protected final MXBeanModel model; + protected final T instance; + private NotificationBroadcasterSupport nbs = null; + private final MBeanInfoBuilder mib; + + protected AnnotatedMBean(MXBeanModel model, T instance) throws IntrospectionException { + this.model = model; + this.instance = instance; + this.mib = new MBeanInfoBuilder(model.getMappingFactory()); + injectFields(); + } + + private void injectFields() throws IntrospectionException { + for(InjectableFieldModel fm : model.getInjectableFields()) { + if (fm.getType().isAssignableFrom(NotificationSender.class)) { + // inject the NotificationSender instances + fm.inject(instance, this); + } + } + } + + private synchronized NotificationBroadcasterSupport getNbs() { + if (nbs == null) { + List ntfs = model.getNotifications().stream() + .map(mib::toNotificationInfo).collect(Collectors.toList()); + nbs = new NotificationBroadcasterSupport(ntfs.toArray(new ModelMBeanNotificationInfo[ntfs.size()])); + } + return nbs; + } + + @Override + public Object getResource() { + return this; + } + + @Override + public String getClassName() { + return model.getName(); + } + + @Override + public Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException { + try { + AttributeModel mam = model.getAttribute(attribute); + if (mam == null) { + throw new AttributeNotFoundException("Can not find attribute '" + attribute + "'"); + } + MXBeanMapping m = getMapping(mam.getType()); + MethodHandle h = mam.getGetter().bindTo(instance); + + Object[] args = new Object[h.type().parameterCount()]; + Class[] paramTypes = h.type().parameterArray(); + for(int i=0;i 0 ? h.invokeWithArguments(args) : h.invoke(); + + return m != null ? m.toOpenValue(val) : val; + } catch (Throwable t) { + throw new MBeanException((Exception) t); + } + } + + @Override + public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { + try { + AttributeModel mam = model.getAttribute(attribute.getName()); + if (mam == null) { + throw new AttributeNotFoundException(); + } + MXBeanMapping m = getMapping(mam.getType()); + MethodHandle h = mam.getSetter().bindTo(instance); + Object[] args = new Object[h.type().parameterCount()]; + Class[] paramTypes = h.type().parameterArray(); + for(int i=0;i type) throws OpenDataException { + return model.getMappingFactory().mappingForType(type, MXBeanMappingFactory.DEFAULT); + } + + private void unwrapCompositeTypes(Object[] params, String[] signature) { + if (params != null) { + for(int i=0;i The type of the given instance + * @param instance The instance + * @return An {@linkplain DynamicMBean} wrapper or null when the provided instance + * is not properly annotated. + * + * @throws IntrospectionException + */ + public static DynamicMBean toMBean(T instance) throws IntrospectionException { + ModelBuilder mb = getBuilder(); + if (mb != null) { + MXBeanModel m = mb.buildModel(instance.getClass()); + if (m != null) { + return new RegisteringAnnotatedMBean<>(m, instance); + } + return null; + } + throw new IntrospectionException("An appropriate MXBean Model Builder can not be located."); + } + + private static ModelBuilder getBuilder() { + if (builder == null) { + ServiceLoader l = ServiceLoader.load(ModelBuilder.class); + Iterator svcIter = l.iterator(); + if (svcIter.hasNext()) { + builder = svcIter.next(); + } + } + return builder; + } +} diff --git a/src/java.management/share/classes/com/sun/jmx/annotations/MBeanInfoBuilder.java b/src/java.management/share/classes/com/sun/jmx/annotations/MBeanInfoBuilder.java new file mode 100644 index 0000000000000..365b152c7f890 --- /dev/null +++ b/src/java.management/share/classes/com/sun/jmx/annotations/MBeanInfoBuilder.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.jmx.annotations; + +import com.sun.jmx.annotations.model.AttributeModel; +import com.sun.jmx.annotations.model.MXBeanModel; +import com.sun.jmx.annotations.model.NotificationModel; +import com.sun.jmx.annotations.model.OperationModel; +import com.sun.jmx.annotations.model.ParameterModel; +import com.sun.jmx.mbeanserver.MXBeanMapping; +import com.sun.jmx.mbeanserver.MXBeanMappingFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import javax.management.Descriptor; +import javax.management.MBeanInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanParameterInfo; +import javax.management.modelmbean.DescriptorSupport; +import javax.management.modelmbean.ModelMBeanAttributeInfo; +import javax.management.modelmbean.ModelMBeanConstructorInfo; +import javax.management.modelmbean.ModelMBeanInfoSupport; +import javax.management.modelmbean.ModelMBeanNotificationInfo; +import javax.management.modelmbean.ModelMBeanOperationInfo; +import javax.management.openmbean.OpenDataException; + +public final class MBeanInfoBuilder { + private final MXBeanMappingFactory mappingFactory; + + MBeanInfoBuilder(MXBeanMappingFactory mf) { + this.mappingFactory = mf; + } + + public MBeanInfoBuilder() { + this.mappingFactory = MXBeanMappingFactory.DEFAULT; + } + + public MBeanInfo toMBeanInfo(MXBeanModel model) { + String className = model.getName(); + List attrs = model.getAttributes().stream() + .map(this::toAttrInfo).collect(Collectors.toList()); + List ops = model.getOperations().stream() + .map(this::toOperationInfo).collect(Collectors.toList()); + List ntfs = model.getNotifications().stream() + .map(this::toNotificationInfo).collect(Collectors.toList()); + + Descriptor d = new DescriptorSupport(); + d.setField("name", className); + d.setField("descriptorType", "mbean"); + d.setField("immutableInfo", true); + if (model.getService() != null) { + d.setField("interfaceClassName", model.getService().getName()); + } + model.getTags().entrySet().stream().forEach(e->{ + d.setField(e.getKey(), e.getValue()); + }); + + return new ModelMBeanInfoSupport( + className, + model.getDescription(), + attrs.toArray(new ModelMBeanAttributeInfo[attrs.size()]), + new ModelMBeanConstructorInfo[0], + ops.toArray(new ModelMBeanOperationInfo[ops.size()]), + ntfs.toArray(new ModelMBeanNotificationInfo[ntfs.size()]), + d + ); + } + + public ModelMBeanNotificationInfo toNotificationInfo(NotificationModel mnm) { + List types = new ArrayList<>(mnm.getTypes()); + String name = mnm.getClazz().getName(); + Descriptor d = new DescriptorSupport(); + d.setField("name", name); + d.setField("displayName", name); + d.setField("descriptorType", "notification"); + d.setField("severity", mnm.getSeverity()); + return new ModelMBeanNotificationInfo(types.toArray(new String[types.size()]), name, mnm.getDescription(), d); + } + + public ModelMBeanAttributeInfo toAttrInfo(AttributeModel mam) { + MXBeanMapping mapping = getMapping(mam.getType()); + String type = mapType(mam.getType()); + Descriptor d = mam.getDescriptor(); + if (mapping != null) { + d.setField("openType", mapping.getOpenType()); + d.setField("originalType", mam.getType().getName()); + } + + boolean isIs = mam.getType().getName().equals("boolean") || mam.getType().equals(Boolean.class); + return new ModelMBeanAttributeInfo(mam.getName(), type, mam.getDescription(), mam.isReadable(), mam.isWritable(), isIs, d); + } + + public ModelMBeanOperationInfo toOperationInfo(OperationModel mom) { + int impact = MBeanOperationInfo.UNKNOWN; + switch (mom.getImpact()) { + case "INFO": { + impact = MBeanOperationInfo.INFO; + break; + } + case "ACTION_INFO": { + impact = MBeanOperationInfo.ACTION_INFO; + break; + } + case "ACTION": { + impact = MBeanOperationInfo.ACTION; + break; + } + } + + String type = mapType(mom.getType()); + return new ModelMBeanOperationInfo(mom.getName(), mom.getDescription(), toParamInfos(mom), type, impact, mom.getDescriptor()); + } + + private MBeanParameterInfo[] toParamInfos(OperationModel mom) { + return toParamInfos(mom.getParameters()); + } + + private MBeanParameterInfo[] toParamInfos(List params) { + List infos = params.stream() + .map(this::toParamInfo) + .collect(Collectors.toList()); + return infos.toArray(new MBeanParameterInfo[infos.size()]); + } + + private MBeanParameterInfo toParamInfo(ParameterModel param) { + String name = param.getName(); + String type = mapType(param.getType()); + String description = param.getDescription(); + + return new MBeanParameterInfo(name, type, description, param.getDescriptor()); + + } + + private String mapType(Class type) { + MXBeanMapping m = getMapping(type); + return m != null ? m.getOpenClass().getName() : type.getName(); + } + + private MXBeanMapping getMapping(Class type) { + try { + return mappingFactory.mappingForType(type, MXBeanMappingFactory.DEFAULT); + } catch (OpenDataException ex) { + ex.printStackTrace(); + } + return null; + } +} \ No newline at end of file diff --git a/src/java.management/share/classes/com/sun/jmx/annotations/ModelBuilder.java b/src/java.management/share/classes/com/sun/jmx/annotations/ModelBuilder.java new file mode 100644 index 0000000000000..f513c11e09ff0 --- /dev/null +++ b/src/java.management/share/classes/com/sun/jmx/annotations/ModelBuilder.java @@ -0,0 +1,18 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.sun.jmx.annotations; + +import com.sun.jmx.annotations.model.MXBeanModel; + +import javax.management.IntrospectionException; + +/** + * + * @author jbachorik + */ +public interface ModelBuilder { + MXBeanModel buildModel(Class clz) throws IntrospectionException; +} diff --git a/src/java.management/share/classes/com/sun/jmx/annotations/RegisteringAnnotatedMBean.java b/src/java.management/share/classes/com/sun/jmx/annotations/RegisteringAnnotatedMBean.java new file mode 100644 index 0000000000000..5506cc32e6903 --- /dev/null +++ b/src/java.management/share/classes/com/sun/jmx/annotations/RegisteringAnnotatedMBean.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.jmx.annotations; + +import com.sun.jmx.annotations.model.MXBeanModel; + +import java.util.concurrent.atomic.AtomicReference; +import javax.management.IntrospectionException; +import javax.management.MBeanRegistration; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.ObjectName; +import javax.management.annotations.RegistrationEvent; +import javax.management.annotations.RegistrationKind; + +/** + * A specific subclass of {@linkplain AnnotatedMBean} to handle the + * registration events. + */ +class RegisteringAnnotatedMBean extends AnnotatedMBean implements MBeanRegistration { + private MBeanServer server; + private ObjectName oName; + + public RegisteringAnnotatedMBean(MXBeanModel model, T instance) throws IntrospectionException { + super(model, instance); + } + + @Override + final public void preRegister2(MBeanServer mbs, ObjectName name) throws Exception { + dispatchEvent(new RegistrationEvent(RegistrationKind.REGISTER, mbs, name)); + } + + @Override + final public void registerFailed() { + try { + dispatchEvent(new RegistrationEvent(RegistrationKind.FAIL, server, oName)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + final public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { + if (name == null && model.getObjectName() == null) { + throw new MBeanRegistrationException(null, "Undefined object name"); + } + + name = name != null ? name : model.getObjectName(); + + if (instance instanceof MBeanRegistration) + name = ((MBeanRegistration) instance).preRegister(server, name); + + this.server = server; + this.oName = name; + + return name; + } + + @Override + final public void postRegister(Boolean registrationDone) { + if (instance instanceof MBeanRegistration) + ((MBeanRegistration) instance).postRegister(registrationDone); + } + + @Override + final public void preDeregister() throws Exception { + if (instance instanceof MBeanRegistration) + ((MBeanRegistration) instance).preDeregister(); + } + + @Override + final public void postDeregister() { + if (instance instanceof MBeanRegistration) { + ((MBeanRegistration) instance).postDeregister(); + } + + try { + dispatchEvent(new RegistrationEvent(RegistrationKind.DEREGISTER, server, oName)); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + this.oName = null; + this.server = null; + } + } + + private void dispatchEvent(RegistrationEvent e) throws Exception { + AtomicReference ex = new AtomicReference<>(); + model.getRegistrationHandlers() + .forEach(h->{ + try { + h = h.bindTo(instance); + if (h.type().parameterCount() == 0) { + h.invoke(); + } else { + h.invokeWithArguments(e); + } + } catch (Throwable t) { + ex.set(new Exception(t)); + } + }); + + if (ex.get() != null) { + throw ex.get(); + } + } +} diff --git a/src/java.management/share/classes/com/sun/jmx/annotations/model/AttributeModel.java b/src/java.management/share/classes/com/sun/jmx/annotations/model/AttributeModel.java new file mode 100644 index 0000000000000..d941ef1746e90 --- /dev/null +++ b/src/java.management/share/classes/com/sun/jmx/annotations/model/AttributeModel.java @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.jmx.annotations.model; + +import com.sun.jmx.mbeanserver.Introspector; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import javax.management.Descriptor; +import javax.management.IntrospectionException; +import javax.management.annotations.AttributeAccess; +import javax.management.annotations.ManagedAttribute; +import javax.management.annotations.Tag; + +/** + * An MBean attribute specific model + * + */ +public final class AttributeModel extends DescribedModel { + private final Class type; + + private MethodHandle getter, setter; + private AttributeAccess access; + + public AttributeModel(Method m) throws IntrospectionException { + ManagedAttribute annot = m.getAnnotation(ManagedAttribute.class); + if (annot == null) { + throw new IntrospectionException("Can not build attribute model for a non-annotated field"); + } + this.access = annot.access(); + + setDescription(annot.description()); + addTags(annot); + + inferAttribute(annot, m); + if (getter != null) { + type = getter.type().returnType(); + } else if (setter != null) { + type = setter.type().parameterType(1); + } else { + throw new IntrospectionException("Unable to infer attribute type from " + m); + } + + setDescriptor(Introspector.descriptorForElement(m)); + } + + public AttributeModel(Field f) throws IntrospectionException { + ManagedAttribute annot = f.getAnnotation(ManagedAttribute.class); + if (annot == null) { + throw new IntrospectionException("Can not build attribute model for a non-annotated field"); + } + + this.access = annot.access(); + + setDescription(annot.description()); + addTags(annot); + type = f.getType(); + + inferAttribute(annot, f); + + setDescriptor(Introspector.descriptorForElement(f)); + } + + private void inferAttribute(ManagedAttribute annot, Method m) throws IntrospectionException { + try { + AttributeAccess attrAccess = annot.access(); + String mName = m.getName(); + String aName = annot.name(); + if (aName.isEmpty()) { + // infer the attribute name and type + if (mName.startsWith("get") || mName.startsWith("is")) { + if (aName.isEmpty()) { + if (mName.startsWith("get")) { + aName = mName.substring(3); + } else { + aName = mName.substring(2); + } + } + attrAccess = annot.setter().isEmpty() ? AttributeAccess.READ : AttributeAccess.READWRITE; + } else if (mName.startsWith("set")) { + if (aName.isEmpty()) { + aName = mName.substring(3); + } + attrAccess = annot.getter().isEmpty() ? AttributeAccess.WRITE : AttributeAccess.READWRITE; + } else { + throw new IntrospectionException("Can not infer a valid attribute name: " + m); + } + if (annot.access() != AttributeAccess.READWRITE && attrAccess != annot.access()) { + throw new IntrospectionException("Declared attribute access [" + annot.access() + "] " + + "differs from the inferred one [" + attrAccess + "]"); + } + } + switch (attrAccess) { + case READ: { + if (!isGetterSignature(m)) { + throw new IntrospectionException("Attribute getter must not have any arguments and must return a value: " + m); + } + getter = toMethodHandle(m); + setter = null; + break; + } + case WRITE: { + if (!isSetterSignature(m)) { + throw new IntrospectionException("Attribute setter must have exactly one argument and must not return a value: " + m); + } + setter = toMethodHandle(m); + getter = null; + break; + } + case READWRITE: { + if (!annot.getter().isEmpty()) { + try { + if (isSetterSignature(m)) { + getter = MethodHandles.publicLookup().findVirtual(m.getDeclaringClass(), annot.getter(), MethodType.methodType(m.getParameterTypes()[0])); + } else { + throw new IntrospectionException("Can not infer the attribute type from method " + m); + } + } catch (NoSuchMethodException | IllegalAccessException ex) { + throw new IntrospectionException(ex.getMessage()); + } + } + if (!annot.setter().isEmpty()) { + try { + if (isGetterSignature(m)) { + setter = MethodHandles.publicLookup().findVirtual(m.getDeclaringClass(), annot.setter(), MethodType.methodType(void.class, m.getReturnType())); + } else { + throw new IntrospectionException("Can not infer the attribute type from method " + m); + } + } catch (NoSuchMethodException | IllegalAccessException ex) { + throw new IntrospectionException(ex.getMessage()); + } + } + if (getter == null && isGetterSignature(m)) { + getter = MethodHandles.publicLookup().unreflect(m); + } + if (setter == null && isSetterSignature(m)) { + setter = MethodHandles.publicLookup().unreflect(m); + } + if (getter == null && setter == null) { + throw new IntrospectionException("An attribute defining method must be either a getter or setter"); + } + break; + } + } + + setName(aName); + } catch (IllegalAccessException ex) { + throw new IntrospectionException(ex.getMessage()); + } + } + + private static boolean isSetterSignature(Method m) { + return m.getParameterCount() == 1 && m.getReturnType().equals(void.class); + } + + private static boolean isGetterSignature(Method m) { + return m.getParameterCount() == 0 && !m.getReturnType().equals(void.class); + } + + private void inferAttribute(ManagedAttribute annot, Field f) throws IntrospectionException { + if (isReadable(annot)) { + if (annot.getter().isEmpty()) { + try { + getter = MethodHandles.publicLookup().findGetter(f.getDeclaringClass(), f.getName(), f.getType()); + } catch (NoSuchFieldException e) { + throw new IntrospectionException(e.getMessage()); + } catch (IllegalAccessException e) { + try { + MethodHandle mh = MethodHandles.lookup().findStatic(Trampoline.class, "getField", MethodType.methodType(Object.class, Field.class, Object.class)); + getter = mh.bindTo(f); + } catch (NoSuchMethodException | IllegalAccessException ex) { + throw new IntrospectionException(ex.getMessage()); + } + } + } else { + try { + getter = MethodHandles.publicLookup().findVirtual(f.getDeclaringClass(), annot.getter(), MethodType.methodType(f.getType())); + } catch (NoSuchMethodException | IllegalAccessException ex) { + throw new IntrospectionException(ex.getMessage()); + } + } + } else { + if (!annot.getter().isEmpty()) { + throw new IntrospectionException("Can not specify 'getter' for a non-readable attribute"); + } + } + + if (isWritable(annot)) { + if (annot.setter().isEmpty()) { + try { + setter = MethodHandles.publicLookup().findSetter(f.getDeclaringClass(), f.getName(), f.getType()); + } catch (NoSuchFieldException e) { + throw new IntrospectionException(e.getMessage()); + } catch (IllegalAccessException e) { + try { + MethodHandle mh = MethodHandles.lookup().findStatic(Trampoline.class, "setField", MethodType.methodType(void.class, Field.class, Object.class, Object.class)); + setter = mh.bindTo(f); + } catch (NoSuchMethodException | IllegalAccessException ex) { + throw new IntrospectionException(ex.getMessage()); + } + } + } else { + try { + setter = MethodHandles.publicLookup().findVirtual(f.getDeclaringClass(), annot.setter(), MethodType.methodType(void.class, f.getType())); + } catch (NoSuchMethodException | IllegalAccessException ex) { + throw new IntrospectionException(ex.getMessage()); + } + } + } else { + if (!annot.setter().isEmpty()) { + throw new IntrospectionException("Can not specify 'setter' for a non-writable attribute"); + } + } + String fldAttrName = f.getName(); + + setName(annot.name().isEmpty() ? fldAttrName : annot.name()); + } + + public MethodHandle getGetter() { + return getter; + } + + public MethodHandle getSetter() { + return setter; + } + + public Class getType() { + return type; + } + + public void setGetter(MethodHandle getter) { + this.getter = getter; + } + + public void setSetter(MethodHandle setter) { + this.setter = setter; + } + + public boolean isReadable() { + return getter != null && + (access == AttributeAccess.READ || + access == AttributeAccess.READWRITE); + } + + public boolean isWritable() { + return setter != null && + (access == AttributeAccess.WRITE || + access == AttributeAccess.READWRITE); + } + + AttributeAccess getAccess() { + return access; + } + + void setAccess(AttributeAccess access) { + this.access = access; + } + + @Override + protected void ammendDescriptor(Descriptor d) { + d.setField("descriptorType", "attribute"); + d.setField("role", "attribute"); + } + + private static boolean isReadable(ManagedAttribute ma) { + return ma.access() == AttributeAccess.READ || + ma.access() == AttributeAccess.READWRITE; + } + + private static boolean isWritable(ManagedAttribute ma) { + return ma.access() == AttributeAccess.WRITE || + ma.access() == AttributeAccess.READWRITE; + } + + private void addTags(ManagedAttribute annot) { + if (!annot.units().isEmpty()) { + addTag("units", annot.units()); + } + for (Tag t : annot.tags()) { + addTag(t.name(), t.value()); + } + } +} diff --git a/src/java.management/share/classes/com/sun/jmx/annotations/model/DescribedModel.java b/src/java.management/share/classes/com/sun/jmx/annotations/model/DescribedModel.java new file mode 100644 index 0000000000000..5b1b6ab8a4cd8 --- /dev/null +++ b/src/java.management/share/classes/com/sun/jmx/annotations/model/DescribedModel.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.jmx.annotations.model; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import javax.management.Descriptor; +import javax.management.modelmbean.DescriptorSupport; + +/** + * A base class for all the models providing name, description and tags. + * + */ +abstract class DescribedModel { + private String description = ""; + private String name; + private final Map tags = new HashMap<>(); + private Descriptor descriptor; + + protected static final String UNITS_KEY = "units"; + + public final String getDescription() { + return description; + } + + public final String getName() { + return name; + } + + public final void setDescription(String description) { + this.description = description; + } + + public final void setName(String name) { + this.name = name; + } + + public void addTag(String key, String value) { + tags.put(key, value); + } + + public Map getTags() { + return Collections.unmodifiableMap(tags); + } + + public final Descriptor getDescriptor() { + Descriptor d = null; + if (descriptor != null) { + String[] fldNames = descriptor.getFieldNames(); + d = new DescriptorSupport( + fldNames, + descriptor.getFieldValues(fldNames) + ); + } else { + d = new DescriptorSupport(); + } + d.setField("name", name); + d.setField("displayName", name); + for(Map.Entry e : tags.entrySet()) { + d.setField(e.getKey(), e.getValue()); + } + ammendDescriptor(d); + return d; + } + + protected void ammendDescriptor(Descriptor d) {} + + public final void setDescriptor(Descriptor descriptor) { + this.descriptor = descriptor; + } + + final protected MethodHandle toMethodHandle(Method m) throws IllegalAccessException { + MethodHandle mh = null; + try { + mh = MethodHandles.publicLookup().unreflect(m); + return mh; + } catch (IllegalAccessException e) { + // implementation in non-public class? + try { + return unreflectNonPublic(m); + } catch (NoSuchMethodException ex) { + // the IllegalAccessException will be rethrown later + } + throw e; + } + } + + private MethodHandle unreflectNonPublic(Method m) throws NoSuchMethodException, IllegalAccessException { + Class clz = m.getDeclaringClass(); + Deque> toSearch = new LinkedList<>(); + toSearch.add(clz); + while ((clz = toSearch.poll()) != null) { + if (!Modifier.isPublic(clz.getModifiers())) { + toSearch.addAll(Arrays.asList(clz.getInterfaces())); + Class supr = clz.getSuperclass(); + if (!supr.equals(Object.class)) { + toSearch.add(supr); + } + } else { + break; + } + } + if (clz != null) { + return MethodHandles.publicLookup().findVirtual(clz, m.getName(), MethodType.methodType(m.getReturnType(), m.getParameterTypes())); + } + + throw new NoSuchMethodException("Unable to find a public version of method '" + m.getName() + "'"); + } +} diff --git a/src/java.management/share/classes/com/sun/jmx/annotations/model/InjectableFieldModel.java b/src/java.management/share/classes/com/sun/jmx/annotations/model/InjectableFieldModel.java new file mode 100644 index 0000000000000..3a6287ef60026 --- /dev/null +++ b/src/java.management/share/classes/com/sun/jmx/annotations/model/InjectableFieldModel.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.jmx.annotations.model; + +import java.lang.reflect.Field; +import javax.management.IntrospectionException; + +public class InjectableFieldModel { + private final Field fld; + + public InjectableFieldModel(Field fld) { + this.fld = fld; + this.fld.setAccessible(true); + } + + public Class getType() { + return this.fld.getType(); + } + + public void inject(Object owner, Object val) throws IntrospectionException { + try { + this.fld.set(owner, val); + } catch (IllegalArgumentException | IllegalAccessException e) { + throw new IntrospectionException(e.getMessage()); + } + } +} diff --git a/src/java.management/share/classes/com/sun/jmx/annotations/model/MXBeanModel.java b/src/java.management/share/classes/com/sun/jmx/annotations/model/MXBeanModel.java new file mode 100644 index 0000000000000..0efa711420bbd --- /dev/null +++ b/src/java.management/share/classes/com/sun/jmx/annotations/model/MXBeanModel.java @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.jmx.annotations.model; + +import com.sun.jmx.mbeanserver.MXBeanMappingFactory; +import java.lang.invoke.MethodHandle; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import javax.management.IntrospectionException; +import javax.management.ObjectName; +import javax.management.annotations.AttributeAccess; + +/** + * A model representing the annotated MBean class + * + * @param The MBean implementation class + */ +final public class MXBeanModel extends DescribedModel { + private ObjectName oName; + private Class svc; + private MXBeanMappingFactory mappingFactory; + + private final Map operations = new HashMap<>(); + private final Map attributes = new HashMap<>(); + private final Set notifications = new HashSet<>(); + private final Set injectableFields = new HashSet<>(); + private final Set registrationHandlers = new HashSet<>(); + + public ObjectName getObjectName() { + return oName; + } + + public void setObjectName(ObjectName oName) { + this.oName = oName; + } + + public Class getService() { + return svc; + } + + public void setService(Class svc) { + this.svc = svc; + } + + public Collection getOperations() { + return Collections.unmodifiableCollection(operations.values()); + } + + public void addOperation(OperationModel m) throws IntrospectionException { + String key = m.getName() + ":" + m.getSignature(); + if (operations.containsKey(key)) { + throw new IntrospectionException( + "Attempting to redefine operation '" + m.getName() + "' " + + "already defined by '" + m.getMethod() + "'"); + } + operations.put(key, m); + } + + public OperationModel getOperation(String name, String type) { + OperationModel m = operations.get(name + ":" + type); + + return m; + } + + public Collection getAttributes() { + return Collections.unmodifiableCollection(attributes.values()); + } + + public void addAttribute(AttributeModel m) throws IntrospectionException { + String aName = m.getName(); + AttributeModel m1 = attributes.get(aName); + if (m1 != null) { + if (m1.getGetter() != null && m.getGetter() != null) { + throw new IntrospectionException( + "Attempting to redefine attribute '" + aName + "' getter " + + "already defined by '" + m1.getGetter() + "'"); + } + if (m1.getSetter() != null && m.getSetter() != null) { + throw new IntrospectionException( + "Attempting to redefine attribute '" + aName + "' setter " + + "already defined by '" + m1.getSetter() + "'"); + } + if (m1.getGetter() == null) { + m1.setGetter(m.getGetter()); + } + + if (m1.getSetter() == null) { + m1.setSetter(m.getSetter()); + } + } else { + attributes.put(aName, m); + } + } + + public AttributeModel getAttribute(String name) { + AttributeModel m = attributes.get(name); + + return m; + } + + public Collection getNotifications() { + return Collections.unmodifiableCollection(notifications); + } + + public void addNotification(NotificationModel n) { + notifications.add(n); + } + + public void addInjectableField(InjectableFieldModel inf) { + injectableFields.add(inf); + } + + public Collection getInjectableFields() { + return Collections.unmodifiableCollection(injectableFields); + } + + public MXBeanMappingFactory getMappingFactory() { + return mappingFactory; + } + + public void addRegistrationHandler(MethodHandle handler) { + registrationHandlers.add(handler); + } + + public Collection getRegistrationHandlers() { + return Collections.unmodifiableCollection(registrationHandlers); + } + + public void setMappingFactory(MXBeanMappingFactory mappingFactory) { + this.mappingFactory = mappingFactory; + } + + public boolean isRegistrationHandler() { + return !registrationHandlers.isEmpty(); + } + + void updateAttributeAccess() throws IntrospectionException { + for (AttributeModel am : getAttributes()) { + switch (am.getAccess()) { + case READ: { + if (am.getGetter() == null) { + throw new IntrospectionException( + "Attribute '" + getName() + "' specifies READ access " + + "but does not provide the getter." + ); + } + if (am.getSetter() != null) { + am.setAccess(AttributeAccess.READWRITE); + } + break; + } + case WRITE: { + if (am.getSetter() == null) { + throw new IntrospectionException( + "Attribute '" + getName() + "' specifies WRITE access " + + "but does not provide the setter." + ); + } + if (am.getGetter() != null) { + am.setAccess(AttributeAccess.READWRITE); + } + break; + } + case READWRITE: { + if (am.getGetter() != null || am.getSetter() != null) { + if (am.getGetter() == null && am.getSetter() != null) { + am.setAccess(AttributeAccess.WRITE); + } + if (am.getSetter() == null && am.getGetter() != null) { + am.setAccess(AttributeAccess.READ); + } + } else { + throw new IntrospectionException( + "Attribute '" + getName() + "' specifies READWRITE access " + + "but does not provide neither getter or setter"); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/java.management/share/classes/com/sun/jmx/annotations/model/MXBeanModelBuilder.java b/src/java.management/share/classes/com/sun/jmx/annotations/model/MXBeanModelBuilder.java new file mode 100644 index 0000000000000..3754bf3d2a1c0 --- /dev/null +++ b/src/java.management/share/classes/com/sun/jmx/annotations/model/MXBeanModelBuilder.java @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.jmx.annotations.model; + +import com.sun.jmx.annotations.ModelBuilder; +import com.sun.jmx.mbeanserver.DefaultMXBeanMappingFactory; +import com.sun.jmx.mbeanserver.Introspector; +import com.sun.jmx.mbeanserver.MXBeanMappingFactory; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.management.Descriptor; +import javax.management.ImmutableDescriptor; +import javax.management.IntrospectionException; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.annotations.ManagedAttribute; +import javax.management.annotations.ManagedService; +import javax.management.annotations.NotificationInfo; +import javax.management.annotations.NotificationInfos; +import javax.management.annotations.NotificationSender; +import javax.management.annotations.ManagedOperation; +import javax.management.annotations.RegistrationEvent; +import javax.management.annotations.RegistrationHandler; +import javax.management.annotations.Tag; +import javax.management.modelmbean.DescriptorSupport; + +/** + * + * @author jbachorik + */ +final public class MXBeanModelBuilder implements ModelBuilder { + @Override + public MXBeanModel buildModel(Class clz) throws IntrospectionException { + return toMXBeanModel(clz); + } + + private MXBeanModel toMXBeanModel(Class clz) throws IntrospectionException { + ManagedService anno = clz.getAnnotation(ManagedService.class); + if (anno == null) { + return null; + } + + MXBeanModel m = new MXBeanModel<>(); + + addAnnotatedFields(clz, m); + addAnnotatedOperations(clz, m); + addAnnotatedAttributes(clz, m); + addAnnotatedRegHandlers(clz, m); + + checkForOverlappingInterfaceMethods(m); + + m.updateAttributeAccess(); + + Descriptor d = new DescriptorSupport(); + d.setField("mxbean2", true); + + m.setDescriptor(ImmutableDescriptor.union(d, Introspector.descriptorForElement(clz))); + + apply(clz.getName(), m, anno); + + return m; + } + + private void checkForOverlappingInterfaceMethods(MXBeanModel m) throws IntrospectionException { + Set opKeys = getOperationsInterfaceMethods(m); + Set attrKeys = getAttributeInterfaceMethods(m); + + opKeys.retainAll(attrKeys); + if (!opKeys.isEmpty()) { + throw new IntrospectionException("Operation names are overlapping with attribute names"); + } + } + + private Set getOperationsInterfaceMethods(MXBeanModel m) { + Set opKeys = m.getOperations().stream() + .map(om->om.getName() + "#" + om.getSignature()) + .collect(Collectors.toSet()); + return opKeys; + } + + private Set getAttributeInterfaceMethods(MXBeanModel m) { + Set attrKeys = m.getAttributes().stream() + .flatMap(am->Stream.of( + am.getGetter() != null && am.getType() != boolean.class && + am.getType() != Boolean.class ? + "get" + am.getName() + "#()" : null, + am.getGetter() != null && (am.getType() == boolean.class || + am.getType() == Boolean.class) ? + "is" + am.getName() + "#()" : null, + am.getSetter() != null ? + "set" + am.getName() + "#(" + am.getType().getName() + ")" : null)) + .filter(i->i != null) + .collect(Collectors.toSet()); + return attrKeys; + } + + private void apply(String className, MXBeanModel m, ManagedService anno) throws IntrospectionException { + String oName = anno.objectName(); + if (!oName.isEmpty()) { + try { + ObjectName on = ObjectName.getInstance(oName); + m.setObjectName(on); + } catch (MalformedObjectNameException e) { + } + } + + if (!anno.service().equals(Object.class)) { + validateServiceInterface(anno.service(), m); + m.setService(anno.service()); + } + m.setName(className); + m.setDescription(anno.description()); + for (Tag t : anno.tags()) { + m.addTag(t.name(), t.value()); + } + setMappingFactory(m, anno); + } + + private void validateServiceInterface(Class service, MXBeanModel model) throws IntrospectionException { + if (service.isInterface()) { + Set unknownMethods = new HashSet<>(); + + for(Method m : service.getMethods()) { + String mName = m.getName(); + if (isGetter(m) || isSetter(m)) { + String attrName = (mName.startsWith("get") || mName.startsWith("set")) ? + mName.substring(3) : + mName.substring(2); + + String attrType = mName.startsWith("get") || mName.startsWith("is") ? + m.getReturnType().getName() : + m.getParameterTypes()[0].getName(); + + AttributeModel am = model.getAttribute(attrName); + if (am.getType().getName().equals(attrType)) continue; + } + + if (model.getOperation(m.getName(), getSignature(m)) != null) continue; + unknownMethods.add(m); + } + if (!unknownMethods.isEmpty()) { + String msg = unknownMethods.stream() + .map(m->m.toString()).collect(Collectors.joining( + "Managed service does not expose the following methods: ", "\n", "") + ); + throw new IntrospectionException(msg); + } + } else { + throw new IntrospectionException("Service class must be an 'interface'"); + } + } + + private static boolean isGetter(Method m) { + String mName = m.getName(); + return (mName.startsWith("get") || mName.startsWith("is")) && + m.getParameterCount() == 0 && + m.getReturnType() != void.class; + } + + private static boolean isSetter(Method m) { + String mName = m.getName(); + return mName.startsWith("set") && m.getParameterCount() == 1 && + m.getReturnType() == void.class; + } + + private static String getSignature(Method m) { + return Arrays.asList(m.getParameters()).stream() + .map(p->p.getType().getName()) + .collect(Collectors.joining(", ", "(", ")")); + } + + private static void setMappingFactory(MXBeanModel m, ManagedService anno) { + // TODO: re-enable when modules are properly set up +// Class fctryClz = anno.mapping(); +// if (fctryClz.equals(DefaultMXBeanMappingFactory.class)) { +// m.setMappingFactory(MXBeanMappingFactory.DEFAULT); +// } else { +// try { +// MXBeanMappingFactory fctry = fctryClz.getConstructor().newInstance(); +// m.setMappingFactory(fctry); +// } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | +// InvocationTargetException e) { +// m.setMappingFactory(MXBeanMappingFactory.DEFAULT); +// } +// } + m.setMappingFactory(MXBeanMappingFactory.DEFAULT); + } + + private static void addAnnotatedFields(Class clz, MXBeanModel model) throws IntrospectionException { + for(Field f : clz.getDeclaredFields()) { + if (f.isAnnotationPresent(NotificationInfos.class)) { + if (f.getType().isAssignableFrom(NotificationSender.class)) { + model.addInjectableField(new InjectableFieldModel(f)); + addNotificationInfos(f, model); + } + } else if (f.isAnnotationPresent(NotificationInfo.class)) { + if (f.getType().isAssignableFrom(NotificationSender.class)) { + model.addInjectableField(new InjectableFieldModel(f)); + model.addNotification(new NotificationModel(f.getAnnotation(NotificationInfo.class))); + } + } + } + } + + private static void addNotificationInfos(AnnotatedElement ae, MXBeanModel model) { + NotificationInfos ns = ae.getAnnotation(NotificationInfos.class); + if (ns != null) { + for (NotificationInfo n : ns.value()) { + model.addNotification(new NotificationModel(n)); + } + } + } + + private static void addAnnotatedOperations(Class clz, MXBeanModel model) throws IntrospectionException { + for(Method m : clz.getMethods()) { + if (m.getAnnotation(ManagedOperation.class) != null) { + OperationModel mom = toOperationModel(m); + if (mom != null) { + model.addOperation(mom); + } + } + } + } + + private static void addAnnotationsFromParameters(Parameter[] params, + MXBeanModel model) { + for (Parameter p : params) { + if (p.isAnnotationPresent(NotificationInfos.class) || + p.isAnnotationPresent(NotificationInfo.class)) { + NotificationInfos ns = p.getAnnotation(NotificationInfos.class); + if (ns != null) { + for (NotificationInfo n : ns.value()) { + model.addNotification(new NotificationModel(n)); + } + } + NotificationInfo n = p.getAnnotation(NotificationInfo.class); + if (n != null) { + model.addNotification(new NotificationModel(n)); + } + } + } + } + + private static void addAnnotatedAttributes(Class clz, MXBeanModel model) throws IntrospectionException { + for (Field f : clz.getDeclaredFields()) { + if (f.getAnnotation(ManagedAttribute.class) != null) { + AttributeModel mam = toAttributeModel(f); + if (mam != null) { + model.addAttribute(mam); + } + } + } + + for (Method m : clz.getMethods()) { + if (m.getAnnotation(ManagedAttribute.class) != null) { + AttributeModel mam = toAttributeModel(m); + if (mam != null) { + model.addAttribute(mam); + addAnnotationsFromParameters(m.getParameters(), model); + } + } + } + } + + private static void addAnnotatedRegHandlers(Class clz, MXBeanModel model) throws IntrospectionException { + for (Method m : clz.getDeclaredMethods()) { + if (m.getAnnotation(RegistrationHandler.class) != null) { + checkRegHandler(m); + try { + MethodHandle mh = MethodHandles.publicLookup().unreflect(m); + model.addRegistrationHandler(mh); + } catch (IllegalAccessException e) { + throw new IntrospectionException(e.getMessage()); + } + } + } + } + + private static void checkRegHandler(Method m) throws IntrospectionException { + if (!isRegHandler(m)) { + throw new IntrospectionException( + "Invalid registration handler '" + m + "'. Registration handler " + + "is expected to be public, non-abstract and having signature of " + + "(javax.management.annotations.RegistrationEvent)void" + ); + } + } + + private static boolean isRegHandler(Method m) { + Class[] types = m.getParameterTypes(); + int mods = m.getModifiers(); + return Modifier.isPublic(mods) && !Modifier.isAbstract(mods) && + m.getReturnType() == void.class && types.length == 1 && + types[0].equals(RegistrationEvent.class); + } + + private static OperationModel toOperationModel(Method m) throws IntrospectionException { + ManagedOperation mo = m.getAnnotation(ManagedOperation.class); + + if (mo == null) { + return null; + } + + return new OperationModel(m); + } + + private static AttributeModel toAttributeModel(Method m) throws IntrospectionException { + ManagedAttribute ma = m.getAnnotation(ManagedAttribute.class); + + if (ma == null) { + return null; + } + + return new AttributeModel(m); + } + + private static AttributeModel toAttributeModel(Field f) throws IntrospectionException { + ManagedAttribute ma = f.getAnnotation(ManagedAttribute.class); + + if (ma == null) { + return null; + } + + return new AttributeModel(f); + } +} diff --git a/src/java.management/share/classes/com/sun/jmx/annotations/model/NotificationModel.java b/src/java.management/share/classes/com/sun/jmx/annotations/model/NotificationModel.java new file mode 100644 index 0000000000000..a4fb5d9344d04 --- /dev/null +++ b/src/java.management/share/classes/com/sun/jmx/annotations/model/NotificationModel.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.jmx.annotations.model; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import javax.management.Notification; +import javax.management.annotations.NotificationInfo; + +/** + * An MBean notification specific model. + * + */ +public final class NotificationModel { + private final Class clazz; + private final Set types = new HashSet<>(); + private final String description; + private final int severity; + + public NotificationModel(NotificationInfo n) { + clazz = n.implementation(); + description = n.description(); + types.addAll(Arrays.asList(n.types())); + severity = n.severity(); + } + + public Class getClazz() { + return clazz; + } + + public String getDescription() { + return description == null ? "" : description; + } + + public Set getTypes() { + return Collections.unmodifiableSet(types); + } + + public int getSeverity() { + return severity; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 29 * hash + Objects.hashCode(this.clazz); + hash = 29 * hash + Objects.hashCode(this.types); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final NotificationModel other = (NotificationModel) obj; + if (!Objects.equals(this.clazz, other.clazz)) { + return false; + } + if (!Objects.equals(this.types, other.types)) { + return false; + } + return true; + } + + +} diff --git a/src/java.management/share/classes/com/sun/jmx/annotations/model/OperationModel.java b/src/java.management/share/classes/com/sun/jmx/annotations/model/OperationModel.java new file mode 100644 index 0000000000000..054b249d04c2f --- /dev/null +++ b/src/java.management/share/classes/com/sun/jmx/annotations/model/OperationModel.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.jmx.annotations.model; + +import com.sun.jmx.mbeanserver.Introspector; +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Method; +import java.util.stream.Collectors; +import javax.management.Descriptor; +import javax.management.IntrospectionException; +import javax.management.annotations.ManagedOperation; +import javax.management.annotations.Tag; + +/** + * An MBean operation specific model + * + */ +public final class OperationModel extends ParametrizedModel { + private MethodHandle methodHandle; + final private Method method; + final private String impact; + + public OperationModel(Method m) throws IntrospectionException { + super(m.getParameters()); + + ManagedOperation mo = m.getAnnotation(ManagedOperation.class); + if (mo == null) { + throw new IntrospectionException("Can not build operation model for a non-annotated method"); + } + + try { + setName(mo.name().isEmpty() ? m.getName() : mo.name()); + setDescription(mo.description()); + if (!mo.units().isEmpty()) { + addTag(UNITS_KEY, mo.units()); + } + for (Tag t : mo.tags()) { + addTag(t.name(), t.value()); + } + + method = m; + methodHandle = toMethodHandle(m); + impact = mo.impact().toString(); + + setDescriptor(Introspector.descriptorForElement(m)); + } catch (IllegalAccessException e) { + throw new IntrospectionException(e.getMessage()); + } + } + + public Method getMethod() { + return method; + } + + public MethodHandle getMethodHandle() { + return methodHandle; + } + + public void setMethodHandle(MethodHandle methodHandle) { + this.methodHandle = methodHandle; + } + + public String getImpact() { + return impact; + } + + public Class getType() { + return method.getReturnType(); + } + + public String getSignature() { + return getParameters().stream() + .map(p->p.getType().getName()) + .collect(Collectors.joining(", ", "(", ")")); + } + + @Override + protected void ammendDescriptor(Descriptor d) { + d.setField("descriptorType", "operation"); + d.setField("role", "operation"); + } + +} diff --git a/src/java.management/share/classes/com/sun/jmx/annotations/model/ParameterModel.java b/src/java.management/share/classes/com/sun/jmx/annotations/model/ParameterModel.java new file mode 100644 index 0000000000000..4986549b5e3de --- /dev/null +++ b/src/java.management/share/classes/com/sun/jmx/annotations/model/ParameterModel.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.jmx.annotations.model; + +import java.lang.reflect.Parameter; +import javax.management.annotations.ParameterInfo; +import javax.management.annotations.Tag; + +/** + * Represents an operation/constructor parameter + */ +public final class ParameterModel extends DescribedModel { + private final Parameter param; + + public ParameterModel(Parameter param) { + this.param = param; + ParameterInfo pa = param.getAnnotation(ParameterInfo.class); + + setName(param.getName()); + if (pa != null) { + if (!pa.name().isEmpty()) { + setName(pa.name()); + } + setDescription(pa.description()); + for(Tag t : pa.tags()) { + addTag(t.name(), t.value()); + } + if (!pa.units().isEmpty()) { + addTag(UNITS_KEY, pa.units()); + } + } + } + + public Class getType() { + return param.getType(); + } +} diff --git a/src/java.management/share/classes/com/sun/jmx/annotations/model/ParametrizedModel.java b/src/java.management/share/classes/com/sun/jmx/annotations/model/ParametrizedModel.java new file mode 100644 index 0000000000000..24f22b9849e80 --- /dev/null +++ b/src/java.management/share/classes/com/sun/jmx/annotations/model/ParametrizedModel.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.jmx.annotations.model; + +import java.lang.reflect.Parameter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * An abstract superclass for models being able to handle parameters. + */ +abstract class ParametrizedModel extends DescribedModel { + + protected final List parameters = new ArrayList<>(); + + public ParametrizedModel(Parameter[] params) { + for (Parameter p : params) { + parameters.add(new ParameterModel(p)); + } + } + + final public List getParameters() { + return Collections.unmodifiableList(parameters); + } +} diff --git a/src/java.management/share/classes/com/sun/jmx/annotations/model/Trampoline.java b/src/java.management/share/classes/com/sun/jmx/annotations/model/Trampoline.java new file mode 100644 index 0000000000000..bf2b76d3c5c85 --- /dev/null +++ b/src/java.management/share/classes/com/sun/jmx/annotations/model/Trampoline.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.jmx.annotations.model; + +import java.lang.reflect.Field; +import java.lang.reflect.ReflectPermission; + +/** + * A trampoline for generating artificial getters/setters for non-public fields. + * + */ +class Trampoline { + private static final ReflectPermission ACCESS_CHECKS = new ReflectPermission("suppressAccessChecks"); + + @SuppressWarnings("unchecked") + static T getField(Field fld, Object instance) { + try { + fld.setAccessible(true); + return (T) fld.get(instance); + } catch (Exception exception) { + return null; + } + } + + @SuppressWarnings("unchecked") + static void setField(Field fld, Object instance, T value) { + try { + fld.setAccessible(true); + fld.set(instance, value); + } catch (Exception exception) { + } + } +} diff --git a/src/java.management/share/classes/com/sun/jmx/annotations/model/package-info.java b/src/java.management/share/classes/com/sun/jmx/annotations/model/package-info.java new file mode 100644 index 0000000000000..2cc5734b7c46b --- /dev/null +++ b/src/java.management/share/classes/com/sun/jmx/annotations/model/package-info.java @@ -0,0 +1,5 @@ +/** + * This whole package should be moved to jdk.management module to enforce + * public API boundaries. + */ +package com.sun.jmx.annotations.model; \ No newline at end of file diff --git a/src/java.management/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java b/src/java.management/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java index e5aa863f9a6b8..9cdf2cfd606d3 100644 --- a/src/java.management/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java +++ b/src/java.management/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java @@ -26,6 +26,7 @@ package com.sun.jmx.interceptor; +import com.sun.jmx.annotations.AnnotatedMBeanIntrospector; // JMX RI import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER; import com.sun.jmx.mbeanserver.DynamicMBean2; @@ -292,6 +293,8 @@ private ObjectInstance createMBean(String className, ObjectName name, Object moi= instantiator.instantiate(theClass, params, signature, server.getClass().getClassLoader()); + moi = wrapAnnotatedMBean(moi); + final String infoClassName = getNewMBeanClassName(moi); return registerObject(infoClassName, moi, name); @@ -307,6 +310,8 @@ public ObjectInstance registerMBean(Object object, ObjectName name) Introspector.checkCompliance(theClass); + object = wrapAnnotatedMBean(object); + final String infoClassName = getNewMBeanClassName(object); checkMBeanPermission(infoClassName, null, name, "registerMBean"); @@ -2015,4 +2020,22 @@ public ModifiableClassLoaderRepository run() { } }); } + + /** + * Will try to wrap the instance of an annotated MBean class as a DynamicMBean instance + * @param moi The instance to be wrapped + * @return Returns the DynamicMBean wrapper or the original instance if the class is not properly annotated + * @throws MBeanException Any exceptions raised during building the wrapper are propagated as {@linkplain MBeanException} + */ + private Object wrapAnnotatedMBean(Object moi) throws NotCompliantMBeanException { + try { + DynamicMBean annotatedBean = AnnotatedMBeanIntrospector.toMBean(moi); + if (annotatedBean != null) { + moi = annotatedBean; + } + } catch (IntrospectionException ex) { + throw new NotCompliantMBeanException(ex.getMessage()); + } + return moi; + } } diff --git a/src/java.management/share/classes/com/sun/jmx/mbeanserver/Introspector.java b/src/java.management/share/classes/com/sun/jmx/mbeanserver/Introspector.java index ea1d685b5fce3..d3ec5485e4772 100644 --- a/src/java.management/share/classes/com/sun/jmx/mbeanserver/Introspector.java +++ b/src/java.management/share/classes/com/sun/jmx/mbeanserver/Introspector.java @@ -54,6 +54,7 @@ import java.lang.reflect.InvocationTargetException; import java.security.AccessController; import javax.management.AttributeNotFoundException; +import javax.management.annotations.ManagedService; import javax.management.openmbean.CompositeData; import sun.reflect.misc.MethodUtil; @@ -148,6 +149,10 @@ public static void testCreation(Class c) public static void checkCompliance(Class mbeanClass) throws NotCompliantMBeanException { + if (mbeanClass.getAnnotation(ManagedService.class) != null) { + // annotated MBean + return; + } // Is DynamicMBean? // if (DynamicMBean.class.isAssignableFrom(mbeanClass)) diff --git a/src/java.management/share/classes/com/sun/jmx/mbeanserver/MXBeanMapping.java b/src/java.management/share/classes/com/sun/jmx/mbeanserver/MXBeanMapping.java index 46134b1ccfcbc..89c3542539fc4 100644 --- a/src/java.management/share/classes/com/sun/jmx/mbeanserver/MXBeanMapping.java +++ b/src/java.management/share/classes/com/sun/jmx/mbeanserver/MXBeanMapping.java @@ -141,7 +141,7 @@ protected MXBeanMapping(Type javaType, OpenType openType) { *

The Java type that was supplied to the constructor.

* @return the Java type that was supplied to the constructor. */ - public final Type getJavaType() { + public Type getJavaType() { return javaType; } @@ -149,7 +149,7 @@ public final Type getJavaType() { *

The Open Type that was supplied to the constructor.

* @return the Open Type that was supplied to the constructor. */ - public final OpenType getOpenType() { + public OpenType getOpenType() { return openType; } @@ -160,7 +160,7 @@ public final OpenType getOpenType() { * Open Type for this mapping. * @see OpenType#getClassName */ - public final Class getOpenClass() { + public Class getOpenClass() { return openClass; } diff --git a/src/java.management/share/classes/java/lang/management/ManagementFactory.java b/src/java.management/share/classes/java/lang/management/ManagementFactory.java index 8e290f373b72b..7a6d4a4feb31b 100644 --- a/src/java.management/share/classes/java/lang/management/ManagementFactory.java +++ b/src/java.management/share/classes/java/lang/management/ManagementFactory.java @@ -57,6 +57,7 @@ import java.util.stream.Stream; import javax.management.JMX; import sun.management.Util; +import javax.management.annotations.ManagedService; import sun.management.spi.PlatformMBeanProvider; import sun.management.spi.PlatformMBeanProvider.PlatformComponent; @@ -896,11 +897,13 @@ private static void addMXBean(final MBeanServer mbs, String name, final Object p ObjectName oname = ObjectName.getInstance(name); // Make DynamicMBean out of MXBean by wrapping it with a StandardMBean AccessController.doPrivileged((PrivilegedExceptionAction) () -> { - final DynamicMBean dmbean; + final Object dmbean; if (pmo instanceof DynamicMBean) { dmbean = DynamicMBean.class.cast(pmo); } else if (pmo instanceof NotificationEmitter) { dmbean = new StandardEmitterMBean(pmo, null, true, (NotificationEmitter) pmo); + } else if (pmo.getClass().isAnnotationPresent(ManagedService.class)) { + dmbean = pmo; } else { dmbean = new StandardMBean(pmo, null, true); } diff --git a/src/java.management/share/classes/java/lang/management/ThreadMXBean.java b/src/java.management/share/classes/java/lang/management/ThreadMXBean.java index 52437adc6b3db..d28cac35b5c66 100644 --- a/src/java.management/share/classes/java/lang/management/ThreadMXBean.java +++ b/src/java.management/share/classes/java/lang/management/ThreadMXBean.java @@ -25,6 +25,7 @@ package java.lang.management; +import javax.management.MXBean; import java.util.Map; /** @@ -126,6 +127,7 @@ * @since 1.5 */ +@MXBean(false) public interface ThreadMXBean extends PlatformManagedObject { /** * Returns the current number of live platform threads including both diff --git a/src/java.management/share/classes/javax/management/IntrospectionException.java b/src/java.management/share/classes/javax/management/IntrospectionException.java index a40129a149713..ed5ec885b049f 100644 --- a/src/java.management/share/classes/javax/management/IntrospectionException.java +++ b/src/java.management/share/classes/javax/management/IntrospectionException.java @@ -26,6 +26,8 @@ package javax.management; +import sun.management.counter.perf.InstrumentationException; + /** * An exception occurred during the introspection of an MBean. * diff --git a/src/java.management/share/classes/javax/management/annotations/AttributeAccess.java b/src/java.management/share/classes/javax/management/annotations/AttributeAccess.java new file mode 100644 index 0000000000000..009c9ff57f72a --- /dev/null +++ b/src/java.management/share/classes/javax/management/annotations/AttributeAccess.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.management.annotations; + +/** + * Attribute access policy + */ +public enum AttributeAccess { + /** + * Read-only attribute + */ + READ, + /** + * Write-only attribute + */ + WRITE, + /** + * Read-write attribute + */ + READWRITE +} diff --git a/src/java.management/share/classes/javax/management/annotations/Impact.java b/src/java.management/share/classes/javax/management/annotations/Impact.java new file mode 100644 index 0000000000000..de06fcedbe7e1 --- /dev/null +++ b/src/java.management/share/classes/javax/management/annotations/Impact.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.management.annotations; + +import javax.management.MBeanOperationInfo; + +/** + * Operation impact enumeration + */ +public enum Impact { + /** + * Indicates that the operation is read-like: + * it returns information but does not change any state. + */ + INFO(MBeanOperationInfo.INFO), + /** + * Indicates that the operation is write-like: it has an effect but does + * not return any information from the MBean. + */ + ACTION(MBeanOperationInfo.ACTION), + /** + * Indicates that the operation is both read-like and write-like: + * it has an effect, and it also returns information from the MBean. + */ + ACTION_INFO(MBeanOperationInfo.ACTION_INFO), + /** + * Indicates that the impact of the operation is unknown or cannot be + * expressed using one of the other values. + */ + UNKNOWN(MBeanOperationInfo.UNKNOWN); + + private int n; + + Impact(int n) { + this.n = n; + } + + /** + * Will convert one of the following values + *
    + *
  • {@linkplain MBeanOperationInfo#ACTION}
  • + *
  • {@linkplain MBeanOperationInfo#ACTION_INFO}
  • + *
  • {@linkplain MBeanOperationInfo#INFO}
  • + *
  • {@linkplain MBeanOperationInfo#UNKNOWN}
  • + *
+ * into the corresponding {@linkplain Impact} value + * @param val A numeric representation of the impact. See {@linkplain MBeanOperationInfo}. + * @return An {@linkplain Impact} instance corresponding to the numeric impact. + */ + public static Impact fromInt(int val) { + switch (val) { + case MBeanOperationInfo.ACTION: return ACTION; + case MBeanOperationInfo.INFO: return INFO; + case MBeanOperationInfo.ACTION_INFO: return ACTION_INFO; + default: return UNKNOWN; + } + } +} diff --git a/src/java.management/share/classes/javax/management/annotations/ManagedAttribute.java b/src/java.management/share/classes/javax/management/annotations/ManagedAttribute.java new file mode 100644 index 0000000000000..4fc1d90ad375c --- /dev/null +++ b/src/java.management/share/classes/javax/management/annotations/ManagedAttribute.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.management.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import javax.management.Descriptor; + +/** + *

Denotes a field or a method to be exposed as an MBean attribute.

+ * + * + * + */ +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ManagedAttribute { + + /** + * The name the attribute is to be known by. + *

+ * May be omitted. In that case the name is inferred from the field name + * or method name (following JavaBeans rules) + * + * @return The attribute name; may be empty + */ + String name() default ""; + + /** + * A textual description. + *

+ * May be omitted. + * + * @return The attribute description; may be empty + */ + String description() default ""; + + /** + * Units the value of this attribute is measured in. + *

+ * May be omitted. + * + * @return The attribute units; may be empty + */ + String units() default ""; + + /** + * The {@link AttributeAccess} policy. + *

+ * Defaults to {@linkplain AttributeAccess#READWRITE} + * + * @return The attribute access ({@linkplain AttributeAccess}) + */ + AttributeAccess access() default AttributeAccess.READWRITE; + + /** + * The name of the custom getter method. + *

+ * Specifying the custom getter will suppress generating the artificial + * field getter method and will use the specified one instead. + *

+ *

+ * The custom getter method must be a publicly accessible method of + * the attribute holder class. The method signature must conform to the + * JavaBeans getter rules. + *

+ * {$code + * @Attribute(getter = "getNameExt") + * private String name; + * ... + * public String getNameExt() { + * return name + "ext"; + * } + * } + * @return The attribute getter method; may be empty + */ + String getter() default ""; + + /** + * The name of the custom setter method. + *

+ * Specifying the custom setter will suppress generating the artificial + * field setter method and will use the specified one instead. + *

+ *

+ * The custom setter method must be a publicly accessible method of + * the attribute holder class. The method signature must conform to the + * JavaBeans setter rules. + *

+ * {$code + * @Attribute(setter = "setNameExt") + * private String name; + * ... + * public void setNameExt(String val) { + * fireNotification(); + * name = val; + * } + * } + * @return The attribute setter method; may be empty + */ + String setter() default ""; + + /** + * Custom tags attached to the attribute. + *

+ * A tag is basically a /key, value/ pair. Tags will be represented as fields + * in the associated {@linkplain Descriptor} + * + * @return The attached tags + */ + Tag[] tags() default {}; +} diff --git a/src/java.management/share/classes/javax/management/annotations/ManagedOperation.java b/src/java.management/share/classes/javax/management/annotations/ManagedOperation.java new file mode 100644 index 0000000000000..8cb3b02c30197 --- /dev/null +++ b/src/java.management/share/classes/javax/management/annotations/ManagedOperation.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.management.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import javax.management.Descriptor; + +/** + *

Denotes a method to be exposed as an MBean operation.

+ * + * + * + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ManagedOperation { + + /** + * The name the operation is to be known by. + *

+ * May be omitted. In that case the name is inferred from the method name + * + * @return The operation name; may be empty + */ + String name() default ""; + + /** + * A textual description. + *

+ * May be omitted. + * + * @return The description; may be empty + */ + String description() default ""; + + /** + * Units the result of this operation is measured in. + *

+ * May be omitted. + * + * @return The operation result units; may be empty + */ + String units() default ""; + + /** + * The operation {@linkplain Impact}. + *

+ * The default is {@linkplain Impact#UNKNOWN} + * + * @return The operation {@linkplain Impact}; defaults to {@linkplain Impact#UNKNOWN} + */ + Impact impact() default Impact.UNKNOWN; + + /** + * Custom tags attached to the operation. + *

+ * A tag is basically a /key, value/ pair. Tags will be represented as fields + * in the associated {@linkplain Descriptor} + * + * @return The attached tags + */ + Tag[] tags() default {}; +} diff --git a/src/java.management/share/classes/javax/management/annotations/ManagedService.java b/src/java.management/share/classes/javax/management/annotations/ManagedService.java new file mode 100644 index 0000000000000..54a9b7f5be313 --- /dev/null +++ b/src/java.management/share/classes/javax/management/annotations/ManagedService.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.management.annotations; + +import com.sun.jmx.mbeanserver.DefaultMXBeanMappingFactory; +import com.sun.jmx.mbeanserver.MXBeanMappingFactory; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import javax.management.ObjectName; + +/** + * Marks a class as an MXBean implementation. + * + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ManagedService { + /** + * A textual description. + *

+ * May be omitted. + * + * @return The description; may be empty + */ + String description() default ""; + + /** + * The default {@linkplain ObjectName} to be used when registering the MBean. + *

+ * May be omitted. + * + * @return The default {@linkplain ObjectName}; may be empty + */ + String objectName() default ""; + + /** + * Custom tags attached to the operation. + *

+ * A tag is basically a /key, value/ pair. Tags will be represented as fields + * in the associated {@linkplain javax.management.Descriptor} + * + * @return The attached tags + */ + Tag[] tags() default {}; + + /** + * The service interface this MBean is intended to be accessed through. + *

+ * May be omitted. + * + * @return The service interface + */ + Class service() default Object.class; + + /** + * A custom {@linkplain MXBeanMappingFactory} implementation. + *

+ * May be omitted. + * + * @return The associated {@linkplain MXBeanMappingFactory} implementation + */ + // TODO fix the modular boundaries + // Class mapping() default DefaultMXBeanMappingFactory.class; +} diff --git a/src/java.management/share/classes/javax/management/annotations/NotificationInfo.java b/src/java.management/share/classes/javax/management/annotations/NotificationInfo.java new file mode 100644 index 0000000000000..b6912b5786d08 --- /dev/null +++ b/src/java.management/share/classes/javax/management/annotations/NotificationInfo.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.management.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import javax.management.Descriptor; +import javax.management.Notification; + +/** + * This annotation may be used for a field of type {@linkplain NotificationSender}. + * The annotated field can then be used to emit annotations of the declared types. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface NotificationInfo { + /** + * The actual notification type to be emitted. + *

+ * Defaults to {@linkplain Notification} + * + * @return The notification implementation class; default is {@linkplain Notification} + */ + Class implementation() default Notification.class; + + /** + * A textual description. + *

+ * May be omitted. + * + * @return The description; may be empty + */ + String description() default ""; + + /** + * The notification types to be emitted (see {@linkplain Notification#getType()}} + * @return The emitted notification types + */ + String[] types(); + + /** + * Indicates the notification severity. Will be propagated as the "severity" field + * in the associated {@linkplain Descriptor} instance. + * @return The notification severity; default is 6 + */ + int severity() default 6; +} diff --git a/src/java.management/share/classes/javax/management/annotations/NotificationInfos.java b/src/java.management/share/classes/javax/management/annotations/NotificationInfos.java new file mode 100644 index 0000000000000..de26a4616ef68 --- /dev/null +++ b/src/java.management/share/classes/javax/management/annotations/NotificationInfos.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.management.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation may be used for a field of type {@linkplain NotificationSender}. + * It is a holder for multiple {@linkplain NotificationInfo} annotations. + * The annotated field can then be used to emit annotations of the declared types. + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface NotificationInfos { + /** + * The enclosed {@link NotificationInfo} values used to publish the information + * about the emitted notifications. + * + * @return The enclosed {@linkplain NotificationInfo} values + */ + NotificationInfo[] value(); +} diff --git a/src/java.management/share/classes/javax/management/annotations/NotificationSender.java b/src/java.management/share/classes/javax/management/annotations/NotificationSender.java new file mode 100644 index 0000000000000..618eb68bede3c --- /dev/null +++ b/src/java.management/share/classes/javax/management/annotations/NotificationSender.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.management.annotations; + +/** + *

Interface for marking a class as being able to send notifications

+ * + * @since 1.9 + */ +public interface NotificationSender { + /** + *

Sends a standard notification.

+ * + * @param type The notification type. + * @param message The notification message. + * @param userData The user data. It is used for whatever data + * the notification source wishes to communicate to its consumers. + */ + void sendNotification(String type, String message, Object userData); + + /** + * Sends a custom notification. + * + * @param notification The notification to send. + */ + void sendNotification(javax.management.Notification notification); +} diff --git a/src/java.management/share/classes/javax/management/annotations/ParameterInfo.java b/src/java.management/share/classes/javax/management/annotations/ParameterInfo.java new file mode 100644 index 0000000000000..0e9c052943b05 --- /dev/null +++ b/src/java.management/share/classes/javax/management/annotations/ParameterInfo.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.management.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Allows to provide additional metadata to the operation parameters. + */ +@Target(value = ElementType.PARAMETER) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface ParameterInfo { + /** + * The name the parameter is to be known by. + *

+ * May be omitted. In that case the name is inferred from the internal + * parameter name. + * + * @return The parameter name; may be empty + */ + String name() default ""; + + /** + * A textual description. + *

+ * May be omitted. + * + * @return The description; may be empty + */ + String description() default ""; + + /** + * Units the value of this parameter is measured in. + *

+ * May be omitted. + * + * @return The parameter units; may be empty + */ + String units() default ""; + + /** + * Custom tags attached to the operation. + *

+ * A tag is basically a /key, value/ pair. Tags will be represented as fields + * in the associated {@linkplain javax.management.Descriptor} + * + * @return The attached tags + */ + Tag[] tags() default {}; +} diff --git a/src/java.management/share/classes/javax/management/annotations/RegistrationEvent.java b/src/java.management/share/classes/javax/management/annotations/RegistrationEvent.java new file mode 100644 index 0000000000000..8cd614546c981 --- /dev/null +++ b/src/java.management/share/classes/javax/management/annotations/RegistrationEvent.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.management.annotations; + +import java.util.Objects; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +/** + * Registration event type. May be obtained in a method annotated by + * {@linkplain RegistrationHandler} annotation. + */ +final public class RegistrationEvent { + private final RegistrationKind kind; + private final MBeanServer mbs; + private final ObjectName on; + + /** + * Constructor + * @param kind The event kind + * @param mbs The server performing the registration + * @param on The object name + */ + public RegistrationEvent(RegistrationKind kind, MBeanServer mbs, ObjectName on) { + this.kind = kind; + this.mbs = mbs; + this.on = on; + } + + /** + * Registration event kind. + * @return An {@linkplain RegistrationKind} value + */ + public RegistrationKind getKind() { + return kind; + } + + /** + * The {@linkplain MBeanServer} this event was generated by. + * @return The associated {@linkplain MBeanServer} instance + */ + public MBeanServer getMBeanServer() { + return mbs; + } + + /** + * The name of the MBean for which the event was generated. + * @return The MBean's {@linkplain ObjectName} + */ + public ObjectName getObjectName() { + return on; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 17 * hash + Objects.hashCode(this.kind); + hash = 17 * hash + Objects.hashCode(this.mbs); + hash = 17 * hash + Objects.hashCode(this.on); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final RegistrationEvent other = (RegistrationEvent) obj; + if (this.kind != other.kind) { + return false; + } + if (!Objects.equals(this.mbs, other.mbs)) { + return false; + } + if (!Objects.equals(this.on, other.on)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "RegistrationEvent{" + "kind=" + kind + ", mbs=" + mbs + ", on=" + on + '}'; + } +} diff --git a/src/java.management/share/classes/javax/management/annotations/RegistrationHandler.java b/src/java.management/share/classes/javax/management/annotations/RegistrationHandler.java new file mode 100644 index 0000000000000..15b391c6a3314 --- /dev/null +++ b/src/java.management/share/classes/javax/management/annotations/RegistrationHandler.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.management.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks a method as an MBean registration lifecycle hook. + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface RegistrationHandler { +} diff --git a/src/java.management/share/classes/javax/management/annotations/RegistrationKind.java b/src/java.management/share/classes/javax/management/annotations/RegistrationKind.java new file mode 100644 index 0000000000000..721cd01a30cdf --- /dev/null +++ b/src/java.management/share/classes/javax/management/annotations/RegistrationKind.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.management.annotations; + +/** + * The registration kind enumeration + */ +public enum RegistrationKind { + /** + * Managed service is getting registered + *//** + * Managed service is getting registered + */ + REGISTER, + /** + * Managed service is getting de-registered + */ + DEREGISTER, + /** + * An attempt to register a managed service has failed + */ + FAIL +} diff --git a/src/java.management/share/classes/javax/management/annotations/Tag.java b/src/java.management/share/classes/javax/management/annotations/Tag.java new file mode 100644 index 0000000000000..eed3bb84bd01e --- /dev/null +++ b/src/java.management/share/classes/javax/management/annotations/Tag.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package javax.management.annotations; + +/** + * A tag to be used in {@linkplain ManagedAttribute#tags()}, {@linkplain ManagedOperation#tags()}, + * {@linkplain ParameterInfo#tags()} and {@linkplain ManagedService#tags()}. + */ +public @interface Tag { + /** + * Tag name + * @return The tag name + */ + String name(); + + /** + * Tag value + * @return The tag value + */ + String value(); +} diff --git a/src/java.management/share/classes/module-info.java b/src/java.management/share/classes/module-info.java index 8544d858ac57b..d3d78a8b4a520 100644 --- a/src/java.management/share/classes/module-info.java +++ b/src/java.management/share/classes/module-info.java @@ -39,6 +39,7 @@ exports java.lang.management; exports javax.management; + exports javax.management.annotations; exports javax.management.loading; exports javax.management.modelmbean; exports javax.management.monitor; @@ -70,7 +71,11 @@ uses javax.management.remote.JMXConnectorProvider; uses javax.management.remote.JMXConnectorServerProvider; uses sun.management.spi.PlatformMBeanProvider; + uses com.sun.jmx.annotations.ModelBuilder; provides javax.security.auth.spi.LoginModule with com.sun.jmx.remote.security.FileLoginModule; + + provides com.sun.jmx.annotations.ModelBuilder with + com.sun.jmx.annotations.model.MXBeanModelBuilder; } diff --git a/src/java.management/share/classes/sun/management/ThreadImpl.java b/src/java.management/share/classes/sun/management/ThreadImpl.java index 7eb67e99bc8be..c85bc40c8e9ce 100644 --- a/src/java.management/share/classes/sun/management/ThreadImpl.java +++ b/src/java.management/share/classes/sun/management/ThreadImpl.java @@ -30,6 +30,9 @@ import java.lang.management.ThreadMXBean; import java.util.stream.Stream; import javax.management.ObjectName; +import javax.management.annotations.ManagedAttribute; +import javax.management.annotations.ManagedService; +import javax.management.annotations.ManagedOperation; import java.util.Arrays; import java.util.Objects; @@ -40,6 +43,10 @@ * jdk.management in the future. */ +@ManagedService( + service = ThreadMXBean.class, + description = "The management interface for the thread system of " + + "the Java virtual machine.") public class ThreadImpl implements ThreadMXBean { private final VMManagement jvm; @@ -58,31 +65,37 @@ protected ThreadImpl(VMManagement vm) { } @Override + @ManagedAttribute public int getThreadCount() { return jvm.getLiveThreadCount(); } @Override + @ManagedAttribute public int getPeakThreadCount() { return jvm.getPeakThreadCount(); } @Override + @ManagedAttribute public long getTotalStartedThreadCount() { return jvm.getTotalThreadCount(); } @Override + @ManagedAttribute public int getDaemonThreadCount() { return jvm.getDaemonThreadCount(); } @Override + @ManagedAttribute public boolean isThreadContentionMonitoringSupported() { return jvm.isThreadContentionMonitoringSupported(); } @Override + @ManagedAttribute public synchronized boolean isThreadContentionMonitoringEnabled() { if (!isThreadContentionMonitoringSupported()) { throw new UnsupportedOperationException( @@ -92,11 +105,13 @@ public synchronized boolean isThreadContentionMonitoringEnabled() { } @Override + @ManagedAttribute public boolean isThreadCpuTimeSupported() { return jvm.isOtherThreadCpuTimeSupported(); } @Override + @ManagedAttribute public boolean isCurrentThreadCpuTimeSupported() { return jvm.isCurrentThreadCpuTimeSupported(); } @@ -106,6 +121,7 @@ protected boolean isThreadAllocatedMemorySupported() { } @Override + @ManagedAttribute public boolean isThreadCpuTimeEnabled() { if (!isThreadCpuTimeSupported() && !isCurrentThreadCpuTimeSupported()) { @@ -128,6 +144,7 @@ protected boolean isThreadAllocatedMemoryEnabled() { } @Override + @ManagedAttribute public long[] getAllThreadIds() { Util.checkMonitorAccess(); Thread[] threads = getThreads(); @@ -135,17 +152,20 @@ public long[] getAllThreadIds() { } @Override + @ManagedOperation public ThreadInfo getThreadInfo(long id) { return getThreadInfo(id, 0); } @Override + @ManagedOperation public ThreadInfo getThreadInfo(long id, int maxDepth) { long[] ids = new long[] { id }; return getThreadInfo(ids, maxDepth)[0]; } @Override + @ManagedOperation public ThreadInfo[] getThreadInfo(long[] ids) { return getThreadInfo(ids, 0); } @@ -166,6 +186,7 @@ private void verifyThreadIds(long[] ids) { } @Override + @ManagedOperation public ThreadInfo[] getThreadInfo(long[] ids, int maxDepth) { verifyThreadIds(ids); @@ -174,10 +195,6 @@ public ThreadInfo[] getThreadInfo(long[] ids, int maxDepth) { "Invalid maxDepth parameter: " + maxDepth); } - // ids has been verified to be non-null - // an empty array of ids should return an empty array of ThreadInfos - if (ids.length == 0) return new ThreadInfo[0]; - Util.checkMonitorAccess(); ThreadInfo[] infos = new ThreadInfo[ids.length]; // nulls @@ -190,6 +207,7 @@ public ThreadInfo[] getThreadInfo(long[] ids, int maxDepth) { } @Override + @ManagedAttribute public void setThreadContentionMonitoringEnabled(boolean enable) { if (!isThreadContentionMonitoringSupported()) { throw new UnsupportedOperationException( @@ -215,6 +233,7 @@ public void setThreadContentionMonitoringEnabled(boolean enable) { } private boolean verifyCurrentThreadCpuTime() { + // check if Thread CPU time measurement is supported. if (!isCurrentThreadCpuTimeSupported()) { throw new UnsupportedOperationException( "Current thread CPU time measurement is not supported."); @@ -223,6 +242,7 @@ private boolean verifyCurrentThreadCpuTime() { } @Override + @ManagedAttribute(units = "ns") public long getCurrentThreadCpuTime() { if (verifyCurrentThreadCpuTime() && !Thread.currentThread().isVirtual()) { return getThreadTotalCpuTime0(0); @@ -231,6 +251,7 @@ public long getCurrentThreadCpuTime() { } @Override + @ManagedOperation(units = "ns") public long getThreadCpuTime(long id) { long[] ids = new long[1]; ids[0] = id; @@ -286,6 +307,7 @@ protected long[] getThreadCpuTime(long[] ids) { } @Override + @ManagedAttribute(units = "ns") public long getCurrentThreadUserTime() { if (verifyCurrentThreadCpuTime() && !Thread.currentThread().isVirtual()) { return getThreadUserCpuTime0(0); @@ -294,6 +316,7 @@ public long getCurrentThreadUserTime() { } @Override + @ManagedOperation(units = "ns") public long getThreadUserTime(long id) { long[] ids = new long[1]; ids[0] = id; @@ -325,6 +348,7 @@ protected long[] getThreadUserTime(long[] ids) { } @Override + @ManagedAttribute public void setThreadCpuTimeEnabled(boolean enable) { if (!isThreadCpuTimeSupported() && !isCurrentThreadCpuTimeSupported()) { @@ -425,6 +449,7 @@ private long[] threadsToIds(Thread[] threads) { } @Override + @ManagedOperation public long[] findMonitorDeadlockedThreads() { Util.checkMonitorAccess(); Thread[] threads = findMonitorDeadlockedThreads0(); @@ -432,6 +457,7 @@ public long[] findMonitorDeadlockedThreads() { } @Override + @ManagedOperation public long[] findDeadlockedThreads() { if (!isSynchronizerUsageSupported()) { throw new UnsupportedOperationException( @@ -445,17 +471,20 @@ public long[] findDeadlockedThreads() { } @Override + @ManagedOperation public void resetPeakThreadCount() { Util.checkControlAccess(); resetPeakThreadCount0(); } @Override + @ManagedOperation public boolean isObjectMonitorUsageSupported() { return jvm.isObjectMonitorUsageSupported(); } @Override + @ManagedOperation public boolean isSynchronizerUsageSupported() { return jvm.isSynchronizerUsageSupported(); } @@ -476,6 +505,7 @@ private void verifyDumpThreads(boolean lockedMonitors, } @Override + @ManagedOperation public ThreadInfo[] getThreadInfo(long[] ids, boolean lockedMonitors, boolean lockedSynchronizers) { @@ -483,6 +513,7 @@ public ThreadInfo[] getThreadInfo(long[] ids, } @Override + @ManagedOperation public ThreadInfo[] getThreadInfo(long[] ids, boolean lockedMonitors, boolean lockedSynchronizers, @@ -501,12 +532,14 @@ public ThreadInfo[] getThreadInfo(long[] ids, } @Override + @ManagedOperation public ThreadInfo[] dumpAllThreads(boolean lockedMonitors, boolean lockedSynchronizers) { return dumpAllThreads(lockedMonitors, lockedSynchronizers, Integer.MAX_VALUE); } @Override + @ManagedOperation public ThreadInfo[] dumpAllThreads(boolean lockedMonitors, boolean lockedSynchronizers, int maxDepth) { diff --git a/src/jdk.management/share/classes/com/sun/management/ThreadMXBean.java b/src/jdk.management/share/classes/com/sun/management/ThreadMXBean.java index c42c273f22f81..ec20fd1373951 100644 --- a/src/jdk.management/share/classes/com/sun/management/ThreadMXBean.java +++ b/src/jdk.management/share/classes/com/sun/management/ThreadMXBean.java @@ -25,6 +25,7 @@ package com.sun.management; +import javax.management.MXBean; import java.util.Map; /** @@ -38,6 +39,7 @@ * @since 6u25 */ +@MXBean(false) public interface ThreadMXBean extends java.lang.management.ThreadMXBean { /** * Returns the total CPU time for each thread whose ID is diff --git a/test/jdk/javax/management/mxbean2/ComplexType.java b/test/jdk/javax/management/mxbean2/ComplexType.java new file mode 100644 index 0000000000000..fe281f6b0b4c7 --- /dev/null +++ b/test/jdk/javax/management/mxbean2/ComplexType.java @@ -0,0 +1,57 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +import java.util.Objects; + +/** + * + * @author jbachorik + */ +public class ComplexType { + private String value; + + public ComplexType(String value) { + this.value = value; + } + + public ComplexType() { + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return "ComplexType{" + "value=" + value + '}'; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 23 * hash + Objects.hashCode(this.value); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ComplexType other = (ComplexType) obj; + if (!Objects.equals(this.value, other.value)) { + return false; + } + return true; + } + +} diff --git a/test/jdk/javax/management/mxbean2/CustomNotification.java b/test/jdk/javax/management/mxbean2/CustomNotification.java new file mode 100644 index 0000000000000..a9839d2b42aec --- /dev/null +++ b/test/jdk/javax/management/mxbean2/CustomNotification.java @@ -0,0 +1,30 @@ + +import javax.management.Notification; + +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +/** + * + * @author jbachorik + */ +public class CustomNotification extends Notification { + public CustomNotification(String type, Object source, long sequenceNumber) { + super(type, source, sequenceNumber); + } + + public CustomNotification(String type, Object source, long sequenceNumber, String message) { + super(type, source, sequenceNumber, message); + } + + public CustomNotification(String type, Object source, long sequenceNumber, long timeStamp) { + super(type, source, sequenceNumber, timeStamp); + } + + public CustomNotification(String type, Object source, long sequenceNumber, long timeStamp, String message) { + super(type, source, sequenceNumber, timeStamp, message); + } +} diff --git a/test/jdk/javax/management/mxbean2/MXBean2Instance.java b/test/jdk/javax/management/mxbean2/MXBean2Instance.java new file mode 100644 index 0000000000000..fe47fdd9fb542 --- /dev/null +++ b/test/jdk/javax/management/mxbean2/MXBean2Instance.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.Date; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.management.annotations.AttributeAccess; +import javax.management.annotations.Impact; +import javax.management.annotations.ManagedAttribute; +import javax.management.annotations.ManagedOperation; +import javax.management.annotations.ManagedService; +import javax.management.annotations.NotificationInfo; +import javax.management.annotations.NotificationSender; +import javax.management.annotations.ParameterInfo; +import javax.management.annotations.RegistrationEvent; +import javax.management.annotations.RegistrationHandler; +import javax.management.annotations.RegistrationKind; +import javax.management.annotations.Tag; + +@ManagedService( + description = "Testing MXBean instance", + objectName = "com.oracle.testing:type=default", + service = MXBeanService.class, + tags = @Tag(name = "customInfo", value = "xyz") +) +public class MXBean2Instance { + @NotificationInfo(types = "test.mbean.label", description = "Label was set") + NotificationSender ns1; + + @NotificationInfo( + implementation = CustomNotification.class, + description = "Custom notification", + types = "com.oracle.testing.notification", + severity = 1) + NotificationSender ns2; + + @NotificationInfo(types = "test.mbean.threshold", description = "Counter threshold reached") + NotificationSender ns3; + + final AtomicBoolean registered = new AtomicBoolean(); + + @ManagedAttribute(access = AttributeAccess.READ, units = "ticks") + int counter = 1; + + @ManagedAttribute(access = AttributeAccess.READ, name = "Label") + private String label = "hello world"; + + @ManagedAttribute(access = AttributeAccess.READWRITE) + boolean enabled; + + @ManagedAttribute(access = AttributeAccess.READWRITE) + ComplexType cType = new ComplexType("string value"); + + @ManagedAttribute(access = AttributeAccess.READ) + String[] fuzy = new String[]{"sa", "ba", "ca"}; + + @ManagedOperation( + impact = Impact.ACTION, + description = "Increases the associated counter by 1" + ) + public int count() { + if (counter >= 5) { + ns3.sendNotification("test.mbean.threshold", "Threshold reached", counter); + } + return ++counter; + } + + @ManagedOperation(impact = Impact.ACTION_INFO) + public void checkTime(@ParameterInfo(units = "ms") long ts) { + System.err.println(new Date(ts)); + } + + @ManagedOperation + public void printComplex(@ParameterInfo(name = "complex") ComplexType cx) { + System.err.println(cx.toString()); + } + + @ManagedAttribute(description = "Combined service status") + public String getStatus() { + return label + " > " + counter; + } + + @ManagedAttribute + public void setLabel(String l) { + ns2.sendNotification("test.mbean.label", "Label set", l); + label = l; + } + + @RegistrationHandler + public void onRegistration(RegistrationEvent e) { + if (e.getKind() == RegistrationKind.REGISTER) { + registered.set(true); + System.err.println("Registered " + e.getObjectName().getCanonicalName()); + } else if (e.getKind() == RegistrationKind.DEREGISTER) { + System.err.println("Unregistered " + e.getObjectName().getCanonicalName()); + registered.set(false); + } + } +} \ No newline at end of file diff --git a/test/jdk/javax/management/mxbean2/MXBean2Test.java b/test/jdk/javax/management/mxbean2/MXBean2Test.java new file mode 100644 index 0000000000000..7ba80eab83c1b --- /dev/null +++ b/test/jdk/javax/management/mxbean2/MXBean2Test.java @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.jmx.mbeanserver.MXBeanMapping; +import com.sun.jmx.mbeanserver.MXBeanMappingFactory; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import javax.management.Descriptor; +import javax.management.MBeanAttributeInfo; +import javax.management.MBeanInfo; +import javax.management.MBeanNotificationInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanParameterInfo; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.ObjectName; +import javax.management.RuntimeOperationsException; +import javax.management.modelmbean.DescriptorSupport; +import javax.management.modelmbean.ModelMBeanAttributeInfo; +import javax.management.modelmbean.ModelMBeanOperationInfo; +import javax.management.openmbean.ArrayType; +import javax.management.openmbean.OpenDataException; +import javax.management.openmbean.SimpleType; +import org.testng.Assert; +import org.testng.annotations.Test; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.AfterMethod; + +/** + * @test + * @bug 8044507 + * @summary Test for the annotation defined MXBeans + * @modules java.management/com.sun.jmx.mbeanserver + * @build MXBean2Instance MXBeanService ComplexType + * @run testng MXBean2Test + */ +public class MXBean2Test { + private static final String INTERFACE_CLASS_NAME_KEY = "interfaceClassName"; + private static ModelMBeanAttributeInfo COUNTER_INFO, STATUS_INFO, + LABEL_INFO, ENABLED_INFO, + FUZY_INFO, CTYPE_INFO; + private static final Set ALL_ATTRS = new HashSet<>(); + private static final Set ALL_OPS = new HashSet<>(); + private static final Set ALL_NOTIFS = new HashSet<>(); + + private MBeanServer mbs; + private MXBean2Instance instance; + private ObjectName oName, oName1; + + static { + setupAttributes(); + setupOperations(); + setupNotifications(); + } + + private static void setupAttributes() throws RuntimeOperationsException, RuntimeException { + Descriptor d = new DescriptorSupport(); + d.setField("name", "counter"); + d.setField("displayName", "counter"); + d.setField("descriptorType", "attribute"); + d.setField("units", "ticks"); + d.setField("originalType", "int"); + d.setField("openType", SimpleType.INTEGER); + d.setField("role", "attribute"); + COUNTER_INFO = new ModelMBeanAttributeInfo( + "counter", "int", "", true, false, false, d + ); + ALL_ATTRS.add(COUNTER_INFO); + + d = new DescriptorSupport(); + d.setField("name", "Status"); + d.setField("displayName", "Status"); + d.setField("descriptorType", "attribute"); + d.setField("originalType", "java.lang.String"); + d.setField("openType", SimpleType.STRING); + d.setField("role", "attribute"); + STATUS_INFO = new ModelMBeanAttributeInfo( + "Status", "java.lang.String", "Combined service status", true, false, false, d + ); + ALL_ATTRS.add(STATUS_INFO); + + d = new DescriptorSupport(); + d.setField("name", "Label"); + d.setField("displayName", "Label"); + d.setField("descriptorType", "attribute"); + d.setField("originalType", "java.lang.String"); + d.setField("openType", SimpleType.STRING); + d.setField("role", "attribute"); + LABEL_INFO = new ModelMBeanAttributeInfo( + "Label", "java.lang.String", "", true, true, false, d + ); + ALL_ATTRS.add(LABEL_INFO); + + d = new DescriptorSupport(); + d.setField("name", "enabled"); + d.setField("displayName", "enabled"); + d.setField("descriptorType", "attribute"); + d.setField("originalType", "boolean"); + d.setField("openType", SimpleType.BOOLEAN); + d.setField("role", "attribute"); + ENABLED_INFO = new ModelMBeanAttributeInfo( + "enabled", "boolean", "", true, true, true, d + ); + ALL_ATTRS.add(ENABLED_INFO); + + try { + d = new DescriptorSupport(); + d.setField("name", "fuzy"); + d.setField("displayName", "fuzy"); + d.setField("descriptorType", "attribute"); + d.setField("originalType", "[Ljava.lang.String;"); + d.setField("openType", ArrayType.getArrayType(SimpleType.STRING)); + d.setField("role", "attribute"); + FUZY_INFO = new ModelMBeanAttributeInfo( + "fuzy", "[Ljava.lang.String;", "", true, false, false, d + ); + ALL_ATTRS.add(FUZY_INFO); + } catch (RuntimeOperationsException |OpenDataException ex) { + throw new RuntimeException(ex); + } + + try { + MXBeanMapping mm = MXBeanMappingFactory.DEFAULT.mappingForType(ComplexType.class, MXBeanMappingFactory.DEFAULT); + d = new DescriptorSupport(); + d.setField("name", "cType"); + d.setField("displayName", "cType"); + d.setField("descriptorType", "attribute"); + d.setField("originalType", "ComplexType"); + d.setField("openType", mm.getOpenType()); + d.setField("role", "attribute"); + CTYPE_INFO = new ModelMBeanAttributeInfo( + "cType", "javax.management.openmbean.CompositeData", "", true, true, false, d + ); + ALL_ATTRS.add(CTYPE_INFO); + } catch (OpenDataException | RuntimeOperationsException e) { + throw new RuntimeException(e); + } + } + + private static void setupOperations() throws RuntimeOperationsException, RuntimeException { + Descriptor d = new DescriptorSupport(); + d.setField("name", "complex"); + d.setField("displayName", "complex"); + MBeanParameterInfo[] paramInfos = new MBeanParameterInfo[] { + new MBeanParameterInfo("complex", "javax.management.openmbean.CompositeData", "", d) + }; + + d = new DescriptorSupport(); + d.setField("name", "printComplex"); + d.setField("displayName", "printComplex"); + d.setField("descriptorType", "operation"); + d.setField("role", "operation"); + ModelMBeanOperationInfo printComplexInfo = new ModelMBeanOperationInfo( + "printComplex", "", paramInfos, "void", MBeanOperationInfo.UNKNOWN, d + ); + ALL_OPS.add(printComplexInfo); + + d = new DescriptorSupport(); + d.setField("name", "arg0"); + d.setField("displayName", "arg0"); + d.setField("units", "ms"); + paramInfos = new MBeanParameterInfo[] { + new MBeanParameterInfo("arg0", "long", "", d) + }; + + d = new DescriptorSupport(); + d.setField("name", "checkTime"); + d.setField("displayName", "checkTime"); + d.setField("descriptorType", "operation"); + d.setField("role", "operation"); + ModelMBeanOperationInfo checkTimeInfo = new ModelMBeanOperationInfo( + "checkTime", "", paramInfos, "void", MBeanOperationInfo.ACTION_INFO, d + ); + ALL_OPS.add(checkTimeInfo); + + d = new DescriptorSupport(); + d.setField("name", "count"); + d.setField("displayName", "count"); + d.setField("descriptorType", "operation"); + d.setField("role", "operation"); + ModelMBeanOperationInfo countInfo = new ModelMBeanOperationInfo( + "count", "Increases the associated counter by 1", new MBeanParameterInfo[0], "int", MBeanOperationInfo.ACTION, d + ); + ALL_OPS.add(countInfo); + } + + private static void setupNotifications() { + Descriptor d = new DescriptorSupport(); + d.setField("name", "javax.management.Notification"); + d.setField("displayName", "javax.management.Notification"); + d.setField("descriptorType", "notification"); + d.setField("severity", 6); + MBeanNotificationInfo methodNotifInfo = new MBeanNotificationInfo( + new String[]{"test.mbean.label"}, "javax.management.Notification", "Label was set", d + ); + ALL_NOTIFS.add(methodNotifInfo); + + d = new DescriptorSupport(); + d.setField("name", "CustomNotification"); + d.setField("displayName", "CustomNotification"); + d.setField("descriptorType", "notification"); + d.setField("severity", 1); + MBeanNotificationInfo classNotifInfo = new MBeanNotificationInfo( + new String[]{"com.oracle.testing.notification"}, "CustomNotification", "Custom notification", d + ); + ALL_NOTIFS.add(classNotifInfo); + + d = new DescriptorSupport(); + d.setField("name", "javax.management.Notification"); + d.setField("displayName", "javax.management.Notification"); + d.setField("descriptorType", "notification"); + d.setField("severity", 6); + MBeanNotificationInfo thresholdNotifInfo = new MBeanNotificationInfo( + new String[]{"test.mbean.threshold"}, "javax.management.Notification", "Counter threshold reached", d + ); + ALL_NOTIFS.add(thresholdNotifInfo); + } + + @BeforeMethod + public void setup() throws Exception { + mbs = MBeanServerFactory.createMBeanServer(); + instance = new MXBean2Instance(); + oName = ObjectName.getInstance("com.oracle.testing:type=mxbean2"); + oName1 = ObjectName.getInstance("com.oracle.testing:type=default"); + } + + @AfterMethod + public void teardown() { + try { + mbs.unregisterMBean(oName); + } catch (Exception e) {} + try { + mbs.unregisterMBean(oName1); + } catch (Exception e) {} + } + + @Test + public void testRegistration() throws Exception { + mbs.registerMBean(instance, oName); + } + + @Test + public void testCreation() throws Exception { + mbs.createMBean(MXBean2Instance.class.getName(), oName); + } + + @Test + public void testCreationDefaultOname() throws Exception { + mbs.createMBean(MXBean2Instance.class.getName(), null); + } + + @Test + public void testMBeanInfo() throws Exception { + mbs.registerMBean(instance, oName); + MBeanInfo mbInfo = mbs.getMBeanInfo(oName); + + validateMBeanInfo(mbInfo); + } + + private static void validateMBeanInfo(MBeanInfo info) { + // check class name + Assert.assertEquals("MXBean2Instance", info.getClassName()); + Descriptor d = info.getDescriptor(); + + Assert.assertEquals("MXBeanService", d.getFieldValue(INTERFACE_CLASS_NAME_KEY)); + Assert.assertEquals("xyz", d.getFieldValue("customInfo")); + + MBeanAttributeInfo[] attrInfos = info.getAttributes(); + Set attrInfoSet = new HashSet<>(Arrays.asList(attrInfos)); + + Assert.assertTrue(attrInfoSet.containsAll(ALL_ATTRS)); + Assert.assertTrue(ALL_ATTRS.containsAll(attrInfoSet)); + + MBeanOperationInfo[] opInfos = info.getOperations(); + Set opInfoSet = new HashSet<>(Arrays.asList(opInfos)); + + Assert.assertTrue(opInfoSet.containsAll(ALL_OPS)); + Assert.assertTrue(ALL_OPS.containsAll(opInfoSet)); + + MBeanNotificationInfo[] notifInfos = info.getNotifications(); + Set notifInfoSet = new HashSet<>(Arrays.asList(notifInfos)); + + Assert.assertTrue(notifInfoSet.containsAll(ALL_NOTIFS)); + Assert.assertTrue(ALL_NOTIFS.containsAll(notifInfoSet)); + + System.err.println(notifInfoSet); + } +} diff --git a/test/jdk/javax/management/mxbean2/MXBeanService.java b/test/jdk/javax/management/mxbean2/MXBeanService.java new file mode 100644 index 0000000000000..882b2d4828b9e --- /dev/null +++ b/test/jdk/javax/management/mxbean2/MXBeanService.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +public interface MXBeanService { + +}