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

io.micronaut.ast.groovy.scan.ClassPathAnnotationScanner Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017-2020 original authors
 *
 * 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
 *
 * https://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 io.micronaut.ast.groovy.scan;

import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.io.scan.AnnotationScanner;
import io.micronaut.core.reflect.ClassUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarFile;
import java.util.stream.Stream;

/**
 * 

An optimized classpath scanner that includes the ability to optionally scan JAR files.

*

The implementation avoids loading the classes themselves by parsing the class definitions and reading * only the annotations.

* * @author Graeme Rocher * @since 1.0 */ @Internal public class ClassPathAnnotationScanner implements AnnotationScanner { private static final Logger LOG = LoggerFactory.getLogger(ClassPathAnnotationScanner.class); private final ClassLoader classLoader; private boolean includeJars; /** * @param classLoader The class loader */ public ClassPathAnnotationScanner(ClassLoader classLoader) { this.classLoader = classLoader; this.includeJars = true; } /** * Default constructor. */ public ClassPathAnnotationScanner() { this(ClassPathAnnotationScanner.class.getClassLoader()); } /** * Whether to include JAR files. * * @param includeJars The jar files to include * @return This scanner */ protected ClassPathAnnotationScanner includeJars(boolean includeJars) { this.includeJars = includeJars; return this; } /** * Scan the given packages. * * @param annotation The annotation to scan for * @param pkg The package to scan * @return A stream of classes */ @Override public @NonNull Stream> scan(@NonNull String annotation, @NonNull String pkg) { if (pkg == null) { return Stream.empty(); } List> classes = doScan(annotation, pkg); return classes.stream(); } /** * @param annotation The annotation * @param pkg The package * @return The list of class */ protected List> doScan(String annotation, String pkg) { try { String packagePath = pkg.replace('.', '/').concat("/"); var classes = new ArrayList>(); Enumeration resources = classLoader.getResources(packagePath); if (!resources.hasMoreElements() && LOG.isDebugEnabled()) { LOG.debug("No resources found under package path: {}", packagePath); } while (resources.hasMoreElements()) { URL url = resources.nextElement(); String protocol = url.getProtocol(); if ("file".equals(protocol)) { try { traverseFile(annotation, classes, Paths.get(url.toURI())); } catch (URISyntaxException e) { if (LOG.isDebugEnabled()) { LOG.debug("Ignoring file [{}] due to URI error: {}", url, e.getMessage(), e); } } } else if (includeJars && Arrays.asList("jar", "zip", "war").contains(protocol)) { URLConnection con = url.openConnection(); if (con instanceof JarURLConnection jarCon) { JarFile jarFile = jarCon.getJarFile(); jarFile.stream() .filter(entry -> { String name = entry.getName(); return name.startsWith(packagePath) && name.endsWith(ClassUtils.CLASS_EXTENSION) && name.indexOf('$') == -1; }) .forEach(entry -> { try (InputStream inputStream = jarFile.getInputStream(entry)) { scanInputStream(annotation, inputStream, classes); } catch (IOException e) { if (LOG.isDebugEnabled()) { LOG.debug("Ignoring JAR entry [{}] due to I/O error: {}", entry.getName(), e.getMessage(), e); } } catch (ClassNotFoundException e) { if (LOG.isDebugEnabled()) { LOG.debug("Ignoring JAR entry [{}]. Class not found: {}", entry.getName(), e.getMessage(), e); } } }); } else { if (LOG.isDebugEnabled()) { LOG.debug("Ignoring JAR URI entry [{}]. No JarURLConnection found.", url); } // TODO: future support for servlet containers } } } return classes; } catch (IOException e) { if (LOG.isDebugEnabled()) { LOG.debug("Ignoring I/O Exception scanning package: {}", pkg, e); } return Collections.emptyList(); } } /** * @param annotation The annotation * @param classes The classes * @param filePath The filePath */ protected void traverseFile(String annotation, List> classes, Path filePath) { if (Files.isDirectory(filePath)) { try (DirectoryStream dirs = Files.newDirectoryStream(filePath)) { dirs.forEach(path -> { if (Files.isDirectory(path)) { traverseFile(annotation, classes, path); } else { scanFile(annotation, path, classes); } }); } catch (IOException e) { if (LOG.isDebugEnabled()) { LOG.debug("Ignoring directory [{}] due to I/O error: {}", filePath, e.getMessage(), e); } } } else { scanFile(annotation, filePath, classes); } } /** * @param annotation The annotation * @param filePath The file path * @param classes The classes */ protected void scanFile(String annotation, Path filePath, List> classes) { String fileName = filePath.getFileName().toString(); if (fileName.endsWith(".class") && fileName.indexOf('$') == -1) { // ignore generated classes try (InputStream inputStream = Files.newInputStream(filePath)) { scanInputStream(annotation, inputStream, classes); } catch (IOException e) { if (LOG.isDebugEnabled()) { LOG.debug("Ignoring file [{}] due to I/O error: {}", fileName, e.getMessage(), e); } } catch (ClassNotFoundException e) { if (LOG.isDebugEnabled()) { LOG.debug("Ignoring file [{}]. Class not found: {}", fileName, e.getMessage(), e); } } } } private void scanInputStream(String annotation, InputStream inputStream, List> classes) throws IOException, ClassNotFoundException { var annotationClassReader = new AnnotationClassReader(inputStream); var classVisitor = new AnnotatedTypeInfoVisitor(); annotationClassReader.accept(classVisitor, AnnotationClassReader.SKIP_DEBUG); if (classVisitor.hasAnnotation(annotation)) { classes.add(classLoader.loadClass(classVisitor.getTypeName())); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy