org.ebayopensource.turmeric.plugins.maven.resources.ResourceLocator.java Source code

Java tutorial

Introduction

Here is the source code for org.ebayopensource.turmeric.plugins.maven.resources.ResourceLocator.java

Source

/*******************************************************************************
 * Copyright (c) 2006-2010 eBay Inc. All Rights Reserved.
 * 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
 *******************************************************************************/
package org.ebayopensource.turmeric.plugins.maven.resources;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarException;
import java.util.jar.JarFile;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.util.StringUtils;

public class ResourceLocator {
    public static class Location {
        private URI uri;
        private File file;
        private boolean local;
        private boolean live;

        /**
         * A reference to a resource
         * 
         * @param uri
         *            the uri to the resource
         * @param project
         *            the maven project to work off of
         * @throws IOException
         */
        public Location(URI uri, MavenProject project) throws MojoExecutionException {
            this.uri = uri;
            this.live = "file".equals(uri.getScheme());
            if (this.live) {
                this.file = new File(uri);
                this.local = this.file.getAbsolutePath().startsWith(project.getBasedir().getAbsolutePath());
            } else {
                this.local = false;
                String filename = FilenameUtils.getName(uri.toASCIIString());
                File targetDir = new File(project.getBuild().getDirectory());
                this.file = new File(targetDir, filename);
                try {
                    FileUtils.copyURLToFile(uri.toURL(), this.file);
                } catch (IOException e) {
                    throw new MojoExecutionException(
                            "Unable to copy resource URI [" + uri + "] to temp location [" + file + "]", e);
                }
            }
        }

        /**
         * A reference to a resource
         * 
         * @param file
         *            the file reference to the resource
         * @param project
         *            the maven project to work off of
         */
        public Location(File file, MavenProject project) {
            this.uri = file.toURI();
            this.live = true;
            if (this.live) {
                this.file = new File(uri);
                this.local = this.file.getAbsolutePath().startsWith(project.getBasedir().getAbsolutePath());
            }
        }

        @Override
        public String toString() {
            StringBuilder msg = new StringBuilder();
            msg.append("Location: ").append(uri.toString());
            msg.append(" [local:").append(local);
            msg.append(", live:").append(live);
            msg.append(", file:").append(file);
            msg.append("]");
            return msg.toString();
        }

        /**
         * Get a file reference to the desired resource.
         * <p>
         * May or may not be the live file.
         * <p>
         * A requested resource that exist in the project's own tree will be referenced directly.<br>
         * {@link #isLocalToProject()} will be true.<br>
         * {@link #isLiveFile()} will be true.
         * <p>
         * A requested resource that exists outside of the project's own tree that can be referenced directly, will be.
         * (This is the case for m2eclipse dependency references with "Workspace Resolution" enabled)
         * {@link #isLocalToProject()} will be false.<br>
         * {@link #isLiveFile()} will be true.
         * <p>
         * A requested resource found in a project dependency's artifact in the local repository, a copy of that desired
         * resource will be copied into the project's own <code>/target/turmeric-maven-plugin-located-resources/</code>
         * directory tree before a file reference to this copied resource is returned. {@link #isLocalToProject()} will
         * be true.<br>
         * {@link #isLiveFile()} will be false.<br>
         * {@link #getUri()} will point to where the resource was fetched from.
         * 
         * @return the file from the live location, or from inside of the project target directory.
         */
        public File getFile() {
            return file;
        }

        public URI getUri() {
            return uri;
        }

        public boolean isLocalToProject() {
            return local;
        }

        public boolean isLiveFile() {
            return live;
        }
    }

    private Log log;
    private MavenProject project;

    public ResourceLocator(Log log, MavenProject project) {
        this.log = log;
        this.project = project;
    }

