/* * 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.geode.management.internal; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Set; import javax.management.ObjectName; import org.apache.geode.distributed.internal.InternalDistributedSystem; import org.apache.geode.i18n.LogWriterI18n; /** * This proxy handler handles all the method call invoked on an MXBean It follows same route as * MBeanProxyInvocationHandler Only difference is after obtaining the result it transforms the open * type to the actual java type * * */ public class MXBeanProxyInvocationHandler { private ObjectName objectName; private MBeanProxyInvocationHandler proxyHandler; private final Map<Method, MethodHandler> methodHandlerMap = OpenTypeUtil.newMap(); private LogWriterI18n logger; public MXBeanProxyInvocationHandler(ObjectName objectName, Class<?> mxbeanInterface, MBeanProxyInvocationHandler proxyHandler) throws Exception { if (mxbeanInterface == null) throw new IllegalArgumentException("Null parameter"); this.objectName = objectName; this.proxyHandler = proxyHandler; this.logger = InternalDistributedSystem.getLoggerI18n(); this.initHandlers(mxbeanInterface); } // Introspect the mbeanInterface and initialize this object's maps. // private void initHandlers(Class<?> mbeanInterface) throws Exception { final Method[] methodArray = mbeanInterface.getMethods(); final List<Method> methods = eliminateCovariantMethods(methodArray); for (Method m : methods) { String name = m.getName(); String attrName = ""; if (name.startsWith("get")) { attrName = name.substring(3); } else if (name.startsWith("is") && m.getReturnType() == boolean.class) { attrName = name.substring(2); } if (attrName.length() != 0 && m.getParameterTypes().length == 0 && m.getReturnType() != void.class) { // For Getters methodHandlerMap.put(m, new GetterHandler(attrName, OpenMethod.from(m))); } else if (name.startsWith("set") && name.length() > 3 && m.getParameterTypes().length == 1 && m.getReturnType() == void.class) { // For Setteres methodHandlerMap.put(m, new SetterHandler(attrName, OpenMethod.from(m))); } else { methodHandlerMap.put(m, new OpHandler(attrName, OpenMethod.from(m))); } } } /** * Eliminate methods that are overridden with a covariant return type. Reflection will return both * the original and the overriding method but we need only the overriding one is of interest * * @param methodArray * @return the method after eliminating covariant menthods */ static List<Method> eliminateCovariantMethods(Method[] methodArray) { final int len = methodArray.length; final Method[] sorted = methodArray.clone(); Arrays.sort(sorted, MethodOrder.instance); final Set<Method> overridden = OpenTypeUtil.newSet(); for (int i = 1; i < len; i++) { final Method m0 = sorted[i - 1]; final Method m1 = sorted[i]; if (!m0.getName().equals(m1.getName())) continue; if (Arrays.equals(m0.getParameterTypes(), m1.getParameterTypes())) { overridden.add(m0); } } final List<Method> methods = OpenTypeUtil.newList(Arrays.asList(methodArray)); methods.removeAll(overridden); return methods; } /** * A comparator that defines a total order so that methods have the same name and identical * signatures appear next to each others. The methods are sorted in such a way that methods which * override each other will sit next to each other, with the overridden method first - e.g. Object * getFoo() is placed before Integer getFoo(). This makes it possible to determine whether a * method overrides another one simply by looking at the method(s) that precedes it in the list. * (see eliminateCovariantMethods). **/ private static class MethodOrder implements Comparator<Method> { public int compare(Method a, Method b) { final int cmp = a.getName().compareTo(b.getName()); if (cmp != 0) return cmp; final Class<?>[] aparams = a.getParameterTypes(); final Class<?>[] bparams = b.getParameterTypes(); if (aparams.length != bparams.length) return aparams.length - bparams.length; if (!Arrays.equals(aparams, bparams)) { return Arrays.toString(aparams).compareTo(Arrays.toString(bparams)); } final Class<?> aret = a.getReturnType(); final Class<?> bret = b.getReturnType(); if (aret == bret) return 0; if (aret.isAssignableFrom(bret)) return -1; return +1; } public final static MethodOrder instance = new MethodOrder(); } /** * Hanlder for MXBean Proxy * * */ private abstract class MethodHandler { MethodHandler(String name, OpenMethod cm) { this.name = name; this.convertingMethod = cm; } String getName() { return name; } OpenMethod getConvertingMethod() { return convertingMethod; } abstract Object invoke(Object proxy, Method method, Object[] args) throws Throwable; private final String name; private final OpenMethod convertingMethod; } private class GetterHandler extends MethodHandler { GetterHandler(String attributeName, OpenMethod cm) { super(attributeName, cm); } @Override Object invoke(Object proxy, Method method, Object[] args) throws Throwable { assert (args == null || args.length == 0); final String methodName = method.getName(); String attrName = ""; if (methodName.startsWith("get")) { attrName = methodName.substring(3); } else if (methodName.startsWith("is") && method.getReturnType() == boolean.class) { attrName = methodName.substring(2); } return proxyHandler.delegateToObjectState(attrName); } } private class SetterHandler extends MethodHandler { SetterHandler(String attributeName, OpenMethod cm) { super(attributeName, cm); } @Override Object invoke(Object proxy, Method method, Object[] args) throws Throwable { final String methodName = method.getName(); final Class[] paramTypes = method.getParameterTypes(); final String[] signature = new String[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) signature[i] = paramTypes[i].getName(); return proxyHandler.delegateToFucntionService(objectName, methodName, args, signature); } } private class OpHandler extends MethodHandler { OpHandler(String operationName, OpenMethod cm) { super(operationName, cm); } Object invoke(Object proxy, Method method, Object[] args) throws Throwable { final String methodName = method.getName(); final Class[] paramTypes = method.getParameterTypes(); final String[] signature = new String[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) signature[i] = paramTypes[i].getName(); return proxyHandler.delegateToFucntionService(objectName, methodName, args, signature); } } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MethodHandler handler = methodHandlerMap.get(method); OpenMethod cm = handler.getConvertingMethod(); Object[] openArgs = cm.toOpenParameters(args); Object result = handler.invoke(proxy, method, openArgs); return cm.fromOpenReturnValue(result); } }