com.serli.maven.plugin.quality.mojo.LicenseMojo.java Source code

Java tutorial

Introduction

Here is the source code for com.serli.maven.plugin.quality.mojo.LicenseMojo.java

Source

package com.serli.maven.plugin.quality.mojo;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.apache.commons.validator.UrlValidator;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactCollector;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
import org.apache.maven.model.License;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectBuilder;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.maven.shared.dependency.tree.DependencyNode;
import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;
import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;

import com.google.code.mojo.license.AbstractLicenseMojo;
import com.google.code.mojo.license.Callback;
import com.google.code.mojo.license.document.Document;
import com.google.code.mojo.license.header.Header;
import com.serli.maven.plugin.quality.model.LightLicense;
import com.serli.maven.plugin.quality.model.MavenProjectComparable;
import com.serli.maven.plugin.quality.util.Util;

/**
 * Goal which checks licenses and headers.
 * 
 * @goal license
 * @requiresDependencyResolution test
 * @phase verify
 */
public class LicenseMojo extends AbstractLicenseMojo {

    /**
     * Whether to fail the build if some file miss license header
     * 
     * @parameter expression="${license.failIfMissing}" default-value="false"
     */
    protected boolean failIfMissing;

    /**
     * Output file.
     * 
     * @parameter expression="${outputFile}"
     */
    private File outputFile;

    /**
     * The Maven project to analyze.
     * 
     * @parameter expression="${project}"
     * @required
     * @readonly
     */
    private MavenProject project;

    /**
     * Dependency tree builder component.
     * 
     * @since 2.1
     * @component
     */
    private DependencyTreeBuilder dependencyTreeBuilder;

    /**
     * Local Repository.
     * 
     * @parameter expression="${localRepository}"
     * @required
     * @readonly
     */
    protected ArtifactRepository localRepository;

    /**
     * Artifact metadata source component.
     * 
     * @component
     */
    protected ArtifactMetadataSource artifactMetadataSource;

    /**
     * Artifact collector component.
     * 
     * @component
     */
    private ArtifactCollector collector;

    /**
     * Artifact Factory component.
     * 
     * @component
     */
    protected ArtifactFactory factory;

    /**
     * Maven Project Builder component.
     * 
     * @component
     */
    private MavenProjectBuilder mavenProjectBuilder;

    /**
     * Remote repositories used for the project.
     * 
     * @since 2.1
     * @parameter expression="${project.remoteArtifactRepositories}"
     * @required
     * @readonly
     */
    private List remoteRepositories;

    private Map<LightLicense, SortedSet<MavenProjectComparable>> resultLicenseMap = new HashMap<LightLicense, SortedSet<MavenProjectComparable>>();

    private SortedSet<MavenProjectComparable> setArtifactNoLicense = new TreeSet<MavenProjectComparable>();

    public final Collection<File> missingHeaders = new ConcurrentLinkedQueue<File>();

    /**
     * Will be filled with license name / set of projects.
     */
    private Map licenseMap = new HashMap() {
        /** {@inheritDoc} */
        public Object put(Object key, Object value) {
            // handle multiple values as a set to avoid duplicates
            SortedSet valueList = (SortedSet) get(key);
            if (valueList == null) {
                valueList = new TreeSet();
            }
            valueList.add(value);
            return super.put(key, valueList);
        }
    };

    private List<LightLicense> projectLicenses = null;

    /** Random used to generate a UID */
    private static SecureRandom RANDOM;

    private List allDependencies = new ArrayList();

    public void execute() throws MojoExecutionException, MojoFailureException {
        try {
            RANDOM = SecureRandom.getInstance("SHA1PRNG");
        } catch (Exception e) {
            e.printStackTrace();
        }

        // TODO s'inspirer de
        // http://maven-license-plugin.googlecode.com/svn/branches/maven-license-plugin-1.x.x/src/main/java/com/google/code/mojo/license/LicenseCheckMojo.java
        // TODO extends de AbstractLicenseMojo, mais ecrire dans un fichier les
        // 'missingHeader'
        // TODO choper le fichier nomm par l'URL

        // TODO voir aussi le project-info pour avoir les licenses des dependances
        // (determiner la compatibilite des licenses ?)
        // TODO voir comment ils gerent avec le DependenciesRenderer :
        // http://svn.apache.org/viewvc/maven/plugins/tags/maven-project-info-reports-plugin-2.2/src/main/java/org/apache/maven/report/projectinfo/dependencies/renderer/DependenciesRenderer.java?view=markup
        projectLicenses = new ArrayList<LightLicense>();
        List licenses = project.getModel().getLicenses();
        for (Iterator i = licenses.iterator(); i.hasNext();) {
            License license = (License) i.next();
            LightLicense projectLicense = new LightLicense();
            projectLicense.setName(license.getName());
            projectLicense.setUrl(license.getUrl());
            header = projectLicense.getUrl();
            projectLicenses.add(projectLicense);

            // TODO que faire si plusieurs licenses pour le projet

            // URL licenseUrl = null;
            // try {
            // licenseUrl = getLicenseURL(project, license.getUrl());
            // System.out.println("licenseURL = " + licenseUrl);
            // } catch (MalformedURLException e) {
            // getLog().error(e.getMessage());
            // } catch (IOException e) {
            // getLog().error(e.getMessage());
            // }
            //
            // if (licenseUrl != null && licenseUrl.getProtocol().equals("file")) {
            // }

        }

        checkHeaders();

        DependencyNode dependencyTreeNode = resolveProject();
        getAllDependencies(dependencyTreeNode);

        stockAllArtifactAndItsLicense(dependencyTreeNode);
        // TODO enlever le projet des autres listes et maps
        // printGroupedLicenses();
        // printLicenses();
        StringBuffer buffer = printResults();
        try {
            Util.writeFile(buffer.toString(), outputFile, getLog());
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private StringBuffer printResults() {
        StringWriter out = new StringWriter();
        PrettyPrintXMLWriter writer = new PrettyPrintXMLWriter(out);
        writer.startElement("licenses");
        writer = printProjectLicense(writer);
        writer = printMissingHeader(writer);
        writer = printDependenciesLicense(writer);
        writer.endElement();
        return out.getBuffer();
    }

    private PrettyPrintXMLWriter printMissingHeader(PrettyPrintXMLWriter writer) {
        writer.startElement("missingHeaders");
        for (File file : missingHeaders) {
            writer.startElement("file");
            writer.writeText(file.getName());
            writer.endElement();
        }
        writer.endElement();
        return writer;
    }

    private PrettyPrintXMLWriter printDependenciesLicense(PrettyPrintXMLWriter writer) {
        writer.startElement("dependenciesLicense");
        if (resultLicenseMap != null && resultLicenseMap.size() > 0) {
            Set<LightLicense> keys = resultLicenseMap.keySet();
            for (LightLicense license : keys) {
                writer.startElement("license");
                String url = license.getUrl();
                String name = license.getName();
                if (StringUtils.isEmpty(name)) {
                    name = "Unnamed";
                }
                if (StringUtils.isEmpty(url)) {
                    url = "unavailable";
                }
                writer.startElement("name");
                writer.writeText(name);
                writer.endElement();
                writer.startElement("url");
                writer.writeText(url);
                writer.endElement();
                SortedSet<MavenProjectComparable> sortedSet = resultLicenseMap.get(license);
                for (MavenProject artifact : sortedSet) {
                    writer.startElement("dependency");
                    String id = artifact.getModel().getName();
                    if (StringUtils.isEmpty(id)) {
                        id = artifact.getArtifactId();
                    }
                    writer.startElement("name");
                    writer.writeText(id);
                    writer.endElement();
                    writer.endElement();
                }
                writer.endElement();
            }
        }

        writer.startElement("unknownLicense");
        for (MavenProject project : setArtifactNoLicense) {
            writer.startElement("dependency");
            String id = project.getModel().getName();
            if (StringUtils.isEmpty(id)) {
                id = project.getArtifactId();
            }
            writer.startElement("name");
            writer.writeText(id);
            writer.endElement();
            writer.endElement();
        }
        writer.endElement();
        writer.endElement();
        return writer;
    }

    private PrettyPrintXMLWriter printProjectLicense(PrettyPrintXMLWriter writer) {
        writer.startElement("projectLicense");
        if (projectLicenses != null && projectLicenses.size() > 0) {
            for (LightLicense license : projectLicenses) {
                writer.startElement("license");
                writer.startElement("name");
                String name = license.getName();
                if (StringUtils.isEmpty(name)) {
                    name = "Unnamed";
                }
                writer.writeText(name);
                writer.endElement();
                String url = license.getUrl();
                if (StringUtils.isNotEmpty(url)) {
                    writer.startElement("url");
                    writer.writeText(url);
                    writer.endElement();
                }
                writer.endElement();
            }
        }
        writer.endElement();
        return writer;
    }

    /**
     * @param project
     *          not null
     * @param url
     *          not null
     * @return a valid URL object from the url string
     * @throws IOException
     *           if any
     */
    protected static URL getLicenseURL(MavenProject project, String url) throws IOException {
        URL licenseUrl = null;
        UrlValidator urlValidator = new UrlValidator(UrlValidator.ALLOW_ALL_SCHEMES);
        // UrlValidator does not accept file URLs because the file
        // URLs do not contain a valid authority (no hostname).
        // As a workaround accept license URLs that start with the
        // file scheme.
        if (urlValidator.isValid(url) || StringUtils.defaultString(url).startsWith("file://")) {
            try {
                licenseUrl = new URL(url);
            } catch (MalformedURLException e) {
                throw new MalformedURLException(
                        "The license url '" + url + "' seems to be invalid: " + e.getMessage());
            }
        } else {
            File licenseFile = new File(project.getBasedir(), url);
            if (!licenseFile.exists()) {
                // Workaround to allow absolute path names while
                // staying compatible with the way it was...
                licenseFile = new File(url);
            }
            if (!licenseFile.exists()) {
                throw new IOException("Maven can't find the file '" + licenseFile + "' on the system.");
            }
            try {
                licenseUrl = licenseFile.toURI().toURL();
            } catch (MalformedURLException e) {
                throw new MalformedURLException(
                        "The license url '" + url + "' seems to be invalid: " + e.getMessage());
            }
        }

        return licenseUrl;
    }

    /**
     * @return resolve the dependency tree
     */
    private DependencyNode resolveProject() {
        try {
            ArtifactFilter artifactFilter = new ScopeArtifactFilter(Artifact.SCOPE_TEST);
            return dependencyTreeBuilder.buildDependencyTree(project, localRepository, factory,
                    artifactMetadataSource, artifactFilter, collector);
        } catch (DependencyTreeBuilderException e) {
            getLog().error("Unable to build dependency tree.", e);
            return null;
        }
    }

    private void stockAllArtifactAndItsLicense(DependencyNode node) {
        Artifact artifact = node.getArtifact();
        String id = artifact.getId();
        String dependencyDetailId = getUUID();
        String imgId = getUUID();

        stockArtifactAndItsLicense(node, dependencyDetailId);

        if (!node.getChildren().isEmpty()) {
            boolean toBeIncluded = false;
            List subList = new ArrayList();
            for (Iterator deps = node.getChildren().iterator(); deps.hasNext();) {
                DependencyNode dep = (DependencyNode) deps.next();

                if (!allDependencies.contains(dep.getArtifact())) {
                    continue;
                }

                subList.add(dep);
                toBeIncluded = true;
            }

            if (toBeIncluded) {
                for (Iterator deps = subList.iterator(); deps.hasNext();) {
                    DependencyNode dep = (DependencyNode) deps.next();

                    stockAllArtifactAndItsLicense(dep);
                }
            }
        }

    }

    private void stockArtifactAndItsLicense(DependencyNode node, String uid) {
        Artifact artifact = node.getArtifact();
        String id = artifact.getId();
        // System.out.println("id = " + id);
        if (!id.equals(project.getId())) {

            if (!Artifact.SCOPE_SYSTEM.equals(artifact.getScope())) {
                try {
                    MavenProjectComparable artifactProject = new MavenProjectComparable(
                            getMavenProjectFromRepository(artifact));
                    String artifactDescription = artifactProject.getDescription();
                    String artifactUrl = artifactProject.getUrl();
                    List licenses = artifactProject.getLicenses();

                    if (StringUtils.isNotEmpty(artifactDescription)) {
                        // System.out.println("artifactDescription : " +
                        // artifactDescription);
                    }

                    if (StringUtils.isNotEmpty(artifactUrl)) {
                        if (Util.isArtifactUrlValid(artifactUrl)) {
                            // System.out.println("artifactURL : " + artifactUrl);
                        } else {
                            // System.out.println("artifactURL : " + artifactUrl);
                        }
                    }

                    if (!licenses.isEmpty()) {
                        for (Iterator iter = licenses.iterator(); iter.hasNext();) {
                            License element = (License) iter.next();
                            String licenseName = element.getName();
                            String licenseUrl = element.getUrl();
                            LightLicense license = new LightLicense();
                            if (StringUtils.isEmpty(licenseName)) {
                                licenseName = "Unnamed";
                            }
                            license.setName(licenseName);
                            license.setUrl(licenseUrl);
                            // System.out.println(artifactProject.getId() + " - ");
                            // System.out.println("licenseName : " + licenseName +
                            // "   - licenseUrl : " + licenseUrl);
                            put(license, artifactProject);
                        }
                    } else {
                        setArtifactNoLicense.add(artifactProject);
                    }
                } catch (ProjectBuildingException e) {
                    getLog().error("ProjectBuildingException error : ", e);
                }
            }
        }

    }

    private void printLicenses() {
        // TODO afficher au format XML et dans un fichier ou non
        Set<LightLicense> keys = resultLicenseMap.keySet();
        for (LightLicense license : keys) {
            String url = license.getUrl();
            String name = license.getName();
            if (StringUtils.isEmpty(name)) {
                name = "Unnamed";
            }
            System.out.println("Licence " + name + " - " + url);
            SortedSet<MavenProjectComparable> sortedSet = resultLicenseMap.get(license);
            for (MavenProject artifact : sortedSet) {
                System.out.println("\t " + artifact.getId());
            }

        }

        System.out.println("Inconnu : ");
        for (MavenProject project : setArtifactNoLicense) {
            System.out.println("\t " + project.getId());
        }

    }

    private void printGroupedLicenses() {
        for (Iterator iter = licenseMap.keySet().iterator(); iter.hasNext();) {
            String licenseName = (String) iter.next();
            if (StringUtils.isEmpty(licenseName)) {
                System.out.println("unnamed");
            } else {
                System.out.println(licenseName + " : ");
            }

            SortedSet projects = (SortedSet) licenseMap.get(licenseName);

            StringBuffer buffer = new StringBuffer();
            for (Iterator iterator = projects.iterator(); iterator.hasNext();) {
                String projectName = (String) iterator.next();
                buffer.append(projectName);
                if (iterator.hasNext()) {
                    buffer.append(", ");
                }
            }
            System.out.println(buffer.toString());

        }
    }

    /**
     * @return a valid HTML ID respecting <a
     *         href="http://www.w3.org/TR/xhtml1/#C_8">XHTML 1.0 section C.8.
     *         Fragment Identifiers</a>
     */
    private static String getUUID() {
        return "_" + Math.abs(RANDOM.nextInt());
    }

    /**
     * Get the <code>Maven project</code> from the repository depending the
     * <code>Artifact</code> given.
     * 
     * @param artifact
     *          an artifact
     * @return the Maven project for the given artifact
     * @throws ProjectBuildingException
     *           if any
     */
    public MavenProject getMavenProjectFromRepository(Artifact artifact) throws ProjectBuildingException {
        Artifact projectArtifact = artifact;

        boolean allowStubModel = false;
        if (!"pom".equals(artifact.getType())) {
            projectArtifact = factory.createProjectArtifact(artifact.getGroupId(), artifact.getArtifactId(),
                    artifact.getVersion(), artifact.getScope());
            allowStubModel = true;
        }

        // TODO: we should use the MavenMetadataSource instead
        return mavenProjectBuilder.buildFromRepository(projectArtifact, remoteRepositories, localRepository,
                allowStubModel);
    }

    /**
     * @return a list of included <code>Artifact</code> returned by the dependency
     *         tree.
     */
    public List getAllDependencies(DependencyNode dependencyTreeNode) {
        // if (allDependencies != null) {
        // return allDependencies;
        // }

        for (Iterator i = dependencyTreeNode.getChildren().iterator(); i.hasNext();) {
            DependencyNode dependencyNode = (DependencyNode) i.next();
            // System.out.println("\t " + dependencyNode.getArtifact().getId());
            if (dependencyNode.getState() != DependencyNode.INCLUDED) {
                continue;
            }

            if (dependencyNode.getArtifact().getGroupId().equals(project.getGroupId())
                    && dependencyNode.getArtifact().getArtifactId().equals(project.getArtifactId())
                    && dependencyNode.getArtifact().getVersion().equals(project.getVersion())) {
                continue;
            }

            if (!allDependencies.contains(dependencyNode.getArtifact())) {
                allDependencies.add(dependencyNode.getArtifact());
            }
            getAllDependencies(dependencyNode);
        }

        return allDependencies;
    }

    private void put(LightLicense key, MavenProjectComparable projectArtifact) {
        // handle multiple values as a set to avoid duplicates
        SortedSet<MavenProjectComparable> valueList = null;
        Set<LightLicense> keySet = resultLicenseMap.keySet();
        String url = key.getUrl();
        for (LightLicense license : keySet) {
            String urlLicense = license.getUrl();
            String nameLicense = license.getName();
            if (license.equals(key)) {
                valueList = resultLicenseMap.remove(license);
                if (Util.isArtifactUrlValid(urlLicense)) {
                    key.setUrl(urlLicense);
                }
                if (StringUtils.isNotEmpty(nameLicense)) {
                    key.setName(nameLicense);
                }
                break;
            }
        }

        if (valueList == null) {
            valueList = new TreeSet<MavenProjectComparable>();
        }
        valueList.add(projectArtifact);
        resultLicenseMap.put(key, valueList);
    }

    private void checkHeaders() throws MojoExecutionException, MojoFailureException {
        super.basedir = project.getBasedir();
        getLog().info("Checking headers...");
        missingHeaders.clear();
        super.project = project;
        execute(new Callback() {
            public void onHeaderNotFound(Document document, Header header) {
                File file = document.getFile();
                info("Missing header in: %s", file.getAbsolutePath());
                missingHeaders.add(file);
            }

            public void onExistingHeader(Document document, Header header) {
                debug("Header OK in: %s", document.getFile());
            }
        });

        if (!missingHeaders.isEmpty()) {
            if (failIfMissing) {
                throw new MojoExecutionException("Some files do not have the expected license header");
            } else {
                getLog().warn("Some files do not have the expected license header");
            }
        }

    }

}