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 java.io.File;
23  import java.io.FileOutputStream;
24  import java.io.OutputStreamWriter;
25  import java.io.Writer;
26  import java.util.ArrayList;
27  import java.util.Collections;
28  import java.util.List;
29  import java.util.Map;
30  
31  import org.apache.maven.plugin.MojoExecutionException;
32  import org.apache.maven.plugin.changes.ChangesXML;
33  import org.apache.maven.plugin.changes.IssueAdapter;
34  import org.apache.maven.plugin.changes.ProjectUtils;
35  import org.apache.maven.plugin.changes.ReleaseUtils;
36  import org.apache.maven.plugin.issues.Issue;
37  import org.apache.maven.plugin.issues.IssueManagementSystem;
38  import org.apache.maven.plugin.issues.IssueUtils;
39  import org.apache.maven.plugin.jira.JiraDownloader;
40  import org.apache.maven.plugin.jira.JIRAIssueManagmentSystem;
41  import org.apache.maven.plugin.trac.TracDownloader;
42  import org.apache.maven.plugin.trac.TracIssueManagmentSystem;
43  import org.apache.maven.plugins.changes.model.Release;
44  import org.apache.maven.project.MavenProject;
45  import org.apache.maven.settings.Settings;
46  import org.apache.velocity.Template;
47  import org.apache.velocity.VelocityContext;
48  import org.apache.velocity.app.VelocityEngine;
49  import org.apache.velocity.context.Context;
50  import org.apache.velocity.exception.ResourceNotFoundException;
51  import org.apache.velocity.exception.VelocityException;
52  import org.codehaus.plexus.util.ReaderFactory;
53  import org.codehaus.plexus.util.StringUtils;
54  import org.codehaus.plexus.velocity.VelocityComponent;
55  
56  /**
57   * Goal which generate the template for an announcement.
58   *
59   * @goal announcement-generate
60   * @requiresDependencyResolution test
61   * @author aramirez@exist.com
62   * @version $Id: AnnouncementMojo.java 1328936 2012-04-22 19:02:59Z bimargulies $
63   * @since 2.0-beta-2
64   * @threadSafe
65   */
66  public class AnnouncementMojo
67      extends AbstractAnnouncementMojo
68  {
69      private static final String CHANGES_XML = "changes.xml";
70  
71      private static final String JIRA = "JIRA";
72  
73      private static final String TRAC = "Trac";
74  
75      /**
76       * The name of the file which will contain the generated announcement. If
77       * no value is specified the plugin will use the name of the template.
78       *
79       * @parameter expression="${changes.announcementFile}"
80       * @since 2.4
81       */
82      private String announcementFile;
83  
84      /**
85       * Map of custom parameters for the announcement.
86       * This Map will be passed to the template.
87       *
88       * @parameter
89       * @since 2.1
90       */
91      private Map announceParameters;
92  
93      /**
94       * @parameter expression="${project.artifactId}"
95       * @readonly
96       */
97      private String artifactId;
98  
99      /**
100      * Name of the team that develops the artifact.
101      * This parameter will be passed to the template.
102      *
103      * @parameter default-value="${project.name} team" expression="${changes.developmentTeam}"
104      * @required
105      */
106     private String developmentTeam;
107 
108     /**
109      * The name of the artifact to be used in the announcement.
110      *
111      * @parameter expression="${changes.finalName}" default-value="${project.build.finalName}"
112      * @required
113      */
114     private String finalName;
115 
116     /**
117      * @parameter expression="${project.groupId}"
118      * @readonly
119      */
120     private String groupId;
121 
122     /**
123      * Short description or introduction of the released artifact.
124      * This parameter will be passed to the template.
125      *
126      * @parameter default-value="${project.description}"
127      */
128     private String introduction;
129 
130     /**
131      * A list of issue management systems to fetch releases from. This parameter
132      * replaces the parameters <code>generateJiraAnnouncement</code> and
133      * <code>jiraMerge</code>.
134      * <p>
135      * Valid values are: <code>changes.xml</code> and <code>JIRA</code>.
136      * </p>
137      * <strong>Note:</strong> Only one issue management system that is
138      * configured in &lt;project&gt;/&lt;issueManagement&gt; can be used. This
139      * currently means that you can combine a changes.xml file with one other
140      * issue management system.
141      *
142      * @parameter
143      * @since 2.4
144      */
145     private List<String> issueManagementSystems;
146  
147     /**
148      * Maps issues types to action types for grouping issues in announcements.
149      * If issue types are not defined for a action type then the default issue type
150      * will be applied. 
151      * <p>
152      * Valid action types: <code>add</code>, <code>fix</code> and <code>update</code>.
153      * </p> 
154      * 
155      * @parameter
156      * @since 2.6
157      */
158     private Map<String, String> issueTypes;
159     
160     /**
161      * Directory where the template file will be generated.
162      *
163      * @parameter expression="${project.build.directory}/announcement"
164      * @required
165      */
166     private File outputDirectory;
167 
168     /**
169      * Packaging structure for the artifact.
170      *
171      * @parameter expression="${project.packaging}"
172      * @readonly
173      */
174     private String packaging;
175 
176     /**
177      * The Maven Project.
178      *
179      * @parameter expression="${project}"
180      * @required
181      * @readonly
182      */
183     private MavenProject project;
184 
185     /**
186      * The Velocity template used to format the announcement.
187      *
188      * @parameter default-value="announcement.vm" expression="${changes.template}"
189      * @required
190      */
191     private String template;
192 
193     /**
194      * Directory that contains the template.
195      * <p>
196      * <b>Note:</b> This directory must be a subdirectory of
197      * <code>/src/main/resources/ or current project base directory</code>.
198      * </p>
199      *
200      * @parameter default-value="org/apache/maven/plugin/announcement" expression="${changes.templateDirectory}"
201      * @required
202      */
203     private String templateDirectory;
204 
205     /**
206      * The template encoding.
207      *
208      * @parameter expression="${changes.templateEncoding}" default-value="${project.build.sourceEncoding}"
209      * @since 2.1
210      */
211     private String templateEncoding;
212 
213     /**
214      * Distribution URL of the artifact.
215      * This parameter will be passed to the template.
216      *
217      * @parameter expression="${project.url}"
218      */
219     private String url;
220 
221     /**
222      * URL where the artifact can be downloaded. If not specified,
223      * no URL is used.
224      * This parameter will be passed to the template.
225      *
226      * @parameter
227      */
228     private String urlDownload;
229 
230     /**
231      * Velocity Component.
232      *
233      * @component role="org.codehaus.plexus.velocity.VelocityComponent" roleHint="maven-changes-plugin"
234      * @readonly
235      */
236     private VelocityComponent velocity;
237     /**
238      * Version of the artifact.
239      *
240      * @parameter expression="${changes.version}" default-value="${project.version}"
241      * @required
242      */
243     private String version;
244     /**
245      * The path of the changes.xml file.
246      *
247      * @parameter expression="${basedir}/src/changes/changes.xml"
248      * @required
249      */
250     private File xmlPath;
251 
252     //=======================================//
253     //  JIRA-Announcement Needed Parameters  //
254     //=======================================//
255 
256     /**
257      * Defines the filter parameters to restrict which issues are retrieved
258      * from JIRA. The filter parameter uses the same format of url
259      * parameters that is used in a JIRA search.
260      *
261      * @parameter default-value=""
262      * @since 2.4
263      */
264     private String filter;
265 
266     /**
267      * Flag to determine if the plugin will generate a JIRA announcement.
268      *
269      * @parameter expression="${generateJiraAnnouncement}" default-value="false"
270      * @required
271      * @deprecated Since version 2.4 this parameter has been deprecated. Please use the issueManagementSystems parameter instead.
272      */
273     private boolean generateJiraAnnouncement;
274 
275     /**
276      * If releases from JIRA should be merged with the releases from a
277      * changes.xml file.
278      *
279      * @parameter expression="${changes.jiraMerge}" default-value="false"
280      * @since 2.1
281      * @deprecated Since version 2.4 this parameter has been deprecated. Please use the issueManagementSystems parameter instead.
282      */
283     private boolean jiraMerge;
284 
285     /**
286      * Defines the JIRA password for authentication into a private JIRA installation.
287      *
288      * @parameter default-value="" expression="${changes.jiraPassword}"
289      * @since 2.1
290      */
291     private String jiraPassword;
292 
293     /**
294      * Defines the JIRA username for authentication into a private JIRA installation.
295      *
296      * @parameter default-value="" expression="${changes.jiraUser}"
297      * @since 2.1
298      */
299     private String jiraUser;
300 
301     /**
302      * Path to the JIRA XML file, which will be parsed.
303      *
304      * @parameter expression="${project.build.directory}/jira-announcement.xml"
305      * @required
306      * @readonly
307      */
308     private File jiraXML;
309 
310     /**
311      * The maximum number of issues to fetch from JIRA.
312      * <p>
313      * <b>Note:</b> In versions 2.0-beta-3 and earlier this parameter was
314      * called "nbEntries".
315      * </p>
316      *
317      * @parameter default-value="25"  expression="${changes.maxEntries}"
318      * @required
319      */
320     private int maxEntries;
321 
322     /**
323      * Include issues from JIRA with these resolution ids. Multiple resolution
324      * ids can be specified as a comma separated list of ids.
325      * <p>
326      * <b>Note:</b> In versions 2.0-beta-3 and earlier this parameter was
327      * called "resolutionId".
328      * </p>
329      *
330      * @parameter default-value="Fixed" expression="${changes.resolutionIds}"
331      */
332     private String resolutionIds;
333 
334     /**
335      * Settings XML configuration.
336      *
337      * @parameter expression="${settings}"
338      * @required
339      * @readonly
340      */
341     private Settings settings;
342 
343     /**
344      * Include issues from JIRA with these status ids. Multiple status ids can
345      * be specified as a comma separated list of ids.
346      * <p>
347      * <b>Note:</b> In versions 2.0-beta-3 and earlier this parameter was
348      * called "statusId".
349      * </p>
350      *
351      * @parameter default-value="Closed" expression="${changes.statusIds}"
352      */
353     private String statusIds;
354 
355     /**
356      * Defines the http user for basic authentication into the JIRA webserver.
357      *
358      * @parameter default-value="" expression="${changes.webUser}"
359      * @since 2.4
360      */
361     private String webUser;
362 
363     /**
364      * Defines the http password for basic authentication into the JIRA webserver.
365      *
366      * @parameter default-value="" expression="${changes.webPassword}"
367      * @since 2.4
368      */
369     private String webPassword;
370 
371     /**
372      * The prefix used when naming versions in JIRA.
373      * <p>
374      * If you have a project in JIRA with several components that have different
375      * release cycles, it is an often used pattern to prefix the version with
376      * the name of the component, e.g. maven-filtering-1.0 etc. To fetch issues
377      * from JIRA for a release of the "maven-filtering" component you would need
378      * to set this parameter to "maven-filtering-".
379      * </p>
380      *
381      * @parameter default-value="" expression="${changes.versionPrefix}"
382      * @since 2.5
383      */
384     private String versionPrefix;
385 
386     //=======================================//
387     //  Trac Parameters                      //
388     //=======================================//
389 
390     /**
391      * Defines the Trac password for authentication into a private Trac
392      * installation.
393      *
394      * @parameter default-value="" expression="${changes.tracPassword}"
395      * @since 2.4
396      */
397     private String tracPassword;
398 
399     /**
400      * Defines the Trac query for searching for tickets.
401      *
402      * @parameter default-value="order=id"
403      * @since 2.4
404      */
405     private String tracQuery;
406 
407     /**
408      * Defines the Trac username for authentication into a private Trac
409      * installation.
410      *
411      * @parameter default-value="" expression="${changes.tracUser}"
412      * @since 2.4
413      */
414     private String tracUser;
415 
416     private ReleaseUtils releaseUtils = new ReleaseUtils( getLog() );
417 
418     private ChangesXML xml;
419 
420     //=======================================//
421     //    announcement-generate execution    //
422     //=======================================//
423 
424     /**
425      * Generate the template
426      *
427      * @throws MojoExecutionException
428      */
429     public void execute()
430         throws MojoExecutionException
431     {
432         // Run only at the execution root
433         if ( runOnlyAtExecutionRoot && !isThisTheExecutionRoot() )
434         {
435             getLog().info( "Skipping the announcement generation in this project because it's not the Execution Root" );
436         }
437         else
438         {
439             if ( issueManagementSystems == null )
440             {
441                 issueManagementSystems = new ArrayList<String>();
442             }
443 
444             // Handle deprecated parameters, in a backward compatible way
445             if ( issueManagementSystems.isEmpty() )
446             {
447                 if ( this.jiraMerge )
448                 {
449                     issueManagementSystems.add( CHANGES_XML );
450                     issueManagementSystems.add( JIRA );
451                 }
452                 else if ( generateJiraAnnouncement )
453                 {
454                     issueManagementSystems.add( JIRA );
455                 }
456                 else
457                 {
458                     issueManagementSystems.add( CHANGES_XML );
459                 }
460             }
461             
462             // Fetch releases from the configured issue management systems
463             List<Release> releases = null;
464             if ( issueManagementSystems.contains( CHANGES_XML ) )
465             {
466                 if ( getXmlPath().exists() )
467                 {
468                     ChangesXML changesXML = new ChangesXML( getXmlPath(), getLog() );
469                     List<Release> changesReleases = releaseUtils.convertReleaseList( changesXML.getReleaseList() );
470                     releases = releaseUtils.mergeReleases( releases, changesReleases );
471                     getLog().info( "Including issues from file " + getXmlPath() + " in announcement..." );
472                 }
473                 else
474                 {
475                     getLog().warn( "changes.xml file " + getXmlPath().getAbsolutePath() + " does not exist." );
476                 }
477             }
478 
479             if ( issueManagementSystems.contains( JIRA ) )
480             {
481                 if ( ProjectUtils.validateIfIssueManagementComplete( project, JIRA, "JIRA announcement", getLog() ) )
482                 {
483                     List<Release> jiraReleases = getJiraReleases();
484                     releases = releaseUtils.mergeReleases( releases, jiraReleases );
485                     getLog().info( "Including issues from JIRA in announcement..." );
486                 }
487                 else
488                 {
489                     throw new MojoExecutionException(
490                         "Something is wrong with the Issue Management section. See previous error messages." );
491                 }
492             }
493 
494             if ( issueManagementSystems.contains( TRAC ) )
495             {
496                 if ( ProjectUtils.validateIfIssueManagementComplete( project, TRAC, "Trac announcement", getLog() ) )
497                 {
498                     List<Release> tracReleases = getTracReleases();
499                     releases = releaseUtils.mergeReleases( releases, tracReleases );
500                     getLog().info( "Including issues from Trac in announcement..." );
501                 }
502                 else
503                 {
504                     throw new MojoExecutionException(
505                         "Something is wrong with the Issue Management section. See previous error messages." );
506                 }
507             }
508 
509             // @todo Add more issue management systems here.
510 
511             // Follow these steps:
512             // 1. Add a constant for the name of the issue management system
513             // 2. Add the @parameters needed to configure the issue management system
514             // 3. Add a protected List get<IMSname>Releases() method that retrieves a list of releases
515             // 4. Merge those releases into the "releases" variable
516             // For help with these steps, you can have a look at how this has been done for JIRA or Trac
517 
518             // Generate the report
519             if ( releases == null || releases.isEmpty() )
520             {
521                 throw new MojoExecutionException(
522                     "No releases found in any of the configured issue management systems." );
523             }
524             else
525             {
526                 doGenerate( releases );
527             }
528         }
529     }
530 
531     /**
532      * Add the parameters to velocity context
533      *
534      * @param releases A <code>List</code> of <code>Release</code>s
535      * @throws MojoExecutionException
536      */
537     public void doGenerate( List<Release> releases )
538         throws MojoExecutionException
539     {
540         String version = ( versionPrefix == null ? "" : versionPrefix ) + getVersion();
541 
542         doGenerate( releases, releaseUtils.getLatestRelease( releases, version ) );
543     }
544 
545     protected void doGenerate( List<Release> releases, Release release )
546         throws MojoExecutionException
547     {
548         try
549         {
550             Context context = new VelocityContext();
551 
552             if ( getIntroduction() == null || getIntroduction().equals( "" ) )
553             {
554                 setIntroduction( getUrl() );
555             }
556 
557             context.put( "releases", releases );
558 
559             context.put( "groupId", getGroupId() );
560 
561             context.put( "artifactId", getArtifactId() );
562 
563             context.put( "version", getVersion() );
564 
565             context.put( "packaging", getPackaging() );
566 
567             context.put( "url", getUrl() );
568 
569             context.put( "release", release );
570 
571             context.put( "introduction", getIntroduction() );
572 
573             context.put( "developmentTeam", getDevelopmentTeam() );
574 
575             context.put( "finalName", getFinalName() );
576 
577             context.put( "urlDownload", getUrlDownload() );
578 
579             context.put( "project", project );
580 
581             if ( announceParameters == null )
582             {
583                 // empty Map to prevent NPE in velocity execution
584                 context.put( "announceParameters", Collections.EMPTY_MAP );
585             }
586             else
587             {
588                 context.put( "announceParameters", announceParameters );
589             }
590 
591 
592             processTemplate( context, getOutputDirectory(), template, announcementFile );
593         }
594         catch ( ResourceNotFoundException rnfe )
595         {
596             throw new MojoExecutionException( "Resource not found.", rnfe );
597         }
598         catch ( VelocityException ve )
599         {
600             throw new MojoExecutionException( ve.toString(), ve );
601         }
602     }
603 
604     /**
605      * Create the velocity template
606      *
607      * @param context velocity context that has the parameter values
608      * @param outputDirectory directory where the file will be generated
609      * @param template velocity template which will the context be merged
610      * @param announcementFile The file name of the generated announcement
611      * @throws ResourceNotFoundException, VelocityException, IOException
612      */
613     public void processTemplate( Context context, File outputDirectory, String template, String announcementFile )
614         throws ResourceNotFoundException, VelocityException, MojoExecutionException
615     {
616         File f;
617 
618         // Use the name of the template as a default value
619         if ( StringUtils.isEmpty( announcementFile ) )
620         {
621             announcementFile = template;
622         }
623 
624         try
625         {
626             f = new File( outputDirectory, announcementFile );
627 
628             if ( !f.getParentFile().exists() )
629             {
630                 f.getParentFile().mkdirs();
631             }
632 
633             VelocityEngine engine = velocity.getEngine();
634 
635             engine.setApplicationAttribute( "baseDirectory", basedir );
636 
637             if ( StringUtils.isEmpty( templateEncoding ) )
638             {
639                 templateEncoding =  ReaderFactory.FILE_ENCODING;
640                 getLog().warn(
641                                "File encoding has not been set, using platform encoding " + templateEncoding
642                                    + ", i.e. build is platform dependent!" );
643             }
644 
645             Writer writer = new OutputStreamWriter( new FileOutputStream( f ), templateEncoding );
646 
647             Template velocityTemplate = engine.getTemplate( templateDirectory + "/" + template, templateEncoding );
648 
649             velocityTemplate.merge( context, writer );
650 
651             writer.flush();
652 
653             writer.close();
654 
655             getLog().info( "Created template " + f );
656         }
657 
658         catch ( ResourceNotFoundException rnfe )
659         {
660             throw new ResourceNotFoundException( "Template not found. ( " + templateDirectory + "/" + template + " )" );
661         }
662         catch ( VelocityException ve )
663         {
664             throw new VelocityException( ve.toString() );
665         }
666 
667         catch ( Exception e )
668         {
669             if ( e.getCause() != null )
670             {
671                 getLog().warn( e.getCause() );
672             }
673             throw new MojoExecutionException( e.toString(), e.getCause() );
674         }
675     }
676 
677     protected List<Release> getJiraReleases()
678         throws MojoExecutionException
679     {
680         JiraDownloader jiraDownloader = new JiraDownloader();
681 
682         File jiraXMLFile = jiraXML;
683 
684         jiraDownloader.setLog( getLog() );
685 
686         jiraDownloader.setOutput( jiraXMLFile );
687 
688         jiraDownloader.setStatusIds( statusIds );
689 
690         jiraDownloader.setResolutionIds( resolutionIds );
691 
692         jiraDownloader.setMavenProject( project );
693 
694         jiraDownloader.setSettings( settings );
695 
696         jiraDownloader.setNbEntries( maxEntries );
697 
698         jiraDownloader.setFilter( filter );
699 
700         jiraDownloader.setJiraUser( jiraUser );
701 
702         jiraDownloader.setJiraPassword( jiraPassword );
703 
704         jiraDownloader.setWebUser( webUser );
705 
706         jiraDownloader.setWebPassword( webPassword );
707 
708         try
709         {
710             jiraDownloader.doExecute();
711 
712             List<Issue> issueList = jiraDownloader.getIssueList();
713 
714             if ( StringUtils.isNotEmpty( versionPrefix ) )
715             {
716                 int originalNumberOfIssues = issueList.size();
717                 issueList = IssueUtils.filterIssuesWithVersionPrefix( issueList, versionPrefix );
718                 getLog().debug( "Filtered out " + issueList.size() + " issues of " + originalNumberOfIssues
719                     + " that matched the versionPrefix '" + versionPrefix + "'." );
720             }
721 
722             return getReleases( issueList, new JIRAIssueManagmentSystem() );
723         }
724         catch ( Exception e )
725         {
726             throw new MojoExecutionException( "Failed to extract issues from JIRA.", e );
727         }
728     }
729 
730     private List<Release> getReleases( List<Issue> issues, IssueManagementSystem ims )
731         throws MojoExecutionException
732     {
733         if ( issueTypes != null ) 
734         {
735             ims.applyConfiguration( issueTypes );
736         }
737         if ( issues.isEmpty() )
738         {
739             return Collections.emptyList();
740         }
741         else
742         {
743             IssueAdapter adapter = new IssueAdapter( ims );
744             return adapter.getReleases( issues );
745         }
746     }
747 
748     protected List<Release> getTracReleases()
749         throws MojoExecutionException
750     {
751         TracDownloader issueDownloader = new TracDownloader();
752 
753         issueDownloader.setProject( project );
754 
755         issueDownloader.setQuery( tracQuery );
756 
757         issueDownloader.setTracPassword( tracPassword );
758 
759         issueDownloader.setTracUser( tracUser );
760 
761         try
762         {
763             return getReleases( issueDownloader.getIssueList(), new TracIssueManagmentSystem() );
764         }
765         catch ( Exception e )
766         {
767             throw new MojoExecutionException( "Failed to extract issues from Trac.", e );
768         }
769     }
770 
771     /*
772      * accessors
773      */
774 
775     public String getArtifactId()
776     {
777         return artifactId;
778     }
779 
780     public void setArtifactId( String artifactId )
781     {
782         this.artifactId = artifactId;
783     }
784 
785     public String getDevelopmentTeam()
786     {
787         return developmentTeam;
788     }
789 
790     public void setDevelopmentTeam( String developmentTeam )
791     {
792         this.developmentTeam = developmentTeam;
793     }
794 
795     public String getFinalName()
796     {
797         return finalName;
798     }
799 
800     public void setFinalName( String finalName )
801     {
802         this.finalName = finalName;
803     }
804 
805     public String getGroupId()
806     {
807         return groupId;
808     }
809 
810     public void setGroupId( String groupId )
811     {
812         this.groupId = groupId;
813     }
814 
815     public String getIntroduction()
816     {
817         return introduction;
818     }
819 
820     public void setIntroduction( String introduction )
821     {
822         this.introduction = introduction;
823     }
824     
825     public void setIssueTypes( Map<String, String> issueTypes )
826     {
827 		this.issueTypes = issueTypes;
828 	}
829 
830     public Map<String, String> getIssueTypes()
831     {
832 		return issueTypes;
833 	}
834 
835     public File getOutputDirectory()
836     {
837         return outputDirectory;
838     }
839 
840     public void setOutputDirectory( File outputDirectory )
841     {
842         this.outputDirectory = outputDirectory;
843     }
844 
845     public String getPackaging()
846     {
847         return packaging;
848     }
849 
850     public void setPackaging( String packaging )
851     {
852         this.packaging = packaging;
853     }
854 
855     public String getUrl()
856     {
857         return url;
858     }
859 
860     public void setUrl( String url )
861     {
862         this.url = url;
863     }
864 
865     public String getUrlDownload()
866     {
867         return urlDownload;
868     }
869 
870     public void setUrlDownload( String urlDownload )
871     {
872         this.urlDownload = urlDownload;
873     }
874 
875     public VelocityComponent getVelocity()
876     {
877         return velocity;
878     }
879 
880     public void setVelocity( VelocityComponent velocity )
881     {
882         this.velocity = velocity;
883     }
884 
885     public String getVersion()
886     {
887         return version;
888     }
889 
890     public void setVersion( String version )
891     {
892         this.version = version;
893     }
894 
895     public ChangesXML getXml()
896     {
897         return xml;
898     }
899 
900     public void setXml( ChangesXML xml )
901     {
902         this.xml = xml;
903     }
904 
905     public File getXmlPath()
906     {
907         return xmlPath;
908     }
909 
910     public void setXmlPath( File xmlPath )
911     {
912         this.xmlPath = xmlPath;
913     }
914 }