/*** * Copyright (c) 2009 Caelum - www.caelum.com.br/opensource 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 * * 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 br.com.caelum.vraptor.scan; import static com.google.common.base.Objects.firstNonNull; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.annotation.Annotation; import java.net.URL; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.scannotation.AnnotationDB; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import br.com.caelum.vraptor.core.BaseComponents; import br.com.caelum.vraptor.ioc.Stereotype; /** * A Scannotation based Component Scanner * * @author Sérgio Lopes * @since 3.2 */ public class ScannotationComponentScanner implements ComponentScanner { private static final Logger logger = LoggerFactory.getLogger(ScannotationComponentScanner.class); public Collection<String> scan(ClasspathResolver resolver) { final URL webInfClasses = resolver.findWebInfClassesLocation(); final List<String> basePackages = resolver.findBasePackages(); HashSet<String> results = new HashSet<String>(); Map<String, Set<String>> webInfClassesAnnotationMap = scanWebInfClasses(webInfClasses); Map<String, Set<String>> basePackagesAnnotationMap = scanBasePackages(basePackages, resolver); Set<String> stereotypeNames = findStereotypes(webInfClassesAnnotationMap, basePackagesAnnotationMap, basePackages); findComponentsFromWebInfClasses(webInfClassesAnnotationMap, stereotypeNames, results); findComponentsFromBasePackages(basePackagesAnnotationMap, basePackages, results); return results; } private Map<String, Set<String>> scanWebInfClasses(URL webInfClasses) { try { AnnotationDB db = createAnnotationDB(); db.scanArchives(webInfClasses); return db.getAnnotationIndex(); } catch (FileNotFoundException e) { return Collections.emptyMap(); } catch (IOException e) { throw new ScannerException("Could not scan WEB-INF/classes", e); } } private Map<String, Set<String>> scanBasePackages(List<String> basePackages, ClasspathResolver resolver) { try { AnnotationDB db = createAnnotationDB(); for (String basePackage : basePackages) { scanPackage(basePackage, db, resolver); } return db.getAnnotationIndex(); } catch (IOException e) { throw new ScannerException("Could not scan base packages", e); } } private void scanPackage(String basePackage, AnnotationDB db, ClasspathResolver resolver) throws IOException { String resource = basePackage.replace('.', '/'); Enumeration<URL> urls = resolver.getClassLoader().getResources(resource); if (!urls.hasMoreElements()) { logger.error("There's no occurence of package {} in classpath", basePackage); return; } do { URL url = urls.nextElement(); String file = toFileName(resource, url); db.scanArchives(new URL(file)); } while (urls.hasMoreElements()); } private static String toFileName(String resource, URL url) { String file = url.getFile().substring(0, url.getFile().length() - resource.length() - 1).replaceAll("(!)(/)?$", ""); if (!file.startsWith("file:")) { file = "file:" + file; } return file; } private Set<String> findStereotypes(Map<String, Set<String>> webInfClassesAnnotationMap, Map<String, Set<String>> basePackagesAnnotationMap, List<String> basePackages) { HashSet<String> results = new HashSet<String>(); addVRaptorStereotypes(results); addWebInfClassesStereotypes(webInfClassesAnnotationMap, results); addBasePackagesStereotypes(basePackagesAnnotationMap, basePackages, results); return results; } private void addBasePackagesStereotypes(Map<String, Set<String>> basePackagesAnnotationMap, List<String> basePackages, HashSet<String> results) { Set<String> libStereotypes = nullToEmpty(basePackagesAnnotationMap.get(Stereotype.class.getName())); for (String stereotype : libStereotypes) { if (packagesContains(basePackages, stereotype)) { results.add(stereotype); } } } private static boolean packagesContains(List<String> basePackages, String clazz) { for (String basePackage : basePackages) { if (clazz.startsWith(basePackage)) { return true; } } return false; } private void addWebInfClassesStereotypes(Map<String, Set<String>> webInfClassesAnnotationMap, HashSet<String> results) { Set<String> myStereotypes = nullToEmpty(webInfClassesAnnotationMap.get(Stereotype.class.getName())); results.addAll(myStereotypes); } private static void addVRaptorStereotypes(HashSet<String> results) { for (Class<? extends Annotation> stereotype : BaseComponents.getStereotypes()) { results.add(stereotype.getName()); } } private void findComponentsFromWebInfClasses(Map<String, Set<String>> index, Set<String> stereotypeNames, Set<String> results) { for (String stereotype : stereotypeNames) { Set<String> classes = nullToEmpty(index.get(stereotype)); results.addAll(classes); } } private void findComponentsFromBasePackages(Map<String, Set<String>> index, List<String> basePackages, Set<String> results) { for (Class<? extends Annotation> stereotype : BaseComponents.getStereotypes()) { Set<String> classes = nullToEmpty(index.get(stereotype.getName())); for (String clazz : classes) { if (packagesContains(basePackages, clazz)) { results.add(clazz); } } } } private <T> Set<T> nullToEmpty(Set<T> set) { return firstNonNull(set, Collections.<T>emptySet()); } private AnnotationDB createAnnotationDB() { AnnotationDB db = new AnnotationDB(); db.setScanClassAnnotations(true); db.setScanFieldAnnotations(false); db.setScanMethodAnnotations(false); db.setScanParameterAnnotations(false); return db; } }