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 void process(BaseHome basehome) throws FileNotFoundException, IOException
365 {
366 Pattern section = Pattern.compile("\\s*\\[([^]]*)\\]\\s*");
367
368 if (!FS.canReadFile(file))
369 {
370 StartLog.debug("Skipping read of missing file: %s",basehome.toShortForm(file));
371 return;
372 }
373
374 try (BufferedReader buf = Files.newBufferedReader(file,StandardCharsets.UTF_8))
375 {
376 String sectionType = "";
377 String line;
378 while ((line = buf.readLine()) != null)
379 {
380 line = line.trim();
381
382 Matcher sectionMatcher = section.matcher(line);
383
384 if (sectionMatcher.matches())
385 {
386 sectionType = sectionMatcher.group(1).trim().toUpperCase(Locale.ENGLISH);
387 }
388 else
389 {
390
391 if ((line.length() == 0) || line.startsWith("#"))
392 {
393 if ("INI-TEMPLATE".equals(sectionType))
394 {
395 defaultConfig.add(line);
396 }
397 }
398 else
399 {
400 switch (sectionType)
401 {
402 case "":
403
404 break;
405 case "DEPEND":
406 parentNames.add(line);
407 break;
408 case "FILES":
409 files.add(line);
410 break;
411 case "DEFAULTS":
412 case "INI-TEMPLATE":
413 defaultConfig.add(line);
414 hasDefaultConfig = true;
415 break;
416 case "LIB":
417 libs.add(line);
418 break;
419 case "LICENSE":
420 case "LICENCE":
421 license.add(line);
422 break;
423 case "NAME":
424 logicalName = line;
425 break;
426 case "OPTIONAL":
427 optionalParentNames.add(line);
428 break;
429 case "EXEC":
430 jvmArgs.add(line);
431 break;
432 case "XML":
433 xmls.add(line);
434 break;
435 default:
436 throw new IOException("Unrecognized Module section: [" + sectionType + "]");
437 }
438 }
439 }
440 }
441 }
442 }
443
444 public void setDepth(int depth)
445 {
446 this.depth = depth;
447 }
448
449 public void setEnabled(boolean enabled)
450 {
451 this.enabled = enabled;
452 }
453
454 public void setParentNames(Set<String> parents)
455 {
456 this.parentNames.clear();
457 this.parentEdges.clear();
458 if (parents != null)
459 {
460 this.parentNames.addAll(parents);
461 }
462 }
463
464 @Override
465 public String toString()
466 {
467 StringBuilder str = new StringBuilder();
468 str.append("Module[").append(logicalName);
469 if (!logicalName.equals(fileRef))
470 {
471 str.append(",file=").append(fileRef);
472 }
473 if (enabled)
474 {
475 str.append(",enabled");
476 }
477 str.append(']');
478 return str.toString();
479 }
480
481 }