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

com.github.jknack.mwa.ClassPathScanner Maven / Gradle / Ivy

package com.github.jknack.mwa;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.commons.lang3.Validate;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;

/**
 * 

* A classpath scanner It offers the following functionality: *

*
    *
  • Scan for packages/sub-packages and collect classes. *
* * @author edgar.espina * @since 0.1 */ public class ClassPathScanner { /** * The classes pattern. */ private static final String DEFAULT_RESOURCE_PATTERN = "/*.class"; /** * The classes set. */ private Set> classes = null; /** * The lock. */ private final ReadWriteLock lock = new ReentrantReadWriteLock(); /** * The candidate packages. */ private Set packages = new LinkedHashSet(); /** * The resource pattern to use when scanning the classpath. * This value will be appended to each base package name. */ private String resourcePattern = DEFAULT_RESOURCE_PATTERN; /** * The type filters. */ private final Set filters = new LinkedHashSet(); /** * Creates a new {@link ClassPathScanner}. */ public ClassPathScanner() { } /** * Add a package to the scanner. * * @param candidatePackage The candidate package. Required. * @return This scanner. */ public ClassPathScanner addPackage(final String candidatePackage) { Validate.notEmpty(candidatePackage, "The package to scan is required."); packages.add(candidatePackage); return this; } /** * Add a package to the scanner. * * @param candidatePackage The candidate package. Required. * @return This scanner. */ public ClassPathScanner addPackage(final Package candidatePackage) { addPackage(checkNotNull(candidatePackage, "The package to scan is required.").getName()); return this; } /** * Add packages to the scanner. * * @param packages The candidate packages. Required. * @return This scanner. */ public ClassPathScanner addPackages(final String... packages) { checkArgument(packages.length != 0, "The packages to scan are required."); for (String candidate : packages) { addPackage(candidate); } return this; } /** * Add packages to the scanner. * * @param packages The candidate packages. Required. * @return This scanner. */ public ClassPathScanner addPackages(final Package... packages) { checkArgument(packages.length != 0, "The packages to scan are required."); for (Package candidate : packages) { addPackage(candidate); } return this; } /** * Enable recursive scanning. * * @return This scanner. */ public ClassPathScanner includeSubPackages() { this.resourcePattern = "/**/*.class"; return this; } /** * Append a type filter. * * @param filters The filter list. Required. * @return This scanner. */ public ClassPathScanner addFilters(final TypeFilter... filters) { checkArgument(filters.length != 0, "The class filter are required."); for (TypeFilter filter : filters) { this.filters.add(filter); } return this; } /** * Scan and collect class resources. * * @return All the matching classes. */ public Set> scan() { lock.readLock().lock(); try { if (classes == null) { lock.readLock().unlock(); lock.writeLock().lock(); try { // re-check state if (classes == null) { classes = new LinkedHashSet>(); ClassLoader loader = getClass().getClassLoader(); for (String classname : list()) { classes.add(loader.loadClass(classname)); } } lock.readLock().lock(); } finally { lock.writeLock().unlock(); } } return classes; } catch (Exception ex) { throw new IllegalStateException("Fail during package scanning", ex); } finally { lock.readLock().unlock(); } } /** * Perform Spring-based scanning for classes. * * @return The matching class names. * @throws IOException If the disk fails. */ private Set list() throws IOException { Set classes = new HashSet(); ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resolver); for (String candidate : packages) { String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(candidate) + resourcePattern; Resource[] resources = resolver.getResources(pattern); for (Resource resource : resources) { if (resource.isReadable()) { MetadataReader reader = readerFactory .getMetadataReader(resource); String className = reader.getClassMetadata() .getClassName(); if (matchesFilter(reader, readerFactory)) { classes.add(className); } } } } return classes; } /** * Check whether any of the configured type filters matches the * current class descriptor contained in the metadata * reader. * * @param reader The metadata reader. * @param readerFactory The metadata reader factory. * @return True if the current resources matches the filter. * @throws IOException If the disk fails. */ private boolean matchesFilter(final MetadataReader reader, final MetadataReaderFactory readerFactory) throws IOException { for (TypeFilter filter : filters) { if (filter.match(reader, readerFactory)) { return true; } } return false; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy