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.File;
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
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 StartLog.debug("module not found [%s]%n",parentName);
121 }
122 else
123 {
124 module.addParentEdge(parent);
125 parent.addChildEdge(module);
126 }
127 }
128
129 for (String optionalParentName : module.getOptionalParentNames())
130 {
131 Module optional = get(optionalParentName);
132 if (optional == null)
133 {
134 StartLog.debug("optional module not found [%s]%n",optionalParentName);
135 }
136 else if (optional.isEnabled())
137 {
138 module.addParentEdge(optional);
139 optional.addChildEdge(module);
140 }
141 }
142 }
143
144
145 Stack<String> refs = new Stack<>();
146 for (Module module : modules.values())
147 {
148 refs.push(module.getName());
149 assertNoCycle(module,refs);
150 refs.pop();
151 }
152
153
154 for (Module module : modules.values())
155 {
156 if (module.getParentEdges().isEmpty())
157 {
158 bfsCalculateDepth(module,0);
159 }
160 }
161 }
162
163 public Integer count()
164 {
165 return modules.size();
166 }
167
168 public void dump()
169 {
170 List<Module> ordered = new ArrayList<>();
171 ordered.addAll(modules.values());
172 Collections.sort(ordered,new Module.NameComparator());
173
174 List<Module> active = resolveEnabled();
175
176 for (Module module : ordered)
177 {
178 boolean activated = active.contains(module);
179 boolean enabled = module.isEnabled();
180 boolean transitive = activated && !enabled;
181
182 char status = '-';
183 if (enabled)
184 {
185 status = '*';
186 }
187 else if (transitive)
188 {
189 status = '+';
190 }
191
192 System.out.printf("%n %s Module: %s%n",status,module.getName());
193 if (!module.getName().equals(module.getFilesystemRef()))
194 {
195 System.out.printf(" Ref: %s%n",module.getFilesystemRef());
196 }
197 for (String parent : module.getParentNames())
198 {
199 System.out.printf(" Parent: %s%n",parent);
200 }
201 for (String lib : module.getLibs())
202 {
203 System.out.printf(" LIB: %s%n",lib);
204 }
205 for (String xml : module.getXmls())
206 {
207 System.out.printf(" XML: %s%n",xml);
208 }
209 if (StartLog.isDebugEnabled())
210 {
211 System.out.printf(" depth: %d%n",module.getDepth());
212 }
213 if (activated)
214 {
215 for (String source : module.getSources())
216 {
217 System.out.printf(" Enabled: <via> %s%n",source);
218 }
219 if (transitive)
220 {
221 System.out.printf(" Enabled: <via transitive reference>%n");
222 }
223 }
224 else
225 {
226 System.out.printf(" Enabled: <not enabled in this configuration>%n");
227 }
228 }
229 }
230
231 public void dumpEnabledTree()
232 {
233 List<Module> ordered = new ArrayList<>();
234 ordered.addAll(modules.values());
235 Collections.sort(ordered,new Module.DepthComparator());
236
237 List<Module> active = resolveEnabled();
238
239 for (Module module : ordered)
240 {
241 if (active.contains(module))
242 {
243
244 String indent = toIndent(module.getDepth());
245 System.out.printf("%s + Module: %s [%s]%n",indent,module.getName(),module.isEnabled()?"enabled":"transitive");
246 }
247 }
248 }
249
250 public void enable(String name, List<String> sources)
251 {
252 if (name.contains("*"))
253 {
254
255 Pattern pat = Pattern.compile(name);
256 for (Map.Entry<String, Module> entry : modules.entrySet())
257 {
258 if (pat.matcher(entry.getKey()).matches())
259 {
260 enableModule(entry.getValue(),sources);
261 }
262 }
263 }
264 else
265 {
266 Module module = modules.get(name);
267 if (module == null)
268 {
269 System.err.printf("WARNING: Cannot enable requested module [%s]: not a valid module name.%n",name);
270 return;
271 }
272 enableModule(module,sources);
273 }
274 }
275
276 private void enableModule(Module module, List<String> sources)
277 {
278 StartLog.debug("Enabling module: %s (via %s)",module.getName(),Main.join(sources,", "));
279 module.setEnabled(true);
280 if (sources != null)
281 {
282 module.addSources(sources);
283 }
284 }
285
286 private void findChildren(Module module, Set<Module> ret)
287 {
288 ret.add(module);
289 for (Module child : module.getChildEdges())
290 {
291 ret.add(child);
292 }
293 }
294
295 private void findParents(Module module, Map<String, Module> ret)
296 {
297 ret.put(module.getName(), module);
298 for (Module parent : module.getParentEdges())
299 {
300 ret.put(parent.getName(), parent);
301 findParents(parent,ret);
302 }
303 }
304
305 public Module get(String name)
306 {
307 return modules.get(name);
308 }
309
310 public int getMaxDepth()
311 {
312 return maxDepth;
313 }
314
315 public Set<Module> getModulesAtDepth(int depth)
316 {
317 Set<Module> ret = new HashSet<>();
318 for (Module module : modules.values())
319 {
320 if (module.getDepth() == depth)
321 {
322 ret.add(module);
323 }
324 }
325 return ret;
326 }
327
328 @Override
329 public Iterator<Module> iterator()
330 {
331 return modules.values().iterator();
332 }
333
334 public List<String> normalizeLibs(List<Module> active)
335 {
336 List<String> libs = new ArrayList<>();
337 for (Module module : active)
338 {
339 for (String lib : module.getLibs())
340 {
341 if (!libs.contains(lib))
342 {
343 libs.add(lib);
344 }
345 }
346 }
347 return libs;
348 }
349
350 public List<String> normalizeXmls(List<Module> active)
351 {
352 List<String> xmls = new ArrayList<>();
353 for (Module module : active)
354 {
355 for (String xml : module.getXmls())
356 {
357 if (!xmls.contains(xml))
358 {
359 xmls.add(xml);
360 }
361 }
362 }
363 return xmls;
364 }
365
366 public Module register(Module module)
367 {
368 modules.put(module.getName(),module);
369 return module;
370 }
371
372 public void registerAll(BaseHome basehome, StartArgs args) throws IOException
373 {
374 for (File file : basehome.listFiles("modules",new FS.FilenameRegexFilter("^.*\\.mod$")))
375 {
376 registerModule(basehome,args,file);
377 }
378
379
380 boolean done = false;
381 while (!done)
382 {
383 done = true;
384 Set<String> missingParents = new HashSet<>();
385
386 for (Module m : modules.values())
387 {
388 for (String parent : m.getParentNames())
389 {
390 if (modules.containsKey(parent) || missingModules.contains(parent))
391 {
392 continue;
393 }
394 done = false;
395 missingParents.add(parent);
396 }
397 }
398
399 for (String missingParent : missingParents)
400 {
401 File file = basehome.getFile("modules/" + missingParent + ".mod");
402 if ( FS.canReadFile(file) )
403 {
404 Module module = registerModule(basehome,args,file);
405 updateParentReferencesTo(module);
406 }
407 else
408 {
409 StartLog.debug("Missing module definition: [ Mod: %s | File: %s]", missingParent, file);
410 missingModules.add(missingParent);
411 }
412 }
413 }
414 }
415
416 private Module registerModule(BaseHome basehome, StartArgs args, File file) throws FileNotFoundException, IOException
417 {
418 if (!FS.canReadFile(file))
419 {
420 throw new IOException("Cannot read file: " + file);
421 }
422 StartLog.debug("Registering Module: %s",basehome.toShortForm(file));
423 Module module = new Module(basehome,file);
424 module.expandProperties(args.getProperties());
425 return register(module);
426 }
427
428 public Set<String> resolveChildModulesOf(String moduleName)
429 {
430 Set<Module> ret = new HashSet<>();
431 Module module = get(moduleName);
432 findChildren(module,ret);
433 return asNameSet(ret);
434 }
435
436
437
438
439
440
441 public List<Module> resolveEnabled()
442 {
443 Map<String, Module> active = new HashMap<String,Module>();
444
445 for (Module module : modules.values())
446 {
447 if (module.isEnabled())
448 {
449 findParents(module,active);
450 }
451 }
452
453
454
455
456
457
458 for ( String missing : missingModules )
459 {
460 for (String activeModule: active.keySet())
461 {
462 if ( missing.startsWith(activeModule) )
463 {
464 StartLog.warn("** Unable to continue, required dependency missing. [%s]", missing);
465 StartLog.warn("** As configured, Jetty is unable to start due to a missing enabled module dependency.");
466 StartLog.warn("** This may be due to a transitive dependency akin to spdy on npn, which resolves based on the JDK in use.");
467 return Collections.emptyList();
468 }
469 }
470 }
471
472 List<Module> ordered = new ArrayList<>();
473 ordered.addAll(active.values());
474 Collections.sort(ordered,new Module.DepthComparator());
475 return ordered;
476 }
477
478 public Set<String> resolveParentModulesOf(String moduleName)
479 {
480 Map<String,Module> ret = new HashMap<>();
481 Module module = get(moduleName);
482 findParents(module,ret);
483 return ret.keySet();
484 }
485
486 private String toIndent(int depth)
487 {
488 char indent[] = new char[depth * 2];
489 Arrays.fill(indent,' ');
490 return new String(indent);
491 }
492
493
494
495
496
497
498
499
500 private void updateParentReferencesTo(Module module)
501 {
502 if (module.getName().equals(module.getFilesystemRef()))
503 {
504
505 return;
506 }
507
508 for (Module m : modules.values())
509 {
510 Set<String> resolvedParents = new HashSet<>();
511 for (String parent : m.getParentNames())
512 {
513 if (parent.equals(module.getFilesystemRef()))
514 {
515
516 resolvedParents.add(module.getName());
517 }
518 else
519 {
520
521 resolvedParents.add(parent);
522 }
523 }
524 m.setParentNames(resolvedParents);
525 }
526 }
527 }