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