View Javadoc

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