All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.phloc.maven.buildinfo.GenerateBuildInfoMojo Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2006-2014 phloc systems
 * http://www.phloc.com
 * office[at]phloc[dot]com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.phloc.maven.buildinfo;

import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.apache.maven.model.Dependency;
import org.apache.maven.model.Exclusion;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.joda.time.DateTime;
import org.slf4j.impl.StaticLoggerBinder;

import com.phloc.commons.CGlobal;
import com.phloc.commons.SystemProperties;
import com.phloc.commons.collections.ContainerHelper;
import com.phloc.commons.io.file.FileIOError;
import com.phloc.commons.io.file.FileOperations;
import com.phloc.commons.io.file.FileUtils;
import com.phloc.commons.io.resource.FileSystemResource;
import com.phloc.commons.microdom.reader.XMLMapHandler;
import com.phloc.commons.regex.RegExHelper;
import com.phloc.commons.string.StringHelper;
import com.phloc.datetime.PDTFactory;
import com.phloc.datetime.config.PDTConfig;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

/**
 * @author Philip Helger, phloc systems
 * @goal generate-buildinfo
 * @phase generate-resources
 * @description Create build information at compile time. The information will
 *              be part of the created JAR/WAR/... file. The resulting file will
 *              reside in the META-INF directory of the created
 *              artifact.
 */
@SuppressFBWarnings (value = { "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD" }, justification = "set via maven property")
public final class GenerateBuildInfoMojo extends AbstractMojo
{
  /** The name of the XML file */
  private static final String DEFAULT_FILENAME_BUILDINFO_XML = "buildinfo.xml";
  /** The name of the properties file */
  private static final String DEFAULT_FILENAME_BUILDINFO_PROPERTIES = "buildinfo.properties";

  /**
   * The Maven Project.
   * 
   * @parameter property=project
   * @required
   * @readonly
   */
  private MavenProject project;

  /**
   * @parameter property=reactorProjects
   * @required
   * @readonly
   */
  private List  reactorProjects;

  /**
   * The directory where the temporary buildinfo files will be saved.
   * 
   * @required
   * @parameter property=tempDirectory
   *            default-value="${project.build.directory}/buildinfo-maven-plugin"
   */
  private File tempDirectory;

  /**
   * Set the time zone to be used. Use "UTC" for the universal timezone.
   * Otherwise Strings like "Europe/Vienna" should be used.
   * 
   * @parameter property="timeZone"
   */
  @SuppressWarnings ("unused")
  private String timeZone;

