Package org.impalaframework.module.definition

Source Code of org.impalaframework.module.definition.DependencyManager

/*
* Copyright 2007-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* 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 org.impalaframework.module.definition;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.impalaframework.exception.InvalidStateException;
import org.impalaframework.graph.CyclicDependencyException;
import org.impalaframework.graph.GraphHelper;
import org.impalaframework.graph.Vertex;
import org.impalaframework.module.Freezable;
import org.impalaframework.module.ModuleDefinition;
import org.impalaframework.module.RootModuleDefinition;
import org.springframework.util.Assert;

/**
* Class with responsibility for identifying dependencies as well as dependents
* (modules which depend on the modules concerned). Also responsible for
* ensuring that these dependencies are correctly sorted according to correct
* module load order, so that modules can be loaded and unloaded in the correct
* sequence, and so that each module's class loader graph can be built
* correctly.
*
* @author Phil Zoio
*/
public class DependencyManager implements Freezable {

    private static final Log logger = LogFactory.getLog(DependencyManager.class);

    private Map<String, Vertex> vertexMap = new LinkedHashMap<String, Vertex>();
    private Map<String, Set<Vertex>> dependents = new LinkedHashMap<String, Set<Vertex>>();
    private List<Vertex> sorted;
    private boolean frozen;

    public DependencyManager(List<ModuleDefinition> definitions) {
        super();
        this.buildVertexMap(definitions);
        freeze();
    }
   
    public DependencyManager(RootModuleDefinition rootDefinition) {
        super();
       
        Assert.notNull(rootDefinition, "rootDefintion cannot be null");
       
        List<ModuleDefinition> definitions = new ArrayList<ModuleDefinition>();
        definitions.add(rootDefinition);
        definitions.addAll(rootDefinition.getSiblings());
       
        this.buildVertexMap(definitions);
       
        if (logger.isDebugEnabled()) {
            logger.debug("Vertices after build dependency registry");
            dump();
        }
        freeze();
    }

    /* ********************* Methods to sort dependencies  ********************* */
   
    /**
     * Sorts in <i>reverse</i> order the collection of module definitions. Order determined by the
     * topological sort order of all {@link ModuleDefinition}s known to this {@link DependencyManager} instance.
     * @see #sort(Collection)
     */
    public List<ModuleDefinition> reverseSort(Collection<ModuleDefinition> sortable) {
       
        Assert.notNull(sortable, "sortable cannot be null");
       
        final List<ModuleDefinition> sorted = doSort(sortable);
        Collections.reverse(sorted);
       
        if (logger.isDebugEnabled()) {
            logger.debug("Reverse sorted module defintions");
            logger.debug("Before: " + ModuleDefinitionUtils.getModuleNamesFromCollection(sortable));
            logger.debug("After: " + ModuleDefinitionUtils.getModuleNamesFromCollection(sorted));
        }
       
        return sorted;
    }
   
    /**
     * Sorts in order the collection of module definitions. Order determined by the
     * topological sort order of all {@link ModuleDefinition}s known to this {@link DependencyManager} instance.
     * @see #reverseSort(Collection)
     */
    public List<ModuleDefinition> sort(Collection<ModuleDefinition> sortable) {

        Assert.notNull(sortable, "sortable cannot be null");
       
        List<ModuleDefinition> sorted = doSort(sortable);
       
        if (logger.isDebugEnabled()) {
            logger.debug("Reverse sorted module defintions");
            logger.debug("Before: " + ModuleDefinitionUtils.getModuleNamesFromCollection(sortable));
            logger.debug("After: " + ModuleDefinitionUtils.getModuleNamesFromCollection(sorted));
        }
       
        return sorted;
    }
   
    /* ********************* Methods to show dependencies and dependents  ********************* */

    /**
     * Gets ordered list of modules definitions on which a particular named module depends
     */
    public List<ModuleDefinition> getOrderedModuleDependencies(String name) {

        Assert.notNull(name, "name cannot be null");
       
        final List<Vertex> vertices = getVertexDependencyList(name);
        List<ModuleDefinition> moduleDefinitions = getVerticesForModuleDefinitions(vertices);
       
        if (logger.isDebugEnabled()) {
            logger.debug("Ordered dependencies for module '" + name + "': " + ModuleDefinitionUtils.getModuleNamesFromCollection(moduleDefinitions));
        }
       
        return moduleDefinitions;
    }
   
    /**
     * Gets subgraph of named module plus dependents
     */
    public List<ModuleDefinition> getOrderedModuleDependants(String name) {

        Assert.notNull(name, "name cannot be null");
       
        List<Vertex> vertices = getVertexAndOrderedDependants(name);
        List<ModuleDefinition> moduleDefinitions = getVerticesForModuleDefinitions(vertices);
       
        if (logger.isDebugEnabled()) {
            logger.debug("Ordered dependents for module '" + name + "': " + ModuleDefinitionUtils.getModuleNamesFromCollection(moduleDefinitions));
        }
       
        return moduleDefinitions;
    }

    /**
     * Gets the {@link ModuleDefinition} which are direct dependents of the {@link ModuleDefinition} argument.
     */
    public Collection<ModuleDefinition> getDirectDependants(String name) {
       
        Assert.notNull(name, "name cannot be null");
       
        //make sure this module is present
        getRequiredVertex(name);
       
        final Collection<Vertex> vertices = dependents.get(name);
       
        if (vertices == null) {
            return Collections.emptySet();
        }
       
        List<ModuleDefinition> moduleDefinitions = getVerticesForModuleDefinitions(vertices);  
       
        if (logger.isDebugEnabled()) {
            logger.debug("Ordered dependents for module '" + name + "': " + ModuleDefinitionUtils.getModuleNamesFromCollection(moduleDefinitions));
        }      
       
        return moduleDefinitions;
    }
   
    /* ********************* returns all the modules known by the dependency registry **************** */
   
    /**
     * Returns all {@link ModuleDefinition} known to this {@link DependencyManager} instance, returned in topological sort order.
     */
    public Collection<ModuleDefinition> getAllModules() {
       
        final Collection<Vertex> vertices = this.vertexMap.values();
       
        List<Vertex> ordered = populatedOrderedVertices(vertices);
       
        final LinkedHashSet<ModuleDefinition> definitions = new LinkedHashSet<ModuleDefinition>();
        for (Vertex vertex : ordered) {
            definitions.add(vertex.getModuleDefinition());
        }
       
        if (logger.isDebugEnabled()) {
            logger.debug("Returning all module defintions: " + ModuleDefinitionUtils.getModuleNamesFromCollection(definitions));
        }  
       
        return definitions;
    }  
   
    /* ********************* Methods to add subgraph of vertices ********************* */
   
    protected void addModule(String parent, ModuleDefinition moduleDefinition) {
       
        ModuleDefinitionUtils.ensureNotFrozen(this);
       
        Assert.notNull(parent, "parent cannot be null");
        Assert.notNull(moduleDefinition, "moduleDefinition cannot be null");
       
        logger.info("With parent '" + parent + "', adding module: " + moduleDefinition);
       
        final Vertex parentVertex = getRequiredVertex(parent);
       
        ModuleDefinition parentDefinition = parentVertex.getModuleDefinition();
        parentDefinition.addChildModuleDefinition(moduleDefinition);
        moduleDefinition.setParentDefinition(parentDefinition);
       
        //now recursively add definitions
        List<Vertex> addedVertices = new ArrayList<Vertex>();
        populateDefinition(addedVertices, moduleDefinition);
       
        populateVertexDependencies(addedVertices);
       
        //rebuild the sorted vertex list
        resort();
       
        if (logger.isInfoEnabled()) {
            logger.info("Added module " + moduleDefinition);
        }
       
        if (logger.isDebugEnabled()) {
            logger.debug("Vertices after adding module");
            dump();
        }
    }

    private Vertex getRequiredVertex(String moduleName) {
        final Vertex parentVertex = vertexMap.get(moduleName);
        if (parentVertex == null) {
           
            if (logger.isDebugEnabled()) {
                logger.debug("Module '" + moduleName + "' not found.");
                dump();
            }
           
            throw new InvalidStateException("No module '"
                    + moduleName + "' is registered with current instance of dependency manager.");
        }
        return parentVertex;
    }
   
    /* ********************* Methods to remove vertices ********************* */
   
    /**
     * Removes the current module as well as any of it's dependents
     */
    protected void removeModule(String name) {
       
        ModuleDefinitionUtils.ensureNotFrozen(this);
       
        Assert.notNull(name, "name cannot be null");
       
        List<Vertex> orderedToRemove = getVertexAndOrderedDependants(name);
        removeVertexInOrder(orderedToRemove);
    }

    /* ********************* Private utility methods ********************* */
   
    private void buildVertexMap(List<ModuleDefinition> definitions) {
       
        Assert.notNull(definitions, "definitions cannot be null");
       
        List<Vertex> addedVertices = new ArrayList<Vertex>();
        for (ModuleDefinition moduleDefinition : definitions) {
            populateDefinition(addedVertices, moduleDefinition);
        }
       
        if (logger.isDebugEnabled()) {
            logger.debug("Added vertices: " + addedVertices);
        }
       
        //add the dependency relationships between the added vertices
        populateVertexDependencies(addedVertices);
       
        //rebuild the sorted vertex list
        resort();
    }

    /**
     * Gets the vertices for the modules for which the named module is a dependency
     */
    private List<Vertex> getVertexDependants(String name) {
       
        Assert.notNull(name, "name cannot be null");
       
        final List<Vertex> fullList = new ArrayList<Vertex>(sorted);
       
        List<Vertex> targetList = new ArrayList<Vertex>();
        populateDependants(targetList, name);

        List<Vertex> moduleVertices = new ArrayList<Vertex>();
       
        //iterate over the full list to get the order, but pick out only the module definitions which are dependents
        for (Vertex vertex : fullList) {
            if (targetList.contains(vertex)) {
                moduleVertices.add(vertex);
            }
        }
        return moduleVertices;
    }
   
    /**
     * Gets a list of vertices including the one corresponding with the name, plus its dependents
     * topologically sorted
     */
    private List<Vertex> getVertexAndOrderedDependants(String name) {
       
        Assert.notNull(name, "name cannot be null");
       
        final Vertex current = getRequiredVertex(name);
       
        //get all dependents
        final List<Vertex> dependents = getVertexDependants(name);
        List<Vertex> ordered = getOrderedDependants(current, dependents);
        return ordered;
    }
   
    /**
     * Gets vertices representing the current and its dependents, topologically sorted
     */
    private List<Vertex> getOrderedDependants(final Vertex currentVertex, final List<Vertex> dependents) {
       
        Assert.notNull(currentVertex, "currentVertex cannot be null");     
        Assert.notNull(dependents, "dependents cannot be null");
       
        List<Vertex> ordered = new ArrayList<Vertex>();
        ordered.add(currentVertex);
        ordered.addAll(populatedOrderedVertices(dependents));
        return ordered;
    }

    /**
     * Returns the {@link List} of {@link Vertex} instances on which the
     * {@link Vertex} corresponding with the name parameter depends.
     */
    private List<Vertex> getVertexDependencyList(String name) {
       
        Assert.notNull(name, "name cannot be null");
       
        Vertex vertex = getRequiredVertex(name);
       
        //list the vertices in the correct order
        final List<Vertex> vertextList = GraphHelper.list(vertex);
        return vertextList;
    }

    /**
     * Returns the vertices contained in <code>sortable</code> according to the topological
     * sort order of vertices known to this {@link DependencyManager} instance.
     */
    private List<Vertex> populatedOrderedVertices(Collection<Vertex> sortable) {
       
        Assert.notNull(sortable, "sortable cannot be null");
       
        Collection<Vertex> copy = new HashSet<Vertex>(sortable);
        List<Vertex> ordered = new ArrayList<Vertex>();
       
        //get the ordered to remove list
        List<Vertex> sorted = this.sorted;
        for (Vertex vertex : sorted) {
            if (copy.contains(vertex)) {
                ordered.add(vertex);
                copy.remove(vertex);
            }
        }
       
        if (!copy.isEmpty()) {
            //should not be possible, as all of the modules have already been converted into vertexes. Hence
            //would be the sign of an non-obvious programming error
            throw new InvalidStateException("Sortable list contains modules not known by the current instance of dependency registry: "
                    + GraphHelper.getModuleNamesFromCollection(copy));
        }
       
        return ordered;
    }
   
    /**
     * Get the list of {@link ModuleDefinition} corresponding with the vertex {@link Collection}
     */
    private static List<ModuleDefinition> getVerticesForModuleDefinitions(Collection<Vertex> moduleVertices) {

        Assert.notNull(moduleVertices, "moduleVertices cannot be null");
       
        List<ModuleDefinition> moduleDefinitions = new ArrayList<ModuleDefinition>();
       
        for (Vertex vertex : moduleVertices) {
            moduleDefinitions.add(vertex.getModuleDefinition());
        }
       
        return moduleDefinitions;
    }
   
    private List<Vertex> getVerticesForModules(Collection<ModuleDefinition> definitions) {

        Assert.notNull(definitions, "definitions cannot be null");
       
        List<Vertex> vertices = new ArrayList<Vertex>();
        for (ModuleDefinition moduleDefinition : definitions) {
            final Vertex vertex = getRequiredVertex(moduleDefinition.getName());
           
            vertices.add(vertex);
        }
        return vertices;
    }

    /**
     * Sets up the dependency relationship between a vertex and its dependency
     * @param vertex the vertex (dependant)
     * @param dependentVertex the dependency, that is the vertex the dependant depends on
     */
    private void populateVertexDependency(Vertex vertex, Vertex dependentVertex) {
       
        Assert.notNull(vertex, "vertex cannot be null");
        Assert.notNull(dependentVertex, "dependentVertex cannot be null");
       
        vertex.addDependency(dependentVertex);
       
        final String dependantName = dependentVertex.getName();
       
        Set<Vertex> list = dependents.get(dependantName);
        if (list == null) {
            list = new HashSet<Vertex>();
            dependents.put(dependantName, list);
        }
        list.add(vertex);
    }

    /**
     * Stores vertex for current module definition. Assumes none present
     * @return
     */
    private Vertex populateVertex(ModuleDefinition moduleDefinition) {
       
        Assert.notNull(moduleDefinition, "moduleDefinition cannot be null");
       
        String name = moduleDefinition.getName();
        final Vertex vertex = new Vertex(moduleDefinition);
        vertexMap.put(name, vertex);
        return vertex;
    }

    /**
     * Recursive method to build the list of dependents for a particular named module.
     * Does not order the dependencies in any way
     */
    private void populateDependants(List<Vertex> targetList, String name) {
       
        Assert.notNull(targetList, "targetList cannot be null");
        Assert.notNull(name, "name cannot be null");
       
        //recursively build the dependant list
        Set<Vertex> dependantList = dependents.get(name);
        if (dependantList != null) {
            targetList.addAll(dependantList);
            for (Vertex vertex : dependantList) {
                populateDependants(targetList, vertex.getName());
            }
        }
    }

    /**
     * Recursive method to add module definition.
     * @param addedVertices
     */
    private void populateDefinition(List<Vertex> addedVertices, ModuleDefinition moduleDefinition) {

        Assert.notNull(addedVertices, "addedVertices cannot be null");
        Assert.notNull(moduleDefinition, "moduleDefinition cannot be null");
       
        addedVertices.add(populateVertex(moduleDefinition));
   
        final Collection<ModuleDefinition> childDefinitions = moduleDefinition.getChildModuleDefinitions();
       
        for (ModuleDefinition childDefinition : childDefinitions) {
            populateDefinition(addedVertices, childDefinition);
        }
    }

    /**
     * Sets up the dependency relationships between vertices based on the
     * dependency module names of the ModuleDefinitions
     * @param addedVertices
     */
    private void populateVertexDependencies(List<Vertex> addedVertices) {
       
        Assert.notNull(addedVertices, "addedVertices cannot be null");
       
        for (Vertex vertex : addedVertices) {
            populateVertexDependencies(vertex);
        }
    }

    /**
     * Sets up the dependencies for a particular named module
     */
    private void populateVertexDependencies(Vertex vertex) {
       
        Assert.notNull(vertex, "vertex cannot be null");
       
        final ModuleDefinition moduleDefinition = vertex.getModuleDefinition();
       
        populateDependencies(vertex, moduleDefinition, false);
        populateDependencies(vertex, moduleDefinition, true);
    }

    /**
     * Populates either optional or mandatory dependencies, depending on whether optional or not
     */
    private void populateDependencies(Vertex vertex, ModuleDefinition moduleDefinition, boolean optional) {
       
        final List<String> dependentModuleNames = moduleDefinition.getDependentModuleNames(optional);
        for (String dependent : dependentModuleNames) {
           
            final Vertex dependentVertex = vertexMap.get(dependent);
           
            if (dependentVertex == null) {
                if (!optional) {
                    throw new InvalidStateException("Unable to find entry for dependency named named '" + dependent
                            + "' for module definition '" + moduleDefinition.getName() + "'");
                }               
            } else {
                //register the vertex dependency
                populateVertexDependency(vertex, dependentVertex);
            }
        }
    }

    /**
     * Deregister from the dependencies list of dependents and the vertex map
     */
    private void removeVertexInOrder(List<Vertex> vertices) {
       
        Assert.notNull(vertices, "vertices cannot be null");
       
        for (Vertex vertex : vertices) {
            removeVertex(vertex);
        }
    }

    private void removeVertex(Vertex vertex) {
       
        Assert.notNull(vertex, "vertex cannot be null");
       
        final List<Vertex> dependencies = vertex.getDependencies();
        for (Vertex dependency : dependencies) {
            final String dependencyName = dependency.getName();
            final Set<Vertex> dependents = this.dependents.get(dependencyName);
            dependents.remove(dependency);
        }
       
        if (logger.isDebugEnabled()) {
            logger.debug("Removing vertex " + vertex.getName());
        }
       
        this.sorted.remove(vertex);
        this.vertexMap.remove(vertex.getName());
    }

    private List<ModuleDefinition> doSort(Collection<ModuleDefinition> sortable) {
       
        Assert.notNull(sortable, "vertex cannot be null");
       
        //convert module definitions to vertices
        List<Vertex> vertices = this.getVerticesForModules(sortable);
       
        //sort these based in order
        List<Vertex> ordered = populatedOrderedVertices(vertices);
       
        //reconvert back to vertices
        return getVerticesForModuleDefinitions(ordered);
    }

    private void resort() {
        final List<Vertex> vertices = new ArrayList<Vertex>(vertexMap.values());
        for (Vertex vertex : vertices) {
            vertex.reset();
        }
        try {
            GraphHelper.topologicalSort(vertices);
        } catch (CyclicDependencyException e) {
            logCyclicDependencyError(vertices);
            throw e;
        }
        this.sorted = vertices;
    }

    private void logCyclicDependencyError(final List<Vertex> vertices) {
        logger.error("Cyclic dependency found. Outputting vertex dependencies:");
        for (Vertex vertex : vertices) {
            logger.error(vertex.getName() + ": ");
            final List<Vertex> dependencies = vertex.getDependencies();
            for (Vertex dependency : dependencies) {
                logger.error("  " + dependency.getName() + ",");
            }
            logger.error("-----------");
        }
    }
   
    private void dump() {
        if (!logger.isDebugEnabled()) return;
        logger.debug("Dependency registry state. Sorted vertices:");
        for (Vertex vertex : this.sorted) {
            logger.debug(vertex);
        }
    }

    public void freeze() {
        this.frozen = true;
    }

    public void unfreeze() {
        this.frozen = false;
    }

    public boolean isFrozen() {
        return frozen;
    }
   
}
TOP

Related Classes of org.impalaframework.module.definition.DependencyManager

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.