Package de.innovationgate.wga.modules

Source Code of de.innovationgate.wga.modules.ModuleRegistry

/*******************************************************************************
* Copyright 2009, 2010 Innovation Gate GmbH
*
* 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
*
*   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 de.innovationgate.wga.modules;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.net.URL;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.log4j.Logger;

import de.innovationgate.utils.UIDGenerator;
import de.innovationgate.wga.config.ConfigBean;
import de.innovationgate.wga.config.WGAConfiguration;
import de.innovationgate.wga.modules.options.OptionCategoryDefinition;
import de.innovationgate.wga.modules.options.OptionDefinition;
import de.innovationgate.wga.modules.options.ValidationContext;

/**
* A general purpose module registry for WGA
*
* The capabilities are:
* <ul>
* <li> Offers a queryable registration for {@link ModuleDefinition} objects, which are mapped by their module type and their implementation class. There can be exactly one module definition for a combination of module type and implementation class
* <li> The amount of module types to use is open and can be dynamically extended by defining new implementations of {@link ModuleType}
* <li> The registry is able to inject itself into {@link ModuleDefinition} instances or even module implementations themselves when they implement {@link RegistryAwareModuleDefinition} and {@link RegistryAwareModule} respectively. So those module (definitions) can use the registry to do further lookups.
* <li> The registry can also register some arbitrary "context objects", that are needed by registry aware module (definitions) to work. This is a map that is retrievable via {@link #getContextObjects()}
* <li> The registry can also register "Option Category Definitions" implemented by {@link OptionCategoryDefinition} to provide titles and descriptions for the category names that option definitions provide via {@link OptionDefinition#getCategory()}. There can be exactly one option category for a combination of category name and module type.
* </ul>
*
*/
public class ModuleRegistry {
   
    /**
     * The name of the text file resource which should contain a full qualified name of a {@link ModuleRegistrar} implementation
     */
    public static final String MODULEREGISTRATORS_RESOURCE = "de/innovationgate/wga/modules/registrar.cfg";

    private static final String KEYBASED_MODULE_PREFIX = "##KEYREGISTRATION-";
   
    /**
     * Module definitions map, storing {@link ModuleDefinition} instances in a two-step String hierarchy
     * - Outer string is the class name of a {@link ModuleType}
     * - Inner string is the class name of a module implementation class or of the mapped module definition class itself (if there is no implementation class)
     */
    private Map<String, Map<String,ModuleDefinition>> _moduleDefinitions = new ConcurrentHashMap<String, Map<String,ModuleDefinition>>();

    /**
     * Module definitions map for key based module definitions, storing {@link ModuleDefinition} instances in a two-step String hierarchy
     * - Outer string is the class name of a {@link ModuleType}
     * - Inner string is the key of the a module definition
     */
    private Map<String, Map<String,ModuleDefinition>> _keyedModuleDefinitions = new ConcurrentHashMap<String, Map<String,ModuleDefinition>>();
   
   
    /**
     * Category definitions map, storing {@link OptionCategoryDefinition} instances
     * - Key string is the class name of the {@link ModuleType} to whose module options the category applies
     */
    private Map<String, Map<String,OptionCategoryDefinition>> _optionCategoryDefinitions = new ConcurrentHashMap<String, Map<String,OptionCategoryDefinition>>();
   
    /**
     * Modules can register arbitrary context objects for registration operations here
     */
    private Map<Class, Object> _contextObjects = new ConcurrentHashMap<Class, Object>();

    private Logger _log = Logger.getLogger("wga.modules");
    private ClassLoader _classLoader = this.getClass().getClassLoader();
   
    /**
     * Add a module definition to the registry.
     * If the definition implements {@link RegistryAwareModuleDefinition}, this also injects a registry reference via method {@link RegistryAwareModuleDefinition#injectRegistry(ModuleRegistry)}
     */
    public synchronized void addModuleDefinition(ModuleDefinition modDef) {
       
        if (modDef instanceof KeyBasedModuleDefinition) {
            KeyBasedModuleDefinition keyModDef = (KeyBasedModuleDefinition) modDef;
            Map<String,ModuleDefinition> keyedModules = getKeyedModulesForType(modDef.getModuleType());
            keyedModules.put(keyModDef.getRegistrationKey(), modDef);
        }
       
        Map<String,ModuleDefinition> modules = getModulesForType(modDef.getModuleType());
        ModuleDefinition oldDefinition = null;
        if (modDef.getImplementationClass() != null) {
            oldDefinition = modules.put(modDef.getImplementationClass().getName(), modDef);
        }
       
        // On key based modules without impl class also use the key to map, mainly so that re-registrations overwrite the previous ones
        else if (modDef instanceof KeyBasedModuleDefinition) {
            KeyBasedModuleDefinition keyModDef = (KeyBasedModuleDefinition) modDef;
            oldDefinition = modules.put(keyModDef.getRegistrationKey(), modDef);
        }
       
        if (modDef instanceof RegistryAwareModuleDefinition) {
            ((RegistryAwareModuleDefinition) modDef).injectRegistry(this);
        }
       
        if (oldDefinition == null) {
            try {
                _log.debug("Registering " + modDef.getModuleType().newInstance().getTitle() + ": " + modDef.getTitle(Locale.getDefault()));
            }
            catch (Exception e) {
            }
        }
    }
   
