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:
parent
1561de0f81
commit
005b513653
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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<>();
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue