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

com.puresoltechnologies.maven.plugins.license.ValidatorMojo Maven / Gradle / Ivy

There is a newer version: 1.4.0
Show newest version
/*
 * Copyright 2013 PureSol Technologies
 * 
 * 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.puresoltechnologies.maven.plugins.license;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.License;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.Execute;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;

import com.puresoltechnologies.maven.plugins.license.internal.ArtifactUtilities;
import com.puresoltechnologies.maven.plugins.license.internal.DependencyTree;
import com.puresoltechnologies.maven.plugins.license.internal.IOUtilities;
import com.puresoltechnologies.maven.plugins.license.parameter.ArtifactInformation;
import com.puresoltechnologies.maven.plugins.license.parameter.KnownLicense;
import com.puresoltechnologies.maven.plugins.license.parameter.ValidationResult;

/**
 * This class is a Maven Mojo to check the validity of licenses specified in the
 * dependencies of a maven module.
 * 
 * Used to configure injection of Plexus components by
 * MavenPluginManager.getConfiguredMojo(...) and special Maven objects as well:
 * 
 * mojoExecution org.apache.maven.plugin.MojoExecution project
 * org.apache.maven.project.MavenProject session
 * org.apache.maven.execution.MavenSession settings
 * org.apache.maven.settings.Settings plugin (Maven-3 only)
 * org.apache.maven.plugin.descriptor.PluginDescriptor
 * 
 * @author Rick-Rainer Ludwig
 */
@Mojo(//
name = "validate", //
requiresDirectInvocation = false, //
requiresProject = true, //
requiresReports = false, //
requiresOnline = false, //
inheritByDefault = true, //
threadSafe = true,//
requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME,//
requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME//
)
@Execute(//
goal = "validate",//
phase = LifecyclePhase.VALIDATE//
)
public class ValidatorMojo extends AbstractValidationMojo {

	private static final String TEST_SCOPE_NAME = "test";

	@Parameter(alias = "knownLicenses", required = true)
	private Set knownLicenses;

	@Parameter(alias = "outputDirectory", required = false, defaultValue = "${project.build.directory}/licenses")
	private File outputDirectory;

	/**
	 * This parameter contains whether this Mojo shall fail fast (with first
	 * occurrence of an invalid license) or should do all the work first before
	 * failing. Default value is false.
	 */
	@Parameter(alias = "failFast", required = false, defaultValue = "false")
	private boolean failFast;

	/**
	 * This parameter contains whether or not the dependency should be checked
	 * recursively or not. Default is true.
	 */
	@Parameter(alias = "recursive", required = false, defaultValue = "true")
	private boolean recursive;

	/**
	 * Specifies whether or not the dependencies in test scope should be
	 * skipped. Default is false.
	 */
	@Parameter(alias = "skipTestScope", required = false, defaultValue = "true")
	private boolean skipTestScope;

	/**
	 * Specifies whether or not the dependencies in provided scope should be
	 * skipped. Default is false.
	 */
	@Parameter(alias = "skipProvidedScope", required = false, defaultValue = "true")
	private boolean skipProvidedScope;

	/**
	 * Specified whether or not to skip archetypes with optional flag.
	 */
	@Parameter(alias = "skipOptionals", required = false, defaultValue = "true")
	private boolean skipOptionals;

	/**
	 * This field contains the writer for the validation result file.
	 */
	private OutputStreamWriter writer;

	@Override
	public void execute() throws MojoExecutionException, MojoFailureException {
		storeSettings();
		DependencyTree dependencyTree = loadArtifacts(recursive, skipTestScope,
				skipProvidedScope, skipOptionals);
		validateArtifacts(dependencyTree);
	}

	private void storeSettings() throws MojoExecutionException {
		File file = IOUtilities
				.createNewSettingsFile(getLog(), outputDirectory);
		try (FileOutputStream fileOutputStream = new FileOutputStream(file);
				OutputStreamWriter propertiesWriter = new OutputStreamWriter(
						fileOutputStream, Charset.defaultCharset())) {
			Properties properties = new Properties();
			properties.setProperty("recursive", Boolean.toString(recursive));
			properties.setProperty("skipTestScope",
					Boolean.toString(skipTestScope));
			properties.setProperty("skipProvidedScope",
					Boolean.toString(skipProvidedScope));
			properties.setProperty("skipOptionals",
					Boolean.toString(skipOptionals));
			properties
					.store(propertiesWriter, "license-maven-plugin settings.");
		} catch (IOException e) {
			throw new MojoExecutionException(
					"Could not write settings.properties.", e);
		}
	}

	/**
	 * This method checks a set of artifacts for validity.
	 * 
	 * @param dependencies
	 *            is a {@link Set} of {@link Artifact} which is to be checked
	 *            for validity.
	 * @throws MojoExecutionException
	 *             is throw if the execution was faulty.
	 * @throws MojoFailureException
	 *             is thrown if an invalid license is found.
	 */
	private void validateArtifacts(DependencyTree dependencyTree)
			throws MojoExecutionException, MojoFailureException {
		File licenseResultsFile = IOUtilities.createNewResultsFile(getLog(),
				outputDirectory);
		try (FileOutputStream outputStream = new FileOutputStream(
				licenseResultsFile)) {
			writer = new OutputStreamWriter(outputStream,
					Charset.defaultCharset());
			try {
				boolean valid = true;
				List checkedArtifact = new ArrayList<>();
				for (DependencyTree dependency : dependencyTree) {
					Artifact artifact = dependency.getArtifact();
					if (artifact == getMavenProject().getArtifact()) {
						// skip self, it is not needed to be evaluated
						continue;
					}
					String artifactIdentifier = ArtifactUtilities
							.toString(artifact);
					if (checkedArtifact.contains(artifactIdentifier)) {
						continue;
					}
					checkedArtifact.add(artifactIdentifier);
					if (!isArtifactValid(dependency)) {
						if (failFast) {
							throw new MojoFailureException(
									"Invalid license(s) was/were found!");
						}
						valid = false;
					}
				}
				if (!valid) {
					throw new MojoFailureException(
							"Invalid license(s) was/were found!");
				}
			} finally {
				try {
					writer.close();
				} finally {
					writer = null;
				}
			}
		} catch (IOException e) {
			throw new MojoExecutionException(
					"Could not write validation result to '"
							+ licenseResultsFile + "'.", e);
		}
	}

	/**
	 * This method checks the validity of a single artifact.
	 * 
	 * @param artifact
	 *            is the {@link Artifact} to be checked for validity.
	 * @return true is returned if the license is valid.
	 *         false is returned otherwise.
	 * @throws MojoFailureException
	 *             is thrown if {@link #failFast} is set to true
	 *             and the license is invalid to enforce the fail fast behavior
	 *             requested.
	 * @throws MojoExecutionException
	 *             is thrown in case of a faulty Maven run.
	 */
	private boolean isArtifactValid(DependencyTree dependency)
			throws MojoFailureException, MojoExecutionException {
		Artifact artifact = dependency.getArtifact();
		ArtifactInformation artifactInformation = new ArtifactInformation(
				artifact);

		if (skipTestScope) {
			if (TEST_SCOPE_NAME.equals(artifact.getScope())) {
				ValidationResult result = new ValidationResult(
						artifactInformation, null, null, null, "test scope",
						true);
				logArtifactResult(result);
				return true;
			}
		}

		List licenses = dependency.getLicenses();
		if (licenses.size() == 0) {
			KnownLicense knownLicense = findKnownLicense(artifactInformation);
			if (knownLicense != null) {
				ValidationResult result = new ValidationResult(
						artifactInformation, knownLicense, null, null,
						"no license found, but dependency is approved", true);
				logArtifactResult(result);
				return true;
			} else {
				ValidationResult result = new ValidationResult(
						artifactInformation, null, null, null,
						"no license found and artifact is not approved", false);
				logArtifactResult(result);
				return false;
			}
		}
		boolean valid = true;
		for (License license : licenses) {
			String licenseName = license.getName();
			URL licenseURL;
			try {
				licenseURL = new URL(license.getUrl());
			} catch (MalformedURLException e) {
				licenseURL = null;
			}
			KnownLicense knownLicense = findKnownLicense(artifactInformation);
			if (knownLicense != null) {
				ValidationResult result = new ValidationResult(
						artifactInformation, knownLicense, licenseName,
						licenseURL, "license is approved by artifact", true);
				logArtifactResult(result);
			} else {
				knownLicense = findKnownLicense(artifactInformation, license);
				if (knownLicense != null) {
					ValidationResult result = new ValidationResult(
							artifactInformation, knownLicense, licenseName,
							licenseURL, "license is approved", true);
					logArtifactResult(result);
				} else {
					ValidationResult result = new ValidationResult(
							artifactInformation, null, licenseName, licenseURL,
							"license is not approved", false);
					logArtifactResult(result);
					valid = false;
				}
			}
		}
		return valid;
	}

