| // |
| // ======================================================================== |
| // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| // |
| |
| package org.eclipse.jetty.start; |
| |
| import java.io.BufferedReader; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.text.CollationKey; |
| import java.text.Collator; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Set; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** |
| * Represents a Module metadata, as defined in Jetty. |
| */ |
| public class Module |
| { |
| public static class DepthComparator implements Comparator<Module> |
| { |
| private Collator collator = Collator.getInstance(); |
| |
| @Override |
| public int compare(Module o1, Module o2) |
| { |
| // order by depth first. |
| int diff = o1.depth - o2.depth; |
| if (diff != 0) |
| { |
| return diff; |
| } |
| // then by name (not really needed, but makes for predictable test cases) |
| CollationKey k1 = collator.getCollationKey(o1.fileRef); |
| CollationKey k2 = collator.getCollationKey(o2.fileRef); |
| return k1.compareTo(k2); |
| } |
| } |
| |
| public static class NameComparator implements Comparator<Module> |
| { |
| private Collator collator = Collator.getInstance(); |
| |
| @Override |
| public int compare(Module o1, Module o2) |
| { |
| // by name (not really needed, but makes for predictable test cases) |
| CollationKey k1 = collator.getCollationKey(o1.fileRef); |
| CollationKey k2 = collator.getCollationKey(o2.fileRef); |
| return k1.compareTo(k2); |
| } |
| } |
| |
| /** The file of the module */ |
| private Path file; |
| /** The name of this Module (as a filesystem reference) */ |
| private String fileRef; |
| /** |
| * The logical name of this module (for property selected references), And to aid in duplicate detection. |
| */ |
| private String logicalName; |
| /** The depth of the module in the tree */ |
| private int depth = 0; |
| /** Set of Modules, by name, that this Module depends on */ |
| private Set<String> parentNames; |
| /** Set of Modules, by name, that this Module optionally depend on */ |
| private Set<String> optionalParentNames; |
| /** The Edges to parent modules */ |
| private Set<Module> parentEdges; |
| /** The Edges to child modules */ |
| private Set<Module> childEdges; |
| /** List of xml configurations for this Module */ |
| private List<String> xmls; |
| /** List of ini template lines */ |
| private List<String> defaultConfig; |
| private boolean hasDefaultConfig = false; |
| /** List of library options for this Module */ |
| private List<String> libs; |
| /** List of files for this Module */ |
| private List<String> files; |
| /** List of jvm Args */ |
| private List<String> jvmArgs; |
| /** License lines */ |
| private List<String> license; |
| |
| /** Is this Module enabled via start.jar command line, start.ini, or start.d/*.ini ? */ |
| private boolean enabled = false; |
| /** List of sources that enabled this module */ |
| private final Set<String> sources = new HashSet<>(); |
| private boolean licenseAck = false; |
| |
| public Module(BaseHome basehome, Path file) throws FileNotFoundException, IOException |
| { |
| this.file = file; |
| |
| // Strip .mod |
| this.fileRef = Pattern.compile(".mod$",Pattern.CASE_INSENSITIVE).matcher(file.getFileName().toString()).replaceFirst(""); |
| this.logicalName = fileRef; |
| |
| init(basehome); |
| process(basehome); |
| } |
| |
| public void addChildEdge(Module child) |
| { |
| if (childEdges.contains(child)) |
| { |
| // already present, skip |
| return; |
| } |
| this.childEdges.add(child); |
| } |
| |
| public void addParentEdge(Module parent) |
| { |
| if (parentEdges.contains(parent)) |
| { |
| // already present, skip |
| return; |
| } |
| this.parentEdges.add(parent); |
| } |
| |
| public void addSources(List<String> sources) |
| { |
| this.sources.addAll(sources); |
| } |
| |
| public void clearSources() |
| { |
| this.sources.clear(); |
| } |
| |
| @Override |
| public boolean equals(Object obj) |
| { |
| if (this == obj) |
| { |
| return true; |
| } |
| if (obj == null) |
| { |
| return false; |
| } |
| if (getClass() != obj.getClass()) |
| { |
| return false; |
| } |
| Module other = (Module)obj; |
| if (fileRef == null) |
| { |
| if (other.fileRef != null) |
| { |
| return false; |
| } |
| } |
| else if (!fileRef.equals(other.fileRef)) |
| { |
| return false; |
| } |
| return true; |
| } |
| |
| public void expandProperties(Props props) |
| { |
| // Expand Parents |
| Set<String> parents = new HashSet<>(); |
| for (String parent : parentNames) |
| { |
| parents.add(props.expand(parent)); |
| } |
| parentNames.clear(); |
| parentNames.addAll(parents); |
| } |
| |
| public Set<Module> getChildEdges() |
| { |
| return childEdges; |
| } |
| |
| public int getDepth() |
| { |
| return depth; |
| } |
| |
| public List<String> getFiles() |
| { |
| return files; |
| } |
| |
| public String getFilesystemRef() |
| { |
| return fileRef; |
| } |
| |
| public List<String> getDefaultConfig() |
| { |
| return defaultConfig; |
| } |
| |
| public boolean hasDefaultConfig() |
| { |
| return hasDefaultConfig; |
| } |
| |
| public List<String> getLibs() |
| { |
| return libs; |
| } |
| |
| public String getName() |
| { |
| return logicalName; |
| } |
| |
| public Set<String> getOptionalParentNames() |
| { |
| return optionalParentNames; |
| } |
| |
| public Set<Module> getParentEdges() |
| { |
| return parentEdges; |
| } |
| |
| public Set<String> getParentNames() |
| { |
| return parentNames; |
| } |
| |
| public Set<String> getSources() |
| { |
| return Collections.unmodifiableSet(sources); |
| } |
| |
| public List<String> getXmls() |
| { |
| return xmls; |
| } |
| |
| public List<String> getJvmArgs() |
| { |
| return jvmArgs; |
| } |
| |
| public boolean hasLicense() |
| { |
| return license != null && license.size() > 0; |
| } |
| |
| public boolean acknowledgeLicense() throws IOException |
| { |
| if (!hasLicense() || licenseAck) |
| { |
| return true; |
| } |
| |
| System.err.printf("%nModule %s:%n",getName()); |
| System.err.printf(" + contains software not provided by the Eclipse Foundation!%n"); |
| System.err.printf(" + contains software not covered by the Eclipse Public License!%n"); |
| System.err.printf(" + has not been audited for compliance with its license%n"); |
| System.err.printf("%n"); |
| for (String l : getLicense()) |
| { |
| System.err.printf(" %s%n",l); |
| } |
| |
| String propBasedAckName = "org.eclipse.jetty.start.ack.license." + getName(); |
| String propBasedAckValue = System.getProperty(propBasedAckName); |
| if (propBasedAckValue != null) |
| { |
| StartLog.log("TESTING MODE", "Programmatic ACK - %s=%s",propBasedAckName,propBasedAckValue); |
| licenseAck = Boolean.parseBoolean(propBasedAckValue); |
| } |
| else |
| { |
| if (Boolean.getBoolean("org.eclipse.jetty.start.testing")) |
| { |
| throw new RuntimeException("Test Configuration Missing - Pre-specify answer to (" + propBasedAckName + ") in test case"); |
| } |
| |
| try (BufferedReader input = new BufferedReader(new InputStreamReader(System.in))) |
| { |
| System.err.printf("%nProceed (y/N)? "); |
| String line = input.readLine(); |
| |
| licenseAck = !(line == null || line.length() == 0 || !line.toLowerCase(Locale.ENGLISH).startsWith("y")); |
| } |
| } |
| |
| return licenseAck; |
| } |
| |
| public List<String> getLicense() |
| { |
| return license; |
| } |
| |
| @Override |
| public int hashCode() |
| { |
| final int prime = 31; |
| int result = 1; |
| result = (prime * result) + ((fileRef == null)?0:fileRef.hashCode()); |
| return result; |
| } |
| |
| private void init(BaseHome basehome) |
| { |
| parentNames = new HashSet<>(); |
| optionalParentNames = new HashSet<>(); |
| parentEdges = new HashSet<>(); |
| childEdges = new HashSet<>(); |
| xmls = new ArrayList<>(); |
| defaultConfig = new ArrayList<>(); |
| libs = new ArrayList<>(); |
| files = new ArrayList<>(); |
| jvmArgs = new ArrayList<>(); |
| license = new ArrayList<>(); |
| |
| String name = basehome.toShortForm(file); |
| |
| // Find module system name (usually in the form of a filesystem reference) |
| Pattern pat = Pattern.compile("^.*[/\\\\]{1}modules[/\\\\]{1}(.*).mod$",Pattern.CASE_INSENSITIVE); |
| Matcher mat = pat.matcher(name); |
| if (!mat.find()) |
| { |
| throw new RuntimeException("Invalid Module location (must be located under /modules/ directory): " + name); |
| } |
| this.fileRef = mat.group(1).replace('\\','/'); |
| this.logicalName = this.fileRef; |
| } |
| |
| public boolean isEnabled() |
| { |
| return enabled; |
| } |
| |
| public boolean hasFiles(BaseHome baseHome) |
| { |
| for (String ref : getFiles()) |
| { |
| FileArg farg = new FileArg(this,ref); |
| Path refPath = baseHome.getBasePath(farg.location); |
| if (!Files.exists(refPath)) |
| { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| public void process(BaseHome basehome) throws FileNotFoundException, IOException |
| { |
| Pattern section = Pattern.compile("\\s*\\[([^]]*)\\]\\s*"); |
| |
| if (!FS.canReadFile(file)) |
| { |
| StartLog.debug("Skipping read of missing file: %s",basehome.toShortForm(file)); |
| return; |
| } |
| |
| try (BufferedReader buf = Files.newBufferedReader(file,StandardCharsets.UTF_8)) |
| { |
| String sectionType = ""; |
| String line; |
| while ((line = buf.readLine()) != null) |
| { |
| line = line.trim(); |
| |
| Matcher sectionMatcher = section.matcher(line); |
| |
| if (sectionMatcher.matches()) |
| { |
| sectionType = sectionMatcher.group(1).trim().toUpperCase(Locale.ENGLISH); |
| } |
| else |
| { |
| // blank lines and comments are valid for ini-template section |
| if ((line.length() == 0) || line.startsWith("#")) |
| { |
| if ("INI-TEMPLATE".equals(sectionType)) |
| { |
| defaultConfig.add(line); |
| } |
| } |
| else |
| { |
| switch (sectionType) |
| { |
| case "": |
| // ignore (this would be entries before first section) |
| break; |
| case "DEPEND": |
| parentNames.add(line); |
| break; |
| case "FILES": |
| files.add(line); |
| break; |
| case "DEFAULTS": |
| case "INI-TEMPLATE": |
| defaultConfig.add(line); |
| hasDefaultConfig = true; |
| break; |
| case "LIB": |
| libs.add(line); |
| break; |
| case "LICENSE": |
| case "LICENCE": |
| license.add(line); |
| break; |
| case "NAME": |
| logicalName = line; |
| break; |
| case "OPTIONAL": |
| optionalParentNames.add(line); |
| break; |
| case "EXEC": |
| jvmArgs.add(line); |
| break; |
| case "XML": |
| xmls.add(line); |
| break; |
| default: |
| throw new IOException("Unrecognized Module section: [" + sectionType + "]"); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| public void setDepth(int depth) |
| { |
| this.depth = depth; |
| } |
| |
| public void setEnabled(boolean enabled) |
| { |
| this.enabled = enabled; |
| } |
| |
| public void setParentNames(Set<String> parents) |
| { |
| this.parentNames.clear(); |
| this.parentEdges.clear(); |
| if (parents != null) |
| { |
| this.parentNames.addAll(parents); |
| } |
| } |
| |
| @Override |
| public String toString() |
| { |
| StringBuilder str = new StringBuilder(); |
| str.append("Module[").append(logicalName); |
| if (!logicalName.equals(fileRef)) |
| { |
| str.append(",file=").append(fileRef); |
| } |
| if (enabled) |
| { |
| str.append(",enabled"); |
| } |
| str.append(']'); |
| return str.toString(); |
| } |
| } |