452323 - Start --list-config makes no hint on transitive enabled modules

+ Refactoring out the Graph + Node logic into its own package
+ Changing Matcher to Predicate
+ Introducing new Node.selection Set which tracks how the various nodes
  are selected, along with with ability to track directly selected (aka
  explicit) vs transitive.
+ Module now extends from Node
+ Modules now extends from Graph<Module>
This commit is contained in:
Joakim Erdfelt 2014-11-19 15:59:54 -07:00
parent 1561de0f81
commit 005b513653
22 changed files with 1528 additions and 828 deletions

View File

@ -25,16 +25,14 @@ import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.start.Modules.AndMatcher;
import org.eclipse.jetty.start.Modules.EnabledMatcher;
import org.eclipse.jetty.start.Modules.Matcher;
import org.eclipse.jetty.start.Modules.SourceSetMatcher;
import org.eclipse.jetty.start.Modules.UniqueSourceMatcher;
import org.eclipse.jetty.start.builders.StartDirBuilder;
import org.eclipse.jetty.start.builders.StartIniBuilder;
import org.eclipse.jetty.start.fileinits.MavenLocalRepoFileInitializer;
import org.eclipse.jetty.start.fileinits.TestFileInitializer;
import org.eclipse.jetty.start.fileinits.UriFileInitializer;
import org.eclipse.jetty.start.graph.HowSetMatcher;
import org.eclipse.jetty.start.graph.Predicate;
import org.eclipse.jetty.start.graph.Selection;
/**
* Build a start configuration in <code>${jetty.base}</code>, including
@ -137,16 +135,18 @@ public class BaseBuilder
String dirSource = "<add-to-startd>";
String iniSource = "<add-to-start-ini>";
Selection startDirSelection = new Selection(dirSource);
Selection startIniSelection = new Selection(iniSource);
int count = 0;
count += modules.enableAll(startArgs.getAddToStartdIni(),dirSource);
count += modules.enableAll(startArgs.getAddToStartIni(),iniSource);
count += modules.selectNodes(startArgs.getAddToStartdIni(),startDirSelection);
count += modules.selectNodes(startArgs.getAddToStartIni(),startIniSelection);
Matcher startDMatcher = new AndMatcher(new EnabledMatcher(),new UniqueSourceMatcher(dirSource));
Matcher startIniMatcher = new AndMatcher(new EnabledMatcher(),new UniqueSourceMatcher(iniSource));
Predicate startDMatcher = new HowSetMatcher(dirSource);
Predicate startIniMatcher = new HowSetMatcher(iniSource);
// look for ambiguous declaration in 2 places
Matcher ambiguousMatcher = new AndMatcher(new EnabledMatcher(),new SourceSetMatcher(dirSource,iniSource));
// look for ambiguous declaration found in both places
Predicate ambiguousMatcher = new HowSetMatcher(dirSource,iniSource);
List<Module> ambiguous = modules.getMatching(ambiguousMatcher);
if (ambiguous.size() > 0)

View File

@ -38,6 +38,8 @@ import java.util.List;
import java.util.Locale;
import org.eclipse.jetty.start.config.CommandLineConfigSource;
import org.eclipse.jetty.start.graph.GraphException;
import org.eclipse.jetty.start.graph.Selection;
/**
* Main start class.
@ -285,31 +287,40 @@ public class Main
StartArgs args = new StartArgs();
args.parse(baseHome.getConfigSources());
// ------------------------------------------------------------
// 3) Module Registration
Modules modules = new Modules(baseHome,args);
StartLog.debug("Registering all modules");
modules.registerAll();
// ------------------------------------------------------------
// 4) Active Module Resolution
for (String enabledModule : args.getEnabledModules())
try
{
List<String> msources = args.getSources(enabledModule);
modules.enable(enabledModule,msources);
}
StartLog.debug("Building Module Graph");
modules.buildGraph();
// ------------------------------------------------------------
// 3) Module Registration
Modules modules = new Modules(baseHome,args);
StartLog.debug("Registering all modules");
modules.registerAll();
args.setAllModules(modules);
List<Module> activeModules = modules.getEnabled();
modules.assertModulesValid(activeModules);
// ------------------------------------------------------------
// 5) Lib & XML Expansion / Resolution
args.expandLibs(baseHome);
args.expandModules(baseHome,activeModules);
// ------------------------------------------------------------
// 4) Active Module Resolution
for (String enabledModule : args.getEnabledModules())
{
for (String source : args.getSources(enabledModule))
{
String shortForm = baseHome.toShortForm(source);
modules.selectNode(enabledModule,new Selection(shortForm));
}
}
StartLog.debug("Building Module Graph");
modules.buildGraph();
args.setAllModules(modules);
List<Module> activeModules = modules.getEnabled();
modules.assertModulesValid(activeModules);
// ------------------------------------------------------------
// 5) Lib & XML Expansion / Resolution
args.expandLibs(baseHome);
args.expandModules(baseHome,activeModules);
} catch(GraphException e) {
throw new UsageException(ERR_BAD_GRAPH,e);
}
// ------------------------------------------------------------
// 6) Resolve Extra XMLs

View File

@ -21,14 +21,12 @@ 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;
@ -37,31 +35,13 @@ import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jetty.start.graph.Node;
/**
* Represents a Module metadata, as defined in Jetty.
*/
public class Module
public class Module extends Node<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();
@ -80,20 +60,7 @@ public class 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 */
@ -108,54 +75,19 @@ public class Module
/** 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
{
super();
this.file = file;
// Strip .mod
this.fileRef = Pattern.compile(".mod$",Pattern.CASE_INSENSITIVE).matcher(file.getFileName().toString()).replaceFirst("");
this.logicalName = fileRef;
this.setName(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)
{
@ -190,22 +122,16 @@ public class Module
{
// Expand Parents
Set<String> parents = new HashSet<>();
for (String parent : parentNames)
for (String parent : getParentNames())
{
parents.add(props.expand(parent));
}
parentNames.clear();
parentNames.addAll(parents);
setParentNames(parents);
}
public Set<Module> getChildEdges()
public List<String> getDefaultConfig()
{
return childEdges;
}
public int getDepth()
{
return depth;
return defaultConfig;
}
public List<String> getFiles()
@ -218,14 +144,9 @@ public class Module
return fileRef;
}
public List<String> getDefaultConfig()
public List<String> getJvmArgs()
{
return defaultConfig;
}
public boolean hasDefaultConfig()
{
return hasDefaultConfig;
return jvmArgs;
}
public List<String> getLibs()
@ -233,29 +154,9 @@ public class Module
return libs;
}
public String getName()
public List<String> getLicense()
{
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);
return license;
}
public List<String> getXmls()
@ -263,85 +164,9 @@ public class Module
return xmls;
}
public List<String> getJvmArgs()
public boolean hasDefaultConfig()
{
return jvmArgs;
}
public boolean hasLicense()
{
return license != null && license.size() > 0;
}
public boolean hasSource(String regex)
{
for (String source : sources)
{
if (source.matches(regex))
{
return true;
}
}
return false;
}
public boolean hasUniqueSource(String regex)
{
if (sources.size() != 1)
{
return false;
}
return sources.iterator().next().matches(regex);
}
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().startsWith("y"));
}
}
return licenseAck;
}
public List<String> getLicense()
{
return license;
return hasDefaultConfig;
}
@Override
@ -353,12 +178,13 @@ public class Module
return result;
}
public boolean hasLicense()
{
return (license != null) && (license.size() > 0);
}
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<>();
@ -376,17 +202,18 @@ public class Module
throw new RuntimeException("Invalid Module location (must be located under /modules/ directory): " + name);
}
this.fileRef = mat.group(1).replace('\\','/');
this.logicalName = this.fileRef;
setName(this.fileRef);
}
@Deprecated
public boolean isEnabled()
{
return enabled;
return isSelected();
}
public boolean isVirtual()
{
return !logicalName.equals(fileRef);
return !getName().equals(fileRef);
}
public void process(BaseHome basehome) throws FileNotFoundException, IOException
@ -431,7 +258,7 @@ public class Module
// ignore (this would be entries before first section)
break;
case "DEPEND":
parentNames.add(line);
addParentName(line);
break;
case "FILES":
files.add(line);
@ -449,10 +276,10 @@ public class Module
license.add(line);
break;
case "NAME":
logicalName = line;
setName(line);
break;
case "OPTIONAL":
optionalParentNames.add(line);
addOptionalParentName(line);
break;
case "EXEC":
jvmArgs.add(line);
@ -469,38 +296,23 @@ public class Module
}
}
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);
}
throw new RuntimeException("Don't enable directly");
}
@Override
public String toString()
{
StringBuilder str = new StringBuilder();
str.append("Module[").append(logicalName);
if (!logicalName.equals(fileRef))
str.append("Module[").append(getName());
if (isVirtual())
{
str.append(",file=").append(fileRef);
}
if (enabled)
if (isSelected())
{
str.append(",enabled");
str.append(",selected");
}
str.append(']');
return str.toString();

View File

@ -28,6 +28,10 @@ import java.nio.file.StandardOpenOption;
import java.util.Collection;
import java.util.List;
import org.eclipse.jetty.start.graph.Graph;
import org.eclipse.jetty.start.graph.Node;
import org.eclipse.jetty.start.graph.Selection;
/**
* Generate a graphviz dot graph of the modules found
*/
@ -182,9 +186,9 @@ public class ModuleGraphWriter
if (module.isEnabled())
{
writeModuleDetailHeader(out,"ENABLED");
for (String source : module.getSources())
for (Selection selection : module.getSelections())
{
writeModuleDetailLine(out,"via: " + source);
writeModuleDetailLine(out,"via: " + selection);
}
}
else if (resolved)
@ -247,11 +251,11 @@ public class ModuleGraphWriter
}
}
private void writeRelationships(PrintWriter out, Modules modules, List<Module> enabled)
private void writeRelationships(PrintWriter out, Graph<Module> modules, List<Module> enabled)
{
for (Module module : modules)
{
for (Module parent : module.getParentEdges())
for (Node<?> parent : module.getParentEdges())
{
out.printf(" \"%s\" -> \"%s\";%n",module.getName(),parent.getName());
}

View File

@ -18,309 +18,55 @@
package org.eclipse.jetty.start;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.regex.Pattern;
import org.eclipse.jetty.start.graph.Graph;
import org.eclipse.jetty.start.graph.GraphException;
import org.eclipse.jetty.start.graph.NodeDepthComparator;
import org.eclipse.jetty.start.graph.OnlyTransitivePredicate;
import org.eclipse.jetty.start.graph.Selection;
/**
* Access for all modules declared, as well as what is enabled.
*/
public class Modules implements Iterable<Module>
public class Modules extends Graph<Module>
{
public static interface Matcher
{
public boolean match(Module module);
}
public static class AllMatcher implements Matcher
{
@Override
public boolean match(Module module)
{
return true;
}
}
public static class AndMatcher implements Matcher
{
private final Matcher matchers[];
public AndMatcher(Matcher ... matchers)
{
this.matchers = matchers;
}
@Override
public boolean match(Module module)
{
for (Matcher matcher : this.matchers)
{
if (!matcher.match(module))
{
return false;
}
}
return true;
}
}
public static class EnabledMatcher implements Matcher
{
@Override
public boolean match(Module module)
{
return module.isEnabled();
}
}
public static class RegexNameMatcher implements Matcher
{
private final Pattern pat;
public RegexNameMatcher(String regex)
{
this.pat = Pattern.compile(regex);
}
@Override
public boolean match(Module module)
{
return pat.matcher(module.getName()).matches();
}
}
public static class UniqueSourceMatcher implements Matcher
{
private final Pattern pat;
public UniqueSourceMatcher(String sourceRegex)
{
this.pat = Pattern.compile(sourceRegex);
}
@Override
public boolean match(Module module)
{
if (module.getSources().size() != 1)
{
// Not unique
return false;
}
String sourceId = module.getSources().iterator().next();
return pat.matcher(sourceId).matches();
}
}
public static class SourceSetMatcher implements Matcher
{
private final Set<String> nameSet;
public SourceSetMatcher(String... names)
{
this.nameSet = new HashSet<>();
for (String name : names)
{
this.nameSet.add(name);
}
}
@Override
public boolean match(Module module)
{
Set<String> sources = module.getSources();
if (sources == null)
{
// empty sources list
return false;
}
if (sources.size() != nameSet.size())
{
// non-equal sized set
return false;
}
for (String source : module.getSources())
{
if (!this.nameSet.contains(source))
{
return false;
}
}
return true;
}
}
private final BaseHome baseHome;
private final StartArgs args;
private Map<String, Module> modules = new HashMap<>();
/*
* modules that may appear in the resolved graph but are undefined in the module system
*
* ex: modules/npn/npn-1.7.0_01.mod (property expansion resolves to non-existent file)
*/
private Set<String> missingModules = new HashSet<String>();
private int maxDepth = -1;
// /*
// * modules that may appear in the resolved graph but are undefined in the module system
// *
// * ex: modules/npn/npn-1.7.0_01.mod (property expansion resolves to non-existent file)
// */
// private Set<String> missingModules = new HashSet<String>();
public Modules(BaseHome basehome, StartArgs args)
{
this.baseHome = basehome;
this.args = args;
this.setSelectionTerm("enable");
this.setNodeTerm("module");
}
private Set<String> asNameSet(Set<Module> moduleSet)
{
Set<String> ret = new HashSet<>();
for (Module module : moduleSet)
{
ret.add(module.getName());
}
return ret;
}
private void assertNoCycle(Module module, Stack<String> refs)
{
for (Module parent : module.getParentEdges())
{
if (refs.contains(parent.getName()))
{
// Cycle detected.
StringBuilder err = new StringBuilder();
err.append("A cyclic reference in the modules has been detected: ");
for (int i = 0; i < refs.size(); i++)
{
if (i > 0)
{
err.append(" -> ");
}
err.append(refs.get(i));
}
err.append(" -> ").append(parent.getName());
throw new IllegalStateException(err.toString());
}
refs.push(parent.getName());
assertNoCycle(parent,refs);
refs.pop();
}
}
private void bfsCalculateDepth(final Module module, final int depthNow)
{
int depth = depthNow + 1;
// Set depth on every child first
for (Module child : module.getChildEdges())
{
child.setDepth(Math.max(depth,child.getDepth()));
this.maxDepth = Math.max(this.maxDepth,child.getDepth());
}
// Dive down
for (Module child : module.getChildEdges())
{
bfsCalculateDepth(child,depth);
}
}
/**
* Using the provided dependencies, build the module graph
*/
public void buildGraph() throws FileNotFoundException, IOException
{
normalizeDependencies();
// Connect edges
for (Module module : modules.values())
{
for (String parentName : module.getParentNames())
{
Module parent = get(parentName);
if (parent == null)
{
if (Props.hasPropertyKey(parentName))
{
StartLog.debug("Module property not expandable (yet) [%s]",parentName);
}
else
{
StartLog.warn("Module not found [%s]",parentName);
}
}
else
{
module.addParentEdge(parent);
parent.addChildEdge(module);
}
}
for (String optionalParentName : module.getOptionalParentNames())
{
Module optional = get(optionalParentName);
if (optional == null)
{
StartLog.debug("Optional module not found [%s]",optionalParentName);
}
else if (optional.isEnabled())
{
module.addParentEdge(optional);
optional.addChildEdge(module);
}
}
}
// Verify there is no cyclic references
Stack<String> refs = new Stack<>();
for (Module module : modules.values())
{
refs.push(module.getName());
assertNoCycle(module,refs);
refs.pop();
}
// Calculate depth of all modules for sorting later
for (Module module : modules.values())
{
if (module.getParentEdges().isEmpty())
{
bfsCalculateDepth(module,0);
}
}
}
public void clearMissing()
{
missingModules.clear();
}
public Integer count()
{
return modules.size();
}
// public void clearMissing()
// {
// missingModules.clear();
// }
public void dump()
{
List<Module> ordered = new ArrayList<>();
ordered.addAll(modules.values());
ordered.addAll(getNodes());
Collections.sort(ordered,new Module.NameComparator());
List<Module> active = getEnabled();
@ -328,236 +74,193 @@ public class Modules implements Iterable<Module>
for (Module module : ordered)
{
boolean activated = active.contains(module);
boolean enabled = module.isEnabled();
boolean transitive = activated && !enabled;
boolean selected = module.isSelected();
boolean transitive = selected && module.matches(OnlyTransitivePredicate.INSTANCE);
char status = '-';
if (enabled)
String status = "[ ]";
if (transitive)
{
status = '*';
status = "[t]";
}
else if (transitive)
else if (selected)
{
status = '+';
status = "[x]";
}
System.out.printf("%n %s Module: %s%n",status,module.getName());
if (!module.getName().equals(module.getFilesystemRef()))
{
System.out.printf(" Ref: %s%n",module.getFilesystemRef());
System.out.printf(" Ref: %s%n",module.getFilesystemRef());
}
for (String parent : module.getParentNames())
{
System.out.printf(" Depend: %s%n",parent);
System.out.printf(" Depend: %s%n",parent);
}
for (String lib : module.getLibs())
{
System.out.printf(" LIB: %s%n",lib);
System.out.printf(" LIB: %s%n",lib);
}
for (String xml : module.getXmls())
{
System.out.printf(" XML: %s%n",xml);
System.out.printf(" XML: %s%n",xml);
}
if (StartLog.isDebugEnabled())
{
System.out.printf(" depth: %d%n",module.getDepth());
System.out.printf(" depth: %d%n",module.getDepth());
}
if (activated)
{
for (String source : module.getSources())
for (Selection selection : module.getSelections())
{
System.out.printf(" Enabled: <via> %s%n",source);
}
if (transitive)
{
System.out.printf(" Enabled: <via transitive reference>%n");
System.out.printf(" Enabled: <via> %s%n",selection);
}
}
else
{
System.out.printf(" Enabled: <not enabled in this configuration>%n");
System.out.printf(" Enabled: <not enabled in this configuration>%n");
}
}
}
public void dumpEnabledTree()
// public int enableAll(List<String> names, String source) throws IOException
// {
// if ((names == null) || (names.isEmpty()))
// {
// // nothing to do
// return 0;
// }
//
// List<String> sources = Collections.singletonList(source);
//
// int count = 0;
// for (String name : names)
// {
// count += enable(name,sources);
// }
// return count;
// }
// public int enable(String name, List<String> sources) throws IOException
// {
// int count = 0;
//
// if (name.contains("*"))
// {
// // A regex!
// List<Module> matching = getMatching(new RegexNamePredicate(name));
//
// // enable them
// for (Module module : matching)
// {
// count += enableModule(module,sources);
// }
// }
// else
// {
// Module module = get(name);
// if (module == null)
// {
// System.err.printf("WARNING: Cannot enable requested module [%s]: not a valid module name.%n",name);
// return count;
// }
// count += enableModule(module,sources);
// }
// return count;
// }
// private int enableModule(Module module, List<String> sources) throws IOException
// {
// int count = 0;
// if (sources == null)
// {
// // We use source for tagging how a node was selected, it should
// // always be required
// throw new RuntimeException("sources should never be empty");
// }
//
// module.addSources(sources);
// String via = Utils.join(sources,", ");
//
// // If already enabled, nothing else to do
// if (module.isEnabled())
// {
// StartLog.debug("Enabled module: %s (via %s)",module.getName(),via);
// return count;
// }
//
// StartLog.debug("Enabling module: %s (via %s)",module.getName(),via);
// module.setEnabled(true);
// count++;
// args.parseModule(module);
// module.expandProperties(args.getProperties());
//
// // enable any parents that haven't been enabled (yet)
// Set<String> parentNames = new HashSet<>();
// parentNames.addAll(module.getParentNames());
// for (String name : parentNames)
// {
// StartLog.debug("Enable parent '%s' of module: %s",name,module.getName());
// Module parent = get(name);
// if (parent == null)
// {
// // parent module doesn't exist, yet
// Path file = baseHome.getPath("modules/" + name + ".mod");
// if (FS.canReadFile(file))
// {
// parent = registerModule(file);
// parent.expandProperties(args.getProperties());
// updateParentReferencesTo(parent);
// }
// else
// {
// if (!Props.hasPropertyKey(name))
// {
// StartLog.debug("Missing module definition: [ Mod: %s | File: %s ]",name,file);
// missingModules.add(name);
// }
// }
// }
// if (parent != null)
// {
// count += enableModule(parent,sources);
// }
// }
// return count;
// }
@Override
public Module resolveNode(String name)
{
List<Module> ordered = new ArrayList<>();
ordered.addAll(modules.values());
Collections.sort(ordered,new Module.DepthComparator());
String expandedName = args.getProperties().expand(name);
List<Module> active = getEnabled();
for (Module module : ordered)
if (Props.hasPropertyKey(expandedName))
{
if (active.contains(module))
{
// Show module name
String indent = toIndent(module.getDepth());
System.out.printf("%s + Module: %s [%s]%n",indent,module.getName(),module.isEnabled()?"enabled":"transitive");
}
}
}
public void enable(String name) throws IOException
{
List<String> empty = Collections.emptyList();
enable(name,empty);
}
public int enableAll(List<String> names, String source) throws IOException
{
if ((names == null) || (names.isEmpty()))
{
// nothing to do
return 0;
throw new GraphException("Unable to expand property in name: " + name);
}
List<String> sources = Collections.singletonList(source);
int count = 0;
for (String name : names)
Path file = baseHome.getPath("modules/" + expandedName + ".mod");
if (FS.canReadFile(file))
{
count += enable(name,sources);
}
return count;
}
public int enable(String name, List<String> sources) throws IOException
{
int count = 0;
if (name.contains("*"))
{
// A regex!
List<Module> matching = getMatching(new RegexNameMatcher(name));
// enable them
for (Module module : matching)
{
count += enableModule(module,sources);
}
Module parent = registerModule(file);
parent.expandProperties(args.getProperties());
updateParentReferencesTo(parent);
return parent;
}
else
{
Module module = modules.get(name);
if (module == null)
if (!Props.hasPropertyKey(name))
{
System.err.printf("WARNING: Cannot enable requested module [%s]: not a valid module name.%n",name);
return count;
StartLog.debug("Missing module definition: [ Mod: %s | File: %s ]",name,file);
}
count += enableModule(module,sources);
return null;
}
return count;
}
private int enableModule(Module module, List<String> sources) throws IOException
{
int count = 0;
if(sources == null)
{
// We use source for tagging how a node was selected, it should
// always be required
throw new RuntimeException("sources should never be empty");
}
module.addSources(sources);
String via = Utils.join(sources, ", ");
// If already enabled, nothing else to do
if (module.isEnabled())
{
StartLog.debug("Enabled module: %s (via %s)",module.getName(),via);
return count;
}
StartLog.debug("Enabling module: %s (via %s)",module.getName(),via);
module.setEnabled(true);
count++;
args.parseModule(module);
module.expandProperties(args.getProperties());
// enable any parents that haven't been enabled (yet)
Set<String> parentNames = new HashSet<>();
parentNames.addAll(module.getParentNames());
for(String name: parentNames)
{
StartLog.debug("Enable parent '%s' of module: %s",name,module.getName());
Module parent = modules.get(name);
if (parent == null)
{
// parent module doesn't exist, yet
Path file = baseHome.getPath("modules/" + name + ".mod");
if (FS.canReadFile(file))
{
parent = registerModule(file);
parent.expandProperties(args.getProperties());
updateParentReferencesTo(parent);
}
else
{
if (!Props.hasPropertyKey(name))
{
StartLog.debug("Missing module definition: [ Mod: %s | File: %s ]",name,file);
missingModules.add(name);
}
}
}
if (parent != null)
{
count += enableModule(parent,sources);
}
}
return count;
}
private void findChildren(Module module, Set<Module> ret)
{
ret.add(module);
for (Module child : module.getChildEdges())
{
ret.add(child);
}
}
private void findParents(Module module, Map<String, Module> ret)
{
ret.put(module.getName(),module);
for (Module parent : module.getParentEdges())
{
ret.put(parent.getName(),parent);
findParents(parent,ret);
}
}
public Module get(String name)
{
return modules.get(name);
}
public int getMaxDepth()
{
return maxDepth;
}
public Set<Module> getModulesAtDepth(int depth)
{
Set<Module> ret = new HashSet<>();
for (Module module : modules.values())
{
if (module.getDepth() == depth)
{
ret.add(module);
}
}
return ret;
}
@Override
public Iterator<Module> iterator()
public void onNodeSelected(Module module)
{
return modules.values().iterator();
args.parseModule(module);
module.expandProperties(args.getProperties());
}
public List<String> normalizeLibs(List<Module> active)
@ -592,18 +295,12 @@ public class Modules implements Iterable<Module>
return xmls;
}
public Module register(Module module)
{
modules.put(module.getName(),module);
return module;
}
public void registerParentsIfMissing(Module module) throws IOException
{
Set<String> parents = new HashSet<>(module.getParentNames());
for (String name : parents)
{
if (!modules.containsKey(name))
if (!containsNode(name))
{
Path file = baseHome.getPath("modules/" + name + ".mod");
if (FS.canReadFile(file))
@ -615,7 +312,7 @@ public class Modules implements Iterable<Module>
}
}
}
public void registerAll() throws IOException
{
for (Path path : baseHome.getPaths("modules/*.mod"))
@ -623,9 +320,9 @@ public class Modules implements Iterable<Module>
registerModule(path);
}
}
// load missing post-expanded dependent modules
private void normalizeDependencies() throws FileNotFoundException, IOException
public void normalizeDependencies() throws IOException
{
Set<String> expandedModules = new HashSet<>();
boolean done = false;
@ -634,12 +331,12 @@ public class Modules implements Iterable<Module>
done = true;
Set<String> missingParents = new HashSet<>();
for (Module m : modules.values())
for (Module m : getNodes())
{
for (String parent : m.getParentNames())
{
String expanded = args.getProperties().expand(parent);
if (modules.containsKey(expanded) || missingModules.contains(parent) || expandedModules.contains(parent))
if (containsNode(expanded) || expandedModules.contains(parent))
{
continue; // found. skip it.
}
@ -672,61 +369,35 @@ public class Modules implements Iterable<Module>
else
{
StartLog.debug("Missing module definition: %s expanded to %s",missingParent,expanded);
missingModules.add(missingParent);
// missingModules.add(missingParent);
}
}
}
}
}
private Module registerModule(Path file) throws FileNotFoundException, IOException
private Module registerModule(Path file)
{
if (!FS.canReadFile(file))
{
throw new IOException("Cannot read file: " + file);
throw new GraphException("Cannot read file: " + file);
}
StartLog.debug("Registering Module: %s",baseHome.toShortForm(file));
Module module = new Module(baseHome,file);
return register(module);
}
public Set<String> resolveChildModulesOf(String moduleName)
{
Set<Module> ret = new HashSet<>();
Module module = get(moduleName);
findChildren(module,ret);
return asNameSet(ret);
}
public List<Module> getEnabled()
{
return getMatching(new EnabledMatcher());
}
/**
* Get the modules from the tree that match the provided matcher.
*
* @param matcher the matcher to use for matches
* @return the list of matching modules in execution order.
*/
public List<Module> getMatching(Matcher matcher)
{
List<Module> selected = new ArrayList<Module>();
for (Module module : modules.values())
String shortName = baseHome.toShortForm(file);
try
{
if (matcher.match(module))
{
selected.add(module);
}
StartLog.debug("Registering Module: %s",shortName);
Module module = new Module(baseHome,file);
return register(module);
}
catch (Throwable t)
{
throw new GraphException("Unable to register module: " + shortName,t);
}
Collections.sort(selected,new Module.DepthComparator());
return selected;
}
/**
* Resolve the execution order of the enabled modules, and all dependent modules, based on depth first transitive reduction.
* Resolve the execution order of the enabled modules, and all dependent modules, based on depth first transitive
* reduction.
*
* @return the list of active modules (plus dependent modules), in execution order.
* @deprecated use {@link #getEnabled()} and {@link #assertModulesValid(Collection)} instead.
@ -736,7 +407,7 @@ public class Modules implements Iterable<Module>
{
Map<String, Module> active = new HashMap<String, Module>();
for (Module module : modules.values())
for (Module module : getNodes())
{
if (module.isEnabled())
{
@ -748,49 +419,35 @@ public class Modules implements Iterable<Module>
List<Module> ordered = new ArrayList<>();
ordered.addAll(active.values());
Collections.sort(ordered,new Module.DepthComparator());
Collections.sort(ordered,new NodeDepthComparator());
return ordered;
}
public void assertModulesValid(Collection<Module> active)
{
/*
* check against the missing modules
*
* Ex: npn should match anything under npn/
*/
for (String missing : missingModules)
{
for (Module module : active)
{
if (missing.startsWith(module.getName()))
{
StartLog.warn("** Unable to continue, required dependency missing. [%s]",missing);
StartLog.warn("** As configured, Jetty is unable to start due to a missing enabled module dependency.");
StartLog.warn("** This may be due to a transitive dependency akin to spdy on npn, which resolves based on the JDK in use.");
throw new UsageException(UsageException.ERR_BAD_ARG, "Missing referenced dependency: " + missing);
}
}
}
}
public Set<String> resolveParentModulesOf(String moduleName)
{
Map<String, Module> ret = new HashMap<>();
Module module = get(moduleName);
findParents(module,ret);
return ret.keySet();
}
private String toIndent(int depth)
{
char indent[] = new char[depth * 2];
Arrays.fill(indent,' ');
return new String(indent);
// /*
// * check against the missing modules
// *
// * Ex: npn should match anything under npn/
// */
// for (String missing : missingModules)
// {
// for (Module module : active)
// {
// if (missing.startsWith(module.getName()))
// {
// StartLog.warn("** Unable to continue, required dependency missing. [%s]",missing);
// StartLog.warn("** As configured, Jetty is unable to start due to a missing enabled module dependency.");
// StartLog.warn("** This may be due to a transitive dependency akin to spdy on npn, which resolves based on the JDK in use.");
// throw new UsageException(UsageException.ERR_BAD_ARG,"Missing referenced dependency: " + missing);
// }
// }
// }
}
/**
* Modules can have a different logical name than to their filesystem reference. This updates existing references to the filesystem form to use the logical
* Modules can have a different logical name than to their filesystem reference. This updates existing references to
* the filesystem form to use the logical
* name form.
*
* @param module
@ -804,7 +461,7 @@ public class Modules implements Iterable<Module>
return;
}
for (Module m : modules.values())
for (Module m : getNodes())
{
Set<String> resolvedParents = new HashSet<>();
for (String parent : m.getParentNames())
@ -829,10 +486,10 @@ public class Modules implements Iterable<Module>
{
StringBuilder str = new StringBuilder();
str.append("Modules[");
str.append("count=").append(modules.size());
str.append("count=").append(count());
str.append(",<");
boolean delim = false;
for (String name : modules.keySet())
for (String name : getNodeNames())
{
if (delim)
{

View File

@ -27,8 +27,9 @@ public class UsageException extends RuntimeException
public static final int ERR_LOGGING = -1;
public static final int ERR_INVOKE_MAIN = -2;
public static final int ERR_NOT_STOPPED = -4;
public static final int ERR_UNKNOWN = -5;
public static final int ERR_BAD_ARG = -6;
public static final int ERR_BAD_ARG = -5;
public static final int ERR_BAD_GRAPH = -6;
public static final int ERR_UNKNOWN = -9;
private int exitCode;
public UsageException(int exitCode, String format, Object... objs)

View File

@ -0,0 +1,31 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.graph;
/**
* Match on everything.
*/
public class AllPredicate implements Predicate
{
@Override
public boolean match(Node<?> node)
{
return true;
}
}

View File

@ -0,0 +1,46 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.graph;
/**
* Match on multiple predicates.
*/
public class AndPredicate implements Predicate
{
private final Predicate predicates[];
public AndPredicate(Predicate... predicates)
{
this.predicates = predicates;
}
@Override
public boolean match(Node<?> node)
{
for (Predicate predicate : this.predicates)
{
if (!predicate.match(node))
{
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,28 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.graph;
public class AnySelectionPredicate implements Predicate
{
@Override
public boolean match(Node<?> input)
{
return !input.getSelections().isEmpty();
}
}

View File

@ -0,0 +1,468 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.graph;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import org.eclipse.jetty.start.Props;
import org.eclipse.jetty.start.StartLog;
/**
* Basic Graph
*/
public abstract class Graph<T extends Node<T>> implements Iterable<T>
{
private String selectionTerm = "select";
private String nodeTerm = "node";
private Map<String, T> nodes = new HashMap<>();
private int maxDepth = -1;
protected Set<String> asNameSet(Set<T> nodeSet)
{
Set<String> ret = new HashSet<>();
for (T node : nodeSet)
{
ret.add(node.getName());
}
return ret;
}
private void assertNoCycle(T node, Stack<String> refs)
{
for (T parent : node.getParentEdges())
{
if (refs.contains(parent.getName()))
{
// Cycle detected.
StringBuilder err = new StringBuilder();
err.append("A cyclic reference in the ");
err.append(this.getClass().getSimpleName());
err.append(" has been detected: ");
for (int i = 0; i < refs.size(); i++)
{
if (i > 0)
{
err.append(" -> ");
}
err.append(refs.get(i));
}
err.append(" -> ").append(parent.getName());
throw new IllegalStateException(err.toString());
}
refs.push(parent.getName());
assertNoCycle(parent,refs);
refs.pop();
}
}
private void bfsCalculateDepth(final T node, final int depthNow)
{
int depth = depthNow + 1;
// Set depth on every child first
for (T child : node.getChildEdges())
{
child.setDepth(Math.max(depth,child.getDepth()));
this.maxDepth = Math.max(this.maxDepth,child.getDepth());
}
// Dive down
for (T child : node.getChildEdges())
{
bfsCalculateDepth(child,depth);
}
}
public void buildGraph() throws FileNotFoundException, IOException
{
normalizeDependencies();
// Connect edges
for (T node : nodes.values())
{
for (String parentName : node.getParentNames())
{
T parent = get(parentName);
if (parent == null)
{
if (Props.hasPropertyKey(parentName))
{
StartLog.debug("Module property not expandable (yet) [%s]",parentName);
}
else
{
StartLog.warn("Module not found [%s]",parentName);
}
}
else
{
node.addParentEdge(parent);
parent.addChildEdge(node);
}
}
for (String optionalParentName : node.getOptionalParentNames())
{
T optional = get(optionalParentName);
if (optional == null)
{
StartLog.debug("Optional module not found [%s]",optionalParentName);
}
else if (optional.isSelected())
{
node.addParentEdge(optional);
optional.addChildEdge(node);
}
}
}
// Verify there is no cyclic references
Stack<String> refs = new Stack<>();
for (T module : nodes.values())
{
refs.push(module.getName());
assertNoCycle(module,refs);
refs.pop();
}
// Calculate depth of all modules for sorting later
for (T module : nodes.values())
{
if (module.getParentEdges().isEmpty())
{
bfsCalculateDepth(module,0);
}
}
}
public boolean containsNode(String name)
{
return nodes.containsKey(name);
}
public int count()
{
return nodes.size();
}
public void dumpEnabledTree()
{
List<T> ordered = new ArrayList<>();
ordered.addAll(nodes.values());
Collections.sort(ordered,new NodeDepthComparator());
List<T> active = getEnabled();
for (T module : ordered)
{
if (active.contains(module))
{
// Show module name
String indent = toIndent(module.getDepth());
System.out.printf("%s + Module: %s [%s]%n",indent,module.getName(),module.isSelected()?"selected":"transitive");
}
}
}
protected void findChildren(T module, Set<T> ret)
{
ret.add(module);
for (T child : module.getChildEdges())
{
ret.add(child);
}
}
protected void findParents(T module, Map<String, T> ret)
{
ret.put(module.getName(),module);
for (T parent : module.getParentEdges())
{
ret.put(parent.getName(),parent);
findParents(parent,ret);
}
}
public T get(String name)
{
return nodes.get(name);
}
/**
* Get the list of Selected nodes.
*/
public List<T> getEnabled()
{
return getMatching(new AnySelectionPredicate());
}
/**
* Get the Nodes from the tree that match the provided predicat.
*
* @param predicate
* the way to match nodes
* @return the list of matching nodes in execution order.
*/
public List<T> getMatching(Predicate predicate)
{
List<T> selected = new ArrayList<T>();
for (T node : nodes.values())
{
if (predicate.match(node))
{
selected.add(node);
}
}
Collections.sort(selected,new NodeDepthComparator());
return selected;
}
public int getMaxDepth()
{
return maxDepth;
}
public Set<T> getModulesAtDepth(int depth)
{
Set<T> ret = new HashSet<>();
for (T node : nodes.values())
{
if (node.getDepth() == depth)
{
ret.add(node);
}
}
return ret;
}
public Collection<String> getNodeNames()
{
return nodes.keySet();
}
public Collection<T> getNodes()
{
return nodes.values();
}
public String getNodeTerm()
{
return nodeTerm;
}
public String getSelectionTerm()
{
return selectionTerm;
}
@Override
public Iterator<T> iterator()
{
return nodes.values().iterator();
}
public void normalizeDependencies() throws IOException
{
// override to implement
}
public abstract void onNodeSelected(T node);
public T register(T node)
{
nodes.put(node.getName(),node);
return node;
}
public Set<String> resolveChildNodesOf(String nodeName)
{
Set<T> ret = new HashSet<>();
T module = get(nodeName);
findChildren(module,ret);
return asNameSet(ret);
}
/**
* Resolve a node just in time.
* <p>
* Useful for nodes that are virtual/transient in nature (such as the npn/alpn modules)
*/
public abstract T resolveNode(String name);
private Set<T> resolveNodes(Set<String> parentNames)
{
Set<T> ret = new HashSet<>();
for (String name : parentNames)
{
T node = get(name);
// Node doesn't exist yet (try to resolve it it just-in-time)
if (node == null)
{
node = resolveNode(name);
}
// Node still doesn't exist? this is now an invalid graph.
if (node == null)
{
throw new GraphException("Missing referenced dependency: " + name);
}
ret.add(node);
}
return ret;
}
public Set<String> resolveParentModulesOf(String nodeName)
{
Map<String, T> ret = new HashMap<>();
T node = get(nodeName);
findParents(node,ret);
return ret.keySet();
}
public int selectNode(Predicate nodePredicate, Selection selection)
{
int count = 0;
List<T> matches = getMatching(nodePredicate);
if (matches.isEmpty())
{
StringBuilder err = new StringBuilder();
err.append("WARNING: Cannot ").append(selectionTerm);
err.append(" requested ").append(nodeTerm);
err.append("s. ").append(nodePredicate);
err.append(" returned no matches.");
System.err.println(err);
return count;
}
// select them
for (T node : matches)
{
count += selectNode(node,selection);
}
return count;
}
public int selectNode(String name, Selection selection)
{
int count = 0;
T node = get(name);
if (node == null)
{
StringBuilder err = new StringBuilder();
err.append("WARNING: Cannot ").append(selectionTerm);
err.append(" requested ").append(nodeTerm);
err.append(" [").append(name).append("]: not a valid ");
err.append(nodeTerm).append(" name.");
System.err.println(err);
return count;
}
count += selectNode(node,selection);
return count;
}
private int selectNode(T node, Selection selection)
{
int count = 0;
if (node.getSelections().contains(selection))
{
// Already enabled with this selection.
return count;
}
StartLog.debug("%s %s: %s (via %s)",toCap(selectionTerm),nodeTerm,node.getName(),selection);
boolean newlySelected = node.getSelections().isEmpty();
// Add self
node.addSelection(selection);
if (newlySelected)
{
onNodeSelected(node);
}
count++;
// Walk transitive
Selection transitive = selection.asTransitive();
Set<String> parentNames = new HashSet<>();
parentNames.addAll(node.getParentNames());
Set<T> parentNodes = resolveNodes(parentNames);
for (T parentNode : parentNodes)
{
count += selectNode(parentNode,transitive);
}
return count;
}
public int selectNodes(Collection<String> names, Selection selection)
{
int count = 0;
for (String name : names)
{
count += selectNode(name,selection);
}
return count;
}
public void setNodeTerm(String nodeTerm)
{
this.nodeTerm = nodeTerm;
}
public void setSelectionTerm(String selectionTerm)
{
this.selectionTerm = selectionTerm;
}
private String toCap(String str)
{
StringBuilder cap = new StringBuilder();
cap.append(Character.toUpperCase(str.charAt(0)));
cap.append(str.substring(1));
return cap.toString();
}
private String toIndent(int depth)
{
char indent[] = new char[depth * 2];
Arrays.fill(indent,' ');
return new String(indent);
}
}

View File

@ -0,0 +1,36 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.graph;
/**
* A non-recoverable graph exception
*/
@SuppressWarnings("serial")
public class GraphException extends RuntimeException
{
public GraphException(String message, Throwable cause)
{
super(message,cause);
}
public GraphException(String message)
{
super(message);
}
}

View File

@ -0,0 +1,64 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.graph;
import java.util.HashSet;
import java.util.Set;
public class HowSetMatcher implements Predicate
{
private final Set<String> howSet;
public HowSetMatcher(String... hows)
{
this.howSet = new HashSet<>();
for (String name : hows)
{
this.howSet.add(name);
}
}
@Override
public boolean match(Node<?> node)
{
Set<Selection> selections = node.getSelections();
if (selections == null)
{
// empty sources list
return false;
}
if (selections.size() != howSet.size())
{
// non-equal sized set
return false;
}
for (Selection selection : selections)
{
if (!this.howSet.contains(selection.getHow()))
{
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,35 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.graph;
public class NamePredicate implements Predicate
{
private final String name;
public NamePredicate(String name)
{
this.name = name;
}
@Override
public boolean match(Node<?> input)
{
return input.getName().equalsIgnoreCase(this.name);
}
}

View File

@ -0,0 +1,171 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.graph;
import java.util.HashSet;
import java.util.Set;
/**
* Basic Graph Node
*/
public abstract class Node<T>
{
/** The logical name of this Node */
private String logicalName;
/** The depth of the Node in the tree */
private int depth = 0;
/** The set of selections for how this node was selected */
private Set<Selection> selections = new HashSet<>();
/** Set of Nodes, by name, that this Node depends on */
private Set<String> parentNames = new HashSet<>();
/** Set of Nodes, by name, that this Node optionally depend on */
private Set<String> optionalParentNames = new HashSet<>();
/** The Edges to parent Nodes */
private Set<T> parentEdges = new HashSet<>();
/** The Edges to child Nodes */
private Set<T> childEdges = new HashSet<>();
public void addChildEdge(T child)
{
if (childEdges.contains(child))
{
// already present, skip
return;
}
this.childEdges.add(child);
}
public void addOptionalParentName(String name)
{
this.optionalParentNames.add(name);
}
public void addParentEdge(T parent)
{
if (parentEdges.contains(parent))
{
// already present, skip
return;
}
this.parentEdges.add(parent);
}
public void addParentName(String name)
{
this.parentNames.add(name);
}
public void addSelection(Selection selection)
{
this.selections.add(selection);
}
public Set<T> getChildEdges()
{
return childEdges;
}
public int getDepth()
{
return depth;
}
@Deprecated
public String getLogicalName()
{
return logicalName;
}
public String getName()
{
return logicalName;
}
public Set<String> getOptionalParentNames()
{
return optionalParentNames;
}
public Set<T> getParentEdges()
{
return parentEdges;
}
public Set<String> getParentNames()
{
return parentNames;
}
public Set<Selection> getSelections()
{
return selections;
}
public Set<String> getSelectedHowSet()
{
Set<String> hows = new HashSet<>();
for (Selection selection : selections)
{
hows.add(selection.getHow());
}
return hows;
}
public boolean isSelected()
{
return !selections.isEmpty();
}
public boolean matches(Predicate predicate)
{
return predicate.match(this);
}
public void setDepth(int depth)
{
this.depth = depth;
}
@Deprecated
public void setLogicalName(String logicalName)
{
this.logicalName = logicalName;
}
public void setName(String name)
{
this.logicalName = name;
}
public void setParentNames(Set<String> parents)
{
this.parentNames.clear();
this.parentEdges.clear();
if (parents != null)
{
this.parentNames.addAll(parents);
}
}
public void setSelections(Set<Selection> selection)
{
this.selections = selection;
}
}

View File

@ -0,0 +1,43 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.graph;
import java.text.CollationKey;
import java.text.Collator;
import java.util.Comparator;
public class NodeDepthComparator implements Comparator<Node<?>>
{
private Collator collator = Collator.getInstance();
@Override
public int compare(Node<?> o1, Node<?> o2)
{
// order by depth first.
int diff = o1.getDepth() - o2.getDepth();
if (diff != 0)
{
return diff;
}
// then by name (not really needed, but makes for predictable test cases)
CollationKey k1 = collator.getCollationKey(o1.getName());
CollationKey k2 = collator.getCollationKey(o2.getName());
return k1.compareTo(k2);
}
}

View File

@ -0,0 +1,41 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.graph;
/**
* Predicate for a node that has no explicitly set selections.
* (They are all transitive)
*/
public class OnlyTransitivePredicate implements Predicate
{
public static final Predicate INSTANCE = new OnlyTransitivePredicate();
@Override
public boolean match(Node<?> input)
{
for (Selection selection : input.getSelections())
{
if (selection.isExplicit())
{
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,27 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.graph;
/**
* Matcher of Nodes
*/
public interface Predicate
{
public boolean match(Node<?> input);
}

View File

@ -0,0 +1,40 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.graph;
import java.util.regex.Pattern;
/**
* Match a node based on name
*/
public class RegexNamePredicate implements Predicate
{
private final Pattern pat;
public RegexNamePredicate(String regex)
{
this.pat = Pattern.compile(regex);
}
@Override
public boolean match(Node<?> node)
{
return pat.matcher(node.getName()).matches();
}
}

View File

@ -0,0 +1,111 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.graph;
public class Selection
{
private final boolean explicit;
private final String how;
public Selection(String how)
{
this(how,true);
}
public Selection(String how, boolean explicit)
{
this.how = how;
this.explicit = explicit;
}
public Selection asTransitive()
{
if (this.explicit)
{
return new Selection(how,false);
}
return this;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (getClass() != obj.getClass())
{
return false;
}
Selection other = (Selection)obj;
if (explicit != other.explicit)
{
return false;
}
if (how == null)
{
if (other.how != null)
{
return false;
}
}
else if (!how.equals(other.how))
{
return false;
}
return true;
}
public String getHow()
{
return how;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = (prime * result) + (explicit?1231:1237);
result = (prime * result) + ((how == null)?0:how.hashCode());
return result;
}
public boolean isExplicit()
{
return explicit;
}
@Override
public String toString()
{
StringBuilder str = new StringBuilder();
if (!explicit)
{
str.append("<transitive from> ");
}
str.append(how);
return str.toString();
}
}

View File

@ -24,17 +24,17 @@ import static org.junit.Assert.*;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.eclipse.jetty.start.Modules.AndMatcher;
import org.eclipse.jetty.start.Modules.EnabledMatcher;
import org.eclipse.jetty.start.Modules.UniqueSourceMatcher;
import org.eclipse.jetty.start.config.CommandLineConfigSource;
import org.eclipse.jetty.start.config.ConfigSources;
import org.eclipse.jetty.start.config.JettyBaseConfigSource;
import org.eclipse.jetty.start.config.JettyHomeConfigSource;
import org.eclipse.jetty.start.graph.HowSetMatcher;
import org.eclipse.jetty.start.graph.Predicate;
import org.eclipse.jetty.start.graph.RegexNamePredicate;
import org.eclipse.jetty.start.graph.Selection;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.junit.Rule;
@ -42,7 +42,7 @@ import org.junit.Test;
public class ModulesTest
{
private final static List<String> TEST_SOURCE = Collections.singletonList("<test>");
private final static String TEST_SOURCE = "<test>";
@Rule
public TestingDir testdir = new TestingDir();
@ -190,7 +190,8 @@ public class ModulesTest
// Test Modules
Modules modules = new Modules(basehome,args);
modules.registerAll();
modules.enable("[sj]{1}.*",TEST_SOURCE);
Predicate sjPredicate = new RegexNamePredicate("[sj]{1}.*");
modules.selectNode(sjPredicate,new Selection(TEST_SOURCE));
modules.buildGraph();
List<String> expected = new ArrayList<>();
@ -247,8 +248,8 @@ public class ModulesTest
modules.registerAll();
// Enable 2 modules
modules.enable("server",TEST_SOURCE);
modules.enable("http",TEST_SOURCE);
modules.selectNode("server",new Selection(TEST_SOURCE));
modules.selectNode("http",new Selection(TEST_SOURCE));
modules.buildGraph();
@ -319,8 +320,8 @@ public class ModulesTest
modules.registerAll();
// Enable 2 modules
modules.enable("websocket",TEST_SOURCE);
modules.enable("http",TEST_SOURCE);
modules.selectNode("websocket",new Selection(TEST_SOURCE));
modules.selectNode("http",new Selection(TEST_SOURCE));
modules.buildGraph();
// modules.dump();
@ -407,13 +408,13 @@ public class ModulesTest
modules.registerAll();
// Enable test modules
modules.enable("http",TEST_SOURCE);
modules.enable("annotations",TEST_SOURCE);
modules.enable("deploy",TEST_SOURCE);
modules.selectNode("http",new Selection(TEST_SOURCE));
modules.selectNode("annotations",new Selection(TEST_SOURCE));
modules.selectNode("deploy",new Selection(TEST_SOURCE));
// Enable alternate modules
String alt = "<alt>";
modules.enable("websocket",Collections.singletonList(alt));
modules.enable("jsp",Collections.singletonList(alt));
modules.selectNode("websocket",new Selection(alt));
modules.selectNode("jsp",new Selection(alt));
modules.buildGraph();
// modules.dump();
@ -455,13 +456,13 @@ public class ModulesTest
for (String expectedAlt : expectedAlts)
{
Module altMod = modules.get(expectedAlt);
assertThat("Alt.mod[" + expectedAlt + "].enabled",altMod.isEnabled(),is(true));
Set<String> sources = altMod.getSources();
assertThat("Alt.mod[" + expectedAlt + "].selected",altMod.isSelected(),is(true));
Set<String> sources = altMod.getSelectedHowSet();
assertThat("Alt.mod[" + expectedAlt + "].sources: [" + Utils.join(sources,", ") + "]",sources,contains(alt));
}
// Now collect the unique source list
List<Module> alts = modules.getMatching(new AndMatcher(new EnabledMatcher(),new UniqueSourceMatcher(alt)));
List<Module> alts = modules.getMatching(new HowSetMatcher(alt));
// Assert names are correct, and in the right order
actualNames = new ArrayList<>();

View File

@ -80,6 +80,4 @@ public class TestBadUseCases
"Missing referenced dependency: protonego-impl/npn-1.7.0_01",
"java.version=1.7.0_01", "protonego=npn");
}
}

View File

@ -0,0 +1,75 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.graph;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
public class NodeTest
{
private static class TestNode extends Node<TestNode>
{
public TestNode(String name)
{
setName(name);
}
@Override
public String toString()
{
return String.format("TestNode[%s]",getName());
}
}
@Test
public void testNoNameMatch()
{
TestNode node = new TestNode("a");
Predicate predicate = new NamePredicate("b");
assertThat(node.toString(),node.matches(predicate),is(false));
}
@Test
public void testNameMatch()
{
TestNode node = new TestNode("a");
Predicate predicate = new NamePredicate("a");
assertThat(node.toString(),node.matches(predicate),is(true));
}
@Test
public void testAnySelectionMatch()
{
TestNode node = new TestNode("a");
node.addSelection(new Selection("test"));
Predicate predicate = new AnySelectionPredicate();
assertThat(node.toString(),node.matches(predicate),is(true));
}
@Test
public void testAnySelectionNoMatch()
{
TestNode node = new TestNode("a");
// NOT Selected - node.addSelection(new Selection("test"));
Predicate predicate = new AnySelectionPredicate();
assertThat(node.toString(),node.matches(predicate),is(false));
}
}