View Javadoc
1   package com.wakaleo.schemaspy;
2   
3   import java.io.File;
4   import java.util.ArrayList;
5   import java.util.List;
6   import java.util.Locale;
7   import org.apache.maven.plugins.annotations.LifecyclePhase;
8   import org.apache.maven.plugins.annotations.Mojo;
9   import org.apache.maven.plugins.annotations.Parameter;
10  import org.apache.maven.reporting.AbstractMavenReport;
11  import org.apache.maven.reporting.MavenReportException;
12  
13  /**
14   * The SchemaSpy Maven plugin report.
15   *
16   * <p>This plugin is designed to generate SchemaSpy report for a Maven website. SchemaSpy also may
17   * need the Graphviz tool (https://www.graphviz.org/) in order to generate graphical representations
18   * of the table/view relationships, so this needs to be installed on your machine.
19   *
20   * <p>The schemaspy goal invokes the SchemaSpy command-line tool. SchemaSpy generates a graphical
21   * and HTML report describing a given relational database.
22   *
23   * @author John Smart
24   * @author mprins
25   */
26  @Mojo(name = "schemaspy", defaultPhase = LifecyclePhase.SITE)
27  public class SchemaSpyReport extends AbstractMavenReport {
28  
29    @Parameter(property = "vizjs", defaultValue = "true")
30    protected boolean vizjs = false;
31  
32    /**
33     * Whether to create the report only on the execution root of a multi-module project.
34     *
35     * @since 5.0.4
36     */
37    @Parameter(property = "runOnExecutionRoot", defaultValue = "false")
38    protected boolean runOnExecutionRoot = false;
39  
40    /** The output directory for the intermediate report. */
41    @Parameter(property = "targetDirectory", defaultValue = "${project.build.directory}")
42    private File targetDirectory;
43  
44    /** The name of the database being analysed. */
45    @Parameter(property = "database", required = true)
46    private String database;
47  
48    /** The host address of the database being analysed. */
49    @Parameter(property = "host")
50    private String host;
51  
52    /** The port, required by some drivers. */
53    @Parameter(property = "port")
54    private String port;
55  
56    /** The type of database being analysed — defaults to ora. */
57    @Parameter(property = "databaseType")
58    private String databaseType;
59  
60    /** Connect to the database with this user id. */
61    @Parameter(property = "user")
62    private String user;
63  
64    /** Database schema to use — defaults to the specified user. */
65    @Parameter(property = "schema")
66    private String schema;
67  
68    /** Database password to use — defaults to none. */
69    @Parameter(property = "password")
70    private String password;
71  
72    /**
73     * If specified, SchemaSpy will look for JDBC drivers on this path, rather than using the
74     * application classpath. Useful if your database has a non-O/S driver not bundled with the
75     * plugin.
76     */
77    @Parameter(property = "pathToDrivers")
78    private String pathToDrivers;
79  
80    /**
81     * Schema description. Displays the specified textual description on summary pages. If your
82     * description includes an equals sign then escape it with a backslash. NOTE: This field doesn't
83     * seem to be used by SchemaSpy in the current version.
84     */
85    @Parameter(property = "schemaDescription")
86    private String schemaDescription;
87  
88    /**
89     * Only include matching tables/views. This is a regular expression that's used to determine which
90     * tables/views to include. For example: -i "(.*book.*)|(library.*)" includes only those
91     * tables/views with 'book' in their names or that start with 'library'. You might want to use
92     * "description" with this option to describe the subset of tables.
93     */
94    @Parameter(property = "includeTableNamesRegex")
95    private String includeTableNamesRegex;
96  
97    /**
98     * Exclude matching columns from relationship analysis to simplify the generated graphs. This is a
99     * regular expression that's used to determine which columns to exclude. It must match table name,
100    * followed by a dot, followed by column name. For example: -x "(book.isbn)|(borrower.address)"
101    * Note that each column name regular expression must be surrounded by ()'s and separated from other
102    * column names by a |.
103    */
104   @Parameter(property = "excludeColumnNamesRegex")
105   private String excludeColumnNamesRegex;
106 
107   /**
108    * Allow HTML In Comments. Any HTML embedded in comments normally gets encoded so that it's
109    * rendered as text. This option allows it to be rendered as HTML.
110    */
111   @Parameter(property = "allowHtmlInComments")
112   private Boolean allowHtmlInComments;
113 
114   /**
115    * Comments Initially Displayed. Column comments are normally hidden by default. This option
116    * displays them by default.
117    *
118    * @deprecated this seems to no longer be a supported option in SchemaSpy
119    */
120   @Deprecated
121   @Parameter(property = "commentsInitiallyDisplayed")
122   private Boolean commentsInitiallyDisplayed;
123 
124   /** Don't include implied foreign key relationships in the generated table details. */
125   @Parameter(property = "noImplied")
126   private Boolean noImplied;
127 
128   /** Only generate files needed for insertion/deletion of data (e.g. for scripts). */
129   @Parameter(property = "noHtml")
130   private Boolean noHtml;
131 
132   /** Detail of execution logging. */
133   @Parameter(property = "logLevel")
134   private String logLevel;
135 
136   /**
137    * Some databases, like Derby, will crash if you use the old driver object to establish a
138    * connection (eg "connection = driver.connect(...)"). In this case, set useDriverManager to true
139    * to use the DriverManager.getConnection() method instead (eg "connection =
140    * java.sql.DriverManager.getConnection(...)"). Other databases (e.g. MySQL) seem to only work with
141    * the first method, so don't use this parameter unless you have to.
142    */
143   @Parameter(property = "useDriverManager")
144   private Boolean useDriverManager;
145 
146   /** The CSS Stylesheet. Allows you to override the default SchemaSpyCSS stylesheet. */
147   @Parameter(property = "cssStylesheet")
148   private String cssStylesheet;
149 
150   /**
151    * Single Sign-On. Don't require a user to be specified with -user to simplify configuration when
152    * running in a single sign-on environment.
153    */
154   @Parameter(property = "singleSignOn")
155   private Boolean singleSignOn;
156 
157   /**
158    * Generate lower-quality diagrams. Various installations of Graphviz (depending on OS and/or
159    * version) will default to generating either higher or lower quality images. That is, some might
160    * not have the "lower quality" libraries and others might not have the "higher quality"
161    * libraries.
162    */
163   @Parameter(property = "lowQuality")
164   private Boolean lowQuality;
165 
166   /**
167    * Generate higher-quality diagrams. Various installations of Graphviz (depending on OS and/or
168    * version) will default to generating either higher or lower quality images. That is, some might
169    * not have the "lower quality" libraries and others might not have the "higher quality"
170    * libraries.
171    */
172   @Parameter(property = "highQuality")
173   private Boolean highQuality;
174 
175   /**
176    * Evaluate all schemas in a database. Generates a high-level index of the schemas evaluated and
177    * allows for traversal of cross-schema foreign key relationships. Use with -schemaSpec
178    * "schemaRegularExpression" to narrow-down the schemas to include.
179    */
180   @Parameter(property = "showAllSchemas")
181   private Boolean showAllSchemas;
182 
183   /**
184    * Evaluate specified schemas. Similar to -showAllSchemas, but explicitly specifies which schema
185    * to evaluate without interrogating the database's metadata. Can be used with databases like
186    * MySQL where a database isn't composed of multiple schemas.
187    */
188   @Parameter(property = "schemas")
189   private String schemas;
190 
191   /** No schema required for this database (e.g. derby). */
192   @Parameter(property = "noSchema")
193   private Boolean noSchema;
194 
195   /** Don't query or display row counts. */
196   @Parameter(property = "noRows")
197   private Boolean noRows;
198 
199   /** Don't query or display row counts. */
200   @Parameter(property = "noViews")
201   private Boolean noViews;
202 
203   /**
204    * Specifies additional properties to be used when connecting to the database. Specify the entries
205    * directly, escaping the ='s with \= and separating each key\=value pair with a ;.
206    *
207    * <p>May also be a file name, useful for hiding connection properties from public logs.
208    */
209   @Parameter(property = "connprops")
210   private String connprops;
211 
212   /**
213    * Don't generate ads in reports.
214    *
215    * @deprecated will be removed
216    */
217   @Deprecated
218   @Parameter(property = "noAds", defaultValue = "true")
219   private Boolean noAds;
220 
221   /**
222    * Don't generate sourceforge logos in reports.
223    *
224    * @deprecated will be removed
225    */
226   @Deprecated
227   @Parameter(property = "noLogo", defaultValue = "true")
228   private Boolean noLogo;
229 
230   @Parameter(property = "catalog", defaultValue = "%")
231   private String catalog;
232 
233   /**
234    * The SchemaSpy analyser that generates the actual report. Can be overridden for testing
235    * purposes.
236    */
237   private MavenSchemaAnalyzer analyzer;
238 
239   protected void setSchemaAnalyzer(final MavenSchemaAnalyzer analyzer) {
240     this.analyzer = analyzer;
241   }
242 
243   /**
244    * Convenience method used to build the schemaspy command line parameters.
245    *
246    * @param argList the current list of schemaspy parameter options.
247    * @param parameter a new parameter to add
248    * @param value the value for this parameter
249    */
250   private void addToArguments(
251       final List<String> argList, final String parameter, final Boolean value) {
252     if (value != null && value) {
253       argList.add(parameter);
254       argList.add(String.valueOf(true));
255     }
256   }
257 
258   /**
259    * Convenience method used to build the schemaspy command line parameters.
260    *
261    * @param argList the current list of schemaspy parameter options.
262    * @param parameter a new parameter to add
263    * @param value the value for this parameter
264    */
265   private void addFlagToArguments(
266       final List<String> argList, final String parameter, final Boolean value) {
267     if (value != null && value) {
268       argList.add(parameter);
269     }
270   }
271 
272   /**
273    * Convenience method used to build the schemaspy command line parameters.
274    *
275    * @param argList the current list of schemaspy parameter options.
276    * @param parameter a new parameter to add
277    * @param value the value for this parameter
278    */
279   private void addToArguments(
280       final List<String> argList, final String parameter, final String value) {
281     if (value != null) {
282       argList.add(parameter);
283       argList.add(value);
284     }
285   }
286 
287   /**
288    * Generate the Schemaspy report.
289    *
290    * @throws MavenReportException if schemaspy crashes
291    * @param locale the language of the report — currently ignored.
292    */
293   @Override
294   protected void executeReport(final Locale locale) throws MavenReportException {
295     File outputDir;
296     if (outputDirectory == null) {
297       // targetDirectory should be set by the maven framework. This is just for unit testing
298       // purposes
299       if (targetDirectory == null) {
300         targetDirectory = new File("target");
301       }
302       File siteDir = new File(targetDirectory, "site");
303       outputDir = new File(siteDir, "schemaspy");
304     } else {
305       outputDir = new File(outputDirectory, "schemaspy");
306     }
307     outputDir.mkdirs();
308 
309     String schemaSpyDirectory = outputDir.getAbsolutePath();
310     getLog().debug("SchemaSpy output directory: " + schemaSpyDirectory);
311 
312     List<String> argList = new ArrayList<>();
313 
314     addToArguments(argList, "-dp", pathToDrivers);
315     addToArguments(argList, "-db", database);
316     addToArguments(argList, "-host", host);
317     addToArguments(argList, "-port", port);
318     addToArguments(argList, "-t", databaseType);
319     addToArguments(argList, "-u", user);
320     addToArguments(argList, "-p", password);
321     if (null != schema && schema.contains(",")) {
322       addToArguments(argList, "-schemas", schema);
323     } else {
324       addToArguments(argList, "-s", schema);
325     }
326     addToArguments(argList, "-o", schemaSpyDirectory);
327     addToArguments(argList, "-desc", schemaDescription);
328     addToArguments(argList, "-i", includeTableNamesRegex);
329     addToArguments(argList, "-x", excludeColumnNamesRegex);
330     addFlagToArguments(argList, "-ahic", allowHtmlInComments);
331     addFlagToArguments(argList, "-noimplied", noImplied);
332     addFlagToArguments(argList, "-nohtml", noHtml);
333     addToArguments(argList, "-loglevel", logLevel);
334     addFlagToArguments(argList, "-norows", noRows);
335     addFlagToArguments(argList, "-noviews", noViews);
336     addFlagToArguments(argList, "-noschema", noSchema);
337     addFlagToArguments(argList, "-all", showAllSchemas);
338     addToArguments(argList, "-schemas", schemas);
339     addToArguments(argList, "-useDriverManager", useDriverManager);
340     addToArguments(argList, "-css", cssStylesheet);
341     addFlagToArguments(argList, "-sso", singleSignOn);
342     addFlagToArguments(argList, "-lq", lowQuality);
343     addFlagToArguments(argList, "-hq", highQuality);
344     addToArguments(argList, "-connprops", connprops);
345     addFlagToArguments(argList, "-cid", commentsInitiallyDisplayed);
346     addFlagToArguments(argList, "-noads", noAds);
347     addFlagToArguments(argList, "-nologo", noLogo);
348     addToArguments(argList, "-cat", catalog);
349     addFlagToArguments(argList, "-vizjs", vizjs);
350     if (getLog().isDebugEnabled()) {
351       addFlagToArguments(argList, "-debug", true);
352     }
353     getLog().debug("SchemaSpy arguments: " + argList);
354 
355     try {
356       if (analyzer == null) {
357         analyzer = new MavenSchemaAnalyzer();
358         analyzer.applyConfiguration(argList);
359       }
360       analyzer.analyze();
361     } catch (Exception e) {
362       throw new MavenReportException(e.getMessage(), e);
363     }
364   }
365 
366   @Override
367   public boolean canGenerateReport() {
368     return !runOnExecutionRoot || project.isExecutionRoot();
369   }
370 
371   @Override
372   public String getDescription(final Locale locale) {
373     return "SchemaSpy database documentation";
374   }
375 
376   @Override
377   public String getName(final Locale locale) {
378     return "SchemaSpy";
379   }
380 
381   @Override
382   public String getOutputName() {
383     return "schemaspy/index";
384   }
385 
386   @Override
387   public String getOutputPath() {
388     return "schemaspy/index";
389   }
390 
391   /**
392    * Always return {@code true} as we're using the report generated by SchemaSpy rather than
393    * creating our own report.
394    *
395    * @return {@code true}
396    */
397   @Override
398   public boolean isExternalReport() {
399     return true;
400   }
401 }