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.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
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
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
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
91 for (Module child : module.getChildEdges())
92 {
93 bfsCalculateDepth(child,depth);
94 }
95 }
96
97
98
99
100 public void buildGraph()
101 {
102
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
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
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
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
326
327
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 }