All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.micronaut.configuration.jmx.context.AbstractDynamicMBeanFactory Maven / Gradle / Ivy

/*
 * Copyright 2017-2020 original authors
 *
 * Licensed 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
 *
 * https://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 io.micronaut.configuration.jmx.context;

import io.micronaut.core.async.publisher.Publishers;
import io.micronaut.core.type.Argument;
import io.micronaut.inject.BeanDefinition;
import io.micronaut.inject.ExecutableMethod;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;

import javax.management.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * A base class that creates dynamic MBeans from a bean definition.
 *
 * @author James Kleeh
 * @since 1.0
 */
public abstract class AbstractDynamicMBeanFactory implements DynamicMBeanFactory {

    @Override
    public Object createMBean(BeanDefinition beanDefinition, Collection methods, Supplier instanceSupplier) {

        MBeanOperationInfo[] operations = methods.stream().map(this::getOperationInfo).toArray(MBeanOperationInfo[]::new);
        MBeanInfo mBeanInfo = new MBeanInfo(beanDefinition.getBeanType().getName(),
                getBeanDescription(beanDefinition),
                new MBeanAttributeInfo[0],
                new MBeanConstructorInfo[0],
                operations,
                new MBeanNotificationInfo[0]
        );

        return new DynamicMBean() {

            @Override
            public Object getAttribute(String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException {
                return null;
            }

            @Override
            public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException {
                //no op
            }

            @Override
            public AttributeList getAttributes(String[] attributes) {
                return new AttributeList();
            }

            @Override
            public AttributeList setAttributes(AttributeList attributes) {
                return attributes;
            }

            @Override
            public Object invoke(String actionName, Object[] params, String[] signature) throws MBeanException, ReflectionException {
                List matchedMethods = methods.stream()
                        .filter(m -> m.getMethodName().equals(actionName))
                        .collect(Collectors.toList());
                if (matchedMethods.size() == 1) {
                    return invoke(matchedMethods.get(0), params);
                } else {
                    for (ExecutableMethod method: matchedMethods) {
                        if (argumentsMatch(method, signature)) {
                            return invoke(method, params);
                        }
                    }
                    return null;
                }
            }

            private boolean argumentsMatch(ExecutableMethod method, String[] signature) {
                Class[] argumentTypes = method.getArgumentTypes();
                if (argumentTypes.length == signature.length) {
                    for (int i = 0; i < argumentTypes.length; i++) {
                        if (!argumentTypes[i].getName().equals(signature[i])) {
                            return false;
                        }
                    }
                    return true;
                } else {
                    return false;
                }
            }

            private Object invoke(ExecutableMethod method, Object[] params) {
                //noinspection unchecked
                Object returnVal = method.invoke(instanceSupplier.get(), params);
                if (returnVal != null) {
                    if (Publishers.isSingle(returnVal.getClass()) || Publishers.isConvertibleToPublisher(returnVal)) {
                        return Flux.from(Publishers.convertPublisher(returnVal, Publisher.class)).blockFirst();
                    }
                }

                return returnVal;
            }

            @Override
            public MBeanInfo getMBeanInfo() {
                return mBeanInfo;
            }
        };
    }

    /**
     * Returns the management bean description.
     * @see MBeanInfo#getDescription()
     *
     * @param beanDefinition The bean definition
     * @return The description for the management bean
     */
    protected abstract String getBeanDescription(BeanDefinition beanDefinition);

    /**
     * Returns the description of a management bean operation.
     * @see MBeanOperationInfo#getDescription()
     *
     * @param method The method
     * @return The description for the management bean operation
     */
    protected abstract String getMethodDescription(ExecutableMethod method);

    /**
     * Returns the description of a management bean operation parameter.
     * @see MBeanParameterInfo#getDescription()
     *
     * @param argument The argument
     * @return The description for the management bean operation parameter
     */
    protected abstract String getParameterDescription(Argument argument);

    /**
     * Extracts parameters from an executable method.
     * @see MBeanOperationInfo#getSignature()
     *
     * @param method The method
     * @return The array of management bean operation parameters
     */
    protected MBeanParameterInfo[] getParameters(ExecutableMethod method) {
        return Arrays.stream(method.getArguments()).map(argument -> {
            return new MBeanParameterInfo(argument.getName(), argument.getType().getName(), getParameterDescription(argument));
        }).toArray(MBeanParameterInfo[]::new);
    }

    /**
     * Returns the return type of the executable method.
     * @see MBeanOperationInfo#getReturnType()
     *
     * @param method The method
     * @return The return type of the method
     */
    protected String getReturnType(ExecutableMethod method) {
        Class returnType = method.getReturnType().getType();
        if (Publishers.isSingle(returnType) || Publishers.isConvertibleToPublisher(returnType)) {
            Argument[] typeParams = method.getReturnType().getTypeParameters();
            if (typeParams.length > 0) {
                returnType = typeParams[0].getType();
            }
        }
        return returnType.getName();
    }

    /**
     * Returns the impact of the provided method.
     * @see MBeanOperationInfo#getImpact()
     *
     * @param method The method
     * @return The impact
     */
    protected abstract int getImpact(ExecutableMethod method);

    /**
     * Returns the operation information.
     * @see MBeanInfo#getOperations()
     *
     * @param method The method
     * @return The operation information
     */
    protected MBeanOperationInfo getOperationInfo(ExecutableMethod method) {
        return new MBeanOperationInfo(method.getMethodName(), getMethodDescription(method), getParameters(method), getReturnType(method), getImpact(method));
    }

}