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