	/**
	 * This method returns the normalized name of the license.
	 * 
	 * @param artifactInformation
	 * 
	 * @param license
	 *            is the {@link License} object which is to be looked up.
	 * @return A {@link KnownLicense} is returned containing the known license.
	 * @throws MojoFailureException
	 *             is thrown if the normalized name cannot be looked up due to
	 *             missing configuration.
	 */
	private KnownLicense findKnownLicense(
			ArtifactInformation artifactInformation, License license)
			throws MojoFailureException {
		for (KnownLicense knownLicense : knownLicenses) {
			if (knownLicense.getName().equals(license.getName())) {
				return knownLicense;
			}
			for (String alias : knownLicense.getAliases()) {
				if ((alias == null) || (alias.isEmpty())) {
					throw new MojoFailureException(
							"An alias was found without identifier.");
				}
				if (alias.equals(license.getName())) {
					return knownLicense;
				}
			}
		}
		return null;
	}

	private KnownLicense findKnownLicense(
			ArtifactInformation artifactInformation)
			throws MojoFailureException {
		for (KnownLicense knownLicense : knownLicenses) {
			for (String approvedDependency : knownLicense
					.getApprovedDependencies()) {
				if ((approvedDependency == null)
						|| (approvedDependency.isEmpty())) {
					throw new MojoFailureException(
							"An approved dependency was found without identifier.");
				}
				if (Pattern.matches(approvedDependency,
						artifactInformation.getIdentifier())) {
					return knownLicense;
				}
			}
		}
		return null;
	}

	/**
	 * This method is used to log results with Maven log {@link Log}.
	 * 
	 * @param artifact
	 *            is the {@link Artifact} which was checked for validity.
	 * @param validationResult
	 *            is the {@link ValidationResult} of the check.
	 * @param licenseOrApprovalMessage
	 *            is the message to be printed containing the license or the
	 *            appoval message.
	 * @param knownLicense
	 * @throws MojoExecutionException
	 * @throws IOException
	 * @throws MojoFailureException
	 */
	private void logArtifactResult(ValidationResult validationResult)
			throws MojoExecutionException, MojoFailureException {
		StringBuffer buffer = new StringBuffer();
		buffer.append("License ");
		String originalLicenseName = validationResult.getOriginalLicenseName();
		if (originalLicenseName != null) {
			buffer.append("'");
			buffer.append(originalLicenseName);
			buffer.append("' ");
			URL originalLicenseURL = validationResult.getOriginalLicenseURL();
			if (originalLicenseURL != null) {
				buffer.append("(");
				buffer.append(originalLicenseURL.toString());
				buffer.append(") ");
			}
		}
		buffer.append("checked for artifact '");
		ArtifactInformation artifactInformation = validationResult
				.getArtifactInformation();
		buffer.append(artifactInformation.getIdentifier());
		buffer.append("': \n     >> ");
		if (validationResult.isValid()) {
			buffer.append("valid as '");
			KnownLicense license = validationResult.getLicense();
			buffer.append(license.getName());
			buffer.append("' (");
			buffer.append(validationResult.getComment());
			buffer.append(")");
			getLog().info(buffer.toString());
		} else {
			buffer.append("invalid (");
			buffer.append(validationResult.getComment());
			buffer.append(")");
			getLog().error(buffer.toString());
		}
		IOUtilities.writeResult(writer, validationResult);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy