Package com.abiquo.hypervisor.plugin

Source Code of com.abiquo.hypervisor.plugin.PluginManager$LoadedPlugin

/**
* Copyright (C) 2008 - Abiquo Holdings S.L. All rights reserved.
*
* Please see /opt/abiquo/tomcat/webapps/legal/ on Abiquo server
* or contact contact@abiquo.com for licensing information.
*/
package com.abiquo.hypervisor.plugin;

import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.ImmutableMap.copyOf;
import static com.google.common.collect.ImmutableSet.copyOf;
import static com.google.common.collect.Iterables.all;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.collect.Sets.newHashSet;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ServiceLoader;
import java.util.Set;

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

import com.abiquo.hypervisor.model.HypervisorConnection;
import com.abiquo.hypervisor.model.enumerator.ConstraintKey;
import com.abiquo.hypervisor.model.enumerator.DiskFormatType;
import com.abiquo.hypervisor.plugin.IHypervisor.OPERATION;
import com.abiquo.hypervisor.plugin.annotation.AgentConnectionMetadata;
import com.abiquo.hypervisor.plugin.annotation.HostConnectionMetadata;
import com.abiquo.hypervisor.plugin.annotation.HypervisorMetadata;
import com.abiquo.hypervisor.plugin.annotation.ManagerConnectionMetadata;
import com.abiquo.hypervisor.plugin.enumerator.FieldConstraint;
import com.abiquo.hypervisor.plugin.exception.HypervisorPluginError;
import com.abiquo.hypervisor.plugin.exception.HypervisorPluginException;
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/**
* Hold {@link IHypervisor} instances loaded using a {@link ServiceLoader}
*/
public class PluginManager
{
    private final static Logger LOG = LoggerFactory.getLogger(PluginManager.class);

    private final static TypeResolver typeResolver = new TypeResolver();

    private static PluginManager instance;

    private final Map<String, LoadedPlugin> instances;

    private class LoadedPlugin
    {
        public Class<IHypervisorConnection> connectionClass;

        public IHypervisor<IHypervisorConnection> hypervisorObject;

        public HypervisorMetadata metadata;

        public Set<OPERATION> unsupportedOperations;

        public Map<ConstraintKey, String> constraints;

        public LoadedPlugin(final Class<IHypervisorConnection> connectionClass,
            final IHypervisor<IHypervisorConnection> hypervisorObject)
        {
            this.connectionClass = connectionClass;
            this.hypervisorObject = hypervisorObject;
            this.metadata = hypervisorObject.getClass().getAnnotation(HypervisorMetadata.class);
            this.unsupportedOperations = newHashSet();
            this.constraints = newHashMap();
        }
    }

    private PluginManager()
    {
        Map<String, LoadedPlugin> plugins = Maps.newHashMap();

        for (IHypervisor<IHypervisorConnection> hypervisor : ServiceLoader.load(IHypervisor.class))
        {
            Optional<LoadedPlugin> plugin = loadPlugin(hypervisor);
            if (plugin.isPresent())
            {
                checkDuplicatedPlugins(plugins, plugin);
                plugins.put(plugin.get().metadata.type(), plugin.get());
            }
        }
        instances = Collections.unmodifiableMap(plugins);
    }

    /** Only one instance per hypervisor plugin. */
    private void checkDuplicatedPlugins(final Map<String, LoadedPlugin> plugins,
        final Optional<LoadedPlugin> plugin)
    {
        if (plugins.containsKey(plugin.get().metadata.type()))
        {
            String message =
                String.format("A plugin for hypervisor %s has been already loaded",
                    plugin.get().metadata.type());

            LOG.error(message);
            throw new IllegalStateException(message);
        }
    }

    /**
     * Returns the {@link PluginManager} singleton instance
     *
     * @return the singleton instance
     */
    public static PluginManager getInstance()
    {
        if (instance == null)
        {
            throw new IllegalStateException("PluginManager not initialized. "
                + "This indicate some problem during the PluginLoaderContextListener startup");
        }

        return instance;
    }

    /**
     * Returns the loaded plugins names.
     *
     * @return the loaded plugins names.
     */
    public Set<String> getSupportedHypervisorTypes()
    {
        return instances.keySet();
    }

    /**
     * Returns the loaded plugins.
     *
     * @return the loaded plugins.
     */
    public Collection<LoadedPlugin> getSupportedHypervisors()
    {
        return instances.values();
    }

    /**
     * Check if there is a plugin loaded for the given hypervisor type.
     *
     * @param hypervisorType the hypervisor type to check
     * @return True if there is a plugin loaded for the given hypervisor type and false otherwise
     */
    public boolean isLoaded(final String hypervisorType)
    {
        return instances.keySet().contains(hypervisorType);
    }

    /**
     * Returns the single {@link IHypervisor} instance based on the given hypervisor type
     *
     * @param type the hypervisor type
     * @return The {@link IHypervisor} instance
     */
    public Optional<IHypervisor<IHypervisorConnection>> getHypervisorPlugin(final String type)
    {
        if (!instances.containsKey(type))
        {
            LOG.error("Trying to get the plugin instance for the non supported hypervisor type {}",
                type);
            return Optional.absent();
        }
        return Optional.of(instances.get(type).hypervisorObject);
    }

    /**
     * Returns the single {@link IManagerHypervisor} instance based on the given hypervisor type
     *
     * @param type the hypervisor type
     * @return The {@link IHypervisor} instance
     */
    public Optional<IManagerHypervisor<IHypervisorConnection>> getManagerHypervisorPlugin(
        final String type)
    {
        if (!instances.containsKey(type))
        {
            LOG.error(
                "Trying to get the plugin instance for the non supported manager hypervisor type {}",
                type);
            return Optional.absent();
        }

        if (!isManagerHypervisor(type))
        {
            LOG.error(
                "Trying to get the manager plugin instance for the non manager hypervisor type {}",
                type);
            return Optional.absent();
        }

        return Optional
            .of((IManagerHypervisor<IHypervisorConnection>) instances.get(type).hypervisorObject);
    }

    public boolean isManagerHypervisor(final String type)
    {
        return instances.containsKey(type) ? IManagerHypervisor.class.isAssignableFrom(instances
            .get(type).hypervisorObject.getClass()) : false;
    }

    private boolean isManagerHypervisor(final IHypervisor<IHypervisorConnection> plugin)
    {
        return IManagerHypervisor.class.isAssignableFrom(plugin.getClass());
    }

    /**
     * Returns the metadata of a plugin based on the given hypervisor type
     *
     * @param type the hypervisor type
     * @return the metadata of a plugin based on the given hypervisor type
     * @throws HypervisorPluginException if there is not a loaded plugin for the given hypervisor
     *             type
     */
    public Optional<HypervisorMetadata> getHypervisorMetadata(final String type)
    {
        if (!instances.containsKey(type))
        {
            LOG.warn("Trying to get the plugin metadata for the non supported hypervisor type {}",
                type);
            return Optional.absent();
        }
        return Optional.fromNullable(instances.get(type).metadata);
    }

    /**
     * Given a loaded plugin retrieve its metadata. If the plugin is not loaded or has no metadata
     * an <code>Optional.absent()</code> is returned.
     *
     * @param plugin to load its metadata.
     * @return metadata of the plugin, Optional.absent() otherwise.
     */
    public Optional<HypervisorMetadata> getHypervisorMetadata(
        final IHypervisor<IHypervisorConnection> plugin)
    {
        return extractHypervisorMetadata(plugin);
    }

    public boolean isOperationSupported(final String hypervisorType, final OPERATION operation)
    {
        LoadedPlugin plugin = instances.get(hypervisorType);
        return plugin != null ? !plugin.unsupportedOperations.contains(operation) : false;
    }

    public String getConstraintValue(final String hypervisorType, final ConstraintKey constraintKey)
    {
        LoadedPlugin plugin = instances.get(hypervisorType);
        return plugin != null ? plugin.constraints.get(constraintKey) : null;
    }

    public Map<ConstraintKey, String> getConstraints(final String hypervisorType)
    {
        LoadedPlugin plugin = instances.get(hypervisorType);
        return plugin != null ? plugin.constraints : Collections.<ConstraintKey, String> emptyMap();
    }

    /**
     * Creates a new instance of {@link IHypervisorConnection} based on the given HypervisorType
     *
     * @param type the hypervisor type
     * @return The {@link IHypervisorConnection} instance
     * @throws HypervisorPluginException if there is not a loaded plugin for the given
     *             HypervisorType
     */
    public IHypervisorConnection newHypervisorConnection(final String type)
        throws HypervisorPluginException
    {
        LoadedPlugin plugin = instances.get(type);
        if (plugin == null)
        {
            LOG.error(
                "Trying to get the connection instance for the non supported hypervisor type {}",
                type);
            throw new HypervisorPluginException(HypervisorPluginError.THURMAN0, "Hypervisor type "
                + type);
        }

        try
        {
            return plugin.connectionClass.newInstance();
        }
        catch (Exception e)
        {
            LOG.error("Unable to create new plugin instance hypervisor type {}", type);
            throw new HypervisorPluginException(HypervisorPluginError.PLUGIN_NEW_INSTANCE_ERROR,
                "Hypervisor type " + type);
        }
    }

    protected Optional<LoadedPlugin> loadPlugin(final IHypervisor<IHypervisorConnection> plugin)
        throws IllegalStateException
    {
        Optional<HypervisorMetadata> optionalMetadata = extractHypervisorMetadata(plugin);
        if (!optionalMetadata.isPresent())
        {
            LOG.error(
                "The plugin {} has invalid format. {} annotation is missing. Ignoring this plugin",
                new Object[] {plugin.getClass().getName(), HypervisorMetadata.class.getName()});
            return Optional.absent();
        }

        HypervisorMetadata metadata = optionalMetadata.get();
        final List<DiskFormatType> diskFormatTypes = Arrays.asList(DiskFormatType.values());
        List<DiskFormatType> compatibleTypes = Arrays.asList(metadata.compatibleDiskFormatTypes());

        // Hypervisor type must be informed
        if (isNullOrEmpty(metadata.type()))
        {
            LOG.error("The type of the plugin {} is null or empty. Ignoring this plugin",
                new Object[] {plugin.getClass().getName()});
            return Optional.absent();
        }

        // Hypervisor friendly name must be informed
        if (isNullOrEmpty(metadata.friendlyName()))
        {
            LOG.error("The friendly name of the plugin {} is null or empty. Ignoring this plugin",
                new Object[] {metadata.type()});
            return Optional.absent();
        }

        // All compatible types must be in DiskFormatType otherwise we don't load this plugin
        boolean allContained = Iterables.any(compatibleTypes, new Predicate<DiskFormatType>()
        {
            @Override
            public boolean apply(final DiskFormatType input)
            {
                return diskFormatTypes.contains(input);
            }
        });

        if (!allContained)
        {
            LOG.error(
                "The plugin {} has invalid configuration compatibles disk format type is not a known disk format types (is not in DiskFormatTypes). Ignoring this plugin",
                metadata.type());
            return Optional.absent();
        }

        // Base format must be in compatible formats otherwise we don't load this plugin
        if (!compatibleTypes.contains(metadata.baseDiskFormatType()))
        {
            LOG.error(
                "The plugin {} has invalid configuration base disk format type is not in compatible disk format types. {} is not in compatibleDiskFormatTypes. Ignoring this plugin",
                new Object[] {metadata.type(), metadata.baseDiskFormatType()});
            return Optional.absent();
        }

        // Validate specific plugin configuration
        try
        {
            plugin.validateConfiguration();
        }
        catch (IllegalStateException e)
        {
            LOG.error("The plugin {} has invalid configuration: {}", new Object[] {metadata.type(),
            e.getMessage()});
            return Optional.absent();
        }

        try
        {
            Class<IHypervisorConnection> connectionClass =
                resolveIHypervisorConnection(plugin.getClass());

            LoadedPlugin loadedPlugin = new LoadedPlugin(connectionClass, plugin);
            loadedPlugin.unsupportedOperations = copyOf(getUnsupportedOperations(plugin));
            loadedPlugin.constraints = copyOf(loadConstraints(plugin));

            LOG.info("Loaded plugin for hypervisor type {}", metadata.type());
            return Optional.of(loadedPlugin);
        }
        catch (Exception e)
        {
            LOG.error("Unable to load plugin for hypervisor type {}", metadata.type(), e);
        }

        return Optional.absent();
    }

    private Optional<HypervisorMetadata> extractHypervisorMetadata(
        final IHypervisor<IHypervisorConnection> plugin)
    {
        Class< ? extends IHypervisor> hypClass = plugin.getClass();
        return Optional.fromNullable(hypClass.getAnnotation(HypervisorMetadata.class));
    }

    private Set<OPERATION> getUnsupportedOperations(final IHypervisor<IHypervisorConnection> plugin)
    {
        Set<OPERATION> unsupportedOperations = Sets.newHashSet();
        for (Method method : plugin.getClass().getMethods())
        {
            if (method.getAnnotation(UnsupportedOperation.class) != null)
            {
                OPERATION op = OPERATION.fromName(method.getName());
                if (op != null)
                {
                    unsupportedOperations.add(op);
                }
            }
        }
        return unsupportedOperations;
    }

    private Map<ConstraintKey, String> loadConstraints(
        final IHypervisor<IHypervisorConnection> plugin)
    {
        Map<ConstraintKey, String> constraints = Maps.newHashMap();
        for (ConstraintKey key : ConstraintKey.values())
        {
            String value = plugin.getConstraint(key.name());
            if (value != null)
            {
                constraints.put(key, value);
            }
        }

        constraints.putAll(loadConnectionConstraints(plugin.getClass().getAnnotation(
            HostConnectionMetadata.class)));
        constraints.putAll(loadConnectionConstraints(plugin.getClass().getAnnotation(
            AgentConnectionMetadata.class)));
        constraints.putAll(loadConnectionConstraints(plugin.getClass().getAnnotation(
            ManagerConnectionMetadata.class)));

        if (isManagerHypervisor(plugin)
            && !constraints.containsKey(ConstraintKey.DISCOVER_THROUGH_MANAGER))
        {
            constraints.put(ConstraintKey.DISCOVER_THROUGH_MANAGER, Boolean.TRUE.toString());
        }

        return constraints;
    }

    private Map<ConstraintKey, String> loadConnectionConstraints(
        final HostConnectionMetadata metadata)
    {
        FieldConstraint ip = FieldConstraint.NO_APPLICABLE;
        FieldConstraint port = FieldConstraint.NO_APPLICABLE;
        FieldConstraint credentials = FieldConstraint.NO_APPLICABLE;

        if (metadata != null)
        {
            ip = metadata.ip();
            port = metadata.port();
            credentials = metadata.credentials();
        }

        return ImmutableMap.<ConstraintKey, String> builder().put(ConstraintKey.HOST_IP, ip.name())
            .put(ConstraintKey.HOST_PORT, port.name())
            .put(ConstraintKey.HOST_CREDENTIALS, credentials.name()).build();
    }

    private Map<ConstraintKey, String> loadConnectionConstraints(
        final ManagerConnectionMetadata metadata)
    {
        FieldConstraint ip = FieldConstraint.NO_APPLICABLE;
        FieldConstraint port = FieldConstraint.NO_APPLICABLE;
        FieldConstraint credentials = FieldConstraint.NO_APPLICABLE;

        if (metadata != null)
        {
            ip = metadata.ip();
            port = metadata.port();
            credentials = metadata.credentials();
        }

        return ImmutableMap.<ConstraintKey, String> builder()
            .put(ConstraintKey.MANAGER_IP, ip.name()).put(ConstraintKey.MANAGER_PORT, port.name())
            .put(ConstraintKey.MANAGER_CREDENTIALS, credentials.name()).build();
    }

    private Map<ConstraintKey, String> loadConnectionConstraints(
        final AgentConnectionMetadata metadata)
    {
        FieldConstraint ip = FieldConstraint.NO_APPLICABLE;
        FieldConstraint port = FieldConstraint.NO_APPLICABLE;
        FieldConstraint credentials = FieldConstraint.NO_APPLICABLE;

        if (metadata != null)
        {
            ip = metadata.ip();
            port = metadata.port();
            credentials = metadata.credentials();
        }

        return ImmutableMap.<ConstraintKey, String> builder()
            .put(ConstraintKey.AGENT_IP, ip.name()).put(ConstraintKey.AGENT_PORT, port.name())
            .put(ConstraintKey.AGENT_CREDENTIALS, credentials.name()).build();
    }

    /**
     * Introspect an {@link IHypervisor} object to know the class of its
     * {@link IHypervisorConnection}
     *
     * @param hclass the class of an {@link IHypervisor} implementation
     * @return the class of the associated generic type for {@link IHypervisorConnection}
     */
    @SuppressWarnings("unchecked")
    // verified by ''typeParametersFor(IHypervisor.class)''
    private static Class<IHypervisorConnection> resolveIHypervisorConnection(final Type hclass)
        throws HypervisorPluginException
    {
        List<ResolvedType> rt = typeResolver.resolve(hclass).typeParametersFor(IHypervisor.class);
        if (rt == null || 1 != rt.size())
        {
            throw new HypervisorPluginException(HypervisorPluginError.THURMAN0, hclass.toString());
        }

        return (Class<IHypervisorConnection>) rt.get(0).getErasedType();
    }

    /**
     * This method is meant to be executed <b>only</b> at start up time! To retrieve the instance
     * use the {@link #getInstance()} instead.
     *
     * @see PluginManager#getInstance()
     * @throws IllegalStateException if the instance is already created.
     */
    public static synchronized void createInstance()
    {
        if (instance != null)
        {
            throw new IllegalStateException("An instance of the PluginManager already exists! Use getInstance() method instead");
        }
        instance = new PluginManager();
    }

    /**
     * Validates the given {@link HypervisorConnection} for the {@link IHypervisor} implementation
     * of the indicated hypervisor plugin type.
     *
     * @param connection the connection to validate
     * @param type the hypervisor type
     * @return True if is a valid connection and false otherwise.
     */
    public boolean validateHostConnection(final HypervisorConnection connection, final String type)
    {
        if (!isLoaded(type))
        {
            return false;
        }

        return all(filter(instances.get(type).constraints.entrySet(), mandatoryFields),
            new Predicate<Entry<ConstraintKey, String>>()
            {
                @Override
                public boolean apply(final Entry<ConstraintKey, String> entry)
                {
                    switch (entry.getKey())
                    {
                        case HOST_IP:
                            return isNotNull(connection.getIp());
                        case HOST_PORT:
                            return isNotNull(connection.getPort());
                        case HOST_CREDENTIALS:
                            return isNotNull(connection.getUser())
                                && isNotNull(connection.getPassword());
                        case MANAGER_IP:
                            return isNotNull(connection.getManagerIp());
                        case MANAGER_PORT:
                            return isNotNull(connection.getManagerPort());
                        case MANAGER_CREDENTIALS:
                            return isNotNull(connection.getManagerUser())
                                && isNotNull(connection.getManagerPassword());
                        case AGENT_IP:
                            return isNotNull(connection.getAgentIp());
                        case AGENT_PORT:
                            return isNotNull(connection.getAgentPort());
                        case AGENT_CREDENTIALS:
                            return isNotNull(connection.getAgentUser())
                                && isNotNull(connection.getAgentPassword());
                        default:
                            return true;
                    }
                }
            });
    }

    /**
     * Validates the given {@link HypervisorConnection} for the {@link IManagerHypervisor}
     * implementation of the indicated hypervisor plugin type.
     *
     * @param connection the connection to validate
     * @param type the hypervisor type
     * @return True if is a valid connection and false otherwise.
     */
    public boolean validateManagerConnection(final HypervisorConnection connection,
        final String type)
    {
        if (!isLoaded(type) || !isManagerHypervisor(type))
        {
            return false;
        }

        return all(filter(instances.get(type).constraints.entrySet(), mandatoryFields),
            new Predicate<Entry<ConstraintKey, String>>()
            {
                @Override
                public boolean apply(final Entry<ConstraintKey, String> entry)
                {
                    switch (entry.getKey())
                    {
                        case MANAGER_IP:
                            return isNotNull(connection.getManagerIp());
                        case MANAGER_PORT:
                            return isNotNull(connection.getManagerPort());
                        case MANAGER_CREDENTIALS:
                            return isNotNull(connection.getManagerUser())
                                && isNotNull(connection.getManagerPassword());
                        default:
                            return true;
                    }
                }
            });
    }

    /**
     * Evaluates if the {@link ConstraintKey} has assigned the value
     * {@link FieldConstraint#MANDATORY}
     */
    private static final Predicate<Entry<ConstraintKey, String>> mandatoryFields =
        new Predicate<Entry<ConstraintKey, String>>()
        {
            @Override
            public boolean apply(final Entry<ConstraintKey, String> entry)
            {
                return FieldConstraint.MANDATORY.name().equalsIgnoreCase(entry.getValue());
            }
        };

    private boolean isNotNull(final Object value)
    {
        return value != null;
    }
}
TOP

Related Classes of com.abiquo.hypervisor.plugin.PluginManager$LoadedPlugin

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.