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

com.abiquo.commons.plugin.internal.PluginInvocationHandler Maven / Gradle / Ivy

/**
 * The Abiquo Platform
 * Cloud management application for hybrid clouds
 * Copyright (C) 2008 - Abiquo Holdings S.L.
 *
 * This application 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 under
 * version 3 of the License
 *
 * This software 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 v.3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
package com.abiquo.commons.plugin.internal;

import static com.abiquo.commons.plugin.internal.function.Functions.TO_CLASS;
import static com.google.common.base.Throwables.propagateIfInstanceOf;
import static com.google.common.collect.Lists.transform;
import static java.util.Arrays.asList;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Vector;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.abiquo.commons.plugin.UnsupportedOperation;
import com.abiquo.commons.plugin.exception.ComputeException;
import com.google.common.reflect.AbstractInvocationHandler;

/**
 * Handles automatically plugin composition.
 * 
 * @author Serafin Sedano
 */
public class PluginInvocationHandler extends AbstractInvocationHandler
{

    private static final Logger LOGGER = LoggerFactory.getLogger(PluginInvocationHandler.class);

    private final Object target;

    /**
     * @param target instanceof Pluggable or Firewall
     */
    public PluginInvocationHandler(final Object target)
    {
        this.target = target;
    }

    @Override
    public Object handleInvocation(final Object proxy, final Method method, final Object[] args)
        throws UnsupportedOperationException, ComputeException
    {
        try
        {
            checkOperationSupported(target, method, args);
            return method.invoke(target, args);
        }
        catch (IllegalAccessException | IllegalArgumentException | SecurityException
            | InvocationTargetException e)
        {
            LOGGER.trace("invoke: Plugin {}. Trying to invoke {}. Error {}:{}", new Object[] {
            target.getClass().getName(), method.getName(), e.getClass().getName(), e.getMessage()});
            propagateIfInstanceOf(e.getCause(), ComputeException.class);
            throw new RuntimeException(e);
        }

    }

    /** If method is not implemented or annotated with UnsupportedOperation is not supported. */
    private void checkOperationSupported(final Object plugin, final Method method,
        final Object[] args) throws UnsupportedOperationException
    {
        long t = System.currentTimeMillis();
        try
        {
            Method operation = findMethod(plugin, method.getName(), getParameters(args));
            if (operation.isAnnotationPresent(UnsupportedOperation.class))
            {
                LOGGER.warn("checkOperationSupported: method {} unsupported for this plugin {}",
                    new Object[] {method.getName(), target.getClass().getName()});
                throw new UnsupportedOperationException(method.getName()
                    + " is not implemented in plugin");
            }
        }
        catch (NoSuchMethodException e)
        {
            LOGGER.warn("checkOperationSupported: method not implemented {} for this plugin {}",
                new Object[] {method.getName(), target.getClass().getName()});
            throw new UnsupportedOperationException(method.getName()
                + " is not implemented in plugin");
        }
        LOGGER.trace("checkOperationSupported: duration {}", t - System.currentTimeMillis());
    }

    private static Method findMethod(final Object plugin, final String name,
        final Class[] parameterTypes) throws NoSuchMethodException
    {
        int l = parameterTypes.length;
        Method[] methods = plugin.getClass().getMethods();

        // First find the applicable methods
        Vector applicableMethods = new Vector<>();
        for (int i = 0; i < methods.length; i++)
        {
            // Check the name matches
            if (!methods[i].getName().equals(name))
            {
                continue;
            }
            // Check the parameters match
            Class[] params = methods[i].getParameterTypes();
            if (params.length != l)
            {
                continue;
            }
            int j;
            for (j = 0; j < l; j++)
            {
                if (!Object.class.equals(parameterTypes[j])
                    && !params[j].isAssignableFrom(parameterTypes[j]))
                {
                    break;
                }
            }
            // If so, add it to the list
            if (j == l)
            {
                applicableMethods.add(methods[i]);
            }
        }

        int size = applicableMethods.size();
        if (size == 0)
        {
            throw new NoSuchMethodException("No such method: " + name);
        }
        // By the definition of our interfaces we exit
        // If this change we should look for the maximally specific
        if (applicableMethods.size() == 1)
        {
            return applicableMethods.elementAt(0);
        }

        /* For a definition of maximally specific, see JLS section 15.11.2.2. */
        int maximallySpecific = -1; // Index of maximally specific method
        for (int i = 0; i < size; i++)
        {
            int j;
            // In terms of the JLS, current is T
            Method current = applicableMethods.elementAt(i);
            Class[] currentParams = current.getParameterTypes();
            Class currentDeclarer = current.getDeclaringClass();
            for (j = 0; j < size; j++)
            {
                if (i == j)
                {
                    continue;
                }
                // In terms of the JLS, test is U
                Method method = applicableMethods.elementAt(j);
                Class[] params = method.getParameterTypes();
                Class declared = method.getDeclaringClass();

                // Check if T is a subclass of U, breaking if not
                if (!declared.isAssignableFrom(currentDeclarer))
                {
                    break;
                }

                // Check if each parameter in T is a subclass of the
                // equivalent parameter in U
                int k;
                for (k = 0; k < l; k++)
                {
                    if (!params[k].isAssignableFrom(currentParams[k]))
                    {
                        break;
                    }
                }
                if (k != l)
                {
                    break;
                }
            }
            // Maximally specific!
            if (j == size)
            {
                if (maximallySpecific != -1)
                {
                    throw new NoSuchMethodException("Ambiguous method search - more than one maximally specific method");
                }
                maximallySpecific = i;
            }
        }
        if (maximallySpecific == -1)
        {
            throw new NoSuchMethodException("No maximally specific method.");
        }
        return applicableMethods.elementAt(maximallySpecific);

    }

    private Class[] getParameters(final Object[] args)
    {
        return transform(asList(args), TO_CLASS).toArray(new Class[0]);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy