Package com.cloud.utils.component

Source Code of com.cloud.utils.component.ComponentLocator$InjectInfo

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// the License.  You may obtain a copy of the License at
//
// http://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 com.cloud.utils.component;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.ejb.Local;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.naming.ConfigurationException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.NoOp;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.xml.DOMConfigurator;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.cloud.utils.Pair;
import com.cloud.utils.PropertiesUtil;
import com.cloud.utils.db.DatabaseCallback;
import com.cloud.utils.db.DatabaseCallbackFilter;
import com.cloud.utils.db.GenericDao;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.mgmt.JmxUtil;
import com.cloud.utils.mgmt.ManagementBean;

/**
* ComponentLocator ties together several different concepts.  First, it
* deals with how a system should be put together.  It manages different
* types of components:
*   - Manager: Singleton implementation of a certain process.
*   - Adapter: Different singleton implementations for the same functions.
*   - SystemIntegrityChecker: Singletons that are called at the load time.
*   - Dao: Data Access Objects.
*  
* These components can be declared in several ways:
*   - ComponentLibrary - A Java class that declares the above components.  The
*     advantage of declaring components here is they change automatically
*     with any refactoring.
*   - components specification - An xml file that overrides the
*     ComponentLibrary.  The advantage of declaring components here is
*     they can change by hand on every deployment.
*
* The two are NOT mutually exclusive.  ComponentLocator basically locates
* the components specification, which specifies the ComponentLibrary within. 
* Components found in the ComponentLibrary are overridden by components
* found in components specification.
*
* Components specification can also be nested.  One components specification
* can point to another components specification and, therefore, "inherits"
* those components but still override one or more components.  ComponentLocator
* reads the child components specification first and follow the chain up. 
* the child's components overrides the ones in the parent.
*
* ComponentLocator looks for the components specification as follows:
*   1. By following the path specified by "cloud-stack-components-specification"
*      within the environment.properties file.
*   2. Look for components.xml in the class path.
*  
* ComponentLocator also ties in component injection.  Components can specify
* an @Inject annotation to components ComponentLocator knows.  When
* instantiating components, ComponentLocator attempts to inject these
* components.
*
**/
@SuppressWarnings("unchecked")
public class ComponentLocator implements ComponentLocatorMBean {
    protected static final Logger                      s_logger     = Logger.getLogger(ComponentLocator.class);

    protected static final ThreadLocal<ComponentLocator> s_tl = new ThreadLocal<ComponentLocator>();
    protected static final ConcurrentHashMap<Class<?>, Singleton> s_singletons = new ConcurrentHashMap<Class<?>, Singleton>(111);
    protected static final HashMap<String, ComponentLocator> s_locators = new HashMap<String, ComponentLocator>();
    protected static final HashMap<Class<?>, InjectInfo> s_factories = new HashMap<Class<?>, InjectInfo>();
    protected static Boolean s_once = false;
    protected static Boolean _hasCheckerRun = false;
    protected static Callback[] s_callbacks = new Callback[] { NoOp.INSTANCE, new DatabaseCallback()};
    protected static CallbackFilter s_callbackFilter = new DatabaseCallbackFilter();
    protected static final List<AnnotationInterceptor<?>> s_interceptors = new ArrayList<AnnotationInterceptor<?>>();
    protected static CleanupThread s_janitor = null;

    protected HashMap<String, Adapters<? extends Adapter>>              _adapterMap;
    protected HashMap<String, ComponentInfo<Manager>>                   _managerMap;
    protected LinkedHashMap<String, ComponentInfo<SystemIntegrityChecker>>     _checkerMap;
    protected LinkedHashMap<String, ComponentInfo<GenericDao<?, ? extends Serializable>>>    _daoMap;
    protected String                                                    _serverName;
    protected Object                                                    _component;
    protected HashMap<Class<?>, Class<?>>                               _factories;
    protected HashMap<String, ComponentInfo<PluggableService>>          _pluginsMap;

    static {
        if (s_janitor == null) {
            s_janitor = new CleanupThread();
            Runtime.getRuntime().addShutdownHook(new CleanupThread());
        }
    }

    public ComponentLocator(String server) {
        _serverName = server;
        if (s_janitor == null) {
            s_janitor = new CleanupThread();
            Runtime.getRuntime().addShutdownHook(new CleanupThread());
        }
    }

    public String getLocatorName() {
        return _serverName;
    }

    @Override
    public String getName() {
        return getLocatorName();
    }

    protected Pair<XmlHandler, HashMap<String, List<ComponentInfo<Adapter>>>> parse2(String filename) {
        try {
            SAXParserFactory spfactory = SAXParserFactory.newInstance();
            SAXParser saxParser = spfactory.newSAXParser();
            _daoMap = new LinkedHashMap<String, ComponentInfo<GenericDao<?, ? extends Serializable>>>();
            _managerMap = new LinkedHashMap<String, ComponentInfo<Manager>>();
            _checkerMap = new LinkedHashMap<String, ComponentInfo<SystemIntegrityChecker>>();
            _adapterMap = new HashMap<String, Adapters<? extends Adapter>>();
            _factories = new HashMap<Class<?>, Class<?>>();
            _pluginsMap = new LinkedHashMap<String, ComponentInfo<PluggableService>>();
            File file = PropertiesUtil.findConfigFile(filename);
            if (file == null) {
                s_logger.info("Unable to find " + filename);
                return null;
            }
            s_logger.info("Config file found at " + file.getAbsolutePath() + ".  Configuring " + _serverName);
            XmlHandler handler = new XmlHandler(_serverName);
            saxParser.parse(file, handler);

            HashMap<String, List<ComponentInfo<Adapter>>> adapters = new HashMap<String, List<ComponentInfo<Adapter>>>();
            if (handler.parent != null) {
                String[] tokens = handler.parent.split(":");
                String parentFile = filename;
                String parentName = handler.parent;
                if (tokens.length > 1) {
                    parentFile = tokens[0];
                    parentName = tokens[1];
                }
                ComponentLocator parentLocator = new ComponentLocator(parentName);
                adapters.putAll(parentLocator.parse2(parentFile).second());
                _daoMap.putAll(parentLocator._daoMap);
                _managerMap.putAll(parentLocator._managerMap);
                _factories.putAll(parentLocator._factories);
                _pluginsMap.putAll(parentLocator._pluginsMap);
            }

            ComponentLibrary library = null;
            if (handler.library != null) {
                Class<?> clazz = Class.forName(handler.library);
                library = (ComponentLibrary)clazz.newInstance();
                _daoMap.putAll(library.getDaos());
                _managerMap.putAll(library.getManagers());
                adapters.putAll(library.getAdapters());
                _factories.putAll(library.getFactories());
                _pluginsMap.putAll(library.getPluggableServices());
            }

            _daoMap.putAll(handler.daos);
            _managerMap.putAll(handler.managers);
            _checkerMap.putAll(handler.checkers);
            adapters.putAll(handler.adapters);
            _pluginsMap.putAll(handler.pluggableServices);

            return new Pair<XmlHandler, HashMap<String, List<ComponentInfo<Adapter>>>>(handler, adapters);
        } catch (ParserConfigurationException e) {
            s_logger.error("Unable to load " + _serverName + " due to errors while parsing " + filename, e);
            System.exit(1);
        } catch (SAXException e) {
            s_logger.error("Unable to load " + _serverName + " due to errors while parsing " + filename, e);
            System.exit(1);
        } catch (IOException e) {
            s_logger.error("Unable to load " + _serverName + " due to errors while reading from " + filename, e);
            System.exit(1);
        } catch (CloudRuntimeException e) {
            s_logger.error("Unable to load configuration for " + _serverName + " from " + filename, e);
            System.exit(1);
        } catch (Exception e) {
            s_logger.error("Unable to load configuration for " + _serverName + " from " + filename, e);
            System.exit(1);
        }
        return null;
    }

    protected void parse(String filename) {
        Pair<XmlHandler, HashMap<String, List<ComponentInfo<Adapter>>>> result = parse2(filename);
        if (result == null) {
            s_logger.info("Skipping configuration using " + filename);
            return;
        }

        instantiatePluggableServices();

        XmlHandler handler = result.first();
        HashMap<String, List<ComponentInfo<Adapter>>> adapters = result.second();
        try {
            runCheckers();
            startDaos();    // daos should not be using managers and adapters.
            instantiateAdapters(adapters);
            instantiateManagers();
            if (handler.componentClass != null) {
                _component = createInstance(handler.componentClass, true, true);
            }
            configureManagers();
            configureAdapters();
            startManagers();
            startAdapters();
            //TODO do we need to follow the instantiate -> inject -> configure -> start -> stop flow of singletons like managers/adapters?
            //TODO do we need to expose pluggableServices to MBean (provide getNames?)
        } catch (CloudRuntimeException e) {
            s_logger.error("Unable to load configuration for " + _serverName + " from " + filename, e);
            System.exit(1);
        } catch (Exception e) {
            s_logger.error("Unable to load configuration for " + _serverName + " from " + filename, e);
            System.exit(1);
        }
    }

    protected void runCheckers() {
        Set<Map.Entry<String, ComponentInfo<SystemIntegrityChecker>>> entries = _checkerMap.entrySet();
        for (Map.Entry<String, ComponentInfo<SystemIntegrityChecker>> entry : entries) {
            ComponentInfo<SystemIntegrityChecker> info = entry.getValue();
            try {
                info.instance = (SystemIntegrityChecker)createInstance(info.clazz, false, info.singleton);
                info.instance.check();
            } catch (Exception e) {
                s_logger.error("Problems with running checker:" + info.name, e);
                System.exit(1);
            }
        }
    }
    /**
     * Daos should not refer to any other components so it is safe to start them
     * here.
     */
    protected void startDaos()  {
        Set<Map.Entry<String, ComponentInfo<GenericDao<?, ? extends Serializable>>>> entries = _daoMap.entrySet();

        for (Map.Entry<String, ComponentInfo<GenericDao<?, ?>>> entry : entries) {
            ComponentInfo<GenericDao<?, ?>> info = entry.getValue();
            try {
                info.instance = (GenericDao<?, ?>)createInstance(info.clazz, true, info.singleton);
                if (info.singleton) {
                    s_logger.info("Starting singleton DAO: " + info.name);
                    Singleton singleton = s_singletons.get(info.clazz);
                    if (singleton.state == Singleton.State.Instantiated) {
                        inject(info.clazz, info.instance);
                        singleton.state = Singleton.State.Injected;
                    }
                    if (singleton.state == Singleton.State.Injected) {
                        if (!info.instance.configure(info.name, info.params)) {
                            s_logger.error("Unable to configure DAO: " + info.name);
                            System.exit(1);
                        }
                        singleton.state = Singleton.State.Started;
                    }
                } else {
                    s_logger.info("Starting DAO: " + info.name);
                    inject(info.clazz, info.instance);
                    if (!info.instance.configure(info.name, info.params)) {
                        s_logger.error("Unable to configure DAO: " + info.name);
                        System.exit(1);
                    }
                }
            } catch (ConfigurationException e) {
                s_logger.error("Unable to configure DAO: " + info.name, e);
                System.exit(1);
            } catch (Exception e) {
                s_logger.error("Problems while configuring DAO: " + info.name, e);
                System.exit(1);
            }
            if (info.instance instanceof ManagementBean) {
                registerMBean((ManagementBean) info.instance);
            }
        }
    }