    /**
     * Attempt to find a resource in the project's configuration, and then the classpath.
     * <p>
     * Search Order:<br>
     * <ol>
     * <li>Project Defined Resource Directories</li>
     * <li>${project.build.outputDirectory}</li>
     * <li>Classpath</li>
     * </ol>
     * 
     * @param pathref
     *            the path to the resource desired. In the same format as you would use for
     *            {@link ClassLoader#getResource(String)}
     * @return the Location of the found resource, or null if not found.
     * @throws MojoExecutionException
     *             if unable to process resource loookup
     */
    public Location findResource(String pathref) throws MojoExecutionException {
        if (StringUtils.isBlank(pathref)) {
            log.warn("Unable to lookup resource for null pathref: " + pathref);
            return null;
        }

        Location location = null;

        location = lookInProject(pathref);
        if (location != null) {
            return location;
        }

        location = lookInOutputDirectory(pathref);
        if (location != null) {
            return location;
        }

        location = lookInProjectCompileArtifacts(pathref);
        if (location != null) {
            return location;
        }

        location = lookInClasspath(pathref);
        if (location != null) {
            return location;
        }

        log.debug("NOT FOUND: resource: " + pathref);
        return null;
    }

    private Location lookInProjectCompileArtifacts(String pathref) throws MojoExecutionException {
        log.debug("Looking for resource in project compile artifacts: " + pathref);

        @SuppressWarnings("unchecked")
        List<Artifact> arts = project.getCompileArtifacts();
        if (arts == null) {
            log.debug("NOT FOUND in project compile artifacts: <no artifacts declared to look in>");
            return null;
        }

        if (log.isDebugEnabled()) {
            StringBuilder dbg = new StringBuilder();

            dbg.append("Project Compile Artifacts:");

            for (Artifact arti : arts) {
                dbg.append("\n  ");
                dbg.append(arti.getFile());
            }

            log.debug(dbg.toString());
        }

        URI uri = null;
        for (Artifact arti : arts) {
            uri = findFileResource(arti.getFile(), pathref);
            if (uri != null) {
                return new Location(uri, project);
            }
        }

        log.debug("NOT FOUND in project compile artifacts");
        return null;
    }

    private URI findFileResource(File path, String pathref) {
        if (path.isFile()) {
            // Assume its an archive.
            String extn = FilenameUtils.getExtension(path.getName());
            if (StringUtils.isBlank(extn)) {
                log.debug("No extension found for file: " + path);
                return null;
            }

            extn = extn.toLowerCase();
            if ("jar".equals(extn)) {
                log.debug("looking inside of jar file: " + path);
                JarFile jar = null;
                try {
                    jar = new JarFile(path);
                    JarEntry entry = jar.getJarEntry(pathref);
                    if (entry == null) {
                        log.debug("JarEntry not found: " + pathref);
                        return null;
                    }

                    String uripath = String.format("jar:%s!/%s", path.toURI(), entry.getName());
                    try {
                        return new URI(uripath);
                    } catch (URISyntaxException e) {
                        log.debug("Unable to create URI reference: " + path, e);
                        return null;
                    }
                } catch (JarException e) {
                    log.debug("Unable to open archive: " + path, e);
                    return null;
                } catch (IOException e) {
                    log.debug("Unable to open archive: " + path, e);
                    return null;
                } finally {
                    if (jar != null) {
                        try {
                            jar.close();
                        } catch (IOException e) {
                            log.debug("Unable to close jar: " + path, e);
                        }
                    }
                }
            }

            log.debug("Unsupported archive file: " + path);
            return null;
        }

        if (path.isDirectory()) {
            File testFile = new File(path, FilenameUtils.separatorsToSystem(pathref));
            if (testFile.exists() && testFile.isFile()) {
                return testFile.toURI();
            }

            log.debug("Not found in directory: " + testFile);
            return null;
        }

        log.debug("Unable to handle non-file, non-directory " + File.class.getName() + " objects: " + path);
        return null;
    }

    private Location lookInClasspath(String pathref) throws MojoExecutionException {
        log.debug("Looking for resource in project classpath: " + pathref);

        if (log.isDebugEnabled()) {
            StringBuilder dbg = new StringBuilder();
            dbg.append("System.getProperty('java.class.path')=");

            String rawcp = System.getProperty("java.class.path");
            for (String cp : rawcp.split(File.pathSeparator)) {
                dbg.append("\n  ").append(cp);
            }

            log.debug(dbg.toString());

            ClassLoader cl = this.getClass().getClassLoader();
            if (cl instanceof URLClassLoader) {
                dbg = new StringBuilder();
                dbg.append("URLClassLoader(");
                dbg.append(cl.getClass().getName());
                dbg.append("):");

                URLClassLoader ucl = (URLClassLoader) cl;

                for (URL url : ucl.getURLs()) {
                    dbg.append("\n  ").append(url.toExternalForm());
                }

                log.debug(dbg.toString());
            }
        }

        List<URL> resources = new ArrayList<URL>();
        try {
            Enumeration<URL> enurls = ClassLoader.getSystemResources(pathref);
            if (enurls != null) {
                while (enurls.hasMoreElements()) {
                    URL url = enurls.nextElement();
                    if (!resources.contains(url)) {
                        resources.add(url);
                    }
                }
            }

            addFoundResource(resources, pathref, Thread.currentThread().getContextClassLoader());
            addFoundResource(resources, pathref, this.getClass().getClassLoader());
            if (resources.isEmpty()) {
                log.debug("NOT FOUND in project classpath");
                return null;
            }

            if (resources.size() > 1) {
                log.warn("Found more than 1 classpath entry for: " + pathref);
                for (URL url : resources) {
                    log.warn(" + " + url.toExternalForm());
                }
            }

            URI uri = resources.get(0).toURI();
            log.debug("FOUND resource in project classpath: " + uri);
            return new Location(uri, project);
        } catch (IOException e) {
            throw new MojoExecutionException(
                    "Unable to process resource lookup in project classpath: " + e.getMessage(), e);
        } catch (URISyntaxException e) {
            throw new MojoExecutionException(
                    "Unable to process resource lookup in project classpath: " + e.getMessage(), e);
        }
    }

    private void addFoundResource(List<URL> resources, String pathref, ClassLoader cl) throws IOException {
        log.debug("Looking in ClassLoader: " + cl);
        Enumeration<URL> enurls = cl.getResources(pathref);
        if (enurls != null) {
            while (enurls.hasMoreElements()) {
                URL url = enurls.nextElement();
                if (!resources.contains(url)) {
                    resources.add(url);
                }
            }
        }
    }

    private Location lookInOutputDirectory(String pathref) {
        File outputDir = new File(project.getBuild().getOutputDirectory());
        log.debug("Looking for resource in project output directory [" + outputDir + "]: " + pathref);

        File testFile = new File(outputDir, toOS(pathref));
        if (testFile.exists()) {
            log.debug("FOUND resource in project output directory: " + testFile);
            return new Location(testFile, project);
        }

        log.debug("NOT FOUND in project output directory");
        return null;
    }

    private Location lookInProject(String pathref) {
        List<?> resources = project.getBuild().getResources();
        log.debug("Looking for resource in [" + resources.size() + "] directories: " + pathref);
        Iterator<?> iter = resources.iterator();
        while (iter.hasNext()) {
            Resource resource = (Resource) iter.next();
            String resDir = resource.getDirectory();
            if (StringUtils.isBlank(resDir)) {
                log.warn("Blank resources directory: " + resource);
                continue;
            }
            File dir = new File(resDir);
            log.debug("Testing for resource in dir: " + dir);
            File possiblePath = new File(dir, pathref);
            if (possiblePath.exists()) {
                log.debug("FOUND resource: " + possiblePath);
                return new Location(possiblePath, project);
            }
        }
        log.debug("NOT FOUND in project resource directories");
        return null;
    }

    private String toOS(String path) {
        return FilenameUtils.separatorsToSystem(path);
    }
}