Package org.impalaframework.classloader.graph

Source Code of org.impalaframework.classloader.graph.DependencyManager

/*
* Copyright 2007-2008 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.classloader.graph;

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.impalaframework.module.definition.ModuleDefinitionUtils;
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.
   */
  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 ********************* */
 
  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
   */
  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();
   
    final List<String> dependentModuleNames = moduleDefinition.getDependentModuleNames();
    for (String dependent : dependentModuleNames) {
     
      final Vertex dependentVertex = vertexMap.get(dependent);
     
      if (dependentVertex == null) {
        throw new InvalidStateException("Unable to 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.classloader.graph.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.