View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.start;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  import java.util.Stack;
33  
34  /**
35   * Access for all modules declared, as well as what is enabled.
36   */
37  public class Modules implements Iterable<Module>
38  {
39      private Map<String, Module> modules = new HashMap<>();
40      private int maxDepth = -1;
41  
42      private Set<String> asNameSet(Set<Module> moduleSet)
43      {
44          Set<String> ret = new HashSet<>();
45          for (Module module : moduleSet)
46          {
47              ret.add(module.getName());
48          }
49          return ret;
50      }
51  
52      private void assertNoCycle(Module module, Stack<String> refs)
53      {
54          for (Module parent : module.getParentEdges())
55          {
56              if (refs.contains(parent.getName()))
57              {
58                  // Cycle detected.
59                  StringBuilder err = new StringBuilder();
60                  err.append("A cyclic reference in the modules has been detected: ");
61                  for (int i = 0; i < refs.size(); i++)
62                  {
63                      if (i > 0)
64                      {
65                          err.append(" -> ");
66                      }
67                      err.append(refs.get(i));
68                  }
69                  err.append(" -> ").append(parent.getName());
70                  throw new IllegalStateException(err.toString());
71              }
72  
73              refs.push(parent.getName());
74              assertNoCycle(parent,refs);
75              refs.pop();
76          }
77      }
78  
79      private void bfsCalculateDepth(final Module module, final int depthNow)
80      {
81          int depth = depthNow + 1;
82  
83          // Set depth on every child first
84          for (Module child : module.getChildEdges())
85          {
86              child.setDepth(Math.max(depth,child.getDepth()));
87              this.maxDepth = Math.max(this.maxDepth,child.getDepth());
88          }
89  
90          // Dive down
91          for (Module child : module.getChildEdges())
92          {
93              bfsCalculateDepth(child,depth);
94          }
95      }
96  
97      /**
98       * Using the provided dependencies, build the module graph
99       */
100     public void buildGraph()
101     {
102         // Connect edges
103         for (Module module : modules.values())
104         {
105             for (String parentName : module.getParentNames())
106             {
107                 Module parent = get(parentName);
108 
109                 if (parent == null)
110                 {
111                     System.err.printf("WARNING: module not found [%s]%n",parentName);
112                 }
113                 else
114                 {
115                     module.addParentEdge(parent);
116                     parent.addChildEdge(module);
117                 }
118             }
119 
120             for (String optionalParentName : module.getOptionalParentNames())
121             {
122                 Module optional = get(optionalParentName);
123                 if (optional == null)
124                 {
125                     System.err.printf("WARNING: module not found [%s]%n",optionalParentName);
126                 }
127                 else if (optional.isEnabled())
128                 {
129                     module.addParentEdge(optional);
130                     optional.addChildEdge(module);
131                 }
132             }
133         }
134 
135         // Verify there is no cyclic references
136         Stack<String> refs = new Stack<>();
137         for (Module module : modules.values())
138         {
139             refs.push(module.getName());
140             assertNoCycle(module,refs);
141             refs.pop();
142         }
143 
144         // Calculate depth of all modules for sorting later
145         for (Module module : modules.values())
146         {
147             if (module.getParentEdges().isEmpty())
148             {
149                 bfsCalculateDepth(module,0);
150             }
151         }
152     }
153 
154     public Integer count()
155     {
156         return modules.size();
157     }
158 
159     public void dump()
160     {
161         List<Module> ordered = new ArrayList<>();
162         ordered.addAll(modules.values());
163         Collections.sort(ordered,new Module.NameComparator());
164 
165         for (Module module : ordered)
166         {
167             System.out.printf("%nModule: %s%n",module.getName());
168             for (String lib : module.getLibs())
169             {
170                 System.out.printf("      LIB: %s%n",lib);
171             }
172             for (String xml : module.getXmls())
173             {
174                 System.out.printf("      XML: %s%n",xml);
175             }
176             System.out.printf("  depends: [%s]%n",Main.join(module.getParentNames(),", "));
177             if (StartLog.isDebugEnabled())
178             {
179                 System.out.printf("    depth: %d%n",module.getDepth());
180             }
181             for (String source : module.getSources())
182             {
183                 System.out.printf("  enabled: %s%n",source);
184             }
185         }
186     }
187 
188     public void dumpEnabledTree()
189     {
190         List<Module> ordered = new ArrayList<>();
191         ordered.addAll(modules.values());
192         Collections.sort(ordered,new Module.DepthComparator());
193 
194         List<Module> active = resolveEnabled();
195 
196         for (Module module : ordered)
197         {
198             if (active.contains(module))
199             {
200                 // Show module name
201                 String indent = toIndent(module.getDepth());
202                 System.out.printf("%s + Module: %s [%s]%n",indent,module.getName(),module.isEnabled()?"enabled":"transitive");
203             }
204         }
205     }
206 
207     public void enable(String name, List<String> sources)
208     {
209         Module module = modules.get(name);
210         if (module == null)
211         {
212             System.err.printf("WARNING: Cannot enable requested module [%s]: not a valid module name.%n",name);
213             return;
214         }
215         StartLog.debug("Enabling module: %s (via %s)",name,Main.join(sources,", "));
216         module.setEnabled(true);
217         if (sources != null)
218         {
219             module.addSources(sources);
220         }
221     }
222 
223     private void findChildren(Module module, Set<Module> ret)
224     {
225         ret.add(module);
226         for (Module child : module.getChildEdges())
227         {
228             ret.add(child);
229         }
230     }
231 
232     private void findParents(Module module, Set<Module> ret)
233     {
234         ret.add(module);
235         for (Module parent : module.getParentEdges())
236         {
237             ret.add(parent);
238             findParents(parent,ret);
239         }
240     }
241 
242     public Module get(String name)
243     {
244         return modules.get(name);
245     }
246 
247     public int getMaxDepth()
248     {
249         return maxDepth;
250     }
251 
252     public Set<Module> getModulesAtDepth(int depth)
253     {
254         Set<Module> ret = new HashSet<>();
255         for (Module module : modules.values())
256         {
257             if (module.getDepth() == depth)
258             {
259                 ret.add(module);
260             }
261         }
262         return ret;
263     }
264 
265     @Override
266     public Iterator<Module> iterator()
267     {
268         return modules.values().iterator();
269     }
270 
271     public List<String> normalizeLibs(List<Module> active)
272     {
273         List<String> libs = new ArrayList<>();
274         for (Module module : active)
275         {
276             for (String lib : module.getLibs())
277             {
278                 if (!libs.contains(lib))
279                 {
280                     libs.add(lib);
281                 }
282             }
283         }
284         return libs;
285     }
286 
287     public List<String> normalizeXmls(List<Module> active)
288     {
289         List<String> xmls = new ArrayList<>();
290         for (Module module : active)
291         {
292             for (String xml : module.getXmls())
293             {
294                 if (!xmls.contains(xml))
295                 {
296                     xmls.add(xml);
297                 }
298             }
299         }
300         return xmls;
301     }
302 
303     public void register(Module module)
304     {
305         modules.put(module.getName(),module);
306     }
307 
308     public void registerAll(BaseHome basehome) throws IOException
309     {
310         for (File file : basehome.listFiles("modules",new FS.FilenameRegexFilter("^.*\\.mod$")))
311         {
312             register(new Module(file));
313         }
314     }
315 
316     public Set<String> resolveChildModulesOf(String moduleName)
317     {
318         Set<Module> ret = new HashSet<>();
319         Module module = get(moduleName);
320         findChildren(module,ret);
321         return asNameSet(ret);
322     }
323 
324     /**
325      * Resolve the execution order of the enabled modules, and all dependant modules, based on depth first transitive reduction.
326      * 
327      * @return the list of active modules (plus dependant modules), in execution order.
328      */
329     public List<Module> resolveEnabled()
330     {
331         Set<Module> active = new HashSet<Module>();
332 
333         for (Module module : modules.values())
334         {
335             if (module.isEnabled())
336             {
337                 findParents(module,active);
338             }
339         }
340 
341         List<Module> ordered = new ArrayList<>();
342         ordered.addAll(active);
343         Collections.sort(ordered,new Module.DepthComparator());
344         return ordered;
345     }
346 
347     public Set<String> resolveParentModulesOf(String moduleName)
348     {
349         Set<Module> ret = new HashSet<>();
350         Module module = get(moduleName);
351         findParents(module,ret);
352         return asNameSet(ret);
353     }
354 
355     private String toIndent(int depth)
356     {
357         char indent[] = new char[depth * 2];
358         Arrays.fill(indent,' ');
359         return new String(indent);
360     }
361 }