Package org.jboss.deployers.plugins.sort

Source Code of org.jboss.deployers.plugins.sort.KahnDeployerSorter

/*
* JBoss, Home of Professional Open Source
* Copyright (c) 2009, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

package org.jboss.deployers.plugins.sort;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.jboss.deployers.spi.Ordered;
import org.jboss.deployers.spi.deployer.Deployer;

/**
* @author <a href="cdewolf@redhat.com">Carlo de Wolf</a>
*/
public class KahnDeployerSorter implements DeployerSorter
{
   private static class Edge
   {
      Deployer from;
      String input;
      Deployer to;

      Edge(Deployer from, String input, Deployer to)
      {
         if(from.equals(to))
            throw new IllegalArgumentException("cyclic edge");
         this.from = from;
         this.input = input;
         this.to = to;
      }

      @Override
      public boolean equals(Object o)
      {
         if(this == o) return true;
         if(o == null || getClass() != o.getClass()) return false;

         Edge edge = (Edge) o;

         if(from != null ? !from.equals(edge.from) : edge.from != null) return false;
         if(!input.equals(edge.input)) return false;
         if(!to.equals(edge.to)) return false;

         return true;
      }

      @Override
      public int hashCode()
      {
         int result = from != null ? from.hashCode() : 0;
         result = 31 * result + input.hashCode();
         result = 31 * result + to.hashCode();
         return result;
      }

      @Override
      public String toString()
      {
         return "Edge{" +
            "from=" + from +
            ", input='" + input + '\'' +
            ", to=" + to +
            '}';
      }
   }

   private static class ScoredDeployer
   {
      Deployer deployer;
      int score;

      ScoredDeployer(Deployer deployer)
      {
         this.deployer = deployer;
         this.score = deployer.getRelativeOrder();
      }

      @Override
      public String toString()
      {
         return "ScoredDeployer{" +
            "deployer=" + deployer +
            ", score=" + score +
            '}';
      }
   }

   protected int compare(Deployer one, Deployer two)
   {
      int relation = one.getRelativeOrder() - two.getRelativeOrder();
      if(relation == 0)
         relation = one.hashCode() - two.hashCode();
      assert relation != 0;
      return relation;
   }

   protected Collection<Edge> createEdges(Deployer from, Map<String, Collection<Deployer>> inputCache, Set<String> outputs)
   {
      Collection<Edge> result = new ArrayList<Edge>();
      for(String output : outputs)
      {
         Collection<Deployer> deployers = inputCache.get(output);
         if(deployers != null) for(Deployer to : deployers)
         {
            if(from != to)
               result.add(new Edge(from, output, to));
         }
      }
      return result;
   }

   protected Collection<Edge> createEdges(Map<String, Collection<Deployer>> outputCache, Set<String> inputs, Deployer to)
   {
      Collection<Edge> result = new ArrayList<Edge>();
      for(String input : inputs)
      {
         Collection<Deployer> deployers = outputCache.get(input);
         if(deployers != null) for(Deployer from : deployers)
         {
            result.add(new Edge(from, input, to));
         }
      }
      return result;
   }

   protected Collection<Edge> findInputs(Map<Deployer, Set<Edge>> edgeCache, Deployer from, Map<String, Collection<Deployer>> cache, Set<String> inputs)
   {
      Collection<Edge> result = new ArrayList<Edge>();
      for(String input : inputs)
      {
         //result.addAll(cache.get(input));
         Collection<Deployer> deployers = cache.get(input);
         if(deployers != null) for(Deployer d : deployers)
         {
            if(d != from)
            {
               //result.add(new Edge(from, input, d));
               Edge potential = new Edge(from, input, d);
               Set<Edge> edges = edgeCache.get(d);
               if(edges.contains(potential))
                  result.add(potential);
            }
         }
      }
      return result;
   }

   protected boolean isInputMaster(Collection<Deployer> deployers, Deployer deployer, String input)
   {
      for(Deployer other : deployers)
      {
         if(isTransient(other, input))
         {
            if(compare(other, deployer) > 0)
               return false;
         }
      }
      return true;
   }

   protected boolean isTransient(Deployer deployer, String input)
   {
      return deployer.getInputs().contains(input) && deployer.getOutputs().contains(input);
   }

   protected void process(Deployer deployer, Collection<Deployer> s, Map<String, Collection<Deployer>> inputCache, Map<Deployer, Set<Edge>> edgeCache, Set<String> outputs, Map<String, Collection<Deployer>> outputCache)
   {
      outputs.addAll(deployer.getOutputs());
      if(deployer.getInputs() == null || deployer.getInputs().size() == 0)
         s.add(deployer);
      else
      {
         Set<Edge> edges = edgeCache.get(deployer);
         assert edges == null;
         edges = new HashSet<Edge>();
         edgeCache.put(deployer, edges);

         for(String input : deployer.getInputs())
         {
            Collection<Deployer> c = inputCache.get(input);
            if(c == null)
            {
               c = new ArrayList<Deployer>();
               inputCache.put(input, c);
            }
            c.add(deployer);
         }

         Collection<Edge> c = createEdges(outputCache, deployer.getInputs(), deployer);
         if(c.isEmpty())
         {
            // might be a bit premature, see below
            s.add(deployer);
         }
         else
         {
            edges.addAll(c);
         }
      }

      for(String output : deployer.getOutputs())
      {
         Collection<Deployer> c = outputCache.get(output);
         if(c == null)
         {
            c = new ArrayList<Deployer>();
            outputCache.put(output, c);
         }
         c.add(deployer);
      }

      Collection<Edge> edges = createEdges(deployer, inputCache, deployer.getOutputs());
      for(Edge e : edges)
      {
         Set<Edge> cachedEdges = edgeCache.get(e.to);
         if(cachedEdges == null)
         {
            cachedEdges = new HashSet<Edge>();
            edgeCache.put(e.to, cachedEdges);
         }
         cachedEdges.add(e);
         // remove a prematurely added deployer
         s.remove(e.to);
      }
   }

   /**
    * Break the cyclic graph by processing transient deployers. Basically choosing one edge which we want removed.
    *
    * @param s the deployers
    * @param inputCache the input cache
    * @param outputCache the output cache
    * @param edgeCache the edge cache
    */
   public void processTransientDeployers(List<Deployer> s, Map<String, Collection<Deployer>> inputCache, Map<String, Collection<Deployer>> outputCache, Map<Deployer, Set<Edge>> edgeCache)
   {
      for(String input : inputCache.keySet())
      {
         Collection<Deployer> others = outputCache.get(input);
         if(others == null)
            continue;
         List<Deployer> deployers = new ArrayList<Deployer>(inputCache.get(input));
         deployers.retainAll(others);
         if(deployers.isEmpty() || deployers.size() == 1)
            continue;

         List<ScoredDeployer> scoredDeployers = new ArrayList<ScoredDeployer>();
         // TODO: too slow
         for(Deployer d : deployers)
         {
            ScoredDeployer scoredDeployer = new ScoredDeployer(d);
            scoredDeployers.add(scoredDeployer);

            Set<Edge> edges = edgeCache.get(d);
            for(Edge e : new HashSet<Edge>(edges))
            {
               if(deployers.contains(e.from) && input.equals(e.input))
                  edges.remove(e);
               else if(deployers.contains(e.from))
                  scoredDeployer.score++;
            }
         }

         Comparator<? super ScoredDeployer> comparator = new Comparator<ScoredDeployer>() {
            public int compare(ScoredDeployer o1, ScoredDeployer o2)
            {
               int relation = o1.score - o2.score;
               if(relation == 0)
                  relation = Ordered.COMPARATOR.compare(o1.deployer, o2.deployer);
               return relation;
            }
         };
         Collections.sort(scoredDeployers, comparator);

         for(int i = 1; i < scoredDeployers.size(); i++)
         {
            Set<Edge> edges = edgeCache.get(scoredDeployers.get(i).deployer);
            edges.add(new Edge(scoredDeployers.get(0).deployer, input, scoredDeployers.get(i).deployer));
         }

         // add the top one if it doesn't have any incoming edges
         // note that the normal process step does the same thing, so exclude it.
         Deployer deployer = scoredDeployers.get(0).deployer;
         if(edgeCache.get(deployer).isEmpty() && !s.contains(deployer))
            s.add(deployer);
      }
   }

   public List<Deployer> sortDeployers(List<Deployer> original, Deployer newDeployer)
   {
      List<Deployer> result = new ArrayList<Deployer>();
      // S ? Set of all nodes with no incoming edges
      List<Deployer> s = new ArrayList<Deployer>();
      Map<String, Collection<Deployer>> inputCache = new HashMap<String, Collection<Deployer>>();
      Map<Deployer, Set<Edge>> edgeCache = new IdentityHashMap<Deployer, Set<Edge>>();
      Set<String> outputs = new HashSet<String>();
      Map<String, Collection<Deployer>> outputCache = new HashMap<String, Collection<Deployer>>();
      for(Deployer deployer : original)
      {
         process(deployer, s, inputCache, edgeCache, outputs, outputCache);
      }
      process(newDeployer, s, inputCache, edgeCache, outputs, outputCache);

      // find transient deployers and sort them out
      processTransientDeployers(s, inputCache, outputCache, edgeCache);

      // never, ever ask why this is here.
      // I really mean it, do not ask.
      // Seriously I will not give a sensible answer to this one.
      // Okay, okay, stop bitching. There is a requirement that deployer need to be in name ordering.
      // setupSillyNameEdges(deployer, newDeployer); // hmm does work out
      Collections.sort(s, Ordered.COMPARATOR);

      while(!s.isEmpty())
      {
         Deployer deployer = s.remove(0);
         result.add(deployer);
         // for each node m with an edge e from n to m do
         Set<Deployer> nextLevel = new TreeSet<Deployer>(Ordered.COMPARATOR);
         for(Edge e : findInputs(edgeCache, deployer, inputCache, deployer.getOutputs()))
         {
            // remove edge e from the graph
            Set<Edge> edges = edgeCache.get(e.to);
            edges.remove(e);
            // if m has no other incoming edges then insert m into S
            if(edges.isEmpty())
               nextLevel.add(e.to);
         }
         s.addAll(nextLevel);
      }
      // if graph has edges then output error message (graph has at least one cycle)
      String message = "";
      for(Set<Edge> edges : edgeCache.values())
      {
         if(!edges.isEmpty())
            message += "edges: " + edges;
      }
      if(message.length() > 0)
         throw new IllegalStateException(message);

      assert result.size() == original.size() + 1 : "not all deployers made it";
      return result;
   }
}
TOP

Related Classes of org.jboss.deployers.plugins.sort.KahnDeployerSorter

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.