    private static Object createInstance(Class<?> clazz, boolean inject, boolean singleton, Object... args) {
        Factory factory = null;
        Singleton entity = null;
        synchronized(s_factories) {
            if (singleton) {
                entity = s_singletons.get(clazz);
                if (entity != null) {
                    s_logger.debug("Found singleton instantiation for " + clazz.toString());
                    return entity.singleton;
                }
            }
            InjectInfo info = s_factories.get(clazz);
            if (info == null) {
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(clazz);
                enhancer.setCallbackFilter(s_callbackFilter);
                enhancer.setCallbacks(s_callbacks);
                factory = (Factory)enhancer.create();
                info = new InjectInfo(enhancer, factory);
                s_factories.put(clazz, info);
            } else {
                factory = info.factory;
            }
        }


        Class<?>[] argTypes = null;
        if (args != null && args.length > 0) {
            Constructor<?>[] constructors = clazz.getConstructors();
            for (Constructor<?> constructor : constructors) {
                Class<?>[] paramTypes = constructor.getParameterTypes();
                if (paramTypes.length == args.length) {
                    boolean found = true;
                    for (int i = 0; i < paramTypes.length; i++) {
                        if (!paramTypes[i].isAssignableFrom(args[i].getClass()) && !paramTypes[i].isPrimitive()) {
                            found = false;
                            break;
                        }
                    }
                    if (found) {
                        argTypes = paramTypes;
                        break;
                    }
                }
            }

            if (argTypes == null) {
                throw new CloudRuntimeException("Unable to find constructor to match parameters given: " + clazz.getName());
            }

            entity = new Singleton(factory.newInstance(argTypes, args, s_callbacks));
        } else {
            entity = new Singleton(factory.newInstance(s_callbacks));
        }

        if (inject) {
            inject(clazz, entity.singleton);
            entity.state = Singleton.State.Injected;
        }

        if (singleton) {
            synchronized(s_factories) {
                s_singletons.put(clazz, entity);
            }
        }

        return entity.singleton;
    }


    protected ComponentInfo<GenericDao<?, ?>> getDao(String name) {
        ComponentInfo<GenericDao<?, ?>> info = _daoMap.get(name);
        if (info == null) {
            throw new CloudRuntimeException("Unable to find DAO " + name);
        }

        return info;
    }

    public static synchronized Object getComponent(String componentName) {
        synchronized(_hasCheckerRun) {
            /* System Integrity checker will run before all components really loaded */
            if (!_hasCheckerRun && !componentName.equalsIgnoreCase(SystemIntegrityChecker.Name)) {
                ComponentLocator.getComponent(SystemIntegrityChecker.Name);
                _hasCheckerRun = true;
            }
        }

        ComponentLocator locator = s_locators.get(componentName);
        if (locator == null) {
            locator = ComponentLocator.getLocator(componentName);
        }
        return locator._component;
    }

    public <T extends GenericDao<?, ? extends Serializable>> T getDao(Class<T> clazz) {
        ComponentInfo<GenericDao<?, ?>> info = getDao(clazz.getName());
        return info != null ? (T)info.instance : null;
    }

    protected void instantiateManagers() {
        Set<Map.Entry<String, ComponentInfo<Manager>>> entries = _managerMap.entrySet();
        for (Map.Entry<String, ComponentInfo<Manager>> entry : entries) {
            ComponentInfo<Manager> info = entry.getValue();
            if (info.instance == null) {
                s_logger.info("Instantiating Manager: " + info.name);
                info.instance = (Manager)createInstance(info.clazz, false, info.singleton);
            }
        }
    }

    protected void configureManagers() {
        Set<Map.Entry<String, ComponentInfo<Manager>>> entries = _managerMap.entrySet();
        for (Map.Entry<String, ComponentInfo<Manager>> entry : entries) {
            ComponentInfo<Manager> info = entry.getValue();
            if (info.singleton) {
                Singleton s = s_singletons.get(info.clazz);
                if (s.state == Singleton.State.Instantiated) {
                    s_logger.debug("Injecting singleton Manager: " + info.name);
                    inject(info.clazz, info.instance);
                    s.state = Singleton.State.Injected;
                }
            } else {
                s_logger.info("Injecting Manager: " + info.name);
                inject(info.clazz, info.instance);
            }
        }
        for (Map.Entry<String, ComponentInfo<Manager>> entry : entries) {
            ComponentInfo<Manager> info = entry.getValue();
            if (info.singleton) {
                Singleton s = s_singletons.get(info.clazz);
                if (s.state == Singleton.State.Injected) {
                    s_logger.info("Configuring singleton Manager: " + info.name);
                    try {
                        info.instance.configure(info.name, info.params);
                    } catch (ConfigurationException e) {
                        s_logger.error("Unable to configure manager: " + info.name, e);
                        System.exit(1);
                    }
                    s.state = Singleton.State.Configured;
                }
            } else {
                s_logger.info("Configuring Manager: " + info.name);
                try {
                    info.instance.configure(info.name, info.params);
                } catch (ConfigurationException e) {
                    s_logger.error("Unable to configure manager: " + info.name, e);
                    System.exit(1);
                }
            }
        }
    }

    protected static void inject(Class<?> clazz, Object entity) {
        ComponentLocator locator = ComponentLocator.getCurrentLocator();

        do {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                Inject inject = field.getAnnotation(Inject.class);
                if (inject == null) {
                    continue;
                }
                Class<?> fc = field.getType();
                Object instance = null;
                if (Manager.class.isAssignableFrom(fc)) {
                    s_logger.trace("Manager: " + fc.getName());
                    instance = locator.getManager(fc);
                } else if (GenericDao.class.isAssignableFrom(fc)) {
                    s_logger.trace("Dao:" + fc.getName());
                    instance = locator.getDao((Class<? extends GenericDao<?, ? extends Serializable>>)fc);
                } else if (Adapters.class.isAssignableFrom(fc)) {
                    s_logger.trace("Adapter" + fc.getName());
                    instance = locator.getAdapters(inject.adapter());
                } else {
                    s_logger.trace("Other:" + fc.getName());
                    instance = locator.getManager(fc);
                }

                if (instance == null) {
                    throw new CloudRuntimeException("Unable to inject " + fc.getSimpleName() + " in " + clazz.getSimpleName());
                }

                try {
                    field.setAccessible(true);
                    field.set(entity, instance);
                } catch (IllegalArgumentException e) {
                    throw new CloudRuntimeException("hmmm....is it really illegal?", e);
                } catch (IllegalAccessException e) {
                    throw new CloudRuntimeException("what! what ! what!", e);
                }
            }
            clazz = clazz.getSuperclass();
        } while (clazz != Object.class && clazz != null);
    }

    protected void startManagers() {
        Set<Map.Entry<String, ComponentInfo<Manager>>> entries = _managerMap.entrySet();
        for (Map.Entry<String, ComponentInfo<Manager>> entry : entries) {
            ComponentInfo<Manager> info = entry.getValue();
            if (info.singleton) {
                Singleton s = s_singletons.get(info.clazz);
                if (s.state == Singleton.State.Configured) {
                    s_logger.info("Starting singleton Manager: " + info.name);
                    if (!info.instance.start()) {
                        throw new CloudRuntimeException("Incorrect Configuration: " + info.name);
                    }
                    if (info.instance instanceof ManagementBean) {
                        registerMBean((ManagementBean) info.instance);
                    }
                    s_logger.info("Started Manager: " + info.name);
                    s.state = Singleton.State.Started;
                }
            } else {
                s_logger.info("Starting Manager: " + info.name);
                if (!info.instance.start()) {
                    throw new CloudRuntimeException("Incorrect Configuration: " + info.name);
                }
                if (info.instance instanceof ManagementBean) {
                    registerMBean((ManagementBean) info.instance);
                }
                s_logger.info("Started Manager: " + info.name);
            }
        }
    }

    protected void registerMBean(ManagementBean mbean) {
        try {
            JmxUtil.registerMBean(mbean);
        } catch (MalformedObjectNameException e) {
            s_logger.warn("Unable to register MBean: " + mbean.getName(), e);
        } catch (InstanceAlreadyExistsException e) {
            s_logger.warn("Unable to register MBean: " + mbean.getName(), e);
        } catch (MBeanRegistrationException e) {
            s_logger.warn("Unable to register MBean: " + mbean.getName(), e);
        } catch (NotCompliantMBeanException e) {
            s_logger.warn("Unable to register MBean: " + mbean.getName(), e);
        }
        s_logger.info("Registered MBean: " + mbean.getName());
    }

    protected ComponentInfo<Manager> getManager(String name) {
        ComponentInfo<Manager> mgr = _managerMap.get(name);
        return mgr;
    }

    public <T> T getManager(Class<T> clazz) {
        ComponentInfo<Manager> info = getManager(clazz.getName());
        if (info == null) {
            return null;
        }
        if (info.instance == null) {
            info.instance = (Manager)createInstance(info.clazz, false, info.singleton);
        }
        return (T)info.instance;
    }

    protected void configureAdapters() {
        for (Adapters<? extends Adapter> adapters : _adapterMap.values()) {
            List<ComponentInfo<Adapter>> infos = adapters._infos;
            for (ComponentInfo<Adapter> info : infos) {
                try {
                    if (info.singleton) {
                        Singleton singleton = s_singletons.get(info.clazz);
                        if (singleton.state == Singleton.State.Instantiated) {
                            s_logger.info("Injecting singleton Adapter: " + info.getName());
                            inject(info.clazz, info.instance);
                            singleton.state = Singleton.State.Injected;
                        }
                        if (singleton.state == Singleton.State.Injected) {
                            s_logger.info("Configuring singleton Adapter: " + info.getName());
                            if (!info.instance.configure(info.name, info.params)) {
                                s_logger.error("Unable to configure adapter: " + info.name);
                                System.exit(1);
                            }
                            singleton.state = Singleton.State.Configured;
                        }
                    } else {
                        s_logger.info("Injecting Adapter: " + info.getName());
                        inject(info.clazz, info.instance);
                        s_logger.info("Configuring singleton Adapter: " + info.getName());
                        if (!info.instance.configure(info.name, info.params)) {
                            s_logger.error("Unable to configure adapter: " + info.name);
                            System.exit(1);
                        }
                    }
                } catch (ConfigurationException e) {
                    s_logger.error("Unable to configure adapter: " + info.name, e);
                    System.exit(1);
                } catch (Exception e) {
                    s_logger.error("Unable to configure adapter: " + info.name, e);
                    System.exit(1);
                }
            }
        }
    }

    protected void populateAdapters(Map<String, List<ComponentInfo<Adapter>>> map) {
        Set<Map.Entry<String, List<ComponentInfo<Adapter>>>> entries = map.entrySet();
        for (Map.Entry<String, List<ComponentInfo<Adapter>>> entry : entries) {
            for (ComponentInfo<Adapter> info : entry.getValue()) {
                s_logger.info("Instantiating Adapter: " + info.name);
                info.instance = (Adapter)createInstance(info.clazz, false, info.singleton);
            }
            Adapters<Adapter> adapters = new Adapters<Adapter>(entry.getKey(), entry.getValue());
            _adapterMap.put(entry.getKey(), adapters);
        }
    }

    protected void instantiateAdapters(Map<String, List<ComponentInfo<Adapter>>> map) {
        Set<Map.Entry<String, List<ComponentInfo<Adapter>>>> entries = map.entrySet();
        for (Map.Entry<String, List<ComponentInfo<Adapter>>> entry : entries) {
            for (ComponentInfo<Adapter> info : entry.getValue()) {
                s_logger.info("Instantiating Adapter: " + info.name);
                info.instance = (Adapter)createInstance(info.clazz, false, info.singleton);
            }
            Adapters<Adapter> adapters = new Adapters<Adapter>(entry.getKey(), entry.getValue());
            _adapterMap.put(entry.getKey(), adapters);
        }
    }

    protected void startAdapters() {
        for (Map.Entry<String, Adapters<? extends Adapter>> entry : _adapterMap.entrySet()) {
            for (ComponentInfo<Adapter> adapter : entry.getValue()._infos) {
                if (adapter.singleton) {
                    Singleton s = s_singletons.get(adapter.clazz);
                    if (s.state == Singleton.State.Configured) {
                        s_logger.info("Starting singleton Adapter: " + adapter.getName());
                        if (!adapter.instance.start()) {
                            throw new CloudRuntimeException("Unable to start adapter: " + adapter.getName());
                        }
                        if (adapter.instance instanceof ManagementBean) {
                            registerMBean((ManagementBean)adapter.instance);
                        }
                        s_logger.info("Started Adapter: " + adapter.instance.getName());
                    }
                    s.state = Singleton.State.Started;
                } else {
                    s_logger.info("Starting Adapter: " + adapter.getName());
                    if (!adapter.instance.start()) {
                        throw new CloudRuntimeException("Unable to start adapter: " + adapter.getName());
                    }
                    if (adapter.instance instanceof ManagementBean) {
                        registerMBean((ManagementBean)adapter.instance);
                    }
                    s_logger.info("Started Adapter: " + adapter.instance.getName());
                }
            }
        }
    }

    protected void instantiatePluggableServices() {
        Set<Map.Entry<String, ComponentInfo<PluggableService>>> entries = _pluginsMap.entrySet();
        for (Map.Entry<String, ComponentInfo<PluggableService>> entry : entries) {
            ComponentInfo<PluggableService> info = entry.getValue();
            if (info.instance == null) {
                s_logger.info("Instantiating PluggableService: " + info.name);
                info.instance = (PluggableService)createInstance(info.clazz, false, info.singleton);

                if (info.instance instanceof Plugin) {
                    Plugin plugin = (Plugin)info.instance;

                    ComponentLibrary lib = plugin.getComponentLibrary();
                    _managerMap.putAll(lib.getManagers());
                    _daoMap.putAll(lib.getDaos());
                }
            }
        }
    }

    protected ComponentInfo<PluggableService> getPluggableService(String name) {
        ComponentInfo<PluggableService> mgr = _pluginsMap.get(name);
        return mgr;
    }

    public <T> T getPluggableService(Class<T> clazz) {
        ComponentInfo<PluggableService> info = getPluggableService(clazz.getName());
        if (info == null) {
            return null;
        }
        if (info.instance == null) {
            info.instance = (PluggableService)createInstance(info.clazz, false, info.singleton);
        }
        return (T)info.instance;
    }

    public <T> List<T> getAllPluggableServices() {
        List<T> services = new ArrayList<T>();
        Set<Map.Entry<String, ComponentInfo<PluggableService>>> entries = _pluginsMap.entrySet();
        for (Map.Entry<String, ComponentInfo<PluggableService>> entry : entries) {
            ComponentInfo<PluggableService> info = entry.getValue();
            if (info.instance == null) {
                s_logger.info("Instantiating PluggableService: " + info.name);
                info.instance = (PluggableService)createInstance(info.clazz, false, info.singleton);
            }
            services.add((T) info.instance);
        }
        return services;
    }

    public static <T> T inject(Class<T> clazz) {
        return (T)createInstance(clazz, true, false);
    }

    public <T> T createInstance(Class<T> clazz) {
        Class<? extends T> impl = (Class<? extends T>)_factories.get(clazz);
        if (impl == null) {
            throw new CloudRuntimeException("Unable to find a factory for " + clazz);
        }
        return inject(impl);
    }

    public static <T> T inject(Class<T> clazz, Object... args) {
        return (T)createInstance(clazz, true, false, args);
    }

    @Override
    public Map<String, List<String>> getAdapterNames() {
        HashMap<String, List<String>> result = new HashMap<String, List<String>>();
        for (Map.Entry<String, Adapters<? extends Adapter>> entry : _adapterMap.entrySet()) {
            Adapters<? extends Adapter> adapters = entry.getValue();
            Enumeration<? extends Adapter> en = adapters.enumeration();
            List<String> lst = new ArrayList<String>();
            while (en.hasMoreElements()) {
                Adapter adapter = en.nextElement();
                lst.add(adapter.getName() + "-" + adapter.getClass().getName());
            }
            result.put(entry.getKey(), lst);
        }
        return result;
    }

    public Map<String, List<String>> getAllAccessibleAdapters() {
        Map<String, List<String>> parentResults = new HashMap<String, List<String>>();
        Map<String, List<String>> results = getAdapterNames();
        parentResults.putAll(results);
        return parentResults;
    }

    @Override
    public Collection<String> getManagerNames() {
        Collection<String> names = new HashSet<String>();
        for (Map.Entry<String, ComponentInfo<Manager>> entry : _managerMap.entrySet()) {
            names.add(entry.getValue().name);
        }
        return names;
    }

    @Override
    public Collection<String> getDaoNames() {
        Collection<String> names = new HashSet<String>();
        for (Map.Entry<String, ComponentInfo<GenericDao<?, ?>>> entry : _daoMap.entrySet()) {
            names.add(entry.getValue().name);
        }
        return names;
    }

    public <T extends Adapter> Adapters<T> getAdapters(Class<T> clazz) {
        return (Adapters<T>)getAdapters(clazz.getName());
    }

    public Adapters<? extends Adapter> getAdapters(String key) {
        Adapters<? extends Adapter> adapters = _adapterMap.get(key);
        if (adapters != null) {
            return adapters;
        }
        return new Adapters<Adapter>(key, new ArrayList<ComponentInfo<Adapter>>());
    }

    protected void resetInterceptors(InterceptorLibrary library) {
        library.addInterceptors(s_interceptors);
        if (s_interceptors.size() > 0) {
            s_callbacks = new Callback[s_interceptors.size() + 2];
            int i = 0;
            s_callbacks[i++] = NoOp.INSTANCE;
            s_callbacks[i++] = new InterceptorDispatcher();
            for (AnnotationInterceptor<?> interceptor : s_interceptors) {
                s_callbacks[i++] = interceptor.getCallback();
            }
            s_callbackFilter = new InterceptorFilter();
        }
    }

    protected static ComponentLocator getLocatorInternal(String server, boolean setInThreadLocal, String configFileName, String log4jFilename) {
        synchronized(s_once) {
            if (!s_once) {
                File file = PropertiesUtil.findConfigFile(log4jFilename + ".xml");
                if (file != null) {
                    s_logger.info("log4j configuration found at " + file.getAbsolutePath());
                    DOMConfigurator.configureAndWatch(file.getAbsolutePath());
                } else {
                    file = PropertiesUtil.findConfigFile(log4jFilename + ".properties");
                    if (file != null) {
                        s_logger.info("log4j configuration found at " + file.getAbsolutePath());
                        PropertyConfigurator.configureAndWatch(file.getAbsolutePath());
                    }
                }
                s_once = true;
            }
        }

        ComponentLocator locator;
        synchronized (s_locators) {
            locator = s_locators.get(server);
            if (locator == null) {
                locator = new ComponentLocator(server);
                s_locators.put(server, locator);
                if (setInThreadLocal) {
                    s_tl.set(locator);
                }
                locator.parse(configFileName);
            } else {
                if (setInThreadLocal) {
                    s_tl.set(locator);
                }
            }
        }

        return locator;
    }

    public static ComponentLocator getLocator(String server, String configFileName, String log4jFilename) {
        return getLocatorInternal(server, true, configFileName, log4jFilename);
    }

    public static ComponentLocator getLocator(String server) {
        String configFile = null;
        try {
            final File propsFile = PropertiesUtil.findConfigFile("environment.properties");
            if (propsFile == null) {
                s_logger.debug("environment.properties could not be opened");
            } else {
                final FileInputStream finputstream = new FileInputStream(propsFile);
                final Properties props = new Properties();
                props.load(finputstream);
                finputstream.close();
                configFile = props.getProperty("cloud-stack-components-specification");
            }
        } catch (IOException e) {
            s_logger.debug("environment.properties could not be loaded:" + e.toString());
        }

        if (configFile == null || PropertiesUtil.findConfigFile(configFile) == null) {
            configFile = "components.xml";
            if (PropertiesUtil.findConfigFile(configFile) == null){
                s_logger.debug("Can not find components.xml");
            }
        }
        return getLocatorInternal(server, true, configFile, "log4j-cloud");
    }

    public static ComponentLocator getCurrentLocator() {
        return s_tl.get();
    }

    public static class ComponentInfo<T> {
        Class<?>                clazz;
        HashMap<String, Object> params = new HashMap<String, Object>();
        String                  name;
        List<String>            keys = new ArrayList<String>();
        T                       instance;
        boolean                 singleton = true;

        protected ComponentInfo() {
        }

        public List<String> getKeys() {
            return keys;
        }

        public String getName() {
            return name;
        }

        public ComponentInfo(String name, Class<? extends T> clazz) {
            this(name, clazz, new ArrayList<Pair<String, Object>>(0));
        }

        public ComponentInfo(String name, Class<? extends T> clazz, T instance) {
            this(name, clazz);
            this.instance = instance;
        }

        public ComponentInfo(String name, Class<? extends T> clazz, List<Pair<String, Object>> params) {
            this(name, clazz, params, true);
        }

        public ComponentInfo(String name, Class<? extends T> clazz, List<Pair<String, Object>> params, boolean singleton) {
            this.name = name;
            this.clazz = clazz;
            this.singleton = singleton;
            for (Pair<String, Object> param : params) {
                this.params.put(param.first(), param.second());
            }
            fillInfo();
        }

        protected void fillInfo() {
            String clazzName = clazz.getName();

            Local local = clazz.getAnnotation(Local.class);
            if (local == null) {
                throw new CloudRuntimeException("Unable to find Local annotation for class " + clazzName);
            }

            // Verify that all interfaces specified in the Local annotation is implemented by the class.
            Class<?>[] classes = local.value();
            for (int i = 0; i < classes.length; i++) {
                if (!classes[i].isInterface()) {
                    throw new CloudRuntimeException(classes[i].getName() + " is not an interface");
                }
                if (classes[i].isAssignableFrom(clazz)) {
                    keys.add(classes[i].getName());
                    s_logger.info("Found component: " + classes[i].getName() + " in " + clazzName + " - " + name);
                } else {
                    throw new CloudRuntimeException(classes[i].getName() + " is not implemented by " + clazzName);
                }
            }
        }

        public void addParameter(String name, String value) {
            params.put(name, value);
        }
    }

    /**
     * XmlHandler is used by AdapterManager to handle the SAX parser callbacks.
     * It builds a hash map of lists of adapters and a hash map of managers.
     **/
    protected class XmlHandler extends DefaultHandler {
        public HashMap<String, List<ComponentInfo<Adapter>>>    adapters;
        public HashMap<String, ComponentInfo<Manager>>          managers;
        public LinkedHashMap<String, ComponentInfo<SystemIntegrityChecker>> checkers;
        public LinkedHashMap<String, ComponentInfo<GenericDao<?, ?>>> daos;
        public HashMap<String, ComponentInfo<PluggableService>>    pluggableServices;
        public String                                  parent;
        public String                                  library;

        List<ComponentInfo<Adapter>>                            lst;
        String                                         paramName;
        StringBuilder                                  value;
        String                                         serverName;
        boolean                                        parse;
        ComponentInfo<?>                               currentInfo;
        Class<?>                                       componentClass;

        public XmlHandler(String serverName) {
            this.serverName = serverName;
            parse = false;
            adapters = new HashMap<String, List<ComponentInfo<Adapter>>>();
            managers = new HashMap<String, ComponentInfo<Manager>>();
            checkers = new LinkedHashMap<String, ComponentInfo<SystemIntegrityChecker>>();
            daos = new LinkedHashMap<String, ComponentInfo<GenericDao<?, ?>>>();
            pluggableServices = new HashMap<String, ComponentInfo<PluggableService>>();
            value = null;
            parent = null;
        }

        protected void fillInfo(Attributes atts, Class<?> interphace, ComponentInfo<?> info) {
            String clazzName = getAttribute(atts, "class");
            if (clazzName == null) {
                throw new CloudRuntimeException("Missing class attribute for " + interphace.getName());
            }
            info.name = getAttribute(atts, "name");
            if (info.name == null) {
                throw new CloudRuntimeException("Missing name attribute for " + interphace.getName());
            }
            s_logger.debug("Looking for class " + clazzName);
            try {
                info.clazz = Class.forName(clazzName);
            } catch (ClassNotFoundException e) {
                throw new CloudRuntimeException("Unable to find class: " + clazzName);
            } catch (Throwable e) {
                throw new CloudRuntimeException("Caught throwable: ", e);
            }

            if (!interphace.isAssignableFrom(info.clazz)) {
                throw new CloudRuntimeException("Class " + info.clazz.toString() + " does not implment " + interphace);
            }
            String singleton = getAttribute(atts, "singleton");
            if (singleton != null) {
                info.singleton = Boolean.parseBoolean(singleton);
            }

            info.fillInfo();
        }

        @Override
        public void startElement(String namespaceURI, String localName, String qName, Attributes atts)
                throws SAXException {
            if (qName.equals("interceptor") && s_interceptors.size() == 0) {
                synchronized(s_interceptors){
                    if (s_interceptors.size() == 0) {
                        String libraryName = getAttribute(atts, "library");
                        try {
                            Class<?> libraryClazz = Class.forName(libraryName);
                            InterceptorLibrary library = (InterceptorLibrary)libraryClazz.newInstance();
                            resetInterceptors(library);
                        } catch (ClassNotFoundException e) {
                            throw new CloudRuntimeException("Unable to find " + libraryName, e);
                        } catch (InstantiationException e) {
                            throw new CloudRuntimeException("Unable to instantiate " + libraryName, e);
                        } catch (IllegalAccessException e) {
                            throw new CloudRuntimeException("Illegal access " + libraryName, e);
                        }
                    }
                }
            }
            if (!parse) {
                if (qName.equals(_serverName)) {
                    parse = true;
                    parent = getAttribute(atts, "extends");
                    String implementationClass = getAttribute(atts, "class");
                    if (implementationClass != null) {
                        try {
                            componentClass = Class.forName(implementationClass);
                        } catch (ClassNotFoundException e) {
                            throw new CloudRuntimeException("Unable to find " + implementationClass, e);
                        }
                    }

                    library = getAttribute(atts, "library");
                }
            } else if (qName.equals("adapters")) {
                lst = new ArrayList<ComponentInfo<Adapter>>();
                String key = getAttribute(atts, "key");
                if (key == null) {
                    throw new CloudRuntimeException("Missing key attribute for adapters");
                }
                adapters.put(key, lst);
            } else if (qName.equals("adapter")) {
                ComponentInfo<Adapter> info = new ComponentInfo<Adapter>();
                fillInfo(atts, Adapter.class, info);
                lst.add(info);
                currentInfo = info;
            } else if (qName.equals("manager")) {
                ComponentInfo<Manager> info = new ComponentInfo<Manager>();
                fillInfo(atts, Manager.class, info);
                s_logger.info("Adding Manager: " + info.name);
                for (String key : info.keys) {
                    s_logger.info("Linking " + key + " to " + info.name);
                    managers.put(key, info);
                }
                currentInfo = info;
            } else if (qName.equals("param")) {
                paramName = getAttribute(atts, "name");
                value = new StringBuilder();
            } else if (qName.equals("dao")) {
                ComponentInfo<GenericDao<?, ?>> info = new ComponentInfo<GenericDao<?, ?>>();
                fillInfo(atts, GenericDao.class, info);
                for (String key : info.keys) {
                    daos.put(key, info);
                }
                currentInfo = info;
            } else if (qName.equals("checker")) {
                ComponentInfo<SystemIntegrityChecker> info = new ComponentInfo<SystemIntegrityChecker>();
                fillInfo(atts, SystemIntegrityChecker.class, info);
                checkers.put(info.name, info);
                s_logger.info("Adding system integrity checker: " + info.name);
                currentInfo = info;
            } else if (qName.equals("pluggableservice") || qName.equals("plugin")) {
                ComponentInfo<PluggableService> info = new ComponentInfo<PluggableService>();
                fillInfo(atts, PluggableService.class, info);
                s_logger.info("Adding PluggableService: " + info.name);
                String key = getAttribute(atts, "key");
                if (key == null) {
                    throw new CloudRuntimeException("Missing key attribute for pluggableservice: "+info.name);
                }               
                s_logger.info("Linking " + key + " to " + info.name);
                pluggableServices.put(key, info);
                currentInfo = info;
            } else {
                // ignore
            }
        }

        protected String getAttribute(Attributes atts, String name) {
            for (int att = 0; att < atts.getLength(); att++) {
                String attName = atts.getQName(att);
                if (attName.equals(name)) {
                    return atts.getValue(att);
                }
            }
            return null;
        }

        @Override
        public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
            if (!parse) {
                return;
            }

            if (qName.equals(_serverName)) {
                parse = false;
            } else if (qName.equals("adapters")) {
            } else if (qName.equals("adapter")) {
            } else if (qName.equals("manager")) {
            } else if (qName.equals("dao")) {
            } else if (qName.equals("pluggableservice")) {
            } else if (qName.equals("param")) {
                currentInfo.params.put(paramName, value.toString());
                paramName = null;
                value = null;
            } else {
                // ignore
            }
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            if (parse && value != null) {
                value.append(ch, start, length);
            }
        }
    }

    protected static class InjectInfo {
        public Factory factory;
        public Enhancer enhancer;

        public InjectInfo(Enhancer enhancer, Factory factory) {
            this.factory = factory;
            this.enhancer = enhancer;
        }
    }

    protected static class CleanupThread extends Thread {
        @Override
        public void run() {
            synchronized (CleanupThread.class) {
                for (ComponentLocator locator : s_locators.values()) {
                    Iterator<Adapters<? extends Adapter>> itAdapters = locator._adapterMap.values().iterator();
                    while (itAdapters.hasNext()) {
                        Adapters<? extends Adapter> adapters = itAdapters.next();
                        itAdapters.remove();
                        for (ComponentInfo<Adapter> adapter : adapters._infos) {
                            if (adapter.singleton) {
                                Singleton singleton = s_singletons.get(adapter.clazz);
                                if (singleton.state == Singleton.State.Started) {
                                    s_logger.info("Asking " + adapter.getName() + " to shutdown.");
                                    adapter.instance.stop();
                                    singleton.state = Singleton.State.Stopped;
                                } else {
                                    s_logger.debug("Skippng " + adapter.getName() + " because it has already stopped");
                                }
                            } else {
                                s_logger.info("Asking " + adapter.getName() + " to shutdown.");
                                adapter.instance.stop();
                            }
                        }
                    }
                }

                for (ComponentLocator locator : s_locators.values()) {
                    Iterator<ComponentInfo<Manager>> itManagers = locator._managerMap.values().iterator();
                    while (itManagers.hasNext()) {
                        ComponentInfo<Manager> manager = itManagers.next();
                        itManagers.remove();
                        if (manager.singleton == true) {
                            Singleton singleton = s_singletons.get(manager.clazz);
                            if (singleton != null && singleton.state == Singleton.State.Started) {
                                s_logger.info("Asking Manager " + manager.getName() + " to shutdown.");
                                manager.instance.stop();
                                singleton.state = Singleton.State.Stopped;
                            } else {
                                s_logger.info("Skipping Manager " + manager.getName() + " because it is not in a state to shutdown.");
                            }
                        }
                    }
                }
            }
        }
    }

    static class Singleton {
        public enum State {
            Instantiated,
            Injected,
            Configured,
            Started,
            Stopped
        }

        public Object singleton;
        public State state;

        public Singleton(Object singleton) {
            this.singleton = singleton;
            this.state = State.Instantiated;
        }
    }

    protected class InterceptorDispatcher implements MethodInterceptor {

        @Override
        public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            ArrayList<Pair<AnnotationInterceptor<Object>, Object>> interceptors = new ArrayList<Pair<AnnotationInterceptor<Object>, Object>>();
            for (AnnotationInterceptor<?> interceptor : s_interceptors) {
                if (interceptor.needToIntercept(method)) {
                    Object obj = interceptor.interceptStart(method);
                    interceptors.add(new Pair<AnnotationInterceptor<Object>, Object>((AnnotationInterceptor<Object>)interceptor, obj));
                }
            }
            boolean success = false;
            try {
                Object obj = methodProxy.invokeSuper(object, args);
                success = true;
                return obj;
            } finally {
                for (Pair<AnnotationInterceptor<Object>, Object> interceptor : interceptors) {
                    if (success) {
                        interceptor.first().interceptComplete(method, interceptor.second());
                    } else {
                        interceptor.first().interceptException(method, interceptor.second());
                    }
                }
            }
        }
    }

    protected static class InterceptorFilter implements CallbackFilter {
        @Override
        public int accept(Method method) {
            int index = 0;
            for (int i = 2; i < s_callbacks.length; i++) {
                AnnotationInterceptor<?> interceptor = (AnnotationInterceptor<?>)s_callbacks[i];
                if (interceptor.needToIntercept(method)) {
                    if (index == 0) {
                        index = i;
                    } else {
                        return 1;
                    }
                }
            }

            return index;
        }
    }
}
TOP

Related Classes of com.cloud.utils.component.ComponentLocator$InjectInfo

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.