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

org.scijava.maven.plugin.enforcer.BanDuplicateClasses Maven / Gradle / Ivy

There is a newer version: 3.0.1
Show newest version
/*
 * #%L
 * A plugin for managing SciJava-based projects.
 * %%
 * Copyright (C) 2014 - 2015 Board of Regents of the University of
 * Wisconsin-Madison.
 * %%
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * #L%
 */

package org.scijava.maven.plugin.enforcer;

/*
 * 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.
 */

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Pattern;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
import org.codehaus.plexus.util.FileUtils;

/**
 * Bans duplicate classes on the classpath.
 */
public class BanDuplicateClasses extends AbstractResolveDependencies {

	/**
	 * The failure message
	 */
	private String message;

	/**
	 * List of classes to ignore. Wildcard at the end accepted
	 */
	private String[] ignoreClasses;

	/**
	 * If {@code false} then the rule will fail at the first duplicate, if
	 * {@code true} then the rule will fail at the end.
	 */
	private boolean findAllDuplicates;

	private List dependencies;

	/**
	 * Only verify dependencies with one of these scopes
	 */
	private List scopes;

	@Override
	protected void handleArtifacts(final Set artifacts)
		throws EnforcerRuleException
	{
		final List ignorableDependencies =
			new ArrayList();
		if (ignoreClasses != null) {
			final IgnorableDependency ignorableDependency = new IgnorableDependency();
			ignorableDependency.applyIgnoreClasses(ignoreClasses, false);
			ignorableDependencies.add(ignorableDependency);
		}
		if (dependencies != null) {
			for (final Dependency dependency : dependencies) {
				getLog().info("Adding ignorable dependency: " + dependency);
				final IgnorableDependency ignorableDependency =
					new IgnorableDependency();
				if (dependency.getGroupId() != null) {
					ignorableDependency.groupId =
						Pattern.compile(asRegex(dependency.getGroupId()));
				}
				if (dependency.getArtifactId() != null) {
					ignorableDependency.artifactId =
						Pattern.compile(asRegex(dependency.getArtifactId()));
				}
				if (dependency.getType() != null) {
					ignorableDependency.type =
						Pattern.compile(asRegex(dependency.getType()));
				}
				if (dependency.getClassifier() != null) {
					ignorableDependency.classifier =
						Pattern.compile(asRegex(dependency.getClassifier()));
				}
				ignorableDependency.applyIgnoreClasses(dependency.getIgnoreClasses(),
					true);
				ignorableDependencies.add(ignorableDependency);
			}
		}

		final Map classNames = new HashMap();
		final Map> duplicates =
			new HashMap>();
		for (final Artifact o : artifacts) {
			if (scopes != null && !scopes.contains(o.getScope())) {
				if (getLog().isDebugEnabled()) {
					getLog().debug("Skipping " + o.toString() + " due to scope");
				}
				continue;
			}
			final File file = o.getFile();
			getLog().debug("Searching for duplicate classes in " + file);
			if (file == null || !file.exists()) {
				getLog().warn("Could not find " + o + " at " + file);
			}
			else if (file.isDirectory()) {
				try {
					@SuppressWarnings("unchecked")
					final List fileNames =
						FileUtils.getFileNames(file, null, null, false);
					for (final String name : fileNames) {
						getLog().debug("  " + name);
						checkAndAddName(o, name, classNames, duplicates,
							ignorableDependencies);
					}
				}
				catch (final IOException e) {
					throw new EnforcerRuleException("Unable to process dependency " +
						o.toString() + " due to " + e.getLocalizedMessage(), e);
				}
			}
			else if (file.isFile() && "jar".equals(o.getType())) {
				try {
					// @todo use UnArchiver as defined per type
					final JarFile jar = new JarFile(file);
					try {
						for (final JarEntry entry : Collections. list(jar
							.entries()))
						{
							checkAndAddName(o, entry.getName(), classNames, duplicates,
								ignorableDependencies);
						}
					}
					finally {
						try {
							jar.close();
						}
						catch (final IOException e) {
							// ignore
						}
					}
				}
				catch (final IOException e) {
					throw new EnforcerRuleException("Unable to process dependency " +
						o.toString() + " due to " + e.getLocalizedMessage(), e);
				}
			}
		}
		if (!duplicates.isEmpty()) {
			final Map, List> inverted =
				new HashMap, List>();
			for (final Map.Entry> entry : duplicates.entrySet())
			{
				List s = inverted.get(entry.getValue());
				if (s == null) {
					s = new ArrayList();
				}
				s.add(entry.getKey());
				inverted.put(entry.getValue(), s);
			}
			final StringBuilder buf =
				new StringBuilder(message == null ? "Duplicate classes found:"
					: message);
			buf.append('\n');
			for (final Map.Entry, List> entry : inverted
				.entrySet())
			{
				buf.append("\n  Found in:");
				for (final Artifact a : entry.getKey()) {
					buf.append("\n    ");
					buf.append(a);
				}
				buf.append("\n  Duplicate classes:");
				for (final String className : entry.getValue()) {
					buf.append("\n    ");
					buf.append(className);
				}
				buf.append('\n');
			}
			throw new EnforcerRuleException(buf.toString());
		}

	}

	private void checkAndAddName(final Artifact artifact, final String name,
		final Map classNames,
		final Map> duplicates,
		final Collection ignores) throws EnforcerRuleException
	{
		if (!name.endsWith(".class")) {
			return;
		}

		for (final IgnorableDependency c : ignores) {
			if (c.matchesArtifact(artifact) && c.matches(name)) {
				if (classNames.containsKey(name)) {
					getLog().debug("Ignoring excluded class " + name);
				}
				return;
			}
		}

		if (classNames.containsKey(name)) {
			final Artifact dup = classNames.put(name, artifact);
			if (!(findAllDuplicates && duplicates.containsKey(name))) {
				for (final IgnorableDependency c : ignores) {
					if (c.matchesArtifact(artifact) && c.matches(name)) {
						getLog().debug("Ignoring duplicate class " + name);
						return;
					}
				}
			}

			if (findAllDuplicates) {
				Set dups = duplicates.get(name);
				if (dups == null) {
					dups = new LinkedHashSet();
					dups.add(dup);
				}
				dups.add(artifact);
				duplicates.put(name, dups);
			}
			else {
				final StringBuilder buf =
					new StringBuilder(message == null ? "Duplicate class found:"
						: message);
				buf.append('\n');
				buf.append("\n  Found in:");
				buf.append("\n    ");
				buf.append(dup);
				buf.append("\n    ");
				buf.append(artifact);
				buf.append("\n  Duplicate classes:");
				buf.append("\n    ");
				buf.append(name);
				buf.append('\n');
				buf
					.append("There may be others but  was set to false, so failing fast");
				throw new EnforcerRuleException(buf.toString());
			}
		}
		else {
			classNames.put(name, artifact);
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy