View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.start;
20  
21  import java.io.BufferedWriter;
22  import java.io.IOException;
23  import java.io.PrintWriter;
24  import java.nio.charset.StandardCharsets;
25  import java.nio.file.Files;
26  import java.nio.file.Path;
27  import java.nio.file.StandardOpenOption;
28  import java.util.Collection;
29  import java.util.List;
30  
31  /**
32   * Generate a graphviz dot graph of the modules found
33   */
34  public class ModuleGraphWriter
35  {
36      private String colorModuleBg;
37      private String colorEnabledBg;
38      private String colorTransitiveBg;
39      private String colorCellBg;
40      private String colorHeaderBg;
41      private String colorModuleFont;
42  
43      public ModuleGraphWriter()
44      {
45          colorModuleBg = "#B8FFB8";
46          colorEnabledBg = "#66FFCC";
47          colorTransitiveBg = "#66CC66";
48          colorCellBg = "#FFFFFF80";
49          colorHeaderBg = "#00000020";
50          colorModuleFont = "#888888";
51      }
52  
53      public void config(Props props)
54      {
55          String prefix = "jetty.graph.";
56          colorModuleBg = getProperty(props,prefix + "color.module.bg",colorModuleBg);
57          colorEnabledBg = getProperty(props,prefix + "color.enabled.bg",colorEnabledBg);
58          colorTransitiveBg = getProperty(props,prefix + "color.transitive.bg",colorTransitiveBg);
59          colorCellBg = getProperty(props,prefix + "color.cell.bg",colorCellBg);
60          colorHeaderBg = getProperty(props,prefix + "color.header.bg",colorHeaderBg);
61          colorModuleFont = getProperty(props,prefix + "color.font",colorModuleFont);
62      }
63  
64      private String getProperty(Props props, String key, String defVal)
65      {
66          String val = props.getString(key,defVal);
67          if (val == null)
68          {
69              return defVal;
70          }
71          val = val.trim();
72          if (val.length() <= 0)
73          {
74              return defVal;
75          }
76          return val;
77      }
78  
79      public void write(Modules modules, Path outputFile) throws IOException
80      {
81          try (BufferedWriter writer = Files.newBufferedWriter(outputFile,StandardCharsets.UTF_8,StandardOpenOption.CREATE_NEW,StandardOpenOption.WRITE); 
82               PrintWriter out = new PrintWriter(writer);)
83          {
84              writeHeaderMessage(out,outputFile);
85  
86              out.println();
87              out.println("digraph modules {");
88  
89              // Node Style
90              out.println("  node [color=gray, style=filled, shape=rectangle];");
91              out.println("  node [fontname=\"Verdana\", size=\"20,20\"];");
92              // Graph Style
93              out.println("  graph [");
94              out.println("    concentrate=false,");
95              out.println("    fontname=\"Verdana\",");
96              out.println("    fontsize = 20,");
97              out.println("    rankdir = LR,");
98              out.println("    ranksep = 1.5,");
99              out.println("    nodesep = .5,");
100             out.println("    style = bold,");
101             out.println("    labeljust = l,");
102             out.println("    label = \"Jetty Modules\",");
103             out.println("    ssize = \"20,40\"");
104             out.println("  ];");
105 
106             List<Module> enabled = modules.resolveEnabled();
107 
108             // Module Nodes
109             writeModules(out,modules,enabled);
110 
111             // Module Relationships
112             writeRelationships(out,modules,enabled);
113 
114             out.println("}");
115             out.println();
116         }
117     }
118 
119     private void writeHeaderMessage(PrintWriter out, Path outputFile)
120     {
121         out.println("/*");
122         out.println(" * GraphViz Graph of Jetty Modules");
123         out.println(" * ");
124         out.println(" * Jetty: http://eclipse.org/jetty/");
125         out.println(" * GraphViz: http://graphviz.org/");
126         out.println(" * ");
127         out.println(" * To Generate Graph image using graphviz:");
128         String filename = outputFile.getFileName().toString();
129         String basename = filename.substring(0,filename.indexOf('.'));
130         out.printf(" *   $ dot -Tpng -Goverlap=false -o %s.png %s%n",basename,filename);
131         out.println(" */");
132     }
133 
134     private void writeModuleDetailHeader(PrintWriter out, String header)
135     {
136         writeModuleDetailHeader(out,header,1);
137     }
138 
139     private void writeModuleDetailHeader(PrintWriter out, String header, int count)
140     {
141         out.printf("  <TR>");
142         out.printf("<TD BGCOLOR=\"%s\" ALIGN=\"LEFT\"><I>",colorHeaderBg);
143         out.printf("%s%s</I></TD>",header,count > 1?"s":"");
144         out.println("</TR>");
145     }
146 
147     private void writeModuleDetailLine(PrintWriter out, String line)
148     {
149         out.printf("  <TR>");
150         StringBuilder escape = new StringBuilder();
151         for(char c: line.toCharArray()) {
152             switch(c) {
153                 case '<': escape.append("&lt;"); break;
154                 case '>': escape.append("&gt;"); break;
155                 default:
156                     escape.append(c);
157                     break;
158             }
159         }
160         
161         out.printf("<TD BGCOLOR=\"%s\" ALIGN=\"LEFT\">%s</TD></TR>%n",colorCellBg,escape.toString());
162     }
163 
164     private void writeModuleNode(PrintWriter out, Module module, boolean resolved)
165     {
166         String color = colorModuleBg;
167         if (module.isEnabled())
168         {
169             // specifically enabled by config
170             color = colorEnabledBg;
171         }
172         else if (resolved)
173         {
174             // enabled by transitive reasons
175             color = colorTransitiveBg;
176         }
177 
178         out.printf("  \"%s\" [ color=\"%s\" label=<",module.getName(),color);
179         out.printf("<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"2\">%n");
180         out.printf("  <TR><TD ALIGN=\"LEFT\"><B>%s</B></TD></TR>%n",module.getName());
181 
182         if (module.isEnabled())
183         {
184             writeModuleDetailHeader(out,"ENABLED");
185             for (String source : module.getSources())
186             {
187                 writeModuleDetailLine(out,"via: " + source);
188             }
189         }
190         else if (resolved)
191         {
192             writeModuleDetailHeader(out,"TRANSITIVE");
193         }
194 
195         if (!module.getXmls().isEmpty())
196         {
197             List<String> xmls = module.getXmls();
198             writeModuleDetailHeader(out,"XML",xmls.size());
199             for (String xml : xmls)
200             {
201                 writeModuleDetailLine(out,xml);
202             }
203         }
204 
205         if (!module.getLibs().isEmpty())
206         {
207             List<String> libs = module.getLibs();
208             writeModuleDetailHeader(out,"LIB",libs.size());
209             for (String lib : libs)
210             {
211                 writeModuleDetailLine(out,lib);
212             }
213         }
214 
215         if (!module.getDefaultConfig().isEmpty())
216         {
217             List<String> inis = module.getDefaultConfig();
218             writeModuleDetailHeader(out,"INI Template",inis.size());
219         }
220 
221         out.printf("</TABLE>>];%n");
222     }
223 
224     private void writeModules(PrintWriter out, Modules allmodules, List<Module> enabled)
225     {
226         out.println();
227         out.println("  /* Modules */");
228         out.println();
229 
230         out.println("  node [ labeljust = l ];");
231 
232         for (int depth = 0; depth <= allmodules.getMaxDepth(); depth++)
233         {
234             out.println();
235             Collection<Module> depthModules = allmodules.getModulesAtDepth(depth);
236             if (depthModules.size() > 0)
237             {
238                 out.printf("  /* Level %d */%n",depth);
239                 out.println("  { rank = same;");
240                 for (Module module : depthModules)
241                 {
242                     boolean resolved = enabled.contains(module);
243                     writeModuleNode(out,module,resolved);
244                 }
245                 out.println("  }");
246             }
247         }
248     }
249 
250     private void writeRelationships(PrintWriter out, Modules modules, List<Module> enabled)
251     {
252         for (Module module : modules)
253         {
254             for (Module parent : module.getParentEdges())
255             {
256                 out.printf("    \"%s\" -> \"%s\";%n",module.getName(),parent.getName());
257             }
258         }
259     }
260 }