org.jasig.maven.notice.LicenseLookupHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.jasig.maven.notice.LicenseLookupHelper.java

Source

/**
 * Licensed to Jasig under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Jasig 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.jasig.maven.notice;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.regex.Pattern;

import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import org.apache.commons.collections.map.LRUMap;
import org.apache.commons.io.IOUtils;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.jasig.maven.notice.lookup.ArtifactLicense;
import org.jasig.maven.notice.lookup.LicenseLookup;
import org.jasig.maven.notice.lookup.MappedVersion;
import org.jasig.maven.notice.lookup.VersionType;
import org.jasig.maven.notice.util.ResourceFinder;

/**
 * Utility to load license-lookup XML files and search through the loaded results for matches on a specified
 * artifact.
 * 
 * @author Eric Dalquist
 * @version $Revision$
 */
public class LicenseLookupHelper {
    /**
     * Cache parse results for the 20 most recently used LicenseLookup files
     */
    @SuppressWarnings("unchecked")
    private static final Map<String, LicenseLookup> LICENSE_LOOKUP_CACHE = new LRUMap(20);
    private static final ReadWriteLock LICENSE_LOOKUP_CACHE_LOCK = new ReentrantReadWriteLock();

    private final Map<String, Map<List<MappedVersion>, ArtifactLicense>> mergedLicenseLookup = new LinkedHashMap<String, Map<List<MappedVersion>, ArtifactLicense>>();
    private final Log logger;
    private final ResourceFinder resourceFinder;

    public LicenseLookupHelper(Log logger, ResourceFinder resourceFinder, String[] licenseLookupFiles)
            throws MojoFailureException {
        this.logger = logger;
        this.resourceFinder = resourceFinder;

        final Unmarshaller unmarshaller = LicenseLookupContext.getUnmarshaller();

        if (licenseLookupFiles == null) {
            licenseLookupFiles = new String[0];
        }

        for (final String licenseLookupFile : licenseLookupFiles) {
            final LicenseLookup licenseLookup = this.loadLicenseLookup(unmarshaller, licenseLookupFile);

            for (final ArtifactLicense artifactLicense : licenseLookup.getArtifact()) {
                final String groupId = artifactLicense.getGroupId();
                final String artifactId = artifactLicense.getArtifactId();
                final String artifactKey = getArtifactKey(groupId, artifactId);

                Map<List<MappedVersion>, ArtifactLicense> artifactVersions = this.mergedLicenseLookup
                        .get(artifactKey);
                if (artifactVersions == null) {
                    artifactVersions = new LinkedHashMap<List<MappedVersion>, ArtifactLicense>();
                    this.mergedLicenseLookup.put(artifactKey, artifactVersions);
                }

                final List<MappedVersion> version = artifactLicense.getVersion();
                artifactVersions.put(version, artifactLicense);
                this.logger.debug("Mapped " + artifactLicense + " from: " + licenseLookupFile);
            }
        }
    }

    public ResolvedLicense lookupLicenseMapping(String groupId, String artifactId,
            ArtifactVersion artifactVersion) {
        //Find license info mapped to the group/artifact
        final String artifactKey = getArtifactKey(groupId, artifactId);
        final Map<List<MappedVersion>, ArtifactLicense> artifactVersions = this.mergedLicenseLookup
                .get(artifactKey);
        if (artifactVersions == null) {
            return null;
        }

        VersionType matchType = null;
        ArtifactLicense artifactLicense = null;

        //Search the mapped versions lists for a matching version
        for (final Entry<List<MappedVersion>, ArtifactLicense> versionEntry : artifactVersions.entrySet()) {
            final List<MappedVersion> versions = versionEntry.getKey();

            //If no versions are specified for an artifact all versions match 
            if (matchType == null && versions.size() == 0) {
                artifactLicense = versionEntry.getValue();
            } else {
                for (final MappedVersion version : versions) {
                    //Use the first REGEX match found, skip checking any subsequent REGEX versions
                    if (VersionType.REGEX == matchType && VersionType.REGEX == version.getType()) {
                        continue;
                    }

                    final boolean matches = this.compareVersions(version, artifactVersion);
                    if (matches) {
                        matchType = version.getType();
                        artifactLicense = versionEntry.getValue();

                        //If a STRING match is found break the loop. First exact match is always used
                        if (VersionType.STRING == matchType) {
                            break;
                        }
                    }
                }
            }

            //If a STRING match is found break the loop. First exact match is always used
            if (VersionType.STRING == matchType) {
                break;
            }
        }

        this.logger.debug("Found " + artifactLicense + " with match " + matchType + " for: " + groupId + ":"
                + artifactId + ":" + artifactVersion);
        return new ResolvedLicense(matchType, artifactLicense);
    }

    protected boolean compareVersions(final MappedVersion version, ArtifactVersion artifactVersion) {
        switch (version.getType()) {
        case REGEX: {
            final Pattern versionPattern = Pattern.compile(version.getValue());
            return versionPattern.matcher(artifactVersion.toString()).matches();
        }
        default: {
            final ArtifactVersion mappedVersion = new DefaultArtifactVersion(version.getValue());
            return mappedVersion.equals(artifactVersion);
        }
        }
    }

    protected String getArtifactKey(String groupId, String artifactId) {
        return groupId + ":" + artifactId;
    }

    protected LicenseLookup loadLicenseLookup(Unmarshaller unmarshaller, String licenseLookupFile)
            throws MojoFailureException {
        final URL licenseLookupUrl = resourceFinder.findResource(licenseLookupFile);

        //Try loading the LicenseLookup from cache
        final Lock readLock = LICENSE_LOOKUP_CACHE_LOCK.readLock();
        LicenseLookup licenseLookup = this.loadLicenseLookup(unmarshaller, licenseLookupFile, licenseLookupUrl,
                readLock, false);
        if (licenseLookup != null) {
            return licenseLookup;
        }

        //Must not have been in the cache, grab the write lock and check again
        final Lock writeLock = LICENSE_LOOKUP_CACHE_LOCK.writeLock();
        return loadLicenseLookup(unmarshaller, licenseLookupFile, licenseLookupUrl, writeLock, true);
    }

    protected LicenseLookup loadLicenseLookup(Unmarshaller unmarshaller, String licenseLookupFile,
            URL licenseLookupUrl, Lock lock, boolean create) throws MojoFailureException {
        final String licenseLookupKey = licenseLookupUrl.toString();

        lock.lock();
        try {
            //Look in the cache to see if the lookup file has already been parsed
            LicenseLookup licenseLookup = LICENSE_LOOKUP_CACHE.get(licenseLookupKey);
            if (licenseLookup != null) {
                logger.info("Loading license lookup mappings from '" + licenseLookupUrl + "' (cached)");
                return licenseLookup;
            }

            //Cache miss, check if we should parse the file, return null if not
            if (!create) {
                return null;
            }

            logger.info("Loading license lookup mappings from '" + licenseLookupUrl + "'");
            InputStream lookupStream = null;
            try {
                lookupStream = licenseLookupUrl.openStream();
                licenseLookup = (LicenseLookup) unmarshaller.unmarshal(lookupStream);
                LICENSE_LOOKUP_CACHE.put(licenseLookupKey, licenseLookup);
                return licenseLookup;
            } catch (IOException e) {
                throw new MojoFailureException(
                        "Failed to read '" + licenseLookupFile + "' from '" + licenseLookupUrl + "'", e);
            } catch (JAXBException e) {
                throw new MojoFailureException(
                        "Failed to parse '" + licenseLookupFile + "' from '" + licenseLookupUrl + "'", e);
            } finally {
                IOUtils.closeQuietly(lookupStream);
            }
        } finally {
            lock.unlock();
        }
    }
}