    /**
     * Adds an option category definition
     */
    public synchronized void addOptionCategoryDefinition(OptionCategoryDefinition catDef) {
       
        Map<String,OptionCategoryDefinition> cats =  getOptionCategoriesForType(catDef.getModuleType());
        cats.put(catDef.getKey(), catDef);
       
    }

    /**
     * Returns the {@link Map} that stores all option category definitions for a given module type.
     * Modifying this map also modifies the registration!
     * @param moduleType The type of module for whom the registered option categories are retrieved
     * @return The map. The keys are option category names. The values are the {@link OptionCategoryDefinition}s themselves
     */
    public Map<String, OptionCategoryDefinition> getOptionCategoriesForType(Class<? extends ModuleType> moduleType) {
        Map<String, OptionCategoryDefinition> cats = _optionCategoryDefinitions.get(moduleType.getName());
        if (cats == null) {
            cats = new ConcurrentHashMap<String,OptionCategoryDefinition>();
            _optionCategoryDefinitions.put(moduleType.getName(), cats);
        }
        return cats;
    }
   
    /**
     * Returns an option category definition
     * @param moduleType The module type for whom the category was registered
     * @param key The category name as returned by option definitions via {@link OptionDefinition#getCategory()}
     * @return The option category definition or null if nothing is registered
     */
    public OptionCategoryDefinition getOptionCategoryDefinition(Class<? extends ModuleType> moduleType, String key) {
       
        Map<String, OptionCategoryDefinition> cats = getOptionCategoriesForType(moduleType);
        return cats.get(key);
       
    }

    /**
     * Returns the {@link Map} that stores all module definitions of a given type.
     * Modifying this map also modifies the registration!
     * @param moduleType The type of module
     * @return The map. The keys are the full qualified names of implementation classes or {@link #KEYBASED_MODULE_PREFIX} plus key for {@link KeyBasedModuleDefinition} instantations. The values are the {@link ModuleDefinition}s themselves
     */
    public synchronized Map<String,ModuleDefinition> getModulesForType(Class<? extends ModuleType> moduleType) {
        Map<String, ModuleDefinition> modules = _moduleDefinitions.get(moduleType.getName());
        if (modules == null) {
            modules = new LinkedHashMap<String,ModuleDefinition>();
            _moduleDefinitions.put(moduleType.getName(), modules);
        }
        return modules;
       
    }
   
    public synchronized Map<String,ModuleDefinition> getModulesForType(String moduleTypeClassName) throws ClassNotFoundException {
        Class moduleTypeClass = loadClass(moduleTypeClassName);
        return getModulesForType(moduleTypeClass);
    }
   
    private synchronized Map<String,ModuleDefinition> getKeyedModulesForType(Class<? extends ModuleType> moduleType) {
        Map<String, ModuleDefinition> modules = _keyedModuleDefinitions.get(moduleType.getName());
        if (modules == null) {
            modules = new LinkedHashMap<String,ModuleDefinition>();
            _keyedModuleDefinitions.put(moduleType.getName(), modules);
        }
        return modules;
       
    }
   
    /**
     * Returns a module definition for a given module type and implementation class
     * @param modType The full qualified class name of the module type
     * @param className The full qualified class name of the implementation class
     * @return The definiton or null if there is nothing registered
     */
    @SuppressWarnings("unchecked")
    public ModuleDefinition getModuleDefinition(String modType, String className) throws ClassNotFoundException {
        Class moduleTypeClass = loadClass(modType);
        return getModuleDefinition(moduleTypeClass, className);
    }

    private Class<?> loadClass(String className) throws ClassNotFoundException {
        return _classLoader.loadClass(className);
    }
   
    /**
     * Returns a module definition for a givne module type and implementation class
     * @param moduleType The module type
     * @param className the full qualified name of the implementation class
     * @return The definiton or null if there is nothing registered
     */
    public ModuleDefinition getModuleDefinition(Class<? extends ModuleType> moduleType, String className) {
        Map<String,ModuleDefinition> modules = getModulesForType(moduleType);
        return modules.get(className);
    }
   
