View Javadoc

1   package org.apache.maven.plugin.announcement;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.plugin.AbstractMojo;
23  import org.apache.maven.plugin.MojoExecutionException;
24  import org.apache.maven.plugin.changes.Action;
25  import org.apache.maven.plugin.changes.ChangesXML;
26  import org.apache.maven.plugin.changes.Release;
27  import org.apache.maven.plugin.jira.JiraXML;
28  import org.apache.maven.project.MavenProject;
29  import org.apache.maven.settings.Settings;
30  import org.apache.velocity.VelocityContext;
31  import org.apache.velocity.context.Context;
32  import org.apache.velocity.exception.ResourceNotFoundException;
33  import org.apache.velocity.exception.VelocityException;
34  import org.codehaus.plexus.velocity.VelocityComponent;
35  
36  import java.io.File;
37  import java.io.FileWriter;
38  import java.io.Writer;
39  import java.util.Iterator;
40  import java.util.List;
41  
42  /**
43   * Goal which generate the template for an announcement.
44   *
45   * @goal announcement-generate
46   * @requiresDependencyResolution test
47   * @author aramirez@exist.com
48   * @version $Id: AnnouncementMojo.html 816584 2012-05-08 12:33:35Z hboutemy $
49   */
50  public class AnnouncementMojo
51      extends AbstractMojo
52  {
53      private static final String SNAPSHOT_SUFFIX = "-SNAPSHOT";
54  
55      /**
56       * Directory where the template file will be generated.
57       *
58       * @parameter expression="${project.build.directory}/announcement"
59       * @required
60       */
61      private File outputDirectory;
62  
63      /**
64       * @parameter expression="${project.groupId}"
65       * @readonly
66       */
67      private String groupId;
68  
69      /**
70       * @parameter expression="${project.artifactId}"
71       * @readonly
72       */
73      private String artifactId;
74  
75      /**
76       * Version of the artifact.
77       *
78       * @parameter expression="${project.version}"
79       * @readonly
80       */
81      private String version;
82  
83      /**
84       * Distribution url of the artifact.
85       *
86       * @parameter expression="${project.url}"
87       * @required
88       */
89      private String url;
90  
91      /**
92       * Packaging structure for the artifact.
93       *
94       * @parameter expression="${project.packaging}"
95       * @readonly
96       */
97      private String packaging;
98  
99      /**
100      * The name of the artifact to be used in the announcement.
101      *
102      * @parameter expression="${project.build.finalName}"
103      * @required
104      */
105     private String finalName;
106 
107     /**
108      * URL where the artifact can be downloaded. If not specified,
109      * no URL is used.
110      *
111      * @parameter
112      */
113     private String urlDownload;
114 
115     /**
116      * The path of the changes.xml file.
117      *
118      * @parameter expression="${basedir}/src/changes/changes.xml"
119      * @required
120      */
121     private File xmlPath;
122 
123     /**
124      * Name of the team that develops the artifact.
125      *
126      * @parameter default-value="${project.name} team"
127      * @required
128      */
129     private String developmentTeam;
130 
131     /**
132      * Short description or introduction of the released artifact.
133      *
134      * @parameter expression="${project.description}"
135      */
136     private String introduction;
137 
138     /**
139      * Velocity Component.
140      *
141      * @parameter expression="${component.org.codehaus.plexus.velocity.VelocityComponent}"
142      * @readonly
143      */
144     private VelocityComponent velocity;
145 
146     /**
147      * The Velocity template used to format the announcement.
148      *
149      * @parameter default-value="announcement.vm"
150      * @required
151      */
152     private String template;
153 
154     /**
155      * Directory that contains the template.
156      * <p>
157      * <b>Note:</b> This directory must be a subdirectory of
158      * <code>/src/main/resources/</code>.
159      * </p>
160      *
161      * @parameter default-value="org/apache/maven/plugin/announcement"
162      * @required
163      */
164     private String templateDirectory;
165 
166     private ChangesXML xml;
167 
168     //=======================================//
169     //  JIRA-Announcement Needed Parameters  //
170     //=======================================//
171 
172     /**
173      * The Maven Project.
174      *
175      * @parameter expression="${project}"
176      * @required
177      * @readonly
178      */
179     private MavenProject project;
180 
181     /**
182      * Settings XML configuration.
183      *
184      * @parameter expression="${settings}"
185      * @required
186      * @readonly
187      */
188     private Settings settings;
189 
190     /**
191      * Flag to determine if the plugin will generate a JIRA announcement.
192      *
193      * @parameter expression="${generateJiraAnnouncement}" default-value="false"
194      * @required
195      */
196     private boolean generateJiraAnnouncement;
197 
198     /**
199      * Only closed issues are needed.
200      * <p>
201      * <b>Note:</b> In versions 2.0-beta-3 and earlier this parameter was
202      * called "statusId".
203      * </p>
204      *
205      * @parameter default-value="Closed"
206      */
207     private String statusIds;
208 
209     /**
210      * Only fixed issues are needed.
211      * <p>
212      * <b>Note:</b> In versions 2.0-beta-3 and earlier this parameter was
213      * called "resolutionId".
214      * </p>
215      *
216      * @parameter default-value="Fixed"
217      */
218     private String resolutionIds;
219 
220     /**
221      * The path of the XML file of JIRA-announcements to be parsed.
222      *
223      * @parameter expression="${project.build.directory}/jira-announcement.xml"
224      * @required
225      * @readonly
226      */
227     private File jiraXML;
228 
229     /**
230      * The maximum number of issues to include.
231      * <p>
232      * <b>Note:</b> In versions 2.0-beta-3 and earlier this parameter was
233      * called "nbEntries".
234      * </p>
235      *
236      * @parameter default-value="25"
237      * @required
238      */
239     private int maxEntries;
240 
241     //=======================================//
242     //    announcement-generate execution    //
243     //=======================================//
244 
245     /**
246      * Generate the template
247      *
248      * @throws MojoExecutionException
249      */
250     public void execute()
251         throws MojoExecutionException
252     {
253         if ( !generateJiraAnnouncement )
254         {
255             setXml( new ChangesXML( getXmlPath(), getLog() ) );
256 
257             getLog().info( "Creating announcement file from " + getXmlPath() + "..." );
258 
259             doGenerate( getXml().getReleaseList() );
260         }
261         else
262         {
263             doJiraGenerate();
264         }
265     }
266 
267     /**
268      * Add the parameters to velocity context
269      *
270      * @param releases A <code>List</code> of <code>Release</code>s
271      * @throws MojoExecutionException
272      */
273     public void doGenerate( List releases )
274         throws MojoExecutionException
275     {
276         try
277         {
278             Context context = new VelocityContext();
279 
280             if ( getIntroduction() == null || getIntroduction().equals( "" ) )
281             {
282                 setIntroduction( getUrl() );
283             }
284 
285             context.put( "releases", releases );
286 
287             context.put( "groupId", getGroupId() );
288 
289             context.put( "artifactId", getArtifactId() );
290 
291             context.put( "version", getVersion() );
292 
293             context.put( "packaging", getPackaging() );
294 
295             context.put( "url", getUrl() );
296 
297             context.put( "release", getLatestRelease( releases ) );
298 
299             context.put( "introduction", getIntroduction() );
300 
301             context.put( "developmentTeam", getDevelopmentTeam() );
302 
303             context.put( "finalName", getFinalName() );
304 
305             context.put( "urlDownload", getUrlDownload() );
306 
307             processTemplate( context, getOutputDirectory(), template );
308         }
309         catch ( ResourceNotFoundException rnfe )
310         {
311             throw new MojoExecutionException( "Resource not found.", rnfe );
312         }
313         catch ( VelocityException ve )
314         {
315             throw new MojoExecutionException( ve.toString(), ve );
316         }
317     }
318 
319     /**
320      * Get the latest release by matching the supplied releases
321      * with the version from the pom.
322      *
323      * @param releases list of releases
324      * @return A <code>Release</code> that matches the next release of the current project
325      * @throws MojoExecutionException
326      */
327     public Release getLatestRelease( List releases )
328         throws MojoExecutionException
329     {
330         boolean isFound = false;
331 
332         Release release = null;
333 
334         // Remove "-SNAPSHOT" from the end, if it's there
335         String pomVersion = getVersion();
336         if ( pomVersion != null && pomVersion.endsWith( SNAPSHOT_SUFFIX ) )
337         {
338             pomVersion = pomVersion.substring( 0, pomVersion.length() - SNAPSHOT_SUFFIX.length() );
339         }
340         getLog().debug( "Found " + releases.size() + " releases." );
341 
342         for ( int i = 0; i < releases.size(); i++ )
343         {
344             release = (Release) releases.get( i );
345             if ( getLog().isDebugEnabled() )
346             {
347                 getLog().debug( "The release: " + release.getVersion()
348                     + " has " + release.getAction().size() + " actions." );
349             }
350 
351             if ( release.getVersion() != null && release.getVersion().equals( pomVersion ) )
352             {
353                 isFound = true;
354                 if ( getLog().isDebugEnabled() )
355                 {
356                     getLog().debug( "Found the correct release: " + release.getVersion() );
357                     logRelease( release );
358                 }
359                 return release;
360             }
361         }
362 
363         if ( !isFound )
364         {
365             throw new MojoExecutionException( "Couldn't find the release '" + pomVersion
366                 + "' among the supplied releases." );
367         }
368         return release;
369     }
370 
371     private void logRelease( Release release )
372     {
373         Action action;
374         for ( Iterator iterator = release.getAction().iterator(); iterator.hasNext(); )
375         {
376             action = (Action) iterator.next();
377             getLog().debug( "o " + action.getType() );
378             getLog().debug( "  - " + action.getIssue() );
379             getLog().debug( "  - " + action.getAction() );
380             getLog().debug( "  - " + action.getDueTo() );
381         }
382     }
383 
384     /**
385      * Create the velocity template
386      *
387      * @param context velocity context that has the parameter values
388      * @param outputDirectory directory where the file will be generated
389      * @param template velocity template which will the context be merged
390      * @throws ResourceNotFoundException, VelocityException, IOException
391      */
392     public void processTemplate( Context context, File outputDirectory, String template )
393         throws ResourceNotFoundException, VelocityException, MojoExecutionException
394     {
395         File f;
396 
397         try
398         {
399             f = new File( outputDirectory, template );
400 
401             if ( !f.getParentFile().exists() )
402             {
403                 f.getParentFile().mkdirs();
404             }
405 
406             Writer writer = new FileWriter( f );
407 
408             getVelocity().getEngine().mergeTemplate( templateDirectory + "/" + template, context, writer );
409 
410             writer.flush();
411 
412             writer.close();
413 
414             getLog().info( "Created template " + f );
415         }
416 
417         catch ( ResourceNotFoundException rnfe )
418         {
419             throw new ResourceNotFoundException( "Template not found. ( " + templateDirectory + "/" + template + " )" );
420         }
421         catch ( VelocityException ve )
422         {
423             throw new VelocityException( ve.toString() );
424         }
425 
426         catch ( Exception e )
427         {
428             if ( e.getCause() != null )
429             {
430                 getLog().warn( e.getCause() );
431             }
432             throw new MojoExecutionException( e.toString(), e.getCause() );
433         }
434     }
435 
436     public void doJiraGenerate()
437         throws MojoExecutionException
438     {
439         JiraDownloader jiraDownloader = new JiraDownloader();
440 
441         File jiraXMLFile = jiraXML;
442 
443         jiraDownloader.setLog( getLog() );
444 
445         jiraDownloader.setOutput( jiraXMLFile );
446 
447         jiraDownloader.setStatusIds( statusIds );
448 
449         jiraDownloader.setResolutionIds( resolutionIds );
450 
451         jiraDownloader.setMavenProject( project );
452 
453         jiraDownloader.setSettings( settings );
454 
455         jiraDownloader.setNbEntries( maxEntries );
456 
457         try
458         {
459             jiraDownloader.doExecute();
460 
461             if ( jiraXMLFile.exists() )
462             {
463                 JiraXML jiraParser = new JiraXML( jiraXMLFile );
464 
465                 List issues = jiraParser.getIssueList();
466 
467                 List releases = JiraXML.getReleases( issues );
468 
469                 getLog().info( "Creating announcement file from JIRA releases..." );
470 
471                 doGenerate( releases );
472             }
473         }
474         catch ( Exception e )
475         {
476             throw new MojoExecutionException( "Failed to extract JIRA issues from the downloaded file", e );
477         }
478     }
479 
480     /*
481      * accessors
482      */
483 
484     public File getXmlPath()
485     {
486         return xmlPath;
487     }
488 
489     public void setXmlPath( File xmlPath )
490     {
491         this.xmlPath = xmlPath;
492     }
493 
494     public File getOutputDirectory()
495     {
496         return outputDirectory;
497     }
498 
499     public void setOutputDirectory( File outputDirectory )
500     {
501         this.outputDirectory = outputDirectory;
502     }
503 
504     public String getGroupId()
505     {
506         return groupId;
507     }
508 
509     public void setGroupId( String groupId )
510     {
511         this.groupId = groupId;
512     }
513 
514     public String getArtifactId()
515     {
516         return artifactId;
517     }
518 
519     public void setArtifactId( String artifactId )
520     {
521         this.artifactId = artifactId;
522     }
523 
524     public String getVersion()
525     {
526         return version;
527     }
528 
529     public void setVersion( String version )
530     {
531         this.version = version;
532     }
533 
534     public String getUrl()
535     {
536         return url;
537     }
538 
539     public void setUrl( String url )
540     {
541         this.url = url;
542     }
543 
544     public ChangesXML getXml()
545     {
546         return xml;
547     }
548 
549     public void setXml( ChangesXML xml )
550     {
551         this.xml = xml;
552     }
553 
554     public String getPackaging()
555     {
556         return packaging;
557     }
558 
559     public void setPackaging( String packaging )
560     {
561         this.packaging = packaging;
562     }
563 
564     public String getDevelopmentTeam()
565     {
566         return developmentTeam;
567     }
568 
569     public void setDevelopmentTeam( String developmentTeam )
570     {
571         this.developmentTeam = developmentTeam;
572     }
573 
574     public String getIntroduction()
575     {
576         return introduction;
577     }
578 
579     public void setIntroduction( String introduction )
580     {
581         this.introduction = introduction;
582     }
583 
584     public VelocityComponent getVelocity()
585     {
586         return velocity;
587     }
588 
589     public void setVelocity( VelocityComponent velocity )
590     {
591         this.velocity = velocity;
592     }
593 
594     public String getFinalName()
595     {
596         return finalName;
597     }
598 
599     public void setFinalName( String finalName )
600     {
601         this.finalName = finalName;
602     }
603 
604     public String getUrlDownload()
605     {
606         return urlDownload;
607     }
608 
609     public void setUrlDownload( String urlDownload )
610     {
611         this.urlDownload = urlDownload;
612     }
613 }