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.BufferedReader;
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.io.InputStreamReader;
25 import java.nio.charset.StandardCharsets;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.text.CollationKey;
29 import java.text.Collator;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.Comparator;
33 import java.util.HashSet;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.Set;
37 import java.util.regex.Matcher;
38 import java.util.regex.Pattern;
39
40
41
42
43 public class Module
44 {
45 public static class DepthComparator implements Comparator<Module>
46 {
47 private Collator collator = Collator.getInstance();
48
49 @Override
50 public int compare(Module o1, Module o2)
51 {
52
53 int diff = o1.depth - o2.depth;
54 if (diff != 0)
55 {
56 return diff;
57 }
58
59 CollationKey k1 = collator.getCollationKey(o1.fileRef);
60 CollationKey k2 = collator.getCollationKey(o2.fileRef);
61 return k1.compareTo(k2);
62 }
63 }
64
65 public static class NameComparator implements Comparator<Module>
66 {
67 private Collator collator = Collator.getInstance();
68
69 @Override
70 public int compare(Module o1, Module o2)
71 {
72
73 CollationKey k1 = collator.getCollationKey(o1.fileRef);
74 CollationKey k2 = collator.getCollationKey(o2.fileRef);
75 return k1.compareTo(k2);
76 }
77 }
78
79
80 private Path file;
81
82 private String fileRef;
83
84
85
86 private String logicalName;
87
88 private int depth = 0;
89
90 private Set<String> parentNames;
91
92 private Set<String> optionalParentNames;
93
94 private Set<Module> parentEdges;
95
96 private Set<Module> childEdges;
97
98 private List<String> xmls;
99
100 private List<String> defaultConfig;
101 private boolean hasDefaultConfig = false;
102
103 private List<String> libs;
104
105 private List<String> files;
106
107 private List<String> jvmArgs;
108
109 private List<String> license;
110
111
112 private boolean enabled = false;
113
114 private final Set<String> sources = new HashSet<>();
115 private boolean licenseAck = false;
116
117 public Module(BaseHome basehome, Path file) throws FileNotFoundException, IOException
118 {
119 this.file = file;
120
121
122 this.fileRef = Pattern.compile(".mod$",Pattern.CASE_INSENSITIVE).matcher(file.getFileName().toString()).replaceFirst("");
123 this.logicalName = fileRef;
124
125 init(basehome);
126 process(basehome);
127 }
128
129 public void addChildEdge(Module child)
130 {
131 if (childEdges.contains(child))
132 {
133
134 return;
135 }
136 this.childEdges.add(child);
137 }
138
139 public void addParentEdge(Module parent)
140 {
141 if (parentEdges.contains(parent))
142 {
143
144 return;
145 }
146 this.parentEdges.add(parent);
147 }
148
149 public void addSources(List<String> sources)
150 {
151 this.sources.addAll(sources);
152 }
153
154 public void clearSources()
155 {
156 this.sources.clear();
157 }
158
159 @Override
160 public boolean equals(Object obj)
161 {
162 if (this == obj)
163 {
164 return true;
165 }
166 if (obj == null)
167 {
168 return false;
169 }
170 if (getClass() != obj.getClass())
171 {
172 return false;
173 }
174 Module other = (Module)obj;
175 if (fileRef == null)
176 {
177 if (other.fileRef != null)
178 {
179 return false;
180 }
181 }
182 else if (!fileRef.equals(other.fileRef))
183 {
184 return false;
185 }
186 return true;
187 }
188
189 public void expandProperties(Props props)
190 {
191
192 Set<String> parents = new HashSet<>();
193 for (String parent : parentNames)
194 {
195 parents.add(props.expand(parent));
196 }
197 parentNames.clear();
198 parentNames.addAll(parents);
199 }
200
201 public Set<Module> getChildEdges()
202 {
203 return childEdges;
204 }
205
206 public int getDepth()
207 {
208 return depth;
209 }
210
211 public List<String> getFiles()
212 {
213 return files;
214 }
215
216 public String getFilesystemRef()
217 {
218 return fileRef;
219 }
220
221 public List<String> getDefaultConfig()
222 {
223 return defaultConfig;
224 }
225
226 public boolean hasDefaultConfig()
227 {
228 return hasDefaultConfig;
229 }
230
231 public List<String> getLibs()
232 {
233 return libs;
234 }
235
236 public String getName()
237 {
238 return logicalName;
239 }
240
241 public Set<String> getOptionalParentNames()
242 {
243 return optionalParentNames;
244 }
245
246 public Set<Module> getParentEdges()
247 {
248 return parentEdges;
249 }
250
251 public Set<String> getParentNames()
252 {
253 return parentNames;
254 }
255
256 public Set<String> getSources()
257 {
258 return Collections.unmodifiableSet(sources);
259 }
260
261 public List<String> getXmls()
262 {
263 return xmls;
264 }
265
266 public List<String> getJvmArgs()
267 {
268 return jvmArgs;
269 }
270
271 public boolean hasLicense()
272 {
273 return license != null && license.size() > 0;
274 }
275
276 public boolean acknowledgeLicense() throws IOException
277 {
278 if (!hasLicense() || licenseAck)
279 {
280 return true;
281 }
282
283 System.err.printf("%nModule %s:%n",getName());
284 System.err.printf(" + contains software not provided by the Eclipse Foundation!%n");
285 System.err.printf(" + contains software not covered by the Eclipse Public License!%n");
286 System.err.printf(" + has not been audited for compliance with its license%n");
287 System.err.printf("%n");
288 for (String l : getLicense())
289 {
290 System.err.printf(" %s%n",l);
291 }
292
293 String propBasedAckName = "org.eclipse.jetty.start.ack.license." + getName();
294 String propBasedAckValue = System.getProperty(propBasedAckName);
295 if (propBasedAckValue != null)
296 {
297 StartLog.log("TESTING MODE", "Programmatic ACK - %s=%s",propBasedAckName,propBasedAckValue);
298 licenseAck = Boolean.parseBoolean(propBasedAckValue);
299 }
300 else
301 {
302 if (Boolean.getBoolean("org.eclipse.jetty.start.testing"))
303 {
304 throw new RuntimeException("Test Configuration Missing - Pre-specify answer to (" + propBasedAckName + ") in test case");
305 }
306
307 try (BufferedReader input = new BufferedReader(new InputStreamReader(System.in)))
308 {
309 System.err.printf("%nProceed (y/N)? ");
310 String line = input.readLine();
311
312 licenseAck = !(line == null || line.length() == 0 || !line.toLowerCase().startsWith("y"));
313 }
314 }
315
316 return licenseAck;
317 }
318
319 public List<String> getLicense()
320 {
321 return license;
322 }
323
324 @Override
325 public int hashCode()
326 {
327 final int prime = 31;
328 int result = 1;
329 result = (prime * result) + ((fileRef == null)?0:fileRef.hashCode());
330 return result;
331 }
332
333 private void init(BaseHome basehome)
334 {
335 parentNames = new HashSet<>();
336 optionalParentNames = new HashSet<>();
337 parentEdges = new HashSet<>();
338 childEdges = new HashSet<>();
339 xmls = new ArrayList<>();
340 defaultConfig = new ArrayList<>();
341 libs = new ArrayList<>();
342 files = new ArrayList<>();
343 jvmArgs = new ArrayList<>();
344 license = new ArrayList<>();
345
346 String name = basehome.toShortForm(file);
347
348
349 Pattern pat = Pattern.compile("^.*[/\\\\]{1}modules[/\\\\]{1}(.*).mod$",Pattern.CASE_INSENSITIVE);
350 Matcher mat = pat.matcher(name);
351 if (!mat.find())
352 {
353 throw new RuntimeException("Invalid Module location (must be located under /modules/ directory): " + name);
354 }
355 this.fileRef = mat.group(1).replace('\\','/');
356 this.logicalName = this.fileRef;
357 }
358
359 public boolean isEnabled()
360 {
361 return enabled;
362 }
363
364 public boolean hasFiles(BaseHome baseHome)
365 {
366 for (String ref : getFiles())
367 {
368 FileArg farg = new FileArg(this,ref);
369 Path refPath = baseHome.getBasePath(farg.location);
370 if (!Files.exists(refPath))
371 {
372 return false;
373 }
374 }
375 return true;
376 }
377
378 public void process(BaseHome basehome) throws FileNotFoundException, IOException
379 {
380 Pattern section = Pattern.compile("\\s*\\[([^]]*)\\]\\s*");
381
382 if (!FS.canReadFile(file))
383 {
384 StartLog.debug("Skipping read of missing file: %s",basehome.toShortForm(file));
385 return;
386 }
387
388 try (BufferedReader buf = Files.newBufferedReader(file,StandardCharsets.UTF_8))
389 {
390 String sectionType = "";
391 String line;
392 while ((line = buf.readLine()) != null)
393 {
394 line = line.trim();
395
396 Matcher sectionMatcher = section.matcher(line);
397
398 if (sectionMatcher.matches())
399 {
400 sectionType = sectionMatcher.group(1).trim().toUpperCase(Locale.ENGLISH);
401 }
402 else
403 {
404
405 if ((line.length() == 0) || line.startsWith("#"))
406 {
407 if ("INI-TEMPLATE".equals(sectionType))
408 {
409 defaultConfig.add(line);
410 }
411 }
412 else
413 {
414 switch (sectionType)
415 {
416 case "":
417
418 break;
419 case "DEPEND":
420 parentNames.add(line);
421 break;
422 case "FILES":
423 files.add(line);
424 break;
425 case "DEFAULTS":
426 case "INI-TEMPLATE":
427 defaultConfig.add(line);
428 hasDefaultConfig = true;
429 break;
430 case "LIB":
431 libs.add(line);
432 break;
433 case "LICENSE":
434 case "LICENCE":
435 license.add(line);
436 break;
437 case "NAME":
438 logicalName = line;
439 break;
440 case "OPTIONAL":
441 optionalParentNames.add(line);
442 break;
443 case "EXEC":
444 jvmArgs.add(line);
445 break;
446 case "XML":
447 xmls.add(line);
448 break;
449 default:
450 throw new IOException("Unrecognized Module section: [" + sectionType + "]");
451 }
452 }
453 }
454 }
455 }
456 }
457
458 public void setDepth(int depth)
459 {
460 this.depth = depth;
461 }
462
463 public void setEnabled(boolean enabled)
464 {
465 this.enabled = enabled;
466 }
467
468 public void setParentNames(Set<String> parents)
469 {
470 this.parentNames.clear();
471 this.parentEdges.clear();
472 if (parents != null)
473 {
474 this.parentNames.addAll(parents);
475 }
476 }
477
478 @Override
479 public String toString()
480 {
481 StringBuilder str = new StringBuilder();
482 str.append("Module[").append(logicalName);
483 if (!logicalName.equals(fileRef))
484 {
485 str.append(",file=").append(fileRef);
486 }
487 if (enabled)
488 {
489 str.append(",enabled");
490 }
491 str.append(']');
492 return str.toString();
493 }
494 }