Package com.github.dandelion.core.utils

Source Code of com.github.dandelion.core.utils.ResourceScanner

/*
* [The "BSD licence"]
* Copyright (c) 2013-2014 Dandelion
* All rights reserved.
*
* 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.
* 3. Neither the name of Dandelion nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*/
package com.github.dandelion.core.utils;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.dandelion.core.DandelionException;

/**
* <p>
* Utility class used for searching for resources in the classpath.
*
* @author Thibault Duchateau
* @since 0.10.0
*/
public final class ResourceScanner {

  // Logger
  private static final Logger LOG = LoggerFactory.getLogger(ResourceScanner.class);

  /**
   * <p>
   * Finds the logical path of the first resource that matches the given
   * {@code resourceName} by scanning the classpath under the given
   * {@code location}.
   *
   * <p>
   * By default, no other condition but the name will be applied to the
   * resource name and the classpath scanning won't be recursive.
   *
   * @param location
   *            The classpath location where to scan.
   * @param nameFilter
   *            The name of the resource to look for.
   * @return The logical path of the resource if found, otherwise {@code null}
   *         .
   * @throws IOException
   *             if something goes wrong during the scanning.
   */
  public static String findResourcePath(String location, String nameFilter) throws IOException {
    Set<String> resourcePaths = scanForResourcePaths(location, null, nameFilter, null, null, false);
    if (resourcePaths.isEmpty()) {
      return null;
    }
    else {
      return resourcePaths.iterator().next();
    }
  }

  /**
   * <p>
   * Finds the virtual path of all resources that match the given conditions
   * by scanning the classpath under the given {@code location}.
   *
   * @param location
   *            The classpath location where to scan.
   * @param excludedPaths
   *            List of paths which will be excluded during the classpath
   *            scanning.
   * @param nameFilter
   *            The name of the resource to look for.
   * @param recursive
   *            Whether the scanning should be recursive or not.
   * @return A set of resource paths that match the given conditions.
   * @throws IOException
   *             if something goes wrong during the scanning.
   */
  public static Set<String> findResourcePaths(String location, Set<String> excludedPaths, String nameFilter,
      boolean recursive) throws IOException {
    return scanForResourcePaths(location, excludedPaths, nameFilter, null, null, recursive);
  }

  /**
   * <p>
   * Finds the virtual path of all resources that match the given conditions
   * by scanning the classpath under the given {@code location}.
   *
   * @param location
   *            The classpath location where to scan.
   * @param excludedPaths
   *            List of paths which will be excluded during the classpath
   *            scanning.
   * @param prefixFilter
   *            The prefix condition to be applied on the resource name.
   * @param suffixFilter
   *            The suffix condition to be applied on the resource name.
   * @param recursive
   *            Whether the scanning should be recursive or not.
   * @return A set of resource paths that match the given conditions.
   * @throws IOException
   *             if something goes wrong during the scanning.
   */
  public static Set<String> findResourcePaths(String location, Set<String> excludedPaths, String prefixFilter,
      String suffixFilter, boolean recursive) throws IOException {
    return scanForResourcePaths(location, excludedPaths, null, prefixFilter, suffixFilter, recursive);
  }

  /**
   * <p>
   * Scans for all resources that match the given confitions by scanning the
   * classpath.
   *
   * @param location
   *            The classpath location where to scan.
   * @param excludedPaths
   *            List of paths which will be excluded during the classpath
   *            scanning.
   * @param nameFilter
   *            The name of the resource to look for.
   * @param prefixFilter
   *            The prefix condition to be applied on the resource name.
   * @param suffixFilter
   *            The suffix condition to be applied on the resource name.
   * @param recursive
   *            Whether the scanning should be recursive or not.
   * @return A set of resource paths that match the given conditions.
   * @throws IOException
   *             if something goes wrong during the scanning.
   * @throws DandelionException
   *             if the URL protocol used to access the resource is not
   *             supported. This may happen with the JBoss VFS which is still
   *             not supported.
   */
  private static Set<String> scanForResourcePaths(String location, Set<String> excludedPaths, String nameFilter,
      String prefixFilter, String suffixFilter, boolean recursive) throws IOException {

    LOG.trace("Scanning for resources at '{}'...", location);

    Set<String> resourcePaths = new HashSet<String>();

    Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(location);

    while (urls.hasMoreElements()) {

      URL url = urls.nextElement();
      if ("file".equals(url.getProtocol())) {

        // Computes the physical root of the classpath to later
        // determine the resource path more easily
        String resourcePath = PathUtils.toFilePath(url);
        String classpathPhysicalRoot = resourcePath.substring(0, resourcePath.length() - location.length());

        // Gets the folder in which files will be scanned
        File folder = new File(resourcePath);

        resourcePaths.addAll(scanForResourcePathsInFileSystem(folder, classpathPhysicalRoot, recursive));
      }
      else if ("jar".equals(url.getProtocol()) || "zip".equals(url.getProtocol()) // Weblogic
          || "wsjar".equals(url.getProtocol())) // Websphere
      {

        resourcePaths.addAll(scanForResourcePathsInJarFile(url));
      }
      else {
        StringBuilder sb = new StringBuilder("The protocol ");
        sb.append(url.getProtocol());
        sb.append(" is not supported.");
        throw new DandelionException(sb.toString());
      }
    }

    LOG.trace("{} resources found before filtering", resourcePaths.size());
    return filterResourcePaths(location, resourcePaths, excludedPaths, nameFilter, prefixFilter, suffixFilter);
  }

