org.kloeckner.maven.plugin.VersionRange Maven / Gradle / Ivy
The newest version!
package org.kloeckner.maven.plugin;
/*
* Copyright 2013 mk.
*
* 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.
*/
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Model;
import org.apache.maven.model.Profile;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.VersionRangeRequest;
import org.eclipse.aether.resolution.VersionRangeResolutionException;
import org.eclipse.aether.resolution.VersionRangeResult;
import org.eclipse.aether.version.Version;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.kloeckner.maven.plugin.util.VersionRangeUtils;
/**
* Goal updates the configured dependencies within the specified ranges.
*
* @goal use-latest-versions
*
* @phase process-sources
*/
public class VersionRange extends AbstractMojo {
/** @parameter default-value="${project}" */
private MavenProject mavenProject;
/**
* The entry point to Aether, i.e. the component doing all the work.
*
* @component
*/
private RepositorySystem repoSystem;
/**
* The current repository/network configuration of Maven.
*
* @parameter default-value="${repositorySystemSession}"
* @readonly
*/
private RepositorySystemSession repoSession;
/**
* The project's remote repositories to use for the resolution of project
* dependencies.
*
* @parameter default-value="${project.remoteProjectRepositories}"
* @readonly
*/
private List remoteRepos;
/**
* @parameter default-value="."
*/
private String dependencyVersionRangePath = ".";
/**
* @parameter default-value="version-range-maven-plugin.properties"
*/
private String dependencyVersionRangeFile = "version-range-maven-plugin.properties";
public void execute() throws MojoExecutionException {
readWritePom(mavenProject);
}
private void readWritePom(MavenProject project)
throws MojoExecutionException {
Document document;
String intro = null;
String outtro = null;
try {
String content = VersionRangeUtils.readXmlFile(
VersionRangeUtils.getStandardPom(project),
VersionRangeUtils.LS);
// we need to eliminate any extra whitespace inside elements, as
// JDOM will nuke it
content = content.replaceAll("<([^!][^>]*?)\\s{2,}([^>]*?)>",
"<$1 $2>");
content = content.replaceAll("(\\s{2,}|[^\\s])/>", "$1 />");
SAXBuilder builder = new SAXBuilder();
document = builder.build(new StringReader(content));
// Normalize line endings to platform's style (XML processors like
// JDOM normalize line endings to "\n" as
// per section 2.11 of the XML spec)
VersionRangeUtils.normaliseLineEndings(document);
// rewrite DOM as a string to find differences, since text outside
// the root element is not tracked
StringWriter w = new StringWriter();
Format format = Format.getRawFormat();
format.setLineSeparator(VersionRangeUtils.LS);
XMLOutputter out = new XMLOutputter(format);
out.output(document.getRootElement(), w);
int index = content.indexOf(w.toString());
if (index >= 0) {
intro = content.substring(0, index);
outtro = content.substring(index + w.toString().length());
} else {
/*
* NOTE: Due to whitespace, attribute reordering or entity
* expansion the above indexOf test can easily fail. So let's
* try harder. Maybe some day, when JDOM offers a StaxBuilder
* and this builder employes
* XMLInputFactory2.P_REPORT_PROLOG_WHITESPACE, this whole mess
* can be avoided.
*/
final String SPACE = "\\s++";
final String XML = "<\\?(?:(?:[^\"'>]++)|(?:\"[^\"]*+\")|(?:'[^\']*+'))*+>";
final String INTSUB = "\\[(?:(?:[^\"'\\]]++)|(?:\"[^\"]*+\")|(?:'[^\']*+'))*+\\]";
final String DOCTYPE = "]++)|(?:\"[^\"]*+\")|(?:'[^\']*+')|(?:"
+ INTSUB + "))*+>";
final String PI = XML;
final String COMMENT = "";
final String INTRO = "(?:(?:" + SPACE + ")|(?:" + XML + ")|(?:"
+ DOCTYPE + ")|(?:" + COMMENT + ")|(?:" + PI + "))*";
final String OUTRO = "(?:(?:" + SPACE + ")|(?:" + COMMENT
+ ")|(?:" + PI + "))*";
final String POM = "(?s)(" + INTRO + ")(.*?)(" + OUTRO + ")";
Matcher matcher = Pattern.compile(POM).matcher(content);
if (matcher.matches()) {
intro = matcher.group(1);
outtro = matcher.group(matcher.groupCount());
}
}
} catch (JDOMException e) {
throw new MojoExecutionException("Error reading POM: "
+ e.getMessage(), e);
} catch (IOException e) {
throw new MojoExecutionException("Error reading POM: "
+ e.getMessage(), e);
}
List reactorProjects = new ArrayList();
Object result = new Object();
transformDocument(project, document.getRootElement(), reactorProjects,
result, false);
// for overwriting:
File pomFile = VersionRangeUtils.getStandardPom(project);
// File pomFile = new File(project.getBasedir(), "newpom.xml");
// if (simulate) {
// File outputFile = new File(pomFile.getParentFile(), pomFile.getName()
// + "." + pomSuffix);
// writePom(outputFile, document, releaseDescriptor,
// project.getModelVersion(), intro, outtro);
// } else {
VersionRangeUtils.writePom(pomFile, document,
project.getModelVersion(), intro, outtro);
// }
}
private void transformDocument(MavenProject project, Element rootElement,
List reactorProjects, Object result, boolean simulate)
throws MojoExecutionException {
Namespace namespace = rootElement.getNamespace();
List eagerArtifactsStrings = VersionRangeUtils
.loadEagerDependencies(dependencyVersionRangePath,
dependencyVersionRangeFile);
Map mappedVersions = getNextVersionMap(eagerArtifactsStrings);
Map originalVersions = getOriginalVersionMap(project);
getLog().debug("mapped Versions (newer Versions):" + mappedVersions);
getLog().debug("original Versions: " + originalVersions);
Model model = project.getModel();
Element properties = rootElement.getChild("properties", namespace);
// String parentVersion = EagerUpdateUtils.rewriteParent(project,
// rootElement, namespace, mappedVersions, originalVersions);
VersionRangeUtils.rewriteParent(project, rootElement, namespace,
mappedVersions, originalVersions);
List roots = new ArrayList();
roots.add(rootElement);
List profileElements = VersionRangeUtils.getChildren(
rootElement, "profiles", "profile");
getLog().debug("got profiles: " + profileElements);
roots.addAll(profileElements);
for (Element root : roots) {
rewriteArtifactVersions(
VersionRangeUtils.getChildren(root, "parent"),
mappedVersions, originalVersions, model, properties, result);
rewriteArtifactVersions(VersionRangeUtils.getChildren(root,
"dependencies", "dependency"), mappedVersions,
originalVersions, model, properties, result);
rewriteArtifactVersions(VersionRangeUtils.getChildren(root,
"dependencyManagement", "dependencies", "dependency"),
mappedVersions, originalVersions, model, properties, result);
}
}
// private Map getOriginalVersionMap(List
// reactorProjects) {
private Map getOriginalVersionMap(MavenProject projects) {
HashMap hashMap = new HashMap();
// TODO depmgmt, parent ...
if (projects.getParentArtifact() != null) {
hashMap.put(ArtifactUtils.versionlessKey(projects
.getParentArtifact().getGroupId(), projects
.getParentArtifact().getArtifactId()), projects
.getParentArtifact().getArtifactId());
}
hashMap.putAll(buildVersionsMap(projects.getDependencies()));
if (projects.getDependencyManagement() != null) {
hashMap.putAll(buildVersionsMap(projects.getDependencyManagement()
.getDependencies()));
}
for (Profile profile : projects.getActiveProfiles()) {
hashMap.putAll(buildVersionsMap(profile.getDependencies()));
if (profile.getDependencyManagement() != null) {
hashMap.putAll(buildVersionsMap(profile
.getDependencyManagement().getDependencies()));
}
}
return hashMap;
}
private Map buildVersionsMap(List dependencies) {
Map hashMap = new HashMap();
for (Dependency dep : dependencies) {
String currentVersion = dep.getVersion();
String versionlessKey = ArtifactUtils.versionlessKey(
dep.getGroupId(), dep.getArtifactId());
hashMap.put(versionlessKey, currentVersion);
}
return hashMap;
}
private Map getNextVersionMap(List eagerArtifacts)
throws MojoExecutionException {
HashMap hashMap = new HashMap();
for (String s : eagerArtifacts) {
DefaultArtifact artifact = new DefaultArtifact(s);
Version newVersion = resolveNewVersion(artifact);
String versionlessKey = ArtifactUtils.versionlessKey(
artifact.getGroupId(), artifact.getArtifactId());
hashMap.put(versionlessKey, newVersion.toString());
}
return hashMap;
}
private Version resolveNewVersion(Artifact artifact)
throws MojoExecutionException {
VersionRangeRequest request = new VersionRangeRequest();
request.setArtifact(artifact);
request.setRepositories(remoteRepos);
getLog().debug(
"Resolving artifact " + artifact + " from " + remoteRepos);
VersionRangeResult rangeResult;
try {
rangeResult = repoSystem.resolveVersionRange(repoSession, request);
} catch (VersionRangeResolutionException e) {
e.printStackTrace();
throw new MojoExecutionException("unable to resolve versions for: "
+ artifact, e);
}
getLog().debug(
"artifactId: " + artifact.getArtifactId() + " - "
+ rangeResult.getVersions());
Version newestVersion = rangeResult.getHighestVersion();
return newestVersion;
}
private void rewriteArtifactVersions(Collection elements,
Map mappedVersions,
Map originalVersions, Model projectModel,
Element properties, Object result) throws MojoExecutionException {
if (elements == null) {
return;
}
String projectId = ArtifactUtils.versionlessKey(
projectModel.getGroupId(), projectModel.getArtifactId());
for (Element element : elements) {
Element versionElement = element.getChild("version",
element.getNamespace());
if (versionElement == null) {
// managed dependency or unversioned plugin
continue;
}
String rawVersion = versionElement.getTextTrim();
Element groupIdElement = element.getChild("groupId",
element.getNamespace());
if (groupIdElement == null) {
if ("plugin".equals(element.getName())) {
groupIdElement = new Element("groupId",
element.getNamespace());
groupIdElement.setText("org.apache.maven.plugins");
} else {
// incomplete dependency
continue;
}
}
String groupId = VersionRangeUtils.interpolate(
groupIdElement.getTextTrim(), projectModel);
Element artifactIdElement = element.getChild("artifactId",
element.getNamespace());
if (artifactIdElement == null) {
// incomplete element
continue;
}
String artifactId = VersionRangeUtils.interpolate(
artifactIdElement.getTextTrim(), projectModel);
String key = ArtifactUtils.versionlessKey(groupId, artifactId);
String mappedVersion = mappedVersions.get(key);
String originalVersion = originalVersions.get(key);
if (mappedVersion != null) {
if (rawVersion.equals(originalVersion)) {
getLog().info(
"Updating " + artifactId + " to " + mappedVersion);
VersionRangeUtils.rewriteValue(versionElement,
mappedVersion);
} else if (rawVersion.matches("\\$\\{.+\\}")) {
String expression = rawVersion.substring(2,
rawVersion.length() - 1);
if (expression.startsWith("project.")
|| expression.startsWith("pom.")
|| "version".equals(expression)) {
if (!mappedVersion
.equals(mappedVersions.get(projectId))) {
getLog().info(
"Updating " + artifactId + " to "
+ mappedVersion);
VersionRangeUtils.rewriteValue(versionElement,
mappedVersion);
} else {
getLog().info(
"Ignoring artifact version update for expression "
+ rawVersion);
}
} else if (properties != null) {
// version is an expression, check for properties to
// update instead
Element property = properties.getChild(expression,
properties.getNamespace());
if (property != null) {
String propertyValue = property.getTextTrim();
if (propertyValue.equals(originalVersion)) {
getLog().info(
"Updating " + rawVersion + " to "
+ mappedVersion);
// change the property only if the property is
// the same as what's in the reactor
VersionRangeUtils.rewriteValue(property,
mappedVersion);
} else if (mappedVersion.equals(propertyValue)) {
// this property may have been updated during
// processing a sibling.
getLog().info(
"Ignoring artifact version update for expression "
+ rawVersion
+ " because it is already updated");
} else if (!mappedVersion.equals(rawVersion)) {
if (mappedVersion.matches("\\$\\{project.+\\}")
|| mappedVersion
.matches("\\$\\{pom.+\\}")
|| "${version}".equals(mappedVersion)) {
getLog().info(
"Ignoring artifact version update for expression "
+ mappedVersion);
// ignore... we cannot update this
// expression
} else {
// the value of the expression conflicts
// with what the user wanted to release
throw new MojoExecutionException(
"The artifact (" + key
+ ") requires a "
+ "different version ("
+ mappedVersion
+ ") than what is found ("
+ propertyValue
+ ") for the expression ("
+ expression + ") in the "
+ "project (" + projectId
+ ").");
}
}
} else {
// the expression used to define the version of this
// artifact may be inherited
// TODO needs a better error message, what pom? what
// dependency?
throw new MojoExecutionException(
"The version could not be updated: "
+ rawVersion);
}
}
} else {
// different/previous version not related to current release
getLog().debug(
"different/previous version not related to current release");
}
} else {
// artifact not related to current release
getLog().debug("artifact not related to current release");
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy