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

org.ow2.util.maven.jbuilding.VersionsMojo Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2007-2012 Bull S.A.S.
 *
 * 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 org.ow2.util.maven.jbuilding;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
import java.util.regex.Pattern;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.DefaultRepositoryRequest;
import org.apache.maven.artifact.repository.RepositoryRequest;
import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
import org.apache.maven.artifact.resolver.ResolutionNode;
import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.codehaus.plexus.util.FileUtils;

/**
 * Process a project and explore artifacts versions.
 * @author Guillaume Sauthier
 * @since 2.0.0
 */
@Mojo(name = "versions",
      defaultPhase = LifecyclePhase.PROCESS_SOURCES)
public class VersionsMojo extends AbstractResolverMojo {

    /**
     * Property pattern.
     */
    private static Pattern PROPERTY_NAME_PATTERN = Pattern.compile(".*\\.version$");

    /**
     * Location of the versions.properties to be created.
     * @since 2.0.0
     */
    @Parameter(defaultValue = "${project.build.directory}/versions.properties")
    private File output;

    /**
     * Group Ids to be joined if they share the same version.
     * @since 2.0.0
     */
    @Parameter
    private List joinedGroupIds = new ArrayList();

    /**
     * @since 2.0.0
     */
    @Parameter(alias = "jbuilding.versions.properties",
               defaultValue = "false")
    private boolean browseProperties = false;

    /**
     * @since 2.0.0
     */
    @Parameter
    private String[] preferedGroupIds;

    /**
     * Dependencies depth filtering.
     * @since 2.0.0
     */
    @Parameter(defaultValue = "3")
    private int depth = 3;

    /**
     * ArtifactCollector filtering on artifact's scope.
     * @since 2.0.0
     */
    @Parameter(defaultValue = Artifact.SCOPE_COMPILE)
    private String collectScope = Artifact.SCOPE_COMPILE;

    /**
     * Allow timestamp in SNAPSHOT version if set to true
     * @since 2.0.0
     */
    @Parameter(defaultValue = "false")
    private boolean allowTimestamp = false;

    /**
     * Joined groupIds with their default version.
     */
    private Map joinGroups = new HashMap();

    /**
     * List of resolvables artifacts (to be used with the collector to resolve first depth dependencies).
     */
    private Set collectableArtifacts = new HashSet();

    /**
     * Default Bean constructor.
     */
    public VersionsMojo() {
        // Default value : no preferences
        preferedGroupIds = new String[1];
        preferedGroupIds[0] = "*";
    }

    /**
     * Writes the versions.properties file.
     * @throws MojoExecutionException
     * @throws MojoFailureException
     */
    public void execute() throws MojoExecutionException, MojoFailureException {

        getLog().debug("Introspecting project " + project.getGroupId() + ":" + project.getArtifactId());

        // Resolve the artifacts provided in the plugin configuration
        Map> map = resolveArtifacts(SERVER_SIDE, BOTH_MODE, ALL_ARTIFACT_ITEMS,
                this.allowTimestamp);

        // Init the Version map
        Map versionsProperties = new TreeMap();

        // Browse properties
        if (browseProperties) {
            getVersionsFromModelProperties(versionsProperties);
            getVersionsFromProjectProperties(versionsProperties);
        }

        // Browse dependencies
        getVersionsFromArtifactItems(map, versionsProperties);
        getVersionsFromProjectDependencies(versionsProperties);

        // Browse transitive dependencies
        getVersionsFromTransitiveDependencies(versionsProperties);

        // Don't forget joined group Ids
        getVersionsFromJoinedGroupIds(versionsProperties);

        // Store the file
        try {
            getLog().debug("Storing in " + output);
            storeVersionsProperties(versionsProperties);
        } catch (IOException ioe) {
            throw new MojoExecutionException("Cannot store " + output, ioe);
        }
    }

    /**
     * Extract version information from project's runtime properties
     * @param versions versions
     */
    public void getVersionsFromProjectProperties(final Map versions) {

        getLog().debug("Looking in project's properties");
        // Look on the project runtime properties
        filterProperties(project.getProperties(), versions);
    }

    /**
     * Extract version information from project's model properties
     * @param versions versions
     */
    public void getVersionsFromModelProperties(final Map versions) {

        getLog().debug("Looking in project's model properties");
        // Look on the ${project.properties.*} values
        filterProperties(project.getModel().getProperties(), versions);
    }

    /**
     * Extract versions informations from ArtifactItems listed in the jbuilding plugin configuration.
     * @param groups groups of ArtifactItems
     * @param versions versions
     */
    public void getVersionsFromArtifactItems(final Map> groups, final Map versions) {

        // Iterates over groups (~ deployment-plans)
        for (Map.Entry> group : groups.entrySet()) {

            // Iterates over artifact items in the group
            for (Artifact artifact : group.getValue()) {

                String groupId = artifact.getGroupId();
                String artifactVersion = null;

                //Bug fix UTIL-84
                if (!this.allowTimestamp && artifact.getBaseVersion() != null) {
                    artifactVersion = artifact.getBaseVersion();
                } else {
                    artifactVersion = artifact.getVersion();
                }

                if (!filterGroupId(groupId, artifactVersion)) {
                    // if the artifact did need to be filtered, just write the property
                    String name = getArtifactName(artifact.getGroupId(),
                                                  artifact.getArtifactId());
                    addVersion(versions, name, artifactVersion);
                }

                // Add this artifact
                collectableArtifacts.add(artifact);
            }
        }
    }

    /**
     * Extract versions informations from project's dependencies.
     * @param versions versions
     */
    @SuppressWarnings({"unchecked"})
    public void getVersionsFromProjectDependencies(final Map versions) {

        // Iterates on all project's dependencies
        List dependencies = project.getDependencies();
        if (dependencies != null) {
            for (Dependency dependency : dependencies) {

                String groupId = dependency.getGroupId();
                String artifactId = dependency.getArtifactId();
                String dependencyVersion = dependency.getVersion();
                String dependencyType = dependency.getType();
                String dependencyClassifier = dependency.getClassifier();

                if (!filterGroupId(groupId, dependencyVersion)) {
                    // if the artifact didn't need to be filtered, just write the property
                    String name = getArtifactName(groupId,
                                                  artifactId);
                    addVersion(versions, name, dependencyVersion);
                }

                // Add this artifact
                collectableArtifacts.add(createArtifact(groupId, artifactId, dependencyVersion, dependencyType, dependencyClassifier));

            }
        }

    }

    /**
     * Extract versions information from transitive dependencies of the
     * collected artifacts (dependencies + bundle items)
     * @param versions versions
     * @throws MojoExecutionException if POM collection failed
     */
    private void getVersionsFromTransitiveDependencies(final Map versions) throws MojoExecutionException {

        // Collect metadata information from all the collected artifacts
        for (Artifact collectable : collectableArtifacts) {
            ArtifactResolutionResult result = resolveArtifactTransitively(collectable);
            Set nodes = result.getArtifactResolutionNodes();
            for (ResolutionNode node : nodes) {
                // Only manage firsts depths nodes
                if (node.getDepth() < depth) {
                    Artifact artifact = node.getArtifact();

                    String groupId = artifact.getGroupId();
                    String artifactId = artifact.getArtifactId();
                    String dependencyVersion = artifact.getVersion();

                    // For the SNAPSHOT artifacts, its base version should be use than its version.
                    if (!this.allowTimestamp && artifact.getBaseVersion() != null) {
                        dependencyVersion = artifact.getBaseVersion();
                    } else {
                        dependencyVersion = artifact.getVersion();
                    }

                    if (!filterGroupId(groupId, dependencyVersion)) {
                        // if the artifact didn't need to be filtered, just write the property
                        String name = getArtifactName(groupId,
                                                      artifactId);
                        addVersion(versions, name, dependencyVersion);
                    }
                }
            }
        }
    }

    /**
     * Extract versions informations from joined groupIds.
     * @param versions versions
     */
    public void getVersionsFromJoinedGroupIds(final Map versions) {

        for (Map.Entry entry : joinGroups.entrySet()) {
            String name = entry.getKey();
            String version = entry.getValue();
            addVersion(versions, name, version);
        }
    }

    /**
     * Filter properties values and only select the one finishing with ".version"
     * @param filteredProperties properties to be filtered
     * @param versions versions
     */
    private void filterProperties(final Properties filteredProperties,
                                  final Map versions) {

        if (filteredProperties != null) {

            // Filter all existing properties
            for (Map.Entry entry : filteredProperties.entrySet()) {
                String name = (String) entry.getKey();
                if (PROPERTY_NAME_PATTERN.matcher(name).matches()) {
                    String value = (String) entry.getValue();
                    addVersion(versions, name, value);

                    getLog().debug(name + "->" + value);
                }
            }
        }
    }

    /**
     * Store the collected versions in the versions.propertier file.
     * @param versions collected versions
     * @throws IOException if File creation failed
     */
    public void storeVersionsProperties(final Map versions) throws IOException {

        // Sort the output using specified patterns
        Stack patterns = createSortingPatterns();

        StringBuilder sb = new StringBuilder();
        sb.append("# Generated the ");
        sb.append(new Date());
        sb.append(" by OW2 Util.\n\n");

        // Used to group artifacts using their grouptId
        String lastGroupId = "";

        // Browse the map using the patterns as a guideline
        while (!patterns.isEmpty()) {
            Pattern pattern = patterns.pop();

            // Extract the keys as an array because we change the Map during the process
            String[] entries = versions.keySet().toArray(new String[versions.size()]);
            for (String name : entries) {

                // Only print the value if it matches the current pattern
                if (pattern.matcher(name).matches()) {

                    String groupId = getGroupId(name);
                    // Add an empty line separating artifacts using their groupIds
                    if (!groupId.startsWith(lastGroupId)) {
                        sb.append("\n");
                    }

                    // print the property
                    sb.append(name);
                    sb.append("   ");
                    sb.append(versions.get(name));
                    sb.append("\n");

                    // remember the last group
                    lastGroupId = groupId;

                    // Remove the property
                    versions.remove(name);
                }
            }
        }

        // Write the file
        FileUtils.fileWrite(output.getPath(), sb.toString());

    }

    /**
     * Extract the group Id the the property name.
     * The property name is of the following form: groupId[_artifactId]
     * @param name The property name
     * @return the groupId contained in this property name
     */
    private String getGroupId(final String name) {

        int index = name.indexOf('_');
        if (index == -1) {
            return name;
        } else {
            return name.substring(0, index);
        }
    }

    /**
     * Create a Stack of pattern used to sort the output.
     * @return new Stack
     */
    private Stack createSortingPatterns() {

        Stack patterns = new Stack();

        // if the last item is not a '*', add it
        String last = preferedGroupIds[preferedGroupIds.length - 1];
        if (!last.equals("*")) {
            patterns.push(createPattern("*"));
        }

        // Inverted iteration (most wanted pattern to be included on top of the stack)
        for (int i = preferedGroupIds.length - 1; i >= 0 ; i--) {
            patterns.push(createPattern(preferedGroupIds[i]));
        }
        return patterns;
    }

    /**
     * Create a pattern using a pseudo regular expression.
     * @param expression pseudo regular expression
     * @return a pattern corresponding to the given expression
     */
    public Pattern createPattern(String expression) {

        if (expression.contains(".")) {
            // protect existing '.' char
            expression = expression.replaceAll("\\.", "\\\\.");
        }

        if (expression.contains("*")) {
            // convert '*' pseudo regular expression to valid pattern
            // '*' -> '.*'
            expression = expression.replaceAll("\\*", "\\.\\*");
        }
        return Pattern.compile(expression);
    }

    /**
     * Add the couple name/version in the versions Map
     * @param versions versions
     * @param name property name
     * @param version property value
     */
    private void addVersion(final Map versions, final String name, final String version) {
        if (!versions.containsKey(name)) {
            versions.put(name, version);
        } else {
            if (getLog().isDebugEnabled()) {
                if (!version.equals(versions.get(name))) {
                    getLog().debug("Another version for " + name + ":" + versions.get(name) + " (" + version + ")");
                }
            }
        }
    }

    /**
     * Create an artifact name from an artifact groupId and artifactId.
     * @param groupId groupId
     * @param artifactId artifactId
     * @return a composed artifact name
     */
    private String getArtifactName(final String groupId, final String artifactId) {
        StringBuilder sb = new StringBuilder();
        // groupId
        sb.append(groupId);
        sb.append("_");

        // artifactId
        String id = artifactId;
        if (artifactId.startsWith(groupId) && (!artifactId.equals(groupId))) {
            // simplify artifact Id
            id = artifactId.substring(groupId.length() + 1);
        }
        sb.append(id);
        return sb.toString();
    }

    /**
     * Filter a groupId.
     * If the given groupId/version couple is already registered (same group, same
     * version), the artifact will be filtered (excluded).
     * @param groupId under test group
     * @param version under test version
     * @return true if the groupId is filtered, false otherwise
     */
    private boolean filterGroupId(final String groupId, final String version) {
        if (joinedGroupIds.contains(groupId)) {
            // This artifact may need to be filtered

            if (joinGroups.containsKey(groupId)) {
                // A previous bundle of the same group has already been filtered

                String storedVersion = joinGroups.get(groupId);
                if (storedVersion.equals(version)) {
                    // Continue to the next artifact
                    return true;
                }
            } else {
                // This group needs to be filtered
                // We just encountered the first artifact that belongs to this group.
                // Store the default version
                joinGroups.put(groupId, version);

                // and continue to the next artifact
                return true;
            }
        }
        return false;
    }

    /**
     * Resolve the given artifact transitively (triggering its download if needed).
     * @param artifact the given artifact to resolve
     * @throws MojoExecutionException if there is failure when resolving
     */
    protected ArtifactResolutionResult resolveArtifactTransitively(final Artifact artifact) throws MojoExecutionException {
        RepositoryRequest repositoryRequest = DefaultRepositoryRequest.getRepositoryRequest(mavenSession, project);
        repositoryRequest.setRemoteRepositories(remoteRepositories);
        ArtifactResolutionRequest request = new ArtifactResolutionRequest(repositoryRequest);
        request.setArtifact(artifact);
        request.setCollectionFilter(new ScopeArtifactFilter(collectScope));
        request.setResolveTransitively(true);

        // Resolve
        ArtifactResolutionResult result = repositorySystem.resolve(request);
        if (!result.isSuccess()) {
            for (Exception e : result.getExceptions()) {
                getLog().warn(e.getMessage());
            }
            getLog().warn("Couldn't transitively resolve artifact " + artifact);
        }
        return result;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy