1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.start;
20
21 import java.io.FileNotFoundException;
22 import java.io.IOException;
23 import java.nio.file.Path;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Set;
33 import java.util.Stack;
34 import java.util.regex.Pattern;
35
36
37
38
39 public class Modules implements Iterable<Module>
40 {
41 private Map<String, Module> modules = new HashMap<>();
42
43
44
45
46
47 private Set<String> missingModules = new HashSet<String>();
48
49 private int maxDepth = -1;
50
51 private Set<String> asNameSet(Set<Module> moduleSet)
52 {
53 Set<String> ret = new HashSet<>();
54 for (Module module : moduleSet)
55 {
56 ret.add(module.getName());
57 }
58 return ret;
59 }
60
61 private void assertNoCycle(Module module, Stack<String> refs)
62 {
63 for (Module parent : module.getParentEdges())
64 {
65 if (refs.contains(parent.getName()))
66 {
67
68 StringBuilder err = new StringBuilder();
69 err.append("A cyclic reference in the modules has been detected: ");
70 for (int i = 0; i < refs.size(); i++)
71 {
72 if (i > 0)
73 {
74 err.append(" -> ");
75 }
76 err.append(refs.get(i));
77 }
78 err.append(" -> ").append(parent.getName());
79 throw new IllegalStateException(err.toString());
80 }
81
82 refs.push(parent.getName());
83 assertNoCycle(parent,refs);
84 refs.pop();
85 }
86 }
87
88 private void bfsCalculateDepth(final Module module, final int depthNow)
89 {
90 int depth = depthNow + 1;
91
92
93 for (Module child : module.getChildEdges())
94 {
95 child.setDepth(Math.max(depth,child.getDepth()));
96 this.maxDepth = Math.max(this.maxDepth,child.getDepth());
97 }
98
99
100 for (Module child : module.getChildEdges())
101 {
102 bfsCalculateDepth(child,depth);
103 }
104 }
105
106
107
108
109 public void buildGraph()
110 {
111
112 for (Module module : modules.values())
113 {
114 for (String parentName : module.getParentNames())
115 {
116 Module parent = get(parentName);
117
118 if (parent == null)
119 {
120 if (parentName.contains("${"))
121 StartLog.debug("module not found [%s]%n",parentName);
122 else
123 StartLog.warn("module not found [%s]%n",parentName);
124 }
125 else
126 {
127 module.addParentEdge(parent);
128 parent.addChildEdge(module);
129 }
130 }
131
132 for (String optionalParentName : module.getOptionalParentNames())
133 {
134 Module optional = get(optionalParentName);
135 if (optional == null)
136 {
137 StartLog.debug("optional module not found [%s]%n",optionalParentName);
138 }
139 else if (optional.isEnabled())
140 {
141 module.addParentEdge(optional);
142 optional.addChildEdge(module);
143 }
144 }
145 }
146
147
148 Stack<String> refs = new Stack<>();
149 for (Module module : modules.values())
150 {
151 refs.push(module.getName());
152 assertNoCycle(module,refs);
153 refs.pop();
154 }
155
156
157 for (Module module : modules.values())
158 {
159 if (module.getParentEdges().isEmpty())
160 {
161 bfsCalculateDepth(module,0);
162 }
163 }
164 }
165
166 public Integer count()
167 {
168 return modules.size();
169 }
170
171 public void dump()
172 {
173 List<Module> ordered = new ArrayList<>();
174 ordered.addAll(modules.values());
175 Collections.sort(ordered,new Module.NameComparator());
176
177 List<Module> active = resolveEnabled();
178
179 for (Module module : ordered)
180 {
181 boolean activated = active.contains(module);
182 boolean enabled = module.isEnabled();
183 boolean transitive = activated && !enabled;
184
185 char status = '-';
186 if (enabled)
187 {
188 status = '*';
189 }
190 else if (transitive)
191 {
192 status = '+';
193 }
194
195 System.out.printf("%n %s Module: %s%n",status,module.getName());
196 if (!module.getName().equals(module.getFilesystemRef()))
197 {
198 System.out.printf(" Ref: %s%n",module.getFilesystemRef());
199 }
200 for (String parent : module.getParentNames())
201 {
202 System.out.printf(" Depend: %s%n",parent);
203 }
204 for (String lib : module.getLibs())
205 {
206 System.out.printf(" LIB: %s%n",lib);
207 }
208 for (String xml : module.getXmls())
209 {
210 System.out.printf(" XML: %s%n",xml);
211 }
212 if (StartLog.isDebugEnabled())
213 {
214 System.out.printf(" depth: %d%n",module.getDepth());
215 }
216 if (activated)
217 {
218 for (String source : module.getSources())
219 {
220 System.out.printf(" Enabled: <via> %s%n",source);
221 }
222 if (transitive)
223 {
224 System.out.printf(" Enabled: <via transitive reference>%n");
225 }
226 }
227 else
228 {
229 System.out.printf(" Enabled: <not enabled in this configuration>%n");
230 }
231 }
232 }
233
234 public void dumpEnabledTree()
235 {
236 List<Module> ordered = new ArrayList<>();
237 ordered.addAll(modules.values());
238 Collections.sort(ordered,new Module.DepthComparator());
239
240 List<Module> active = resolveEnabled();
241
242 for (Module module : ordered)
243 {
244 if (active.contains(module))
245 {
246
247 String indent = toIndent(module.getDepth());
248 System.out.printf("%s + Module: %s [%s]%n",indent,module.getName(),module.isEnabled()?"enabled":"transitive");
249 }
250 }
251 }
252
253 public void enable(String name, List<String> sources)
254 {
255 if (name.contains("*"))
256 {
257
258 Pattern pat = Pattern.compile(name);
259 for (Map.Entry<String, Module> entry : modules.entrySet())
260 {
261 if (pat.matcher(entry.getKey()).matches())
262 {
263 enableModule(entry.getValue(),sources);
264 }
265 }
266 }
267 else
268 {
269 Module module = modules.get(name);
270 if (module == null)
271 {
272 System.err.printf("WARNING: Cannot enable requested module [%s]: not a valid module name.%n",name);
273 return;
274 }
275 enableModule(module,sources);
276 }
277 }
278
279 private void enableModule(Module module, List<String> sources)
280 {
281 StartLog.debug("Enabling module: %s (via %s)",module.getName(),Main.join(sources,", "));
282 module.setEnabled(true);
283 if (sources != null)
284 {
285 module.addSources(sources);
286 }
287 }
288
289 private void findChildren(Module module, Set<Module> ret)
290 {
291 ret.add(module);
292 for (Module child : module.getChildEdges())
293 {
294 ret.add(child);
295 }
296 }
297
298 private void findParents(Module module, Map<String, Module> ret)
299 {
300 ret.put(module.getName(),module);
301 for (Module parent : module.getParentEdges())
302 {
303 ret.put(parent.getName(),parent);
304 findParents(parent,ret);
305 }
306 }
307
308 public Module get(String name)
309 {
310 return modules.get(name);
311 }
312
313 public int getMaxDepth()
314 {
315 return maxDepth;
316 }
317
318 public Set<Module> getModulesAtDepth(int depth)
319 {
320 Set<Module> ret = new HashSet<>();
321 for (Module module : modules.values())
322 {
323 if (module.getDepth() == depth)
324 {
325 ret.add(module);
326 }
327 }
328 return ret;
329 }
330
331 @Override
332 public Iterator<Module> iterator()
333 {
334 return modules.values().iterator();
335 }
336
337 public List<String> normalizeLibs(List<Module> active)
338 {
339 List<String> libs = new ArrayList<>();
340 for (Module module : active)
341 {
342 for (String lib : module.getLibs())
343 {
344 if (!libs.contains(lib))
345 {
346 libs.add(lib);
347 }
348 }
349 }
350 return libs;
351 }
352
353 public List<String> normalizeXmls(List<Module> active)
354 {
355 List<String> xmls = new ArrayList<>();
356 for (Module module : active)
357 {
358 for (String xml : module.getXmls())
359 {
360 if (!xmls.contains(xml))
361 {
362 xmls.add(xml);
363 }
364 }
365 }
366 return xmls;
367 }
368
369 public Module register(Module module)
370 {
371 modules.put(module.getName(),module);
372 return module;
373 }
374
375 public void registerParentsIfMissing(BaseHome basehome, StartArgs args, Module module) throws IOException
376 {
377 Set<String> parents = new HashSet<>(module.getParentNames());
378 for (String name : parents)
379 {
380 if (!modules.containsKey(name))
381 {
382 Path file = basehome.getPath("modules/" + name + ".mod");
383 if (FS.canReadFile(file))
384 {
385 Module parent = registerModule(basehome,args,file);
386 updateParentReferencesTo(parent);
387 registerParentsIfMissing(basehome, args, parent);
388 }
389 }
390 }
391 }
392
393 public void registerAll(BaseHome basehome, StartArgs args) throws IOException
394 {
395 for (Path path : basehome.getPaths("modules/*.mod"))
396 {
397 registerModule(basehome,args,path);
398 }
399
400
401 boolean done = false;
402 while (!done)
403 {
404 done = true;
405 Set<String> missingParents = new HashSet<>();
406
407 for (Module m : modules.values())
408 {
409 for (String parent : m.getParentNames())
410 {
411 if (modules.containsKey(parent) || missingModules.contains(parent))
412 {
413 continue;
414 }
415 done = false;
416 missingParents.add(parent);
417 }
418 }
419
420 for (String missingParent : missingParents)
421 {
422 Path file = basehome.getPath("modules/" + missingParent + ".mod");
423 if (FS.canReadFile(file))
424 {
425 Module module = registerModule(basehome,args,file);
426 updateParentReferencesTo(module);
427 }
428 else
429 {
430 StartLog.debug("Missing module definition: [ Mod: %s | File: %s]",missingParent,file);
431 missingModules.add(missingParent);
432 }
433 }
434 }
435 }
436
437 private Module registerModule(BaseHome basehome, StartArgs args, Path file) throws FileNotFoundException, IOException
438 {
439 if (!FS.canReadFile(file))
440 {
441 throw new IOException("Cannot read file: " + file);
442 }
443 StartLog.debug("Registering Module: %s",basehome.toShortForm(file));
444 Module module = new Module(basehome,file);
445 module.expandProperties(args.getProperties());
446 return register(module);
447 }
448
449 public Set<String> resolveChildModulesOf(String moduleName)
450 {
451 Set<Module> ret = new HashSet<>();
452 Module module = get(moduleName);
453 findChildren(module,ret);
454 return asNameSet(ret);
455 }
456
457
458
459
460
461
462 public List<Module> resolveEnabled()
463 {
464 Map<String, Module> active = new HashMap<String, Module>();
465
466 for (Module module : modules.values())
467 {
468 if (module.isEnabled())
469 {
470 findParents(module,active);
471 }
472 }
473
474
475
476
477
478
479 for (String missing : missingModules)
480 {
481 for (String activeModule : active.keySet())
482 {
483 if (missing.startsWith(activeModule))
484 {
485 StartLog.warn("** Unable to continue, required dependency missing. [%s]",missing);
486 StartLog.warn("** As configured, Jetty is unable to start due to a missing enabled module dependency.");
487 StartLog.warn("** This may be due to a transitive dependency akin to spdy on npn, which resolves based on the JDK in use.");
488 return Collections.emptyList();
489 }
490 }
491 }
492
493 List<Module> ordered = new ArrayList<>();
494 ordered.addAll(active.values());
495 Collections.sort(ordered,new Module.DepthComparator());
496 return ordered;
497 }
498
499 public Set<String> resolveParentModulesOf(String moduleName)
500 {
501 Map<String, Module> ret = new HashMap<>();
502 Module module = get(moduleName);
503 findParents(module,ret);
504 return ret.keySet();
505 }
506
507 private String toIndent(int depth)
508 {
509 char indent[] = new char[depth * 2];
510 Arrays.fill(indent,' ');
511 return new String(indent);
512 }
513
514
515
516
517
518
519
520
521 private void updateParentReferencesTo(Module module)
522 {
523 if (module.getName().equals(module.getFilesystemRef()))
524 {
525
526 return;
527 }
528
529 for (Module m : modules.values())
530 {
531 Set<String> resolvedParents = new HashSet<>();
532 for (String parent : m.getParentNames())
533 {
534 if (parent.equals(module.getFilesystemRef()))
535 {
536
537 resolvedParents.add(module.getName());
538 }
539 else
540 {
541
542 resolvedParents.add(parent);
543 }
544 }
545 m.setParentNames(resolvedParents);
546 }
547 }
548
549 @Override
550 public String toString()
551 {
552 StringBuilder str = new StringBuilder();
553 str.append("Modules[");
554 str.append("count=").append(modules.size());
555 str.append(",<");
556 boolean delim = false;
557 for (String name : modules.keySet())
558 {
559 if (delim)
560 {
561 str.append(',');
562 }
563 str.append(name);
564 delim = true;
565 }
566 str.append(">");
567 str.append("]");
568 return str.toString();
569 }
570 }