    /**
     * Tries to retrieve a {@link KeyBasedModuleDefinition} from registry via it's key
     * @param moduleType The type of module
     * @param regKey The registration key that the module definition returns via {@link KeyBasedModuleDefinition#getRegistrationKey()}
     * @return The module definition or null if nothing is registered
     */
    public ModuleDefinition getModuleDefinitionByKey(Class<? extends ModuleType> moduleType, String regKey) {
        Map<String,ModuleDefinition> modules = getKeyedModulesForType(moduleType);
        return modules.get(regKey);
    }
   
    /**
     * Returns a module definition for a givne module type and implementation class
     * @param moduleType The module type
     * @param implClass The implementation class
     * @return The definiton or null if there is nothing registered
     */
    public ModuleDefinition getModuleDefinition(Class<? extends ModuleType> moduleType, Class implClass) {
        return getModuleDefinition(moduleType, implClass.getName());
    }
   
    /**
     * Triggers the search for {@link ModuleRegistrar}s on classpath.
     * The method searches for all known text file resources of name {@value #MODULEREGISTRATORS_RESOURCE}.
     * These files should contain the full qualified name of a {@link ModuleRegistrar} implementation which then is instantiated.
     * The method {@link ModuleRegistrar#registerModules(ModuleRegistry)} is called so it can register it's known module definitions.
     * @throws IOException
     */
    public synchronized void searchModuleDefinitions() throws IOException {
       
        Enumeration<URL> registratorResources = _classLoader.getResources(MODULEREGISTRATORS_RESOURCE);
        while (registratorResources.hasMoreElements()) {
            URL url = (URL) registratorResources.nextElement();
            _log.debug("Processing module registrar at URL " + url.toString());
            LineNumberReader reader = new LineNumberReader(new InputStreamReader(url.openStream(), "UTF-8"));
            String line;
            while ((line = reader.readLine()) != null) {
                String className = line.trim();
                try {
                    Class<ModuleRegistrar> registratorClass = (Class<ModuleRegistrar>) loadClass(className);
                    registratorClass.newInstance().registerModules(this);
                }
                catch (Throwable e) {
                    _log.error("Exception using module registrator " + className + " from registrator resource " + url.toString(), e);
                }
            }
            reader.close();
        }
    }

    /**
     * Sets the class loader that is used to search for {@link ModuleRegistrar}s
     */
    public void setClassLoader(ClassLoader classLoader) {
        _classLoader = classLoader;
    }
   

   
    /**
     * Creates a validation context for option validations
     */
    public ValidationContext createValidationContext(WGAConfiguration configCopy) {
        return new ValidationContext(this, configCopy);
    }
   
   

    /**
     * Returns a map of arbitrary context objects that may be needed for registry aware {@link ModuleDefinition} implementations or modules themselves to work
     */
    public Map<Class, Object> getContextObjects() {
        return _contextObjects;
    }
   
    /**
     * Method to create a module instantiation.
     * While modules are obliged to have a default constructor and therefor could be directly instantiated, module instantiation functions SHOULD use this method
     * (or {@link #instantiate(Class)} to create module instances, because it is capable of injecting the registry to {@link RegistryAwareModule}s.
     * @param def The definition of the module
     * @return The module instance
     * @throws ModuleInstantiationException
     */
    public Object instantiate(ModuleDefinition def) throws ModuleInstantiationException {
       
        if (def.getImplementationClass() == null) {
            throw new ModuleInstantiationException("The module definition does not offer an implementation class");
        }
       
        Class<? extends Object> moduleClass = def.getImplementationClass();
        return instantiate(moduleClass);
    }

    /**
     * Method to create a module instantiation.
     * While modules are obliged to have a default constructor and therefor could be directly instantiated, module instantiation functions SHOULD use this method
     * (or {@link #instantiate(ModuleDefinition)} to create module instances, because it is capable of injecting the registry to {@link RegistryAwareModule}s.
     * @param moduleClass The implementation class of the module
     * @return The module instance
     * @throws ModuleInstantiationException
     */
    public Object instantiate(Class<? extends Object> moduleClass) throws ModuleInstantiationException {
        try {
            Object obj = moduleClass.newInstance();
            if (obj instanceof RegistryAwareModule) {
                ((RegistryAwareModule) obj).injectRegistry(this);
            }
            return obj;
        }
        catch (Throwable e) {
            throw new ModuleInstantiationException("Exception instantiating module " + moduleClass, e);
        }
    }

    public ClassLoader getClassLoader() {
        return _classLoader;
    }
   
}
TOP

Related Classes of de.innovationgate.wga.modules.ModuleRegistry

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.