/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library 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 Lesser General Public License for more
* details.
*/
package com.liferay.portal.fabric.status;
import com.liferay.portal.kernel.concurrent.NoticeableFuture;
import com.liferay.portal.kernel.process.ProcessCallable;
import com.liferay.portal.kernel.process.ProcessException;
import com.liferay.portal.kernel.util.ProxyUtil;
import java.io.Serializable;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;
import java.lang.management.PlatformManagedObject;
import java.lang.management.ThreadInfo;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import javax.management.Attribute;
import javax.management.AttributeNotFoundException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeData;
/**
* @author Shuyang Zhou
*/
public class JMXProxyUtil {
public static <T> T newProxy(
ObjectName objectName, Class<T> interfaceClass,
ProcessCallableExecutor processCallableExecutor) {
ClassLoader classLoader = interfaceClass.getClassLoader();
if (classLoader == null) {
classLoader = ClassLoader.getSystemClassLoader();
}
return (T)ProxyUtil.newProxyInstance(
classLoader, new Class<?>[] {interfaceClass},
new JMXProxyInvocationHandler(objectName, processCallableExecutor));
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Optional {
}
public interface ProcessCallableExecutor {
public <V extends Serializable> NoticeableFuture<V> execute(
ProcessCallable<V> processCallable);
}
protected static Object decode(
Class<?> decodedClass, Serializable serializable) {
if (serializable instanceof CompositeData) {
return decodeCompositeData(
decodedClass, (CompositeData)serializable);
}
if ((serializable instanceof CompositeData[]) &&
decodedClass.isArray()) {
return decodeCompositeDataArray(
decodedClass, (CompositeData[])serializable);
}
if (decodedClass == List.class) {
Class<?> clazz = serializable.getClass();
if (clazz.isArray()) {
return decodeArrayToList(serializable);
}
}
return serializable;
}
protected static Object decodeArrayToList(Object array) {
List<Object> list = new ArrayList<>();
for (int i = 0; i < Array.getLength(array); i++) {
list.add(Array.get(array, i));
}
return list;
}
protected static Object decodeCompositeData(
Class<?> decodedClass, CompositeData compositeData) {
if (decodedClass == MemoryUsage.class) {
return MemoryUsage.from(compositeData);
}
if (decodedClass == ThreadInfo.class) {
return ThreadInfo.from(compositeData);
}
return compositeData;
}
protected static Object decodeCompositeDataArray(
Class<?> decodedClass, CompositeData[] compositeDatas) {
Object array = Array.newInstance(
decodedClass.getComponentType(), compositeDatas.length);
for (int i = 0; i < compositeDatas.length; i++) {
Array.set(
array, i,
decodeCompositeData(
decodedClass.getComponentType(), compositeDatas[i]));
}
return array;
}
protected static boolean equals(ObjectName objectName, Object target) {
if (target instanceof PlatformManagedObject) {
PlatformManagedObject platformManagedObject =
(PlatformManagedObject)target;
return objectName.equals(platformManagedObject.getObjectName());
}
if (ProxyUtil.isProxyClass(target.getClass())) {
InvocationHandler invocationHandler =
ProxyUtil.getInvocationHandler(target);
if (invocationHandler instanceof JMXProxyInvocationHandler) {
JMXProxyInvocationHandler jmxProxyInvocationHandler =
(JMXProxyInvocationHandler)invocationHandler;
return objectName.equals(jmxProxyInvocationHandler._objectName);
}
}
return false;
}
protected static boolean isGetGetter(
String methodName, Class<?>... parameterTypes) {
if (methodName.startsWith("get") && (parameterTypes.length == 0)) {
return true;
}
return false;
}
protected static boolean isIsGetter(
String methodName, Class<?>... parameterTypes) {
if (methodName.startsWith("is") && (parameterTypes.length == 0)) {
return true;
}
return false;
}
protected static boolean isObjectEquals(Method method) {
if (method.getDeclaringClass() == Object.class) {
String methodName = method.getName();
if (methodName.equals("equals")) {
return true;
}
}
return false;
}
protected static boolean isObjectHashCode(Method method) {
if (method.getDeclaringClass() == Object.class) {
String methodName = method.getName();
if (methodName.equals("hashCode")) {
return true;
}
}
return false;
}
protected static boolean isObjectToString(Method method) {
if (method.getDeclaringClass() == Object.class) {
String methodName = method.getName();
if (methodName.equals("toString")) {
return true;
}
}
return false;
}
protected static boolean isOptional(Method method) {
if (method.getAnnotation(Optional.class) == null) {
return false;
}
return true;
}
protected static boolean isSetter(
String methodName, Class<?>... parameterTypes) {
if (methodName.startsWith("set") && (parameterTypes.length == 1)) {
return true;
}
return false;
}
protected static class GetAttributeProcessCallable
implements ProcessCallable<Serializable> {
public GetAttributeProcessCallable(
ObjectName objectName, String attributeName, boolean optional) {
_objectName = objectName;
_attributeName = attributeName;
_optional = optional;
}
@Override
public Serializable call() throws ProcessException {
MBeanServer mBeanServer =
ManagementFactory.getPlatformMBeanServer();
try {
return (Serializable)mBeanServer.getAttribute(
_objectName, _attributeName);
}
catch (AttributeNotFoundException anfe) {
if (_optional) {
return null;
}
throw new ProcessException(anfe);
}
catch (Exception e) {
throw new ProcessException(e);
}
}
private static final long serialVersionUID = 1L;
private final String _attributeName;
private final ObjectName _objectName;
private final boolean _optional;
}
protected static class JMXProxyInvocationHandler
implements InvocationHandler {
public JMXProxyInvocationHandler(
ObjectName objectName,
ProcessCallableExecutor processCallableExecutor) {
_objectName = objectName;
_processCallableExecutor = processCallableExecutor;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (isObjectEquals(method)) {
return JMXProxyUtil.equals(_objectName, args[0]);
}
if (isObjectHashCode(method)) {
return _objectName.hashCode();
}
if (isObjectToString(method)) {
return _objectName.toString();
}
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
ProcessCallable<Serializable> processCallable = null;
if (isGetGetter(methodName, parameterTypes)) {
processCallable = new GetAttributeProcessCallable(
_objectName, methodName.substring(3), isOptional(method));
}
else if (isIsGetter(methodName, parameterTypes)) {
processCallable = new GetAttributeProcessCallable(
_objectName, methodName.substring(2), isOptional(method));
}
else if (isSetter(methodName, parameterTypes)) {
processCallable = new SetAttributeProcessCallable(
_objectName, methodName.substring(3), (Serializable)args[0],
isOptional(method));
}
else {
String[] parameterTypeNames = new String[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
parameterTypeNames[i] = parameterTypes[i].getName();
}
processCallable = new OperationProcessCallable(
_objectName, methodName, args, parameterTypeNames);
}
Future<? extends Serializable> future =
_processCallableExecutor.execute(processCallable);
return decode(method.getReturnType(), future.get());
}
private final ObjectName _objectName;
private final ProcessCallableExecutor _processCallableExecutor;
}
protected static class OperationProcessCallable
implements ProcessCallable<Serializable> {
public OperationProcessCallable(
ObjectName objectName, String operationName, Object[] arguments,
String[] parameterTypeNames) {
_objectName = objectName;
_operationName = operationName;
_arguments = arguments;
_parameterTypeNames = parameterTypeNames;
}
@Override
public Serializable call() throws ProcessException {
MBeanServer mBeanServer =
ManagementFactory.getPlatformMBeanServer();
try {
return (Serializable)mBeanServer.invoke(
_objectName, _operationName, _arguments,
_parameterTypeNames);
}
catch (Exception e) {
throw new ProcessException(e);
}
}
private static final long serialVersionUID = 1L;
private final Object[] _arguments;
private final ObjectName _objectName;
private final String _operationName;
private final String[] _parameterTypeNames;
}
protected static class SetAttributeProcessCallable
implements ProcessCallable<Serializable> {
public SetAttributeProcessCallable(
ObjectName objectName, String attributeName,
Serializable attributeValue, boolean optional) {
_objectName = objectName;
_attributeName = attributeName;
_attributeValue = attributeValue;
_optional = optional;
}
@Override
public Serializable call() throws ProcessException {
MBeanServer mBeanServer =
ManagementFactory.getPlatformMBeanServer();
try {
mBeanServer.setAttribute(
_objectName,
new Attribute(_attributeName, _attributeValue));
}
catch (AttributeNotFoundException anfe) {
if (!_optional) {
throw new ProcessException(anfe);
}
}
catch (Exception e) {
throw new ProcessException(e);
}
return null;
}
private static final long serialVersionUID = 1L;
private final String _attributeName;
private final Serializable _attributeValue;
private final ObjectName _objectName;
private final boolean _optional;
}
}