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.BufferedWriter;
23 import java.io.FileNotFoundException;
24 import java.io.IOException;
25 import java.io.PrintWriter;
26 import java.nio.charset.StandardCharsets;
27 import java.nio.file.Files;
28 import java.nio.file.Path;
29 import java.util.ArrayList;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Locale;
33 import java.util.Set;
34 import java.util.function.Function;
35 import java.util.regex.Matcher;
36 import java.util.regex.Pattern;
37 import java.util.stream.Collectors;
38
39 import org.eclipse.jetty.start.Props.Prop;
40 import org.eclipse.jetty.start.config.CommandLineConfigSource;
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 public class Module implements Comparable<Module>
62 {
63 private static final String VERSION_UNSPECIFIED = "9.2";
64 static Pattern MOD_NAME = Pattern.compile("^(.*)\\.mod",Pattern.CASE_INSENSITIVE);
65 static Pattern SET_PROPERTY = Pattern.compile("^(#?)\\s*([^=\\s]+)=(.*)$");
66
67
68 private final Path _path;
69
70
71 private final String _name;
72
73
74 private final boolean _dynamic;
75
76
77 private Version version;
78
79
80 private final List<String> _description=new ArrayList<>();
81
82
83 private final List<String> _xmls=new ArrayList<>();
84
85
86 private final List<String> _iniTemplate=new ArrayList<>();
87
88
89 private final List<String> _defaultConfig=new ArrayList<>();
90
91
92 private final List<String> _libs=new ArrayList<>();
93
94
95 private final List<String> _files=new ArrayList<>();
96
97
98 private final Set<String> _enables=new HashSet<>();
99
100
101 private final Set<String> _provides=new HashSet<>();
102
103
104 private final List<String> _tags=new ArrayList<>();
105
106
107 private boolean _notTransitive;
108
109
110 private boolean _skipFilesValidation = false;
111
112
113 private final List<String> _jvmArgs=new ArrayList<>();
114
115
116 private final List<String> _license=new ArrayList<>();
117
118
119 private final Set<String> _depends=new HashSet<>();
120
121
122 private final Set<String> _optional=new HashSet<>();
123
124 public Module(BaseHome basehome, Path path) throws FileNotFoundException, IOException
125 {
126 super();
127 _path = path;
128
129
130 int m=-1;
131 for (int i=path.getNameCount();i-->0;)
132 {
133 if ("modules".equals(path.getName(i).toString()))
134 {
135 m=i;
136 break;
137 }
138 }
139 if (m<0)
140 throw new IllegalArgumentException("Module not contained within modules directory: "+basehome.toShortForm(path));
141 String n=path.getName(m+1).toString();
142 for (int i=m+2;i<path.getNameCount();i++)
143 n=n+"/"+path.getName(i).toString();
144 Matcher matcher=MOD_NAME.matcher(n);
145 if (!matcher.matches())
146 throw new IllegalArgumentException("Module filename must have .mod extension: "+basehome.toShortForm(path));
147 _name=matcher.group(1);
148
149 _provides.add(_name);
150 _dynamic=_name.contains("/");
151
152 process(basehome);
153 }
154
155 public String getName()
156 {
157 return _name;
158 }
159
160 @Override
161 public boolean equals(Object obj)
162 {
163 if (this == obj)
164 {
165 return true;
166 }
167 if (obj == null)
168 {
169 return false;
170 }
171 if (getClass() != obj.getClass())
172 {
173 return false;
174 }
175 Module other = (Module)obj;
176 if (_path == null)
177 return other._path == null;
178
179 return _path.equals(other._path);
180 }
181
182 public void expandProperties(Props props)
183 {
184 Function<String,String> expander = d->{return props.expand(d);};
185
186 Set<String> tmp=_depends.stream().map(expander).collect(Collectors.toSet());
187 _depends.clear();
188 _depends.addAll(tmp);
189 tmp=_optional.stream().map(expander).collect(Collectors.toSet());
190 _optional.clear();
191 _optional.addAll(tmp);
192 }
193
194 public List<String> getDefaultConfig()
195 {
196 return _defaultConfig;
197 }
198
199 public List<String> getIniTemplate()
200 {
201 return _iniTemplate;
202 }
203
204 public List<String> getFiles()
205 {
206 return _files;
207 }
208
209 public boolean isSkipFilesValidation()
210 {
211 return _skipFilesValidation;
212 }
213
214 public List<String> getJvmArgs()
215 {
216 return _jvmArgs;
217 }
218
219 public List<String> getLibs()
220 {
221 return _libs;
222 }
223
224 public List<String> getLicense()
225 {
226 return _license;
227 }
228
229 public List<String> getXmls()
230 {
231 return _xmls;
232 }
233
234 public Version getVersion()
235 {
236 return version;
237 }
238
239 public boolean hasDefaultConfig()
240 {
241 return !_defaultConfig.isEmpty();
242 }
243
244 public boolean hasIniTemplate()
245 {
246 return !_iniTemplate.isEmpty();
247 }
248
249 @Override
250 public int hashCode()
251 {
252 return _name.hashCode();
253 }
254
255 public boolean hasLicense()
256 {
257 return (_license != null) && (_license.size() > 0);
258 }
259
260
261
262
263
264
265 public boolean isDynamic()
266 {
267 return _dynamic;
268 }
269
270 public boolean hasFiles(BaseHome baseHome, Props props)
271 {
272 for (String ref : getFiles())
273 {
274 FileArg farg = new FileArg(this,props.expand(ref));
275 Path refPath = baseHome.getBasePath(farg.location);
276 if (!Files.exists(refPath))
277 {
278 return false;
279 }
280 }
281 return true;
282 }
283
284 public void process(BaseHome basehome) throws FileNotFoundException, IOException
285 {
286 Pattern section = Pattern.compile("\\s*\\[([^]]*)\\]\\s*");
287
288 if (!FS.canReadFile(_path))
289 {
290 StartLog.debug("Skipping read of missing file: %s",basehome.toShortForm(_path));
291 return;
292 }
293
294 try (BufferedReader buf = Files.newBufferedReader(_path,StandardCharsets.UTF_8))
295 {
296 String sectionType = "";
297 String line;
298 while ((line = buf.readLine()) != null)
299 {
300 line = line.trim();
301
302 Matcher sectionMatcher = section.matcher(line);
303
304 if (sectionMatcher.matches())
305 {
306 sectionType = sectionMatcher.group(1).trim().toUpperCase(Locale.ENGLISH);
307 }
308 else
309 {
310
311 if ((line.length() == 0) || line.startsWith("#"))
312 {
313
314
315 if ("INI-TEMPLATE".equals(sectionType))
316 {
317 _iniTemplate.add(line);
318 }
319 }
320 else
321 {
322 switch (sectionType)
323 {
324 case "":
325
326 break;
327 case "DESCRIPTION":
328 _description.add(line);
329 break;
330 case "DEPEND":
331 case "DEPENDS":
332 _depends.add(line);
333 break;
334 case "FILE":
335 case "FILES":
336 _files.add(line);
337 break;
338 case "TAG":
339 case "TAGS":
340 _tags.add(line);
341 break;
342 case "DEFAULTS":
343 case "INI":
344 _defaultConfig.add(line);
345 break;
346 case "INI-TEMPLATE":
347 _iniTemplate.add(line);
348 break;
349 case "LIB":
350 case "LIBS":
351 _libs.add(line);
352 break;
353 case "LICENSE":
354 case "LICENSES":
355 case "LICENCE":
356 case "LICENCES":
357 _license.add(line);
358 break;
359 case "NAME":
360 StartLog.warn("Deprecated [name] used in %s",basehome.toShortForm(_path));
361 _provides.add(line);
362 break;
363 case "PROVIDE":
364 case "PROVIDES":
365 _provides.add(line);
366 break;
367 case "OPTIONAL":
368 _optional.add(line);
369 break;
370 case "EXEC":
371 _jvmArgs.add(line);
372 break;
373 case "VERSION":
374 if (version != null)
375 {
376 throw new IOException("[version] already specified");
377 }
378 version = new Version(line);
379 break;
380 case "XML":
381 _xmls.add(line);
382 break;
383 default:
384 throw new IOException("Unrecognized module section: [" + sectionType + "]");
385 }
386 }
387 }
388 }
389 }
390
391 if (version == null)
392 {
393 version = new Version(VERSION_UNSPECIFIED);
394 }
395 }
396
397 public boolean clearTransitiveEnable()
398 {
399 if (_notTransitive)
400 throw new IllegalStateException("Not Transitive");
401 if (isEnabled())
402 {
403 _enables.clear();
404 return true;
405 }
406 return false;
407 }
408
409 public void setSkipFilesValidation(boolean skipFilesValidation)
410 {
411 this._skipFilesValidation = skipFilesValidation;
412 }
413
414 @Override
415 public String toString()
416 {
417 StringBuilder str = new StringBuilder();
418 str.append(getName());
419 char sep='{';
420 if (isDynamic())
421 {
422 str.append(sep).append("dynamic");
423 sep=',';
424 }
425 if (isEnabled())
426 {
427 str.append(sep).append("enabled");
428 sep=',';
429 }
430 if (isTransitive())
431 {
432 str.append(sep).append("transitive");
433 sep=',';
434 }
435 if (sep!='{')
436 str.append('}');
437 return str.toString();
438 }
439
440 public Set<String> getDepends()
441 {
442 return new HashSet<>(_depends);
443 }
444
445 public Set<String> getProvides()
446 {
447 return new HashSet<>(_provides);
448 }
449
450 public Set<String> getOptional()
451 {
452 return new HashSet<>(_optional);
453 }
454
455 public List<String> getDescription()
456 {
457 return _description;
458 }
459
460 public List<String> getTags()
461 {
462 return _tags;
463 }
464
465 public String getPrimaryTag()
466 {
467 return _tags.isEmpty()?"*":_tags.get(0);
468 }
469
470 public boolean isEnabled()
471 {
472 return !_enables.isEmpty();
473 }
474
475 public Set<String> getEnableSources()
476 {
477 return new HashSet<>(_enables);
478 }
479
480
481
482
483
484
485 public boolean enable(String source,boolean transitive)
486 {
487 boolean updated=_enables.isEmpty();
488 if (transitive)
489 {
490
491 if (!_notTransitive)
492 _enables.add(source);
493 }
494 else
495 {
496 if (!_notTransitive)
497 {
498
499 updated=true;
500 _enables.clear();
501 }
502 _notTransitive=true;
503 _enables.add(source);
504 }
505 return updated;
506 }
507
508 public boolean isTransitive()
509 {
510 return isEnabled() && !_notTransitive;
511 }
512
513 public void writeIniSection(BufferedWriter writer, Props props)
514 {
515 PrintWriter out = new PrintWriter(writer);
516 out.println("# --------------------------------------- ");
517 out.println("# Module: " + getName());
518 for (String line : getDescription())
519 out.append("# ").println(line);
520 out.println("# --------------------------------------- ");
521 out.println("--module=" + getName());
522 out.println();
523 for (String line : getIniTemplate())
524 {
525 Matcher m = SET_PROPERTY.matcher(line);
526 if (m.matches() && m.groupCount()==3)
527 {
528 String name = m.group(2);
529 Prop p = props.getProp(name);
530 if (p!=null && p.origin.startsWith(CommandLineConfigSource.ORIGIN_CMD_LINE))
531 {
532 StartLog.info("%-15s property set %s=%s",this._name,name,p.value);
533 out.printf("%s=%s%n",name,p.value);
534 }
535 else
536 out.println(line);
537 }
538 else
539 out.println(line);
540 }
541 out.println();
542 out.flush();
543 }
544
545 @Override
546 public int compareTo(Module m)
547 {
548 int by_tag = getPrimaryTag().compareTo(m.getPrimaryTag());
549 if (by_tag!=0)
550 return by_tag;
551 return getName().compareTo(m.getName());
552 }
553 }