  /**
   * Should all system properties be emitted into the build info? If this flag
   * is set, the selectedSystemProperties are cleared, so either this flag or
   * the selectedSystemProperties should be used.
* All contained system properties are prefixed with * systemproperty. in the generated file. * * @parameter property="withAllSystemProperties" default-value="false" */ private boolean withAllSystemProperties = false; /** * A selected subset of system property names to be emitted. Each element can * be a regular expression to match more than one potential system property. * If this set is not empty, the withSystemProperties property should not need * to be enabled.
* All contained system properties are prefixed with * systemproperty. in the generated file. * * @parameter property="selectedSystemProperties" */ private Set selectedSystemProperties; /** * A selected subset of system property names to be ignored. Each element can * be a regular expression to match more than one potential system property. * Ignored system properties take precedence over selected system properties. * * @parameter property="ignoredSystemProperties" */ private Set ignoredSystemProperties; /** * Should all environment variables be emitted into the build info? If this * flag is set, the selectedEnvVars are cleared, so either this flag or the * selectedEnvVars should be used.
* All contained environment variables are prefixed with envvar. * in the generated file. * * @parameter property="withAllEnvVars" default-value="false" */ private boolean withAllEnvVars = false; /** * A selected subset of environment variables names to be emitted. Each * element can be a regular expression to match more than one potential * environment variables. If this set is not empty, the withEnvVars property * does not need to be enabled.
* All contained environment variables are prefixed with envvar. * in the generated file. * * @parameter property="selectedEnvVars" */ private Set selectedEnvVars; /** * A selected subset of environment variables names to be ignored. Each * element can be a regular expression to match more than one potential * environment variables. Ignored environment variables take precedence over * selected environment variables. * * @parameter property="ignoredEnvVars" */ private Set ignoredEnvVars; /** * Generate build info in .XML format? It is safe to generate multiple formats * in one run!
* The generated file has the following layout: * *
   * <mapping>
   *   <map key="buildinfo.version" value="2" />
   *   <map key="project.groupid" value="com.phloc.maven" />
   *   ...
   * </mapping>
   * 
* * @parameter property="formatXML" default-value="true" */ private boolean formatXML = true; /** * Generate build info in .properties format? It is safe to generate multiple * formats in one run! * * @parameter property="formatProperties" default-value="false" */ private boolean formatProperties = false; public void setTempDirectory (@Nonnull final File aDir) { tempDirectory = aDir; if (!tempDirectory.isAbsolute ()) tempDirectory = new File (project.getBasedir (), aDir.getPath ()); final FileIOError aResult = FileOperations.createDirRecursiveIfNotExisting (tempDirectory); if (aResult.isFailure ()) getLog ().error ("Failed to create temp directory " + aResult.toString ()); else getLog ().info ("Successfully created temp directory " + aDir.toString ()); } @SuppressFBWarnings (value = "URF_UNREAD_FIELD") public void setTimeZone (final String sTimeZone) { if (PDTConfig.setDefaultDateTimeZoneID (sTimeZone).isSuccess ()) timeZone = sTimeZone; else getLog ().warn ("Unknown time zone '" + sTimeZone + "'"); } public void setWithAllSystemProperties (final boolean bEnable) { withAllSystemProperties = bEnable; if (withAllSystemProperties) { // No selection if we have all system properties if (selectedSystemProperties != null && !selectedSystemProperties.isEmpty ()) { getLog ().warn ("Clearing all selected system properties, because all system properties are enabled!"); setSelectedSystemProperties (null); } } } // Important: parameter type must match member type! public void setSelectedSystemProperties (final Set aCollection) { selectedSystemProperties = new HashSet (); if (aCollection != null) { for (final String sName : aCollection) if (StringHelper.hasText (sName)) if (!selectedSystemProperties.add (sName)) getLog ().warn ("The selected system property '" + sName + "' is contained more than once"); } if (!selectedSystemProperties.isEmpty ()) { // If we have a set of selected, don't use all system properties if (withAllSystemProperties) { getLog ().warn ("Disabling all system properties, because selected system properties are defined!"); setWithAllSystemProperties (false); } } } // Important: parameter type must match member type! public void setIgnoredSystemProperties (final Set aCollection) { ignoredSystemProperties = new HashSet (); if (aCollection != null) { for (final String sName : aCollection) if (StringHelper.hasText (sName)) if (!ignoredSystemProperties.add (sName)) getLog ().warn ("The ignored system property '" + sName + "' is contained more than once"); } } public void setWithAllEnvVars (final boolean bEnable) { withAllEnvVars = bEnable; if (withAllEnvVars) { // No selection if we have all environment variables if (selectedEnvVars != null && !selectedEnvVars.isEmpty ()) { getLog ().warn ("Clearing all environment variables, because all environment variables are enabled!"); setSelectedEnvVars (null); } } } // Important: parameter type must match member type! public void setSelectedEnvVars (final Set aCollection) { selectedEnvVars = new HashSet (); if (aCollection != null) { for (final String sName : aCollection) if (StringHelper.hasText (sName)) if (!selectedEnvVars.add (sName)) getLog ().warn ("The selected environment variable '" + sName + "' is contained more than once"); } if (!selectedEnvVars.isEmpty ()) { // If we have a set of selected, don't use all if (withAllEnvVars) { getLog ().warn ("Disabling all environment variables, because selected environment variables are defined!"); setWithAllEnvVars (false); } } } // Important: parameter type must match member type! public void setIgnoredEnvVars (final Set aCollection) { ignoredEnvVars = new HashSet (); if (aCollection != null) { for (final String sName : aCollection) if (StringHelper.hasText (sName)) if (!ignoredEnvVars.add (sName)) getLog ().warn ("The ignored environment variable '" + sName + "' is contained more than once"); } } public void setFormatXML (final boolean bEnable) { formatXML = bEnable; } public void setFormatProperties (final boolean bEnable) { formatProperties = bEnable; } private static boolean _matches (@Nullable final Set aSet, @Nonnull final String sName) { if (aSet == null) return false; // Direct match? if (aSet.contains (sName)) return true; // RegEx match? for (final String sSelected : aSet) if (RegExHelper.stringMatchesPattern (sSelected, sName)) return true; // No match! return false; } private Map _determineBuildInfoProperties () { // Get the current time, using the time zone specified in the settings final DateTime aDT = PDTFactory.getCurrentDateTime (); // Build the default properties final Map aProps = new LinkedHashMap (); // Version 1: initial // Version 2: added dependency information; added per build plugin the key // property aProps.put ("buildinfo.version", "2"); // Project information aProps.put ("project.groupid", project.getGroupId ()); aProps.put ("project.artifactid", project.getArtifactId ()); aProps.put ("project.version", project.getVersion ()); aProps.put ("project.name", project.getName ()); aProps.put ("project.packaging", project.getPackaging ()); // Parent project information (if available) final MavenProject aParentProject = project.getParent (); if (aParentProject != null) { aProps.put ("parentproject.groupid", aParentProject.getGroupId ()); aProps.put ("parentproject.artifactid", aParentProject.getArtifactId ()); aProps.put ("parentproject.version", aParentProject.getVersion ()); aProps.put ("parentproject.name", aParentProject.getName ()); } // All reactor projects (nested projects) // Don't emit this, if this is "1" as than only the current project would be // listed if (reactorProjects != null && reactorProjects.size () != 1) { final String sPrefix = "reactorproject."; // The number of reactor projects aProps.put (sPrefix + "count", Integer.toString (reactorProjects.size ())); // Show details of all reactor projects, index starting at 0 int nIndex = 0; for (final MavenProject aReactorProject : reactorProjects) { aProps.put (sPrefix + nIndex + ".groupid", aReactorProject.getGroupId ()); aProps.put (sPrefix + nIndex + ".artifactid", aReactorProject.getArtifactId ()); aProps.put (sPrefix + nIndex + ".version", aReactorProject.getVersion ()); aProps.put (sPrefix + nIndex + ".name", aReactorProject.getName ()); ++nIndex; } } // Build Plugins final List aBuildPlugins = project.getBuildPlugins (); if (aBuildPlugins != null) { final String sPrefix = "build.plugin."; // The number of build plugins aProps.put (sPrefix + "count", Integer.toString (aBuildPlugins.size ())); // Show details of all plugins, index starting at 0 int nIndex = 0; for (final Object aObj : aBuildPlugins) { final Plugin aPlugin = (Plugin) aObj; aProps.put (sPrefix + nIndex + ".groupid", aPlugin.getGroupId ()); aProps.put (sPrefix + nIndex + ".artifactid", aPlugin.getArtifactId ()); aProps.put (sPrefix + nIndex + ".version", aPlugin.getVersion ()); final Object aConfiguration = aPlugin.getConfiguration (); if (aConfiguration != null) { // Will emit an XML structure! aProps.put (sPrefix + nIndex + ".configuration", aConfiguration.toString ()); } aProps.put (sPrefix + nIndex + ".key", aPlugin.getKey ()); ++nIndex; } } // Build dependencies final List aDependencies = project.getDependencies (); if (aDependencies != null) { final String sDepPrefix = "dependency."; // The number of build plugins aProps.put (sDepPrefix + "count", Integer.toString (aDependencies.size ())); // Show details of all dependencies, index starting at 0 int nDepIndex = 0; for (final Object aDepObj : aDependencies) { final Dependency aDependency = (Dependency) aDepObj; aProps.put (sDepPrefix + nDepIndex + ".groupid", aDependency.getGroupId ()); aProps.put (sDepPrefix + nDepIndex + ".artifactid", aDependency.getArtifactId ()); aProps.put (sDepPrefix + nDepIndex + ".version", aDependency.getVersion ()); aProps.put (sDepPrefix + nDepIndex + ".type", aDependency.getType ()); if (aDependency.getClassifier () != null) aProps.put (sDepPrefix + nDepIndex + ".classifier", aDependency.getClassifier ()); aProps.put (sDepPrefix + nDepIndex + ".scope", aDependency.getScope ()); if (aDependency.getSystemPath () != null) aProps.put (sDepPrefix + nDepIndex + ".systempath", aDependency.getSystemPath ()); aProps.put (sDepPrefix + nDepIndex + ".optional", Boolean.toString (aDependency.isOptional ())); aProps.put (sDepPrefix + nDepIndex + ".managementkey", aDependency.getManagementKey ()); // Add all exclusions of the current dependency final List aExclusions = aDependency.getExclusions (); if (aExclusions != null) { final String sExclusionPrefix = sDepPrefix + nDepIndex + ".exclusion."; // The number of build plugins aProps.put (sExclusionPrefix + "count", Integer.toString (aExclusions.size ())); // Show details of all dependencies, index starting at 0 int nExclusionIndex = 0; for (final Object aExclusionObj : aExclusions) { final Exclusion aExclusion = (Exclusion) aExclusionObj; aProps.put (sExclusionPrefix + nExclusionIndex + ".groupid", aExclusion.getGroupId ()); aProps.put (sExclusionPrefix + nExclusionIndex + ".artifactid", aExclusion.getArtifactId ()); ++nExclusionIndex; } } ++nDepIndex; } } // Build date and time aProps.put ("build.datetime", aDT.toString ()); aProps.put ("build.datetime.millis", Long.toString (aDT.getMillis ())); aProps.put ("build.datetime.date", aDT.toLocalDate ().toString ()); aProps.put ("build.datetime.time", aDT.toLocalTime ().toString ()); aProps.put ("build.datetime.timezone", aDT.getZone ().getID ()); final int nOfsMilliSecs = aDT.getZone ().getOffset (aDT); aProps.put ("build.datetime.timezone.offsethours", Long.toString (nOfsMilliSecs / CGlobal.MILLISECONDS_PER_HOUR)); aProps.put ("build.datetime.timezone.offsetmins", Long.toString (nOfsMilliSecs / CGlobal.MILLISECONDS_PER_MINUTE)); aProps.put ("build.datetime.timezone.offsetsecs", Long.toString (nOfsMilliSecs / CGlobal.MILLISECONDS_PER_SECOND)); aProps.put ("build.datetime.timezone.offsetmillisecs", Integer.toString (nOfsMilliSecs)); // Emit system properties? if (withAllSystemProperties || ContainerHelper.isNotEmpty (selectedSystemProperties)) for (final Map.Entry aEntry : ContainerHelper.getSortedByKey (SystemProperties.getAllProperties ()) .entrySet ()) { final String sName = aEntry.getKey (); if (withAllSystemProperties || _matches (selectedSystemProperties, sName)) if (!_matches (ignoredSystemProperties, sName)) aProps.put ("systemproperty." + sName, aEntry.getValue ()); } // Emit environment variable? if (withAllEnvVars || ContainerHelper.isNotEmpty (selectedEnvVars)) for (final Map.Entry aEntry : ContainerHelper.getSortedByKey (System.getenv ()).entrySet ()) { final String sName = aEntry.getKey (); if (withAllEnvVars || _matches (selectedEnvVars, sName)) if (!_matches (ignoredEnvVars, sName)) aProps.put ("envvar." + sName, aEntry.getValue ()); } return aProps; } private void _writeBuildinfoXML (final Map aProps) throws MojoExecutionException { // Write the XML in the format that it can easily be read by the // com.phloc.common.microdom.reader.XMLMappingReader class final File aFile = new File (tempDirectory, DEFAULT_FILENAME_BUILDINFO_XML); if (XMLMapHandler.writeMap (aProps, new FileSystemResource (aFile)).isFailure ()) throw new MojoExecutionException ("Failed to write XML file to " + aFile); getLog ().debug ("Wrote buildinfo XML file to " + aFile); } private void _writeBuildinfoProperties (final Map aProps) throws MojoExecutionException { // Write properties file final File aFile = new File (tempDirectory, DEFAULT_FILENAME_BUILDINFO_PROPERTIES); final Properties p = new Properties (); p.putAll (aProps); try { p.store (FileUtils.getOutputStream (aFile), "Generated - do not edit!"); } catch (final IOException ex) { throw new MojoExecutionException ("Failed to write properties file to " + aFile, ex); } getLog ().debug ("Wrote buildinfo properties file to " + aFile); } public void execute () throws MojoExecutionException { StaticLoggerBinder.getSingleton ().setMavenLog (getLog ()); if (tempDirectory == null) throw new MojoExecutionException ("No buildinfo temp directory specified!"); if (tempDirectory.exists () && !tempDirectory.isDirectory ()) throw new MojoExecutionException ("The specified buildinfo temp directory " + tempDirectory + " is not a directory!"); if (!tempDirectory.exists ()) { // Ensure that the directory exists if (!tempDirectory.mkdirs ()) throw new MojoExecutionException ("Failed to create buildinfo temp directory " + tempDirectory); getLog ().info ("Created buildinfo temp directory " + tempDirectory); } if (!formatProperties && !formatXML) throw new MojoExecutionException ("No buildinfo output format was specified. Nothing will be generated!"); final Map aProps = _determineBuildInfoProperties (); if (formatXML) _writeBuildinfoXML (aProps); if (formatProperties) _writeBuildinfoProperties (aProps); // Add output directory as a resource-directory final Resource aResource = new Resource (); aResource.setDirectory (tempDirectory.getAbsolutePath ()); aResource.addInclude ("**/*"); aResource.setFiltering (false); aResource.setTargetPath ("META-INF"); project.addResource (aResource); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy