Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoakim Erdfelt2013-08-30 20:08:32 +0000
committerJoakim Erdfelt2013-08-30 20:08:32 +0000
commit444a49f97ebb25037a635fc38792138f8b8ee750 (patch)
tree7d5f775b2edd0a2feea66bcb3fcb1c8dd0a39611
parent8fdc6b05bbe87a870fdd978e0a7a8f6993f6fac9 (diff)
downloadorg.eclipse.jetty.project-444a49f97ebb25037a635fc38792138f8b8ee750.tar.gz
org.eclipse.jetty.project-444a49f97ebb25037a635fc38792138f8b8ee750.tar.xz
org.eclipse.jetty.project-444a49f97ebb25037a635fc38792138f8b8ee750.zip
Adding --write-module-graph=<filename>
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/Main.java10
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/Module.java5
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/ModuleGraphWriter.java257
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java22
-rw-r--r--jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java13
-rw-r--r--jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt7
-rw-r--r--jetty-start/src/test/java/org/eclipse/jetty/start/ModuleGraphWriterTest.java60
7 files changed, 369 insertions, 5 deletions
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java
index 1c6b616bb9..be928d6cdf 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java
@@ -611,6 +611,16 @@ public class Main
{
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
if (args.isDryRun())
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Module.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Module.java
index c0563b72a1..4c57c3672a 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/Module.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Module.java
@@ -30,17 +30,14 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
-import java.util.Locale;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.omg.CORBA.INITIALIZE;
-
/**
* Represents a Module metadata, as defined in Jetty.
*/
-public class Module // extends TextFile
+public class Module
{
public static class NameComparator implements Comparator<Module>
{
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/ModuleGraphWriter.java b/jetty-start/src/main/java/org/eclipse/jetty/start/ModuleGraphWriter.java
new file mode 100644
index 0000000000..7eacae35da
--- /dev/null
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/ModuleGraphWriter.java
@@ -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("&lt;"); break;
+ case '>': escape.append("&gt;"); 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());
+ }
+ }
+ }
+}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java
index bac372bb2e..8b21764733 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java
@@ -37,6 +37,7 @@ import java.util.Stack;
public class Modules implements Iterable<Module>
{
private Map<String, Module> modules = new HashMap<>();
+ private int maxDepth = -1;
private Set<String> asNameSet(Set<Module> moduleSet)
{
@@ -83,6 +84,7 @@ public class Modules implements Iterable<Module>
for (Module child : module.getChildEdges())
{
child.setDepth(Math.max(depth,child.getDepth()));
+ this.maxDepth = Math.max(this.maxDepth,child.getDepth());
}
// Dive down
@@ -118,7 +120,7 @@ public class Modules implements Iterable<Module>
for (String optionalParentName : module.getOptionalParentNames())
{
Module optional = get(optionalParentName);
- if (optional==null)
+ if (optional == null)
{
System.err.printf("WARNING: module not found [%s]%n",optionalParentName);
}
@@ -242,6 +244,24 @@ public class Modules implements Iterable<Module>
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()
{
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java b/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java
index 57f4cee27d..d3001317a1 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java
@@ -79,6 +79,7 @@ public class StartArgs
private List<String> jvmArgs = new ArrayList<>();
private List<String> moduleIni = new ArrayList<>();
private List<String> moduleStartIni = new ArrayList<>();
+ private String moduleGraphFilename;
private Modules allModules;
// Should the server be run?
@@ -439,6 +440,11 @@ public class StartArgs
return System.getProperty("main.class",mainclass);
}
+ public String getModuleGraphFilename()
+ {
+ return moduleGraphFilename;
+ }
+
public List<String> getModuleIni()
{
return moduleIni;
@@ -711,6 +717,13 @@ public class StartArgs
return;
}
+ if (arg.startsWith("--write-module-graph="))
+ {
+ this.moduleGraphFilename = getValue(arg);
+ run = false;
+ return;
+ }
+
// Start property (syntax similar to System property)
if (arg.startsWith("-D"))
{
diff --git a/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt b/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt
index 6817c422e8..c72edbb554 100644
--- a/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt
+++ b/jetty-start/src/main/resources/org/eclipse/jetty/start/usage.txt
@@ -82,6 +82,13 @@ Module Management:
enabled in the ${jetty.base}/start.ini using the same
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:
--------------------------------
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleGraphWriterTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleGraphWriterTest.java
new file mode 100644
index 0000000000..a8d42e1f24
--- /dev/null
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/ModuleGraphWriterTest.java
@@ -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));
+ }
+}

Back to the top