| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.ignite.internal.mxbean; |
| |
| import java.lang.reflect.Method; |
| import java.util.HashMap; |
| import java.util.Map; |
| import javax.management.MBeanAttributeInfo; |
| import javax.management.MBeanInfo; |
| import javax.management.MBeanOperationInfo; |
| import javax.management.MBeanParameterInfo; |
| import javax.management.NotCompliantMBeanException; |
| import javax.management.StandardMBean; |
| import org.apache.ignite.internal.util.typedef.internal.U; |
| import org.apache.ignite.mxbean.MXBeanDescription; |
| import org.apache.ignite.mxbean.MXBeanParametersDescriptions; |
| import org.apache.ignite.mxbean.MXBeanParametersNames; |
| |
| /** |
| * Extension of standard Java MBean. Overrides some hooks to return |
| * annotation based descriptions. |
| */ |
| public class IgniteStandardMXBean extends StandardMBean { |
| /** |
| * Objects maps from primitive classes to primitive object classes. |
| */ |
| private static final Map<String, Class<?>> primCls = new HashMap<>(); |
| |
| /** |
| * Static constructor. |
| */ |
| static{ |
| primCls.put(Boolean.TYPE.toString().toLowerCase(), Boolean.TYPE); |
| primCls.put(Character.TYPE.toString().toLowerCase(), Character.TYPE); |
| primCls.put(Byte.TYPE.toString().toLowerCase(), Byte.TYPE); |
| primCls.put(Short.TYPE.toString().toLowerCase(), Short.TYPE); |
| primCls.put(Integer.TYPE.toString().toLowerCase(), Integer.TYPE); |
| primCls.put(Long.TYPE.toString().toLowerCase(), Long.TYPE); |
| primCls.put(Float.TYPE.toString().toLowerCase(), Float.TYPE); |
| primCls.put(Double.TYPE.toString().toLowerCase(), Double.TYPE); |
| } |
| |
| /** |
| * Make a DynamicMBean out of the object implementation, using the specified |
| * mbeanInterface class. |
| * |
| * @param implementation The implementation of this MBean. |
| * @param mbeanInterface The Management Interface exported by this |
| * MBean's implementation. If {@code null}, then this |
| * object will use standard JMX design pattern to determine |
| * the management interface associated with the given |
| * implementation. |
| * If {@code null} value passed then information will be built by |
| * {@link StandardMBean} |
| * |
| * @exception NotCompliantMBeanException if the {@code mbeanInterface} |
| * does not follow JMX design patterns for Management Interfaces, or |
| * if the given {@code implementation} does not implement the |
| * specified interface. |
| */ |
| public <T> IgniteStandardMXBean(T implementation, Class<T> mbeanInterface) |
| throws NotCompliantMBeanException { |
| super(implementation, mbeanInterface); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override protected String getDescription(MBeanAttributeInfo info) { |
| String str = super.getDescription(info); |
| |
| String methodName = (info.isIs() ? "is" : "get") + info.getName(); |
| |
| try { |
| // Recursively get method. |
| Method mtd = findMethod(getMBeanInterface(), methodName, new Class[]{}); |
| |
| if (mtd != null) { |
| MXBeanDescription desc = mtd.getAnnotation(MXBeanDescription.class); |
| |
| if (desc != null) { |
| str = desc.value(); |
| |
| assert str != null : "Failed to find method: " + mtd; |
| assert str.trim().length() > 0 : "Method description cannot be empty: " + mtd; |
| |
| // Enforce proper English. |
| assert Character.isUpperCase(str.charAt(0)) == true : |
| "Description must start with upper case: " + str; |
| |
| assert str.charAt(str.length() - 1) == '.' : "Description must end with period: " + str; |
| } |
| } |
| } |
| catch (SecurityException e) { |
| // No-op. Default value will be returned. |
| } |
| |
| return str; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override protected String getDescription(MBeanInfo info) { |
| String str = super.getDescription(info); |
| |
| // Return either default one or given by annotation. |
| MXBeanDescription desc = U.getAnnotation(getMBeanInterface(), MXBeanDescription.class); |
| |
| if (desc != null) { |
| str = desc.value(); |
| |
| assert str != null; |
| assert str.trim().length() > 0; |
| |
| // Enforce proper English. |
| assert Character.isUpperCase(str.charAt(0)) == true : str; |
| assert str.charAt(str.length() - 1) == '.' : str; |
| } |
| |
| return str; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override protected String getDescription(MBeanOperationInfo info) { |
| String str = super.getDescription(info); |
| |
| try { |
| Method m = getMethod(info); |
| |
| MXBeanDescription desc = m.getAnnotation(MXBeanDescription.class); |
| |
| if (desc != null) { |
| str = desc.value(); |
| |
| assert str != null; |
| assert str.trim().length() > 0; |
| |
| // Enforce proper English. |
| assert Character.isUpperCase(str.charAt(0)) == true : str; |
| assert str.charAt(str.length() - 1) == '.' : str; |
| } |
| } |
| catch (SecurityException | ClassNotFoundException e) { |
| // No-op. Default value will be returned. |
| } |
| |
| return str; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override protected String getDescription(MBeanOperationInfo op, MBeanParameterInfo param, int seq) { |
| String str = super.getDescription(op, param, seq); |
| |
| try { |
| Method m = getMethod(op); |
| |
| MXBeanParametersDescriptions decsAnn = m.getAnnotation(MXBeanParametersDescriptions.class); |
| |
| if (decsAnn != null) { |
| assert decsAnn.value() != null; |
| assert seq < decsAnn.value().length; |
| |
| str = decsAnn.value()[seq]; |
| |
| assert str != null; |
| assert str.trim().length() > 0; |
| |
| // Enforce proper English. |
| assert Character.isUpperCase(str.charAt(0)) == true : str; |
| assert str.charAt(str.length() - 1) == '.' : str; |
| } |
| } |
| catch (SecurityException | ClassNotFoundException e) { |
| // No-op. Default value will be returned. |
| } |
| |
| return str; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override protected String getParameterName(MBeanOperationInfo op, MBeanParameterInfo param, int seq) { |
| String str = super.getParameterName(op, param, seq); |
| |
| try { |
| Method m = getMethod(op); |
| |
| MXBeanParametersNames namesAnn = m.getAnnotation(MXBeanParametersNames.class); |
| |
| if (namesAnn != null) { |
| assert namesAnn.value() != null; |
| assert seq < namesAnn.value().length; |
| |
| str = namesAnn.value()[seq]; |
| |
| assert str != null; |
| assert str.trim().length() > 0; |
| } |
| } |
| catch (SecurityException | ClassNotFoundException e) { |
| // No-op. Default value will be returned. |
| } |
| |
| return str; |
| } |
| |
| /** |
| * Gets method by operation info. |
| * |
| * @param op MBean operation info. |
| * @return Method. |
| * @throws ClassNotFoundException Thrown if parameter type is unknown. |
| * @throws SecurityException Thrown if method access is not allowed. |
| */ |
| private Method getMethod(MBeanOperationInfo op) throws ClassNotFoundException, SecurityException { |
| String methodName = op.getName(); |
| |
| MBeanParameterInfo[] signature = op.getSignature(); |
| |
| Class<?>[] params = new Class<?>[signature.length]; |
| |
| for (int i = 0; i < signature.length; i++) { |
| // Parameter type is either a primitive type or class. Try both. |
| Class<?> type = primCls.get(signature[i].getType().toLowerCase()); |
| |
| if (type == null) |
| type = Class.forName(signature[i].getType()); |
| |
| params[i] = type; |
| } |
| |
| return findMethod(getMBeanInterface(), methodName, params); |
| } |
| |
| /** |
| * Finds method for the given interface. |
| * |
| * @param itf MBean interface. |
| * @param methodName Method name. |
| * @param params Method parameters. |
| * @return Method. |
| */ |
| @SuppressWarnings("unchecked") |
| private Method findMethod(Class itf, String methodName, Class[] params) { |
| assert itf.isInterface() == true; |
| |
| Method res = null; |
| |
| // Try to get method from given interface. |
| try { |
| res = itf.getDeclaredMethod(methodName, params); |
| |
| if (res != null) |
| return res; |
| } |
| catch (NoSuchMethodException e) { |
| // No-op. Default value will be returned. |
| } |
| |
| // Process recursively super interfaces. |
| Class[] superItfs = itf.getInterfaces(); |
| |
| for (Class superItf: superItfs) { |
| res = findMethod(superItf, methodName, params); |
| |
| if (res != null) |
| return res; |
| } |
| |
| return res; |
| } |
| } |