  /**
   * <p>
   * Scans for all resources in the given file system {@code folder}.
   *
   * @param folder
   *            Folder in which the files will be scanned/listed.
   * @param classpathPhysicalRoot
   *            Physical root of the classpath, used to compute the resource
   *            path.
   * @param recursive
   *            Whether the scanning should be recursive or not.
   * @return A set of non-filtered resource paths.
   * @throws IOException
   *             if something goes wrong during the scanning.
   */
  private static Set<String> scanForResourcePathsInFileSystem(File folder, String classpathPhysicalRoot,
      boolean recursive) throws IOException {

    Set<String> extractedResourcePaths = new HashSet<String>();

    for (File file : folder.listFiles()) {
      if (file.canRead()) {

        if (file.isDirectory() && recursive) {
          extractedResourcePaths.addAll(scanForResourcePathsInFileSystem(file, classpathPhysicalRoot,
              recursive));
        }
        else {
          String filePath = URLDecoder.decode(file.toURI().toURL().getFile(), "UTF-8");
          String resourcePath = filePath.substring(classpathPhysicalRoot.length());
          extractedResourcePaths.add(resourcePath);
        }
      }
    }

    return extractedResourcePaths;
  }

  /**
   * <p>
   * Scans for all resources in the given {@code url} that refers to a JAR
   * file.
   *
   * @param url
   *            The URL that refers to the JAR file in which resources will
   *            be scanned.
   * @return A set of non-filtered resource paths.
   * @throws IOException
   *             if something goes wrong during the scanning.
   */
  private static Set<String> scanForResourcePathsInJarFile(URL url) throws IOException {

    Set<String> extractedResourcePaths = new HashSet<String>();

        // Recreate the URL if its inside Websphere
        if (url.getProtocol().startsWith("wsjar")) {
            url = new URL("jar", url.getHost(), url.getPort(), url.getPath());
        }

    URLConnection connection = url.openConnection();

    if (connection instanceof JarURLConnection) {

      JarURLConnection jarConnection = (JarURLConnection) connection;
      jarConnection.setUseCaches(false);
      JarFile jarFile = jarConnection.getJarFile();

      try {
        Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
          String resourcePath = entries.nextElement().getName();
          extractedResourcePaths.add(resourcePath);
        }
      }
      finally {
        jarFile.close();
      }
    }

    return extractedResourcePaths;
  }
 
  /**
   * <p>
   * Tests whether the given {@code path} is authorized according to the
   * passed list of paths to exclude.
   *
   * @param resourcePath
   *            The path name that must not be present in the list of excluded
   *            paths.
   * @param authorizedLocation
   *            Current location being scanned by the scanner.
   * @param excludedPaths
   *            List of paths which will be excluded during the classpath
   *            scanning.
   * @return {@code true} if the path is authorized, otherwise {@code false}.
   */
  private static boolean isPathAuthorized(String resourcePath, String authorizedLocation, Set<String> excludedPaths) {

    if (excludedPaths != null) {
      for (String excludedFolder : excludedPaths) {
        if (resourcePath.startsWith(excludedFolder)) {
          return false;
        }
      }
      return true;
    }
    else if (resourcePath.startsWith(authorizedLocation)) {
      return true;
    }
    else {
      return false;
    }
  }

  /**
   * <p>
   * Filters the given set of resource paths in multiple ways:
   * <ul>
   * <li>If the resource path contains any of the given {@code excludedPaths},
   * the resource path will be filtered out.</li>
   * <li>If the {@code nameFilter} is used, the resource is only filtered on
   * its name. Suffix and prefix have no effect.</li>
   * <li>If either {@code prefixFilter} or {@code suffixFilter} or both are
   * used, the resource won't be filtered on its name at all.</li>
   * </ul>
   *
   * @param resourcePaths
   *            The scanned resource paths.
   * @param excludedPaths
   *            List of paths which will be excluded during the classpath
   *            scanning.
   * @param nameFilter
   *            The name of the resource to look for.
   * @param prefixFilter
   *            The prefix condition to be applied on the resource name.
   * @param suffixFilter
   *            The suffix condition to be applied on the resource name;
   * @return A set of resource paths that match the given conditions.
   */
  private static Set<String> filterResourcePaths(String location, Set<String> resourcePaths,
      Set<String> excludedPaths, String nameFilter, String prefixFilter, String suffixFilter) {
    Set<String> filteredResources = new HashSet<String>();

    LOG.debug("Filtering scanned resources");
    for (String resourcePath : resourcePaths) {

      if (isPathAuthorized(resourcePath, location, excludedPaths)) {

        String resourceName = resourcePath.substring(resourcePath.lastIndexOf("/") + 1);

        if (StringUtils.isBlank(nameFilter) && StringUtils.isBlank(prefixFilter)
            && StringUtils.isBlank(suffixFilter)) {
          filteredResources.add(resourcePath);
          continue;
        }

        // if name condition is set, it's the only test on resources.
        if (StringUtils.isNotBlank(nameFilter)) {
          if (nameFilter.equalsIgnoreCase(resourceName)) {
            filteredResources.add(resourcePath);
          }
        }
        else {
          // otherwise prefix and suffix conditions are verified
          if (suffixFilter == null && resourceName.startsWith(prefixFilter)) {
            filteredResources.add(resourcePath);
          }
          else if (prefixFilter == null && resourceName.endsWith(suffixFilter)) {
            filteredResources.add(resourcePath);
          }
          else if (prefixFilter != null && suffixFilter != null && resourceName.startsWith(prefixFilter)
              && resourceName.endsWith(suffixFilter)) {
            filteredResources.add(resourcePath);
          }
        }
      }
    }

    LOG.debug("{} resources found after filtering", filteredResources.size());
    return filteredResources;
  }

  /**
   * Prevents instantiation.
   */
  private ResourceScanner() {
  }
}
TOP

Related Classes of com.github.dandelion.core.utils.ResourceScanner

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.