Adding --write-module-graph=<filename>
This commit is contained in:
parent
8fdc6b05bb
commit
444a49f97e
|
@ -611,6 +611,16 @@ public class Main
|
||||||
{
|
{
|
||||||
listModules(args);
|
listModules(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate Module Graph File
|
||||||
|
if (args.getModuleGraphFilename() != null)
|
||||||
|
{
|
||||||
|
File outputFile = baseHome.getBaseFile(args.getModuleGraphFilename());
|
||||||
|
System.out.printf("Generating GraphViz Graph of Jetty Modules at %s%n",baseHome.toShortForm(outputFile));
|
||||||
|
ModuleGraphWriter writer = new ModuleGraphWriter();
|
||||||
|
writer.config(args.getProperties());
|
||||||
|
writer.write(args.getAllModules(),outputFile);
|
||||||
|
}
|
||||||
|
|
||||||
// Show Command Line to execute Jetty
|
// Show Command Line to execute Jetty
|
||||||
if (args.isDryRun())
|
if (args.isDryRun())
|
||||||
|
|
|
@ -30,17 +30,14 @@ import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.omg.CORBA.INITIALIZE;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a Module metadata, as defined in Jetty.
|
* Represents a Module metadata, as defined in Jetty.
|
||||||
*/
|
*/
|
||||||
public class Module // extends TextFile
|
public class Module
|
||||||
{
|
{
|
||||||
public static class NameComparator implements Comparator<Module>
|
public static class NameComparator implements Comparator<Module>
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,257 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.start;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a graphviz dot graph of the modules found
|
||||||
|
*/
|
||||||
|
public class ModuleGraphWriter
|
||||||
|
{
|
||||||
|
private String colorModuleBg;
|
||||||
|
private String colorEnabledBg;
|
||||||
|
private String colorTransitiveBg;
|
||||||
|
private String colorCellBg;
|
||||||
|
private String colorHeaderBg;
|
||||||
|
private String colorModuleFont;
|
||||||
|
|
||||||
|
public ModuleGraphWriter()
|
||||||
|
{
|
||||||
|
colorModuleBg = "#B8FFB8";
|
||||||
|
colorEnabledBg = "#66FFCC";
|
||||||
|
colorTransitiveBg = "#66CC66";
|
||||||
|
colorCellBg = "#FFFFFF80";
|
||||||
|
colorHeaderBg = "#00000020";
|
||||||
|
colorModuleFont = "#888888";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void config(Properties props)
|
||||||
|
{
|
||||||
|
String prefix = "jetty.graph.";
|
||||||
|
colorModuleBg = getProperty(props,prefix + "color.module.bg",colorModuleBg);
|
||||||
|
colorEnabledBg = getProperty(props,prefix + "color.enabled.bg",colorEnabledBg);
|
||||||
|
colorTransitiveBg = getProperty(props,prefix + "color.transitive.bg",colorTransitiveBg);
|
||||||
|
colorCellBg = getProperty(props,prefix + "color.cell.bg",colorCellBg);
|
||||||
|
colorHeaderBg = getProperty(props,prefix + "color.header.bg",colorHeaderBg);
|
||||||
|
colorModuleFont = getProperty(props,prefix + "color.font",colorModuleFont);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getProperty(Properties props, String key, String defVal)
|
||||||
|
{
|
||||||
|
String val = props.getProperty(key,defVal);
|
||||||
|
if (val == null)
|
||||||
|
{
|
||||||
|
return defVal;
|
||||||
|
}
|
||||||
|
val = val.trim();
|
||||||
|
if (val.length() <= 0)
|
||||||
|
{
|
||||||
|
return defVal;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(Modules modules, File outputFile) throws IOException
|
||||||
|
{
|
||||||
|
try (FileWriter writer = new FileWriter(outputFile,false); PrintWriter out = new PrintWriter(writer);)
|
||||||
|
{
|
||||||
|
writeHeaderMessage(out,outputFile);
|
||||||
|
|
||||||
|
out.println();
|
||||||
|
out.println("digraph modules {");
|
||||||
|
|
||||||
|
// Node Style
|
||||||
|
out.println(" node [color=gray, style=filled, shape=rectangle];");
|
||||||
|
out.println(" node [fontname=\"Verdana\", size=\"20,20\"];");
|
||||||
|
// Graph Style
|
||||||
|
out.println(" graph [");
|
||||||
|
out.println(" concentrate=false,");
|
||||||
|
out.println(" fontname=\"Verdana\",");
|
||||||
|
out.println(" fontsize = 20,");
|
||||||
|
out.println(" rankdir = LR,");
|
||||||
|
out.println(" ranksep = 1.5,");
|
||||||
|
out.println(" nodesep = .5,");
|
||||||
|
out.println(" style = bold,");
|
||||||
|
out.println(" labeljust = l,");
|
||||||
|
out.println(" label = \"Jetty Modules\",");
|
||||||
|
out.println(" ssize = \"20,40\"");
|
||||||
|
out.println(" ];");
|
||||||
|
|
||||||
|
List<Module> enabled = modules.resolveEnabled();
|
||||||
|
|
||||||
|
// Module Nodes
|
||||||
|
writeModules(out,modules,enabled);
|
||||||
|
|
||||||
|
// Module Relationships
|
||||||
|
writeRelationships(out,modules,enabled);
|
||||||
|
|
||||||
|
out.println("}");
|
||||||
|
out.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeHeaderMessage(PrintWriter out, File outputFile)
|
||||||
|
{
|
||||||
|
out.println("/*");
|
||||||
|
out.println(" * GraphViz Graph of Jetty Modules");
|
||||||
|
out.println(" * ");
|
||||||
|
out.println(" * Jetty: http://eclipse.org/jetty/");
|
||||||
|
out.println(" * GraphViz: http://graphviz.org/");
|
||||||
|
out.println(" * ");
|
||||||
|
out.println(" * To Generate Graph image using graphviz:");
|
||||||
|
String filename = outputFile.getName();
|
||||||
|
String basename = filename.substring(0,filename.indexOf('.'));
|
||||||
|
out.printf(" * $ dot -Tpng -Goverlap=false -o %s.png %s%n",basename,filename);
|
||||||
|
out.println(" */");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeModuleDetailHeader(PrintWriter out, String header)
|
||||||
|
{
|
||||||
|
writeModuleDetailHeader(out,header,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeModuleDetailHeader(PrintWriter out, String header, int count)
|
||||||
|
{
|
||||||
|
out.printf(" <TR>");
|
||||||
|
out.printf("<TD BGCOLOR=\"%s\" ALIGN=\"LEFT\"><I>",colorHeaderBg);
|
||||||
|
out.printf("%s%s</I></TD>",header,count > 1?"s":"");
|
||||||
|
out.println("</TR>");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeModuleDetailLine(PrintWriter out, String line)
|
||||||
|
{
|
||||||
|
out.printf(" <TR>");
|
||||||
|
StringBuilder escape = new StringBuilder();
|
||||||
|
for(char c: line.toCharArray()) {
|
||||||
|
switch(c) {
|
||||||
|
case '<': escape.append("<"); break;
|
||||||
|
case '>': escape.append(">"); break;
|
||||||
|
default:
|
||||||
|
escape.append(c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.printf("<TD BGCOLOR=\"%s\" ALIGN=\"LEFT\">%s</TD></TR>%n",colorCellBg,escape.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeModuleNode(PrintWriter out, Module module, boolean resolved)
|
||||||
|
{
|
||||||
|
String color = colorModuleBg;
|
||||||
|
if (module.isEnabled())
|
||||||
|
{
|
||||||
|
// specifically enabled by config
|
||||||
|
color = colorEnabledBg;
|
||||||
|
}
|
||||||
|
else if (resolved)
|
||||||
|
{
|
||||||
|
// enabled by transitive reasons
|
||||||
|
color = colorTransitiveBg;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.printf(" \"%s\" [ color=\"%s\" label=<",module.getName(),color);
|
||||||
|
out.printf("<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"2\">%n");
|
||||||
|
out.printf(" <TR><TD ALIGN=\"LEFT\"><B>%s</B></TD></TR>%n",module.getName());
|
||||||
|
|
||||||
|
if (module.isEnabled())
|
||||||
|
{
|
||||||
|
writeModuleDetailHeader(out,"ENABLED");
|
||||||
|
for (String source : module.getSources())
|
||||||
|
{
|
||||||
|
writeModuleDetailLine(out,"via: " + source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (resolved)
|
||||||
|
{
|
||||||
|
writeModuleDetailHeader(out,"TRANSITIVE");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!module.getXmls().isEmpty())
|
||||||
|
{
|
||||||
|
List<String> xmls = module.getXmls();
|
||||||
|
writeModuleDetailHeader(out,"XML",xmls.size());
|
||||||
|
for (String xml : xmls)
|
||||||
|
{
|
||||||
|
writeModuleDetailLine(out,xml);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!module.getLibs().isEmpty())
|
||||||
|
{
|
||||||
|
List<String> libs = module.getLibs();
|
||||||
|
writeModuleDetailHeader(out,"LIB",libs.size());
|
||||||
|
for (String lib : libs)
|
||||||
|
{
|
||||||
|
writeModuleDetailLine(out,lib);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!module.getInitialise().isEmpty())
|
||||||
|
{
|
||||||
|
List<String> inis = module.getInitialise();
|
||||||
|
writeModuleDetailHeader(out,"INI Template",inis.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
out.printf("</TABLE>>];%n");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeModules(PrintWriter out, Modules allmodules, List<Module> enabled)
|
||||||
|
{
|
||||||
|
out.println();
|
||||||
|
out.println(" /* Modules */");
|
||||||
|
out.println();
|
||||||
|
|
||||||
|
out.println(" node [ labeljust = l ];");
|
||||||
|
|
||||||
|
for (int depth = 0; depth <= allmodules.getMaxDepth(); depth++)
|
||||||
|
{
|
||||||
|
out.println();
|
||||||
|
Collection<Module> depthModules = allmodules.getModulesAtDepth(depth);
|
||||||
|
if (depthModules.size() > 0)
|
||||||
|
{
|
||||||
|
out.printf(" /* Level %d */%n",depth);
|
||||||
|
out.println(" { rank = same;");
|
||||||
|
for (Module module : depthModules)
|
||||||
|
{
|
||||||
|
boolean resolved = enabled.contains(module);
|
||||||
|
writeModuleNode(out,module,resolved);
|
||||||
|
}
|
||||||
|
out.println(" }");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeRelationships(PrintWriter out, Modules modules, List<Module> enabled)
|
||||||
|
{
|
||||||
|
for (Module module : modules)
|
||||||
|
{
|
||||||
|
for (Module parent : module.getParentEdges())
|
||||||
|
{
|
||||||
|
out.printf(" \"%s\" -> \"%s\";%n",module.getName(),parent.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,6 +37,7 @@ import java.util.Stack;
|
||||||
public class Modules implements Iterable<Module>
|
public class Modules implements Iterable<Module>
|
||||||
{
|
{
|
||||||
private Map<String, Module> modules = new HashMap<>();
|
private Map<String, Module> modules = new HashMap<>();
|
||||||
|
private int maxDepth = -1;
|
||||||
|
|
||||||
private Set<String> asNameSet(Set<Module> moduleSet)
|
private Set<String> asNameSet(Set<Module> moduleSet)
|
||||||
{
|
{
|
||||||
|
@ -83,6 +84,7 @@ public class Modules implements Iterable<Module>
|
||||||
for (Module child : module.getChildEdges())
|
for (Module child : module.getChildEdges())
|
||||||
{
|
{
|
||||||
child.setDepth(Math.max(depth,child.getDepth()));
|
child.setDepth(Math.max(depth,child.getDepth()));
|
||||||
|
this.maxDepth = Math.max(this.maxDepth,child.getDepth());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dive down
|
// Dive down
|
||||||
|
@ -118,7 +120,7 @@ public class Modules implements Iterable<Module>
|
||||||
for (String optionalParentName : module.getOptionalParentNames())
|
for (String optionalParentName : module.getOptionalParentNames())
|
||||||
{
|
{
|
||||||
Module optional = get(optionalParentName);
|
Module optional = get(optionalParentName);
|
||||||
if (optional==null)
|
if (optional == null)
|
||||||
{
|
{
|
||||||
System.err.printf("WARNING: module not found [%s]%n",optionalParentName);
|
System.err.printf("WARNING: module not found [%s]%n",optionalParentName);
|
||||||
}
|
}
|
||||||
|
@ -242,6 +244,24 @@ public class Modules implements Iterable<Module>
|
||||||
return modules.get(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
|
@Override
|
||||||
public Iterator<Module> iterator()
|
public Iterator<Module> iterator()
|
||||||
{
|
{
|
||||||
|
|
|
@ -79,6 +79,7 @@ public class StartArgs
|
||||||
private List<String> jvmArgs = new ArrayList<>();
|
private List<String> jvmArgs = new ArrayList<>();
|
||||||
private List<String> moduleIni = new ArrayList<>();
|
private List<String> moduleIni = new ArrayList<>();
|
||||||
private List<String> moduleStartIni = new ArrayList<>();
|
private List<String> moduleStartIni = new ArrayList<>();
|
||||||
|
private String moduleGraphFilename;
|
||||||
|
|
||||||
private Modules allModules;
|
private Modules allModules;
|
||||||
// Should the server be run?
|
// Should the server be run?
|
||||||
|
@ -439,6 +440,11 @@ public class StartArgs
|
||||||
return System.getProperty("main.class",mainclass);
|
return System.getProperty("main.class",mainclass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getModuleGraphFilename()
|
||||||
|
{
|
||||||
|
return moduleGraphFilename;
|
||||||
|
}
|
||||||
|
|
||||||
public List<String> getModuleIni()
|
public List<String> getModuleIni()
|
||||||
{
|
{
|
||||||
return moduleIni;
|
return moduleIni;
|
||||||
|
@ -711,6 +717,13 @@ public class StartArgs
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arg.startsWith("--write-module-graph="))
|
||||||
|
{
|
||||||
|
this.moduleGraphFilename = getValue(arg);
|
||||||
|
run = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Start property (syntax similar to System property)
|
// Start property (syntax similar to System property)
|
||||||
if (arg.startsWith("-D"))
|
if (arg.startsWith("-D"))
|
||||||
{
|
{
|
||||||
|
|
|
@ -82,6 +82,13 @@ Module Management:
|
||||||
enabled in the ${jetty.base}/start.ini using the same
|
enabled in the ${jetty.base}/start.ini using the same
|
||||||
techniques.
|
techniques.
|
||||||
|
|
||||||
|
--write-module-graph=<filename>
|
||||||
|
Create a graphviz *.dot file of the module graph as it
|
||||||
|
exists for the active ${jetty.base}.
|
||||||
|
See http://graphviz.org/ for details on how to post-process
|
||||||
|
this file into the output best suited for your needs.
|
||||||
|
|
||||||
|
|
||||||
Startup / Shutdown Command Line:
|
Startup / Shutdown Command Line:
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.start;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||||
|
import org.eclipse.jetty.toolchain.test.TestingDir;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class ModuleGraphWriterTest
|
||||||
|
{
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private final static List<String> TEST_SOURCE = Collections.singletonList("<test>");
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public TestingDir testdir = new TestingDir();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGenerate_NothingEnabled() throws IOException
|
||||||
|
{
|
||||||
|
File homeDir = MavenTestingUtils.getTestResourceDir("usecases/home");
|
||||||
|
File baseDir = testdir.getEmptyDir();
|
||||||
|
BaseHome basehome = new BaseHome(homeDir,baseDir);
|
||||||
|
|
||||||
|
Modules modules = new Modules();
|
||||||
|
modules.registerAll(basehome);
|
||||||
|
modules.buildGraph();
|
||||||
|
|
||||||
|
File outputFile = new File(baseDir,"graph.dot");
|
||||||
|
|
||||||
|
ModuleGraphWriter writer = new ModuleGraphWriter();
|
||||||
|
writer.write(modules,outputFile);
|
||||||
|
|
||||||
|
Assert.assertThat("Output File Exists",outputFile.exists(),is(true));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue