001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.fabric.status;
016    
017    import com.liferay.portal.kernel.concurrent.NoticeableFuture;
018    import com.liferay.portal.kernel.process.ProcessCallable;
019    import com.liferay.portal.kernel.process.ProcessException;
020    import com.liferay.portal.kernel.util.ProxyUtil;
021    
022    import java.io.Serializable;
023    
024    import java.lang.annotation.ElementType;
025    import java.lang.annotation.Retention;
026    import java.lang.annotation.RetentionPolicy;
027    import java.lang.annotation.Target;
028    import java.lang.management.ManagementFactory;
029    import java.lang.management.MemoryUsage;
030    import java.lang.management.PlatformManagedObject;
031    import java.lang.management.ThreadInfo;
032    import java.lang.reflect.Array;
033    import java.lang.reflect.InvocationHandler;
034    import java.lang.reflect.Method;
035    
036    import java.util.ArrayList;
037    import java.util.List;
038    import java.util.concurrent.Future;
039    
040    import javax.management.Attribute;
041    import javax.management.AttributeNotFoundException;
042    import javax.management.MBeanServer;
043    import javax.management.ObjectName;
044    import javax.management.openmbean.CompositeData;
045    
046    /**
047     * @author Shuyang Zhou
048     */
049    public class JMXProxyUtil {
050    
051            public static <T> T newProxy(
052                    ObjectName objectName, Class<T> interfaceClass,
053                    ProcessCallableExecutor processCallableExecutor) {
054    
055                    ClassLoader classLoader = interfaceClass.getClassLoader();
056    
057                    if (classLoader == null) {
058                            classLoader = ClassLoader.getSystemClassLoader();
059                    }
060    
061                    return (T)ProxyUtil.newProxyInstance(
062                            classLoader, new Class<?>[] {interfaceClass},
063                            new JMXProxyInvocationHandler(objectName, processCallableExecutor));
064            }
065    
066            @Retention(RetentionPolicy.RUNTIME)
067            @Target(ElementType.METHOD)
068            public @interface Optional {
069            }
070    
071            public interface ProcessCallableExecutor {
072    
073                    public <V extends Serializable> NoticeableFuture<V> execute(
074                            ProcessCallable<V> processCallable);
075    
076            }
077    
078            protected static Object decode(
079                    Class<?> decodedClass, Serializable serializable) {
080    
081                    if (serializable instanceof CompositeData) {
082                            return decodeCompositeData(
083                                    decodedClass, (CompositeData)serializable);
084                    }
085    
086                    if ((serializable instanceof CompositeData[]) &&
087                            decodedClass.isArray()) {
088    
089                            return decodeCompositeDataArray(
090                                    decodedClass, (CompositeData[])serializable);
091                    }
092    
093                    if (decodedClass == List.class) {
094                            Class<?> clazz = serializable.getClass();
095    
096                            if (clazz.isArray()) {
097                                    return decodeArrayToList(serializable);
098                            }
099                    }
100    
101                    return serializable;
102            }
103    
104            protected static Object decodeArrayToList(Object array) {
105                    List<Object> list = new ArrayList<>();
106    
107                    for (int i = 0; i < Array.getLength(array); i++) {
108                            list.add(Array.get(array, i));
109                    }
110    
111                    return list;
112            }
113    
114            protected static Object decodeCompositeData(
115                    Class<?> decodedClass, CompositeData compositeData) {
116    
117                    if (decodedClass == MemoryUsage.class) {
118                            return MemoryUsage.from(compositeData);
119                    }
120    
121                    if (decodedClass == ThreadInfo.class) {
122                            return ThreadInfo.from(compositeData);
123                    }
124    
125                    return compositeData;
126            }
127    
128            protected static Object decodeCompositeDataArray(
129                    Class<?> decodedClass, CompositeData[] compositeDatas) {
130    
131                    Object array = Array.newInstance(
132                            decodedClass.getComponentType(), compositeDatas.length);
133    
134                    for (int i = 0; i < compositeDatas.length; i++) {
135                            Array.set(
136                                    array, i,
137                                    decodeCompositeData(
138                                            decodedClass.getComponentType(), compositeDatas[i]));
139                    }
140    
141                    return array;
142            }
143    
144            protected static boolean equals(ObjectName objectName, Object target) {
145                    if (target instanceof PlatformManagedObject) {
146                            PlatformManagedObject platformManagedObject =
147                                    (PlatformManagedObject)target;
148    
149                            return objectName.equals(platformManagedObject.getObjectName());
150                    }
151    
152                    if (ProxyUtil.isProxyClass(target.getClass())) {
153                            InvocationHandler invocationHandler =
154                                    ProxyUtil.getInvocationHandler(target);
155    
156                            if (invocationHandler instanceof JMXProxyInvocationHandler) {
157                                    JMXProxyInvocationHandler jmxProxyInvocationHandler =
158                                            (JMXProxyInvocationHandler)invocationHandler;
159    
160                                    return objectName.equals(jmxProxyInvocationHandler._objectName);
161                            }
162                    }
163    
164                    return false;
165            }
166    
167            protected static boolean isGetGetter(
168                    String methodName, Class<?>... parameterTypes) {
169    
170                    if (methodName.startsWith("get") && (parameterTypes.length == 0)) {
171                            return true;
172                    }
173    
174                    return false;
175            }
176    
177            protected static boolean isIsGetter(
178                    String methodName, Class<?>... parameterTypes) {
179    
180                    if (methodName.startsWith("is") && (parameterTypes.length == 0)) {
181                            return true;
182                    }
183    
184                    return false;
185            }
186    
187            protected static boolean isObjectEquals(Method method) {
188                    if (method.getDeclaringClass() == Object.class) {
189                            String methodName = method.getName();
190    
191                            if (methodName.equals("equals")) {
192                                    return true;
193                            }
194                    }
195    
196                    return false;
197            }
198    
199            protected static boolean isObjectHashCode(Method method) {
200                    if (method.getDeclaringClass() == Object.class) {
201                            String methodName = method.getName();
202    
203                            if (methodName.equals("hashCode")) {
204                                    return true;
205                            }
206                    }
207    
208                    return false;
209            }
210    
211            protected static boolean isObjectToString(Method method) {
212                    if (method.getDeclaringClass() == Object.class) {
213                            String methodName = method.getName();
214    
215                            if (methodName.equals("toString")) {
216                                    return true;
217                            }
218                    }
219    
220                    return false;
221            }
222    
223            protected static boolean isOptional(Method method) {
224                    if (method.getAnnotation(Optional.class) == null) {
225                            return false;
226                    }
227    
228                    return true;
229            }
230    
231            protected static boolean isSetter(
232                    String methodName, Class<?>... parameterTypes) {
233    
234                    if (methodName.startsWith("set") && (parameterTypes.length == 1)) {
235                            return true;
236                    }
237    
238                    return false;
239            }
240    
241            protected static class GetAttributeProcessCallable
242                    implements ProcessCallable<Serializable> {
243    
244                    public GetAttributeProcessCallable(
245                            ObjectName objectName, String attributeName, boolean optional) {
246    
247                            _objectName = objectName;
248                            _attributeName = attributeName;
249                            _optional = optional;
250                    }
251    
252                    @Override
253                    public Serializable call() throws ProcessException {
254                            MBeanServer mBeanServer =
255                                    ManagementFactory.getPlatformMBeanServer();
256    
257                            try {
258                                    return (Serializable)mBeanServer.getAttribute(
259                                            _objectName, _attributeName);
260                            }
261                            catch (AttributeNotFoundException anfe) {
262                                    if (_optional) {
263                                            return null;
264                                    }
265    
266                                    throw new ProcessException(anfe);
267                            }
268                            catch (Exception e) {
269                                    throw new ProcessException(e);
270                            }
271                    }
272    
273                    private static final long serialVersionUID = 1L;
274    
275                    private final String _attributeName;
276                    private final ObjectName _objectName;
277                    private final boolean _optional;
278    
279            }
280    
281            protected static class JMXProxyInvocationHandler
282                    implements InvocationHandler {
283    
284                    public JMXProxyInvocationHandler(
285                            ObjectName objectName,
286                            ProcessCallableExecutor processCallableExecutor) {
287    
288                            _objectName = objectName;
289                            _processCallableExecutor = processCallableExecutor;
290                    }
291    
292                    @Override
293                    public Object invoke(Object proxy, Method method, Object[] args)
294                            throws Throwable {
295    
296                            if (isObjectEquals(method)) {
297                                    return JMXProxyUtil.equals(_objectName, args[0]);
298                            }
299    
300                            if (isObjectHashCode(method)) {
301                                    return _objectName.hashCode();
302                            }
303    
304                            if (isObjectToString(method)) {
305                                    return _objectName.toString();
306                            }
307    
308                            String methodName = method.getName();
309    
310                            Class<?>[] parameterTypes = method.getParameterTypes();
311    
312                            ProcessCallable<Serializable> processCallable = null;
313    
314                            if (isGetGetter(methodName, parameterTypes)) {
315                                    processCallable = new GetAttributeProcessCallable(
316                                            _objectName, methodName.substring(3), isOptional(method));
317                            }
318                            else if (isIsGetter(methodName, parameterTypes)) {
319                                    processCallable = new GetAttributeProcessCallable(
320                                            _objectName, methodName.substring(2), isOptional(method));
321                            }
322                            else if (isSetter(methodName, parameterTypes)) {
323                                    processCallable = new SetAttributeProcessCallable(
324                                            _objectName, methodName.substring(3), (Serializable)args[0],
325                                            isOptional(method));
326                            }
327                            else {
328                                    String[] parameterTypeNames = new String[parameterTypes.length];
329    
330                                    for (int i = 0; i < parameterTypes.length; i++) {
331                                            parameterTypeNames[i] = parameterTypes[i].getName();
332                                    }
333    
334                                    processCallable = new OperationProcessCallable(
335                                            _objectName, methodName, args, parameterTypeNames);
336                            }
337    
338                            Future<? extends Serializable> future =
339                                    _processCallableExecutor.execute(processCallable);
340    
341                            return decode(method.getReturnType(), future.get());
342                    }
343    
344                    private final ObjectName _objectName;
345                    private final ProcessCallableExecutor _processCallableExecutor;
346    
347            }
348    
349            protected static class OperationProcessCallable
350                    implements ProcessCallable<Serializable> {
351    
352                    public OperationProcessCallable(
353                            ObjectName objectName, String operationName, Object[] arguments,
354                            String[] parameterTypeNames) {
355    
356                            _objectName = objectName;
357                            _operationName = operationName;
358                            _arguments = arguments;
359                            _parameterTypeNames = parameterTypeNames;
360                    }
361    
362                    @Override
363                    public Serializable call() throws ProcessException {
364                            MBeanServer mBeanServer =
365                                    ManagementFactory.getPlatformMBeanServer();
366    
367                            try {
368                                    return (Serializable)mBeanServer.invoke(
369                                            _objectName, _operationName, _arguments,
370                                            _parameterTypeNames);
371                            }
372                            catch (Exception e) {
373                                    throw new ProcessException(e);
374                            }
375                    }
376    
377                    private static final long serialVersionUID = 1L;
378    
379                    private final Object[] _arguments;
380                    private final ObjectName _objectName;
381                    private final String _operationName;
382                    private final String[] _parameterTypeNames;
383    
384            }
385    
386            protected static class SetAttributeProcessCallable
387                    implements ProcessCallable<Serializable> {
388    
389                    public SetAttributeProcessCallable(
390                            ObjectName objectName, String attributeName,
391                            Serializable attributeValue, boolean optional) {
392    
393                            _objectName = objectName;
394                            _attributeName = attributeName;
395                            _attributeValue = attributeValue;
396                            _optional = optional;
397                    }
398    
399                    @Override
400                    public Serializable call() throws ProcessException {
401                            MBeanServer mBeanServer =
402                                    ManagementFactory.getPlatformMBeanServer();
403    
404                            try {
405                                    mBeanServer.setAttribute(
406                                            _objectName,
407                                            new Attribute(_attributeName, _attributeValue));
408                            }
409                            catch (AttributeNotFoundException anfe) {
410                                    if (!_optional) {
411                                            throw new ProcessException(anfe);
412                                    }
413                            }
414                            catch (Exception e) {
415                                    throw new ProcessException(e);
416                            }
417    
418                            return null;
419                    }
420    
421                    private static final long serialVersionUID = 1L;
422    
423                    private final String _attributeName;
424                    private final Serializable _attributeValue;
425                    private final ObjectName _objectName;
426                    private final boolean _optional;
427    
428            }
429    
430    }