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