View Javadoc

1   /*
2    * Copyright 2006-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.plugin.buildmetadata;
17  
18  import java.io.File;
19  import java.text.DateFormat;
20  import java.text.SimpleDateFormat;
21  import java.util.Date;
22  import java.util.Locale;
23  import java.util.Properties;
24  
25  import org.apache.maven.plugin.MojoExecutionException;
26  import org.apache.maven.plugin.MojoFailureException;
27  import org.apache.maven.scm.manager.ScmManager;
28  import org.apache.maven.settings.Settings;
29  import org.codehaus.plexus.util.StringUtils;
30  
31  import de.smartics.maven.plugin.buildmetadata.common.Constant;
32  import de.smartics.maven.plugin.buildmetadata.common.ScmControl;
33  import de.smartics.maven.plugin.buildmetadata.common.ScmCredentials;
34  import de.smartics.maven.plugin.buildmetadata.common.ScmInfo;
35  import de.smartics.maven.plugin.buildmetadata.data.HostMetaDataProvider;
36  import de.smartics.maven.plugin.buildmetadata.data.MavenMetaDataProvider;
37  import de.smartics.maven.plugin.buildmetadata.data.MavenMetaDataSelection;
38  import de.smartics.maven.plugin.buildmetadata.data.ScmMetaDataProvider;
39  import de.smartics.maven.plugin.buildmetadata.io.BuildPropertiesFileHelper;
40  import de.smartics.maven.plugin.buildmetadata.io.BuildXmlFileHelper;
41  import de.smartics.maven.plugin.buildmetadata.scm.ScmNoRevisionException;
42  import de.smartics.maven.util.LoggingUtils;
43  
44  /**
45   * Provides the build information defined in {@link Constant}. This information
46   * is also written to a properties file.
47   *
48   * @goal provide-buildmetadata
49   * @phase initialize
50   * @requiresProject
51   * @threadSafe
52   * @since 1.0
53   * @description Provides a build meta data to the build process.
54   */
55  public final class BuildMetaDataMojo extends AbstractBuildMojo // NOPMD
56  { // NOPMD
57    // ********************************* Fields *********************************
58  
59    // --- constants ------------------------------------------------------------
60  
61    // --- members --------------------------------------------------------------
62  
63    // ... Mojo infrastructure ..................................................
64  
65    /**
66     * The user's settings.
67     *
68     * @parameter expression="${settings}"
69     * @required
70     * @readonly
71     * @since 1.0
72     */
73    private Settings settings;
74  
75    /**
76     * If set to <code>true</code>, build properties will be generate even if they
77     * already exist in the target folder.
78     *
79     * @parameter default-value="false"
80     * @since 1.0
81     */
82    private boolean forceNewProperties;
83  
84    /**
85     * In offline mode the plugin will not generate revision information.
86     *
87     * @parameter default-value="${settings.offline}"
88     * @required
89     * @readonly
90     * @since 1.0
91     */
92    private boolean offline;
93  
94    /**
95     * Add SCM information if set to <code>true</code>, skip it, if set to
96     * <code>false</code>. If you are not interested in SCM information, set this
97     * to <code>false</code>.
98     * <p>
99     * For security reasons you may want to remove the properties file from the
100    * META-INF folder. Please refer to <code>propertiesOutputFile</code>
101    * property.
102    * </p>
103    *
104    * @parameter expression="${buildMetaData.addScmInfo}" default-value="true"
105    * @since 1.0
106    */
107   private boolean addScmInfo;
108 
109   /**
110    * Fail if revision is requested to be retrieved, access to SCM is provided,
111    * system is online, nothing should prevent the build from fetching the
112    * information.
113    * <p>
114    * If set to <code>true</code> the build will fail, if revision cannot be
115    * fetched under the conditions mentioned above. If set to <code>false</code>
116    * the build will continue silently so that the meta data do not contain the
117    * revision.
118    * </p>
119    *
120    * @parameter expression="${buildMetaData.failOnMissingRevision}"
121    *            default-value="false"
122    * @since 1.0
123    */
124   private boolean failOnMissingRevision;
125 
126   /**
127    * Add host information if set to <code>true</code>, skip it, if set to
128    * <code>false</code>. If you are not interested in host information (e.g. for
129    * security reasons), set this to <code>false</code>.
130    * <p>
131    * For security reasons you may want to remove the properties file from the
132    * META-INF folder. Please refer to <code>propertiesOutputFile</code>
133    * property.
134    * </p>
135    *
136    * @parameter expression="${buildMetaData.addHostInfo}" default-value="true"
137    * @since 1.0
138    */
139   private boolean addHostInfo;
140 
141   /**
142    * Add environment variables if set to <code>true</code>, skip it, if set to
143    * <code>false</code>. If you are not interested in the environment variables
144    * of the host (e.g. for security reasons), set this to <code>false</code>.
145    * <p>
146    * For security reasons you may want to remove the properties file from the
147    * META-INF folder. Please refer to <code>propertiesOutputFile</code>
148    * property.
149    * </p>
150    *
151    * @parameter expression="${buildMetaData.addEnvInfo}" default-value="false"
152    * @since 1.0
153    */
154   private boolean addEnvInfo;
155 
156   /**
157    * Add information about the Java runtime running the build if set to
158    * <code>true</code>, skip it, if set to <code>false</code>.
159    *
160    * @parameter expression="${buildMetaData.addJavaRuntimeInfo}"
161    *            default-value="true"
162    * @since 1.0
163    */
164   private boolean addJavaRuntimeInfo;
165 
166   /**
167    * Add information about the operating system the build is run in if set to
168    * <code>true</code>, skip it, if set to <code>false</code>.
169    *
170    * @parameter expression="${buildMetaData.addOsInfo}" default-value="true"
171    * @since 1.0
172    */
173   private boolean addOsInfo;
174 
175   /**
176    * Add Maven execution information (all properties starting with
177    * <code>build.maven.execution</code>, like command line, goals, profiles,
178    * etc.) if set to <code>true</code>, skip it, if set to <code>false</code>.
179    * If you are not interested in execution information, set this to
180    * <code>false</code>.
181    * <p>
182    * For security reasons you may want to remove the properties file from the
183    * META-INF folder. Please refer to <code>propertiesOutputFile</code>
184    * property.
185    * </p>
186    *
187    * @parameter expression="${buildMetaData.addMavenExecutionInfo}"
188    *            default-value="true"
189    * @since 1.0
190    */
191   private boolean addMavenExecutionInfo;
192 
193   /**
194    * Add project information (homepage URL, categories, tags, etc.) if set to
195    * <code>true</code>, skip it, if set to <code>false</code>. If you are not
196    * interested in execution information, set this to <code>false</code>.
197    *
198    * @parameter expression="${buildMetaData.addProjectInfo}"
199    *            default-value="false"
200    * @since 1.1
201    */
202   private boolean addProjectInfo;
203 
204   /**
205    * While the command line may be useful to refer to for a couple of reasons,
206    * displaying it with the build properties is a security issue. Some plugins
207    * allow to read passwords as properties from the command line and this
208    * sensible data will be shown.
209    * <p>
210    * Therefore the command line is hidden by default (<code>true</code>). If you
211    * want to include this information, use a value of <code>false</code>.
212    * </p>
213    *
214    * @parameter expression="${buildMetaData.hideCommandLineInfo}"
215    *            default-value="true"
216    * @since 1.0
217    */
218   private boolean hideCommandLineInfo;
219 
220   /**
221    * While the <code>MAVEN_OPTS</code> may be useful to refer to for a couple of
222    * reasons, displaying them with the build properties is a security issue.
223    * Some plugins allow to read passwords as properties from the command line
224    * and this sensible data will be shown.
225    * <p>
226    * Therefore the <code>MAVEN_OPTS</code> are hidden by default (
227    * <code>true</code>). If you want to include this information, use a value of
228    * <code>false</code>.
229    * </p>
230    * <p>
231    * This exclusion does not prevent the property from being written as part of
232    * <code>addEnvInfo</code>!
233    * </p>
234    *
235    * @parameter expression="${buildMetaData.hideMavenOptsInfo}"
236    *            default-value="true"
237    * @since 1.0
238    */
239   private boolean hideMavenOptsInfo;
240 
241   /**
242    * While the <code>JAVA_OPTS</code> may be useful to refer to for a couple of
243    * reasons, displaying them with the build properties is a security issue.
244    * Some plugins allow to read passwords as properties from the command line
245    * and this sensible data will be shown.
246    * <p>
247    * Therefore the <code>JAVA_OPTS</code> are hidden by default (
248    * <code>true</code>). If you want to include this information, use a value of
249    * <code>false</code>.
250    * </p>
251    * <p>
252    * This exclusion does not prevent the property from being written as part of
253    * <code>addEnvInfo</code>!
254    * </p>
255    *
256    * @parameter expression="${buildMetaData.hideJavaOptsInfo}"
257    *            default-value="true"
258    * @since 1.0
259    */
260   private boolean hideJavaOptsInfo;
261 
262   /**
263    * A simple flag to skip the generation of the build information. If set on
264    * the command line use <code>-DbuildMetaData.skip</code>.
265    *
266    * @parameter expression="${buildMetaData.skip}" default-value="false"
267    * @since 1.0
268    */
269   private boolean skip;
270 
271   /**
272    * If it should be checked if the local files are up-to-date with the remote
273    * files in the SCM repository. If the value is <code>true</code> the result
274    * of the check, including the list of changed files, is added to the build
275    * meta data.
276    *
277    * @parameter expression="${buildMetaData.validateCheckout}"
278    *            default-value="true"
279    * @since 1.0
280    */
281   private boolean validateCheckout;
282 
283   /**
284    * Specifies the log level used for this plugin.
285    * <p>
286    * Allowed values are <code>SEVERE</code>, <code>WARNING</code>,
287    * <code>INFO</code> and <code>FINEST</code>.
288    * </p>
289    *
290    * @parameter expression="${buildMetaData.logLevel}"
291    * @since 1.0
292    */
293   private String logLevel;
294 
295   /**
296    * The manager instance to access the SCM system. Provides access to the
297    * repository and the provider information.
298    *
299    * @component
300    * @since 1.0
301    */
302   private ScmManager scmManager;
303 
304   /**
305    * Allows the user to choose which scm connection to use when connecting to
306    * the scm. Can either be "connection" or "developerConnection".
307    *
308    * @parameter default-value="connection"
309    * @required
310    * @since 1.0
311    */
312   private String connectionType;
313 
314   // ... core information .....................................................
315 
316   /**
317    * The date pattern to use to format the build and revision dates. Please
318    * refer to the <a href =
319    * "http://java.sun.com/j2se/1.5.0/docs/api/java/text/SimpleDateFormat.html"
320    * >SimpleDateFormat</a> class for valid patterns.
321    *
322    * @parameter expression="${buildMetaData.buildDate.pattern}"
323    *            default-value="dd.MM.yyyy"
324    * @since 1.0
325    */
326   protected String buildDatePattern = Constant.DEFAULT_DATE_PATTERN; // NOPMD
327 
328   /**
329    * The property to query for the build user.
330    *
331    * @parameter default-value="username"
332    * @since 1.0
333    */
334   private String buildUserPropertyName;
335 
336   // ... build information related ............................................
337 
338   /**
339    * Flag to add the build date to the full version separated by a '-'. If
340    * <code>true</code> the build date is added, if <code>false</code> it is not.
341    *
342    * @parameter expression="${buildMetaData.addBuildDateToFullVersion}"
343    *            default-value="true"
344    * @since 1.0
345    */
346   private boolean addBuildDateToFullVersion;
347 
348   // ... svn related ..........................................................
349 
350   /**
351    * Used to specify the date format of the log entries that are retrieved from
352    * your SCM system.
353    *
354    * @parameter expression="${changelog.dateFormat}"
355    *            default-value="yyyy-MM-dd HH:mm:ss"
356    * @required
357    * @since 1.0
358    */
359   private String scmDateFormat;
360 
361   /**
362    * Input dir. Directory where the files under SCM control are located.
363    *
364    * @parameter expression="${basedir}"
365    * @required
366    * @since 1.0
367    */
368   private File basedir;
369 
370   /**
371    * The user name (used by svn and starteam protocol).
372    *
373    * @parameter expression="${username}"
374    * @since 1.0
375    */
376   private String userName;
377 
378   /**
379    * The user password (used by svn and starteam protocol).
380    *
381    * @parameter expression="${password}"
382    * @since 1.0
383    */
384   private String password;
385 
386   /**
387    * The private key (used by java svn).
388    *
389    * @parameter expression="${privateKey}"
390    * @since 1.0
391    */
392   private String privateKey;
393 
394   /**
395    * The passphrase (used by java svn).
396    *
397    * @parameter expression="${passphrase}"
398    * @since 1.0
399    */
400   private String passphrase;
401 
402   /**
403    * The url of tags base directory (used by svn protocol).
404    *
405    * @parameter expression="${tagBase}"
406    * @since 1.0
407    */
408   private String tagBase;
409 
410   /**
411    * Flag to add the revision number to the full version separated by an 'r'. If
412    * <code>true</code> the revision number is added, if <code>false</code> it is
413    * not.
414    *
415    * @parameter expression="${buildMetaData.addReleaseNumberToFullVersion}"
416    *            default-value="true"
417    * @since 1.0
418    */
419   private boolean addReleaseNumberToFullVersion;
420 
421   /**
422    * Flag to add the tag <code>-locally-modified</code> to the full version
423    * string to make visible that this artifact has been created with locally
424    * modified sources. This is often the case while the artifact is built while
425    * still working on an issue before it is committed to the SCM repository.
426    *
427    * @parameter expression="${buildMetaData.addLocallyModifiedTagToFullVersion}"
428    *            default-value="true"
429    * @since 1.0
430    */
431   private boolean addLocallyModifiedTagToFullVersion;
432 
433   /**
434    * The range of the query in days to fetch change log entries from the SCM. If
435    * no change logs have been found, the range is incremented up to {@value
436    * de.smartics.maven.plugin.buildmetadata.scm.maven.ScmAccessInfo;#
437    * DEFAULT_RETRY_COUNT} (5) times. If no change log has been found after these
438    * {@value de.smartics.maven.plugin.buildmetadata.scm.maven.ScmAccessInfo;#
439    * DEFAULT_RETRY_COUNT} (5) additional queries, the revision number will not
440    * be set with a valid value.
441    *
442    * @parameter expression="${buildMetaData.queryRangeInDays}"
443    *            default-value="30"
444    * @since 1.0
445    */
446   private int queryRangeInDays;
447 
448   /**
449    * Flag to fail if local modifications have been found. The value is
450    * <code>true</code> if the build should fail if there are modifications (any
451    * files not in-sync with the remote repository), <code>false</code> if the
452    * fact is only to be noted in the build properties.
453    *
454    * @parameter default-value="false"
455    * @since 1.0
456    */
457   private boolean failOnLocalModifications;
458 
459   /**
460    * The flag to ignore files and directories starting with a dot for checking
461    * modified files. This implicates that any files or directories, starting
462    * with a dot, are ignored when the check on changed files is run. If the
463    * value is <code>true</code>, dot files are ignored, if it is set to
464    * <code>false</code>, dot files are respected.
465    *
466    * @parameter default-value="true"
467    * @since 1.0
468    */
469   private boolean ignoreDotFilesInBaseDir;
470 
471   // ****************************** Initializer *******************************
472 
473   // ****************************** Constructors ******************************
474 
475   // ****************************** Inner Classes *****************************
476 
477   // ********************************* Methods ********************************
478 
479   // --- init -----------------------------------------------------------------
480 
481   // --- get&set --------------------------------------------------------------
482 
483   // --- business -------------------------------------------------------------
484 
485   /**
486    * {@inheritDoc}
487    */
488   public void execute() throws MojoExecutionException, MojoFailureException
489   {
490     if (!skip)
491     {
492       super.execute();
493 
494       final BuildPropertiesFileHelper helper =
495           new BuildPropertiesFileHelper(getLog(), propertiesOutputFile);
496       final Properties projectProperties = helper.getProjectProperties(project);
497       if (!isBuildPropertiesAlreadySet(projectProperties))
498       {
499         LoggingUtils.configureLogger(getLog(), logLevel);
500         final Properties buildMetaDataProperties = new Properties();
501         if (isBuildPropertiesToBeRebuild())
502         {
503           final Date buildDate = session.getStartTime();
504 
505           provideBuildUser(projectProperties, buildMetaDataProperties);
506           provideMavenMetaData(buildMetaDataProperties);
507           provideHostMetaData(buildMetaDataProperties);
508           final ScmInfo scmInfo = provideScmMetaData(buildMetaDataProperties);
509           provideBuildDateMetaData(buildMetaDataProperties, buildDate);
510 
511           // The custom providers are required to be run at the end.
512           // This allows these providers to access the information generated
513           // by the built-in providers.
514           provideBuildMetaData(buildMetaDataProperties, scmInfo, providers,
515               false);
516 
517           writeBuildMetaData(helper, buildMetaDataProperties);
518         }
519         else
520         {
521           getLog().info("Reusing previously built metadata file.");
522           helper.readBuildPropertiesFile(buildMetaDataProperties);
523         }
524 
525         updateMavenEnvironment(buildMetaDataProperties, helper);
526       }
527     }
528     else
529     {
530       getLog().info("Skipping buildmetadata collection since skip=true.");
531     }
532   }
533 
534   private void writeBuildMetaData(final BuildPropertiesFileHelper helper,
535       final Properties buildMetaDataProperties) throws MojoExecutionException
536   {
537     helper.writePropertiesFile(buildMetaDataProperties);
538     if (createXmlReport)
539     {
540       final BuildXmlFileHelper xmlHelper =
541           new BuildXmlFileHelper(getLog(), this.xmlOutputFile, this.properties);
542       xmlHelper.writeXmlFile(buildMetaDataProperties);
543     }
544   }
545 
546   private void provideMavenMetaData(final Properties buildMetaDataProperties)
547   {
548     final MavenMetaDataSelection selection = new MavenMetaDataSelection();
549     selection.setAddMavenExecutionInfo(addMavenExecutionInfo);
550     selection.setAddEnvInfo(addEnvInfo);
551     selection.setAddJavaRuntimeInfo(addJavaRuntimeInfo);
552     selection.setAddOsInfo(addOsInfo);
553     selection.setAddProjectInfo(addProjectInfo);
554     selection.setHideCommandLineInfo(hideCommandLineInfo);
555     selection.setHideJavaOptsInfo(hideJavaOptsInfo);
556     selection.setHideMavenOptsInfo(hideMavenOptsInfo);
557     selection.setSelectedSystemProperties(properties);
558 
559     final MavenMetaDataProvider mavenMetaDataProvider =
560         new MavenMetaDataProvider(project, session, runtime, selection);
561     mavenMetaDataProvider.provideBuildMetaData(buildMetaDataProperties);
562   }
563 
564   private ScmInfo provideScmMetaData(final Properties buildMetaDataProperties)
565     throws MojoFailureException
566   {
567     try
568     {
569       final ScmInfo scmInfo = createScmInfo();
570       final ScmMetaDataProvider scmMetaDataProvider =
571           new ScmMetaDataProvider(project, scmInfo);
572       scmMetaDataProvider.provideBuildMetaData(buildMetaDataProperties);
573       return scmInfo;
574     }
575     catch (final ScmNoRevisionException e)
576     {
577       throw new MojoFailureException(e.getMessage()); // NOPMD
578     }
579   }
580 
581   private void provideHostMetaData(final Properties buildMetaDataProperties)
582     throws MojoExecutionException
583   {
584     if (addHostInfo)
585     {
586       final HostMetaDataProvider hostMetaData = new HostMetaDataProvider();
587       hostMetaData.provideBuildMetaData(buildMetaDataProperties);
588     }
589   }
590 
591   private void provideBuildDateMetaData(
592       final Properties buildMetaDataProperties, final Date buildDate)
593   {
594     final String buildDateString =
595         createBuildDate(buildMetaDataProperties, buildDate);
596     createYears(buildMetaDataProperties, buildDate);
597     createBuildVersion(buildMetaDataProperties, buildDate, buildDateString);
598   }
599 
600   private ScmInfo createScmInfo()
601   {
602     final ScmCredentials scmCredentials =
603         new ScmCredentials(settings, userName, password, privateKey, passphrase);
604     final ScmControl scmControl =
605         new ScmControl(failOnLocalModifications, ignoreDotFilesInBaseDir,
606             offline, addScmInfo, validateCheckout, failOnMissingRevision);
607     final ScmInfo scmInfo =
608         new ScmInfo(scmManager, connectionType, scmDateFormat, basedir,
609             scmCredentials, tagBase, queryRangeInDays, buildDatePattern,
610             scmControl);
611     return scmInfo;
612   }
613 
614   private boolean isBuildPropertiesToBeRebuild()
615   {
616     return forceNewProperties || !propertiesOutputFile.exists();
617   }
618 
619   private boolean isBuildPropertiesAlreadySet(final Properties projectProperties)
620   {
621     return projectProperties.getProperty(Constant.PROP_NAME_FULL_VERSION) != null;
622   }
623 
624   /**
625    * Provides the name of the user running the build. The value is either
626    * specified in the project properties or is taken from the Java system
627    * properties (<code>user.name</code>).
628    *
629    * @param projectProperties the project properties.
630    * @param buildMetaDataProperties the build meta data properties.
631    */
632   private void provideBuildUser(final Properties projectProperties,
633       final Properties buildMetaDataProperties)
634   {
635     String userNameValue = System.getProperty("user.name");
636     if ((buildUserPropertyName != null))
637     {
638       final String value = projectProperties.getProperty(buildUserPropertyName);
639       if (!StringUtils.isBlank(value))
640       {
641         userNameValue = value;
642       }
643     }
644 
645     if (userNameValue != null)
646     {
647       buildMetaDataProperties.setProperty(Constant.PROP_NAME_BUILD_USER,
648           userNameValue);
649     }
650   }
651 
652   /**
653    * Creates and adds the build date information.
654    *
655    * @param buildMetaDataProperties the build meta data properties.
656    * @param buildDate the date of the build.
657    * @return the formatted build date.
658    */
659   private String createBuildDate(final Properties buildMetaDataProperties,
660       final Date buildDate)
661   {
662     final DateFormat format =
663         new SimpleDateFormat(buildDatePattern, Locale.ENGLISH);
664     final String buildDateString = format.format(buildDate);
665     final String timestamp = String.valueOf(buildDate.getTime());
666 
667     buildMetaDataProperties.setProperty(Constant.PROP_NAME_BUILD_DATE,
668         buildDateString);
669     buildMetaDataProperties.setProperty(Constant.PROP_NAME_BUILD_DATE_PATTERN,
670         this.buildDatePattern);
671     buildMetaDataProperties.setProperty(Constant.PROP_NAME_BUILD_TIMESTAMP,
672         timestamp);
673 
674     return buildDateString;
675   }
676 
677   /**
678    * Adds the build and copyright year information.
679    *
680    * @param buildMetaDataProperties the build meta data properties.
681    * @param buildDate the build date to create the build year information.
682    */
683   private void createYears(final Properties buildMetaDataProperties,
684       final Date buildDate)
685   {
686     final DateFormat yearFormat = new SimpleDateFormat("yyyy", Locale.ENGLISH);
687     final String buildYearString = yearFormat.format(buildDate);
688     buildMetaDataProperties.setProperty(Constant.PROP_NAME_BUILD_YEAR,
689         buildYearString);
690     final String inceptionYearString = project.getInceptionYear();
691     final String copyrightYearString =
692         (buildYearString.equals(inceptionYearString) ? inceptionYearString
693             : inceptionYearString + '-' + buildYearString);
694     buildMetaDataProperties.setProperty(Constant.PROP_NAME_COPYRIGHT_YEAR,
695         copyrightYearString);
696   }
697 
698   /**
699    * Adds the version information of the artifact.
700    *
701    * @param buildMetaDataProperties the build meta data properties.
702    * @param buildDate the date of the build.
703    * @param buildDateString the formatted date string.
704    */
705   private void createBuildVersion(final Properties buildMetaDataProperties,
706       final Date buildDate, final String buildDateString)
707   {
708     final String version = project.getVersion();
709     buildMetaDataProperties.setProperty(Constant.PROP_NAME_VERSION, version);
710     buildMetaDataProperties.setProperty(Constant.PROP_NAME_GROUP_ID,
711         project.getGroupId());
712     buildMetaDataProperties.setProperty(Constant.PROP_NAME_ARTIFACT_ID,
713         project.getArtifactId());
714     buildMetaDataProperties.setProperty(Constant.PROP_NAME_BUILD_DATE,
715         buildDateString);
716 
717     final String fullVersion =
718         createFullVersion(buildMetaDataProperties, buildDate);
719     buildMetaDataProperties.setProperty(Constant.PROP_NAME_FULL_VERSION,
720         fullVersion);
721   }
722 
723   /**
724    * Creates the full version string which may include the date, the build, and
725    * the revision.
726    *
727    * @param buildMetaDataProperties the generated build meta data properties.
728    * @param buildDate the date of the current build.
729    * @return the full version string.
730    */
731   private String createFullVersion(final Properties buildMetaDataProperties,
732       final Date buildDate)
733   {
734     final String version = project.getVersion();
735 
736     final DateFormat format = new SimpleDateFormat("yyyyMMdd", Locale.ENGLISH);
737     final String datePart = format.format(buildDate);
738     final String revisionId =
739         buildMetaDataProperties.getProperty(Constant.PROP_NAME_SCM_REVISION_ID);
740 
741     final String versionPrefix, versionSuffix;
742     if (version.endsWith("-SNAPSHOT"))
743     {
744       versionPrefix = version.substring(0, version.lastIndexOf('-'));
745       versionSuffix = "-SNAPSHOT";
746     }
747     else
748     {
749       versionPrefix = version;
750       versionSuffix = "";
751     }
752 
753     final String modified;
754     if (addLocallyModifiedTagToFullVersion
755         && "true".equals(buildMetaDataProperties
756             .getProperty(Constant.PROP_NAME_SCM_LOCALLY_MODIFIED)))
757     {
758       modified = "-locally-modified";
759     }
760     else
761     {
762       modified = "";
763     }
764 
765     final String fullVersion =
766         versionPrefix
767             + (addBuildDateToFullVersion ? '-' + datePart : "")
768             + (addReleaseNumberToFullVersion
769                && StringUtils.isNotBlank(revisionId) ? "r" + revisionId : "")
770             + modified + versionSuffix;
771 
772     return fullVersion;
773   }
774 
775   // --- object basics --------------------------------------------------------
776 
777 }