View Javadoc

1   /*
2    * Copyright 2012 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.maven.bugzilla;
17  
18  import java.io.IOException;
19  import java.io.InputStream;
20  import java.lang.reflect.Field;
21  import java.util.List;
22  import java.util.Properties;
23  
24  import org.apache.commons.io.IOUtils;
25  import org.apache.commons.lang.reflect.FieldUtils;
26  import org.apache.maven.artifact.factory.ArtifactFactory;
27  import org.apache.maven.artifact.repository.ArtifactRepository;
28  import org.apache.maven.execution.MavenSession;
29  import org.apache.maven.plugin.AbstractMojo;
30  import org.apache.maven.plugin.MojoExecutionException;
31  import org.apache.maven.plugin.MojoFailureException;
32  import org.apache.maven.plugin.PluginManager;
33  import org.apache.maven.plugin.logging.Log;
34  import org.apache.maven.plugins.help.DescribeMojo;
35  import org.apache.maven.project.MavenProject;
36  import org.apache.maven.project.MavenProjectBuilder;
37  import org.apache.maven.settings.Settings;
38  
39  /**
40   * Displays help on the bugzilla maven plugin.
41   *
42   * @goal help
43   * @requiresProject false
44   * @description Displays help on the bugzilla maven plugin.
45   * @aggregator
46   * @threadSafe
47   * @since 1.0
48   */
49  public final class HelpMojo extends AbstractMojo // NOPMD
50  { // NOPMD
51    // ********************************* Fields *********************************
52  
53    // --- constants ------------------------------------------------------------
54  
55    // --- members --------------------------------------------------------------
56  
57    // ... Mojo infrastructure ..................................................
58  
59    /**
60     * Maven Artifact Factory component.
61     *
62     * @component
63     */
64    private ArtifactFactory artifactFactory;
65  
66    /**
67     * The Plugin manager instance used to resolve Plugin descriptors.
68     *
69     * @component role="org.apache.maven.plugin.PluginManager"
70     */
71    private PluginManager pluginManager;
72  
73    /**
74     * The project builder instance used to retrieve the super-project instance in
75     * the event there is no current MavenProject instance. Some MavenProject
76     * instance has to be present to use in the plugin manager APIs.
77     *
78     * @component role="org.apache.maven.project.MavenProjectBuilder"
79     */
80    private MavenProjectBuilder projectBuilder;
81  
82    /**
83     * The current project, if there is one. This is listed as optional, since the
84     * help plugin should be able to function on its own. If this parameter is
85     * empty at execution time, this Mojo will instead use the super-project.
86     *
87     * @parameter expression="${project}"
88     * @readonly
89     * @since 1.0
90     */
91    private MavenProject project;
92  
93    /**
94     * The current user system settings for use in Maven. This is used for plugin
95     * manager API calls.
96     *
97     * @parameter expression="${settings}"
98     * @required
99     * @readonly
100    * @since 1.0
101    */
102   private Settings settings;
103 
104   /**
105    * The current build session instance. This is used for plugin manager API
106    * calls.
107    *
108    * @parameter expression="${session}"
109    * @required
110    * @readonly
111    * @since 1.0
112    */
113   private MavenSession session;
114 
115   /**
116    * The local repository ArtifactRepository instance. This is used for plugin
117    * manager API calls.
118    *
119    * @parameter expression="${localRepository}"
120    * @required
121    * @readonly
122    * @since 1.0
123    */
124   private ArtifactRepository localRepository;
125 
126   /**
127    * Remote repositories used for the project.
128    *
129    * @parameter expression="${project.remoteArtifactRepositories}"
130    * @required
131    * @readonly
132    * @since 1.0
133    */
134   private List<?> remoteRepositories;
135 
136   // ... parameter ............................................................
137 
138   /**
139    * The name of the goal help is requested for. If not given, a list of all
140    * goals is returned.
141    *
142    * @parameter expression="${goal}"
143    * @since 1.0
144    */
145   private String goal;
146 
147   /**
148    * This flag specifies that a detailed (verbose) list of goal (Mojo)
149    * information should be given.
150    * <p>
151    * If information on a goal is requested, the detail value is set to
152    * <code>true</code> automatically.
153    * </p>
154    *
155    * @parameter expression="${detail}" default-value="false" alias="full"
156    * @since 1.0
157    */
158   private boolean detail;
159 
160   /**
161    * This flag specifies that a medium list of goal (Mojo) information should be
162    * given.
163    *
164    * @parameter expression="${medium}" default-value="true"
165    * @since 1.0
166    */
167   private boolean medium;
168 
169   /**
170    * This flag specifies that a minimal list of goal (Mojo) information should
171    * be given.
172    *
173    * @parameter expression="${minimal}" default-value="false"
174    * @since 1.0
175    */
176   private boolean minimal;
177 
178   // ... plugin details .......................................................
179 
180   /**
181    * The plugin identifier containing group, artifact id plus version.
182    */
183   private String pluginId;
184 
185   /**
186    * The version of this plugin.
187    */
188   private String version;
189 
190   /**
191    * The sort identifier of the plugin. The name in front of the first dash of
192    * the artifact identifier.
193    */
194   private String pluginShortId;
195 
196   // ****************************** Initializer *******************************
197 
198   // ****************************** Constructors ******************************
199 
200   /**
201    * Logger implementation to catch output messages of the describe mojo.
202    */
203   private final class LogCatcher implements Log
204   { // NOPMD
205     // ******************************** Fields ********************************
206 
207     // --- constants ----------------------------------------------------------
208 
209     // --- members ------------------------------------------------------------
210 
211     /**
212      * The logger to delegate configuration calls.
213      */
214     private final Log delegate;
215 
216     // ***************************** Initializer ******************************
217 
218     // ***************************** Constructors *****************************
219 
220     private LogCatcher(final Log delegate)
221     {
222       this.delegate = delegate;
223     }
224 
225     // ***************************** Inner Classes ****************************
226 
227     // ******************************** Methods *******************************
228 
229     // --- init ---------------------------------------------------------------
230 
231     // --- get&set ------------------------------------------------------------
232 
233     // --- business -----------------------------------------------------------
234 
235     // --- object basics ------------------------------------------------------
236 
237     /**
238      * {@inheritDoc}
239      *
240      * @see org.apache.maven.plugin.logging.Log#isDebugEnabled()
241      */
242     @Override
243     public boolean isDebugEnabled()
244     {
245       return delegate.isDebugEnabled();
246     }
247 
248     /**
249      * {@inheritDoc}
250      *
251      * @see org.apache.maven.plugin.logging.Log#debug(java.lang.CharSequence)
252      */
253     @Override
254     public void debug(final CharSequence content)
255     {
256       delegate.debug(content);
257     }
258 
259     /**
260      * {@inheritDoc}
261      *
262      * @see org.apache.maven.plugin.logging.Log#debug(java.lang.CharSequence,
263      *      java.lang.Throwable)
264      */
265     @Override
266     public void debug(final CharSequence content, final Throwable error)
267     {
268       delegate.debug(content, error);
269     }
270 
271     /**
272      * {@inheritDoc}
273      *
274      * @see org.apache.maven.plugin.logging.Log#debug(java.lang.Throwable)
275      */
276     @Override
277     public void debug(final Throwable error)
278     {
279       delegate.debug(error);
280     }
281 
282     /**
283      * {@inheritDoc}
284      *
285      * @see org.apache.maven.plugin.logging.Log#isInfoEnabled()
286      */
287     @Override
288     public boolean isInfoEnabled()
289     {
290       return delegate.isInfoEnabled();
291     }
292 
293     /**
294      * {@inheritDoc}
295      *
296      * @see org.apache.maven.plugin.logging.Log#info(java.lang.CharSequence)
297      */
298     @Override
299     public void info(final CharSequence content)
300     {
301       final String string = content.toString();
302 
303       if (string.startsWith(pluginId))
304       {
305         final String strippedContent = stripAllGoals(string);
306 
307         final String fullContent =
308             strippedContent + ".\nFor detailed information on a goal, run"
309                 + " 'mvn bugzilla:help -Dgoal=GOAL'.";
310         delegate.info(fullContent);
311       }
312       else if (string.startsWith("Mojo: '" + pluginShortId))
313       {
314         final String strippedContent = stripSingleGoal(string);
315         delegate.info(strippedContent);
316       }
317       else
318       {
319         delegate.info(content);
320       }
321     }
322 
323     private String stripAllGoals(final String string)
324     {
325       final String tag = "Goal Prefix: " + pluginShortId;
326       final int startIndex = string.indexOf(tag) + tag.length();
327       final String fragment = string.substring(startIndex).trim();
328       final String replaced = replaceHelpIds(fragment);
329       return replaced;
330     }
331 
332     private String replaceHelpIds(final String fragment)
333     {
334       final String replaced =
335           fragment.replaceAll("help:describe", pluginShortId + ":help")
336               .replace(" [...]", "");
337       return replaced;
338     }
339 
340     private String stripSingleGoal(final String string)
341     {
342       final String replaced = replaceHelpIds(string);
343       return replaced;
344     }
345 
346     /**
347      * {@inheritDoc}
348      *
349      * @see org.apache.maven.plugin.logging.Log#info(java.lang.CharSequence,
350      *      java.lang.Throwable)
351      */
352     @Override
353     public void info(final CharSequence content, final Throwable error)
354     {
355       delegate.info(content, error);
356     }
357 
358     /**
359      * {@inheritDoc}
360      *
361      * @see org.apache.maven.plugin.logging.Log#info(java.lang.Throwable)
362      */
363     @Override
364     public void info(final Throwable error)
365     {
366       delegate.info(error);
367     }
368 
369     /**
370      * {@inheritDoc}
371      *
372      * @see org.apache.maven.plugin.logging.Log#isWarnEnabled()
373      */
374     @Override
375     public boolean isWarnEnabled()
376     {
377       return delegate.isWarnEnabled();
378     }
379 
380     /**
381      * {@inheritDoc}
382      *
383      * @see org.apache.maven.plugin.logging.Log#warn(java.lang.CharSequence)
384      */
385     @Override
386     public void warn(final CharSequence content)
387     {
388       delegate.warn(content);
389     }
390 
391     /**
392      * {@inheritDoc}
393      *
394      * @see org.apache.maven.plugin.logging.Log#warn(java.lang.CharSequence,
395      *      java.lang.Throwable)
396      */
397     @Override
398     public void warn(final CharSequence content, final Throwable error)
399     {
400       delegate.warn(content, error);
401     }
402 
403     /**
404      * {@inheritDoc}
405      *
406      * @see org.apache.maven.plugin.logging.Log#warn(java.lang.Throwable)
407      */
408     @Override
409     public void warn(final Throwable error)
410     {
411       delegate.warn(error);
412     }
413 
414     /**
415      * {@inheritDoc}
416      *
417      * @see org.apache.maven.plugin.logging.Log#isErrorEnabled()
418      */
419     @Override
420     public boolean isErrorEnabled()
421     {
422       return delegate.isErrorEnabled();
423     }
424 
425     /**
426      * {@inheritDoc}
427      *
428      * @see org.apache.maven.plugin.logging.Log#error(java.lang.CharSequence)
429      */
430     @Override
431     public void error(final CharSequence content)
432     {
433       delegate.error(content);
434     }
435 
436     /**
437      * {@inheritDoc}
438      *
439      * @see org.apache.maven.plugin.logging.Log#error(java.lang.CharSequence,
440      *      java.lang.Throwable)
441      */
442     @Override
443     public void error(final CharSequence content, final Throwable error)
444     {
445       delegate.error(content, error);
446     }
447 
448     /**
449      * {@inheritDoc}
450      *
451      * @see org.apache.maven.plugin.logging.Log#error(java.lang.Throwable)
452      */
453     @Override
454     public void error(final Throwable error)
455     {
456       delegate.error(error);
457     }
458   }
459 
460   // ****************************** Inner Classes *****************************
461 
462   // ********************************* Methods ********************************
463 
464   // --- init -----------------------------------------------------------------
465 
466   // --- get&set --------------------------------------------------------------
467 
468   // --- business -------------------------------------------------------------
469 
470   /**
471    * {@inheritDoc}
472    *
473    * @see org.apache.maven.plugin.Mojo#execute()
474    */
475   @Override
476   public void execute() throws MojoExecutionException, MojoFailureException
477   {
478     initPluginInfo();
479 
480     if (goal != null)
481     {
482       this.detail = true;
483     }
484 
485     final DescribeMojo describeMojo = createHelpMojo();
486 
487     try
488     {
489       describeMojo.execute();
490 
491       if (!this.detail)
492       {
493         provideBestPracticesInformation();
494       }
495     }
496     catch (final MojoExecutionException e)
497     {
498       throw new MojoExecutionException(createErrorMessage(), e);
499     }
500     catch (final MojoFailureException e)
501     {
502       throw new MojoExecutionException(createErrorMessage(), e);
503     }
504   }
505 
506   private DescribeMojo createHelpMojo() throws MojoFailureException
507   {
508     final DescribeMojo describeMojo = new DescribeMojo()
509     {
510       @Override
511       public Log getLog()
512       {
513         return new LogCatcher(super.getLog());
514       }
515     };
516 
517     setHelpMojoValues(describeMojo);
518 
519     return describeMojo;
520   }
521 
522   private void setHelpMojoValues(final DescribeMojo describeMojo)
523     throws MojoFailureException
524   {
525     setValue(describeMojo, "artifactFactory", artifactFactory);
526     setValue(describeMojo, "pluginManager", pluginManager);
527     setValue(describeMojo, "projectBuilder", projectBuilder);
528 
529     setValue(describeMojo, "project", project);
530     setValue(describeMojo, "settings", settings);
531     setValue(describeMojo, "session", session);
532 
533     setValue(describeMojo, "localRepository", localRepository);
534     setValue(describeMojo, "remoteRepositories", remoteRepositories);
535 
536     setValue(describeMojo, "plugin", pluginId);
537     setValue(describeMojo, "goal", goal);
538 
539     setValue(describeMojo, "detail", detail);
540     setValue(describeMojo, "medium", medium);
541     setValue(describeMojo, "minimal", minimal);
542   }
543 
544   private void provideBestPracticesInformation()
545   {
546     final String server = "https://www.smartics.eu";
547     final String product = server + "/plugins/bugzilla-maven-plugin";
548     getLog()
549         .info(
550             "== Best Practices ==\n"
551                 + " > Project Setup: mvn bugzilla:init\n"
552                 + "   See "
553                 + product
554                 + '/'
555                 + version
556                 + "/best-practices.html#Project_Set_Up\n"
557                 + "\n"
558                 + " > After a Release: mvn bugzilla:sync -DreleasedVersion=VERSION\n"
559                 + "   See " + product + '/' + version
560                 + "/best-practices.html#Release\n" + "\n"
561                 + "For more and detailed information please visit:\n" + "   "
562                 + product + '/' + version + "/index.html");
563   }
564 
565   private void initPluginInfo() throws MojoExecutionException
566   {
567     final Properties properties = new Properties();
568     final InputStream inStream =
569         getClass().getResourceAsStream("/META-INF/build.properties");
570     try
571     {
572       properties.load(inStream);
573 
574       final String groupId = properties.getProperty("build.groupId");
575       final String artifactId = properties.getProperty("build.artifactId");
576       this.version = properties.getProperty("build.version");
577 
578       this.pluginId = groupId + ':' + artifactId + ':' + version;
579 
580       final int endIndexShortId = artifactId.indexOf('-');
581       this.pluginShortId = artifactId.substring(0, endIndexShortId);
582     }
583     catch (final IOException e)
584     {
585       throw new MojoExecutionException("Cannot determine plugin properties.", e);
586     }
587     finally
588     {
589       IOUtils.closeQuietly(inStream);
590     }
591   }
592 
593   private String createErrorMessage()
594   {
595     return "Cannot provide help information"
596            + (goal != null ? " on " + goal : "") + '.';
597   }
598 
599   private static void setValue(final Object object, final String fieldName,
600       final Object value) throws MojoFailureException
601   {
602     final Class<?> type = object.getClass();
603 
604     try
605     {
606       final Field field = FieldUtils.getField(type, fieldName, true);
607       field.setAccessible(true);
608       field.set(object, value);
609     }
610     catch (final Exception e)
611     {
612       throw new MojoFailureException("Cannot change describe mojo's field '"
613                                      + fieldName + "'.", e);
614     }
615   }
616 
617   // --- object basics --------------------------------------------------------
618 
619 }