blob: 287ebd35903b3c1ca2e417e72382672b4b7cb263 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.maven.dist.tools;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
import org.apache.maven.artifact.repository.MavenArtifactRepository;
import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
import org.apache.maven.artifact.repository.metadata.Metadata;
import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.doxia.sink.Sink;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.reporting.AbstractMavenReport;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
/**
* Abstract AbstractDistCheckReport class.
*
* @author skygo
*/
public abstract class AbstractDistCheckReport extends AbstractMavenReport {
private static final String CONF = "dist-tool.conf";
private static final String EOL = System.getProperty("line.separator");
/**
* Abstract Dist Check Report.
*/
public AbstractDistCheckReport() {}
/**
* URL of repository where artifacts are stored.
*/
@Parameter(property = "repositoryUrl", defaultValue = "https://repo.maven.apache.org/maven2/")
protected String repoBaseUrl;
/**
* List of configuration line for specific inspection.
*/
@Parameter(property = "configurationLines", defaultValue = "")
private List<String> configurationLines;
/**
* Failures directory.
*/
@Parameter(defaultValue = "${project.build.directory}/dist-tool")
protected File failuresDirectory;
/**
* list of artifacts repositories.
*/
protected List<ArtifactRepository> artifactRepositories = new LinkedList<>();
/**
* Location of distribution area
*/
protected String distributionAreaUrl;
/**
* Path in index page mapping, when path is not the classical /artifact-id/
* The configuration in <code>dist-tool.conf</code> looks like this:
* <pre>artifact-id index-path = /directory/</pre>
*/
protected Map<String, String> paths = new HashMap<String, String>();
/**
* Site url mapping, when site url read in pom doesn't get the expected value
* The configuration in <code>dist-tool.conf</code> looks like this:
* <pre>artifact-id site = site url
*artifact-id:version site = site url</pre>
*/
protected Map<String, String> sites = new HashMap<String, String>();
/**
* is it index page check mojo?
* necessary to only check index page information for plugins marked with asterisk * in db,
* because they are released as part of a global component (archetype, scm, release, ...)
*
* @return if it is a index page check.
*/
protected abstract boolean isIndexPageCheck();
/**
* <p>checkArtifact.</p>
*
* @param request a {@link org.apache.maven.dist.tools.ConfigurationLineInfo} object
* @param repoBase a {@link java.lang.String} object
* @throws org.apache.maven.plugin.MojoExecutionException if any.
*/
protected abstract void checkArtifact(ConfigurationLineInfo request, String repoBase) throws MojoExecutionException;
/**
* <p>getFailuresFilename.</p>
*
* @return a {@link java.lang.String} object
*/
protected abstract String getFailuresFilename();
/** {@inheritDoc} */
@Override
public String getOutputName() {
return "dist-tool-" + getFailuresFilename().replace(".log", "");
}
private void loadConfiguration() throws MojoExecutionException {
URL configuration = Thread.currentThread().getContextClassLoader().getResource(CONF);
try (BufferedReader in = new BufferedReader(new InputStreamReader(configuration.openStream()))) {
String text;
while ((text = in.readLine()) != null) {
configurationLines.add(text);
}
} catch (IOException e) {
throw new MojoExecutionException("error while reading " + configuration, e);
}
}
private ArtifactRepositoryPolicy newArtifactRepositoryPolicy(boolean enabled) {
return new ArtifactRepositoryPolicy(
enabled, ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS, ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN);
}
/** {@inheritDoc} */
@Override
public void execute() throws MojoExecutionException {
ArtifactRepository aa = new MavenArtifactRepository(
"central",
repoBaseUrl,
new DefaultRepositoryLayout(),
newArtifactRepositoryPolicy(false),
newArtifactRepositoryPolicy(true));
artifactRepositories.add(aa);
if (configurationLines.isEmpty()) {
loadConfiguration();
}
File failures = getFailuresFile();
if (failures.exists()) {
failures.delete();
} else {
failuresDirectory.mkdirs();
}
ConfigurationLineInfo currentGroup = null;
for (String line : configurationLines) {
ConfigurationLineInfo aLine = null;
String trim = line.trim();
if ("".equals(trim) || trim.startsWith("##")) {
// empty line or comment: ignore
continue;
}
getLog().info(line);
line = trim;
if (line.startsWith(">")) {
// parameter
int index = line.indexOf('=');
if (index < 0) {
throw new MojoExecutionException("unparseable configuration line, missing '=': " + line);
}
String param = line.substring(1, index).trim();
String value = line.substring(index + 1).trim();
if ("dist-area".equals(param)) {
distributionAreaUrl = value;
} else if (param.contains(" ")) {
// parameter for an artifactId
index = param.indexOf(' ');
String artifactId = param.substring(0, index);
param = param.substring(index).trim();
if ("index-path".equals(param)) {
paths.put(artifactId, value);
} else if ("site".equals(param)) {
sites.put(artifactId, value);
} else {
throw new MojoExecutionException(
"unknown artifact parameter '" + param + "' in configuration line: " + line);
}
} else {
throw new MojoExecutionException("unparseable configuration line: " + line);
}
continue;
} else if (line.startsWith("/")) {
// definition of a group, in a dist-area directory
currentGroup = new ConfigurationLineInfo(line.split(" "));
if (currentGroup.getArtifactId() == null) {
continue;
}
// check group's parent pom artifact
aLine = currentGroup;
} else {
// artifact definition
if (line.startsWith("*")) {
// special artifact
if (!isIndexPageCheck()) {
// not check-index-page mojo, so ignore this artifact
continue;
}
// remove the asterisk before running the check
line = line.substring(1).trim();
}
try {
aLine = new ConfigurationLineInfo(currentGroup, line.split(" "));
} catch (InvalidVersionSpecificationException e) {
throw new MojoExecutionException(e.getMessage());
}
}
checkArtifact(aLine, getVersion(aLine));
}
getLog().info("");
}
private String getVersion(ConfigurationLineInfo aLine) throws MojoExecutionException {
String metadataUrl = aLine.getMetadataFileURL(repoBaseUrl);
try (InputStream input = new BufferedInputStream(new URL(metadataUrl).openStream())) {
MetadataXpp3Reader metadataReader = new MetadataXpp3Reader();
Metadata metadata = metadataReader.read(input);
aLine.setMetadata(metadata);
String version;
if (aLine.getVersionRange() != null) {
if (aLine.getVersionRange().hasRestrictions()) {
List<ArtifactVersion> artifactVersions = new ArrayList<>();
for (String versioningVersion : metadata.getVersioning().getVersions()) {
artifactVersions.add(new DefaultArtifactVersion(versioningVersion));
}
version = aLine.getVersionRange()
.matchVersion(artifactVersions)
.toString();
} else {
version = aLine.getVersionRange().getRecommendedVersion().toString();
}
aLine.setForceVersion(version);
} else {
version = metadata.getVersioning().getLatest();
}
if (getLog().isDebugEnabled()) {
getLog().debug(" available versions in repository " + repoBaseUrl);
// revert sort versions (not handling alpha and
// complex version schemes but more useful versions are displayed left side)
Collections.sort(metadata.getVersioning().getVersions(), Collections.reverseOrder());
getLog().debug(" " + metadata.getVersioning().getVersions());
}
if (aLine.getForcedVersion() != null) {
if (aLine.getVersionRange().hasRestrictions()) {
getLog().debug(aLine.getGroupId() + ":" + aLine.getArtifactId()
+ " metadata latest version value is "
+ metadata.getVersioning().getLatest()
+ " but check was restricted to " + aLine.getVersionRange()
+ " which selected " + aLine.getForcedVersion());
} else {
getLog().info(aLine.getGroupId() + ":" + aLine.getArtifactId()
+ " metadata latest version value is "
+ metadata.getVersioning().getLatest()
+ " but check was manually set to " + aLine.getForcedVersion());
}
}
return version;
} catch (IOException | XmlPullParserException ex) {
throw new MojoExecutionException("error while reading " + metadataUrl, ex);
}
}
/**
* add an error icon.
*
* @param sink doxiasink
*/
public static void iconError(Sink sink) {
icon(sink, "icon_error_sml");
}
/**
* add a warning icon.
*
* @param sink doxiasink
*/
public static void iconWarning(Sink sink) {
icon(sink, "icon_warning_sml");
}
/**
* add an success icon.
*
* @param sink doxiasink
*/
public static void iconSuccess(Sink sink) {
icon(sink, "icon_success_sml");
}
/**
* add a "remove" icon.
*
* @param sink doxiasink
*/
protected static void iconRemove(Sink sink) {
icon(sink, "remove");
}
private static void icon(Sink sink, String level) {
sink.figureGraphics("images/" + level + ".gif");
}
/**
* Log and add Error line to logs.txt if not configured to ignore the artifact+version
*
* @param cli {@link org.apache.maven.dist.tools.ConfigurationLineInfo}
* @param version The version.
* @param ignore the list of ignores.
* @param message The message.
* @return true if real error, or false if ignored
*/
protected boolean addErrorLine(ConfigurationLineInfo cli, String version, List<String> ignore, String message) {
if ((ignore != null)
&& (ignore.contains(cli.getArtifactId() + ':' + version) || ignore.contains(cli.getArtifactId()))) {
getLog().warn(message);
return false;
}
getLog().error(message);
try (PrintWriter output = new PrintWriter(new FileWriter(getFailuresFile(), true))) {
output.printf("%s%s", message, EOL);
} catch (Exception e) {
getLog().error("Cannot append to " + getFailuresFilename());
}
return true;
}
private File getFailuresFile() {
return new File(failuresDirectory, getFailuresFilename());
}
}