org.nuunframework.kernel.internal.scanner.ClasspathScannerInternal.java Source code

Java tutorial

Introduction

Here is the source code for org.nuunframework.kernel.internal.scanner.ClasspathScannerInternal.java

Source

/**
 * Copyright (C) 2013 Kametic <epo.jemba@kametic.com>
 *
 * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE, Version 3, 29 June 2007;
 * or any later version
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.gnu.org/licenses/lgpl-3.0.txt
 *
 * 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 org.nuunframework.kernel.internal.scanner;

import static org.reflections.util.FilterBuilder.prefix;

import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import org.nuunframework.kernel.KernelException;
import org.nuunframework.kernel.annotations.Ignore;
import org.nuunframework.kernel.commons.AssertUtils;
import org.nuunframework.kernel.commons.specification.Specification;
import org.reflections.ReflectionUtils;
import org.reflections.Reflections;
import org.reflections.Store;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.scanners.Scanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.reflections.scanners.TypesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;

class ClasspathScannerInternal implements ClasspathScanner {

    Logger logger = LoggerFactory.getLogger(ClasspathScannerInternal.class);

    private final List<String> packageRoots;
    private final boolean reachAbstractClass;
    private final ClasspathStrategy classpathStrategy;
    private Set<URL> additionalClasspath;
    private Set<URL> urls;

    private final List<ScannerCommand> commands;

    public ClasspathScannerInternal(ClasspathStrategy classpathStrategy, String... packageRoots_) {
        this(classpathStrategy, false, null, packageRoots_);

    }

    public ClasspathScannerInternal(ClasspathStrategy classpathStrategy, boolean reachAbstractClass,
            String packageRoot, String... packageRoots_) {
        this.packageRoots = new LinkedList<String>();

        if (packageRoot != null) {
            this.packageRoots.add(packageRoot);
        }

        for (String packageRoot_ : packageRoots_) {
            this.packageRoots.add(packageRoot_);
        }
        this.reachAbstractClass = reachAbstractClass;
        this.classpathStrategy = classpathStrategy;
        commands = new ArrayList<ClasspathScannerInternal.ScannerCommand>();
    }

    static interface ScannerCommand {
        void execute(Reflections reflections);

        Scanner scanner();
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    public void scanClasspathForAnnotation(final Class<? extends Annotation> annotationType,
            final Callback callback) {
        //        ConfigurationBuilder configurationBuilder = configurationBuilder();
        //        Set<URL> computeUrls = computeUrls();
        //        Reflections reflections = new Reflections(configurationBuilder.addUrls(computeUrls).setScanners(new TypeAnnotationsScanner()));

        ScannerCommand command = new ScannerCommand() {
            @Override
            public Scanner scanner() {
                return new TypeAnnotationsScanner();
            }

            @Override
            public void execute(Reflections reflections) {
                Collection<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(annotationType);
                if (typesAnnotatedWith == null) {
                    typesAnnotatedWith = Collections.emptySet();
                }
                callback.callback((Collection) postTreatment((Collection) typesAnnotatedWith));
            }
        };

        queue(command);

    }

    private void queue(ScannerCommand command) {
        commands.add(command);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    public void scanClasspathForMetaAnnotation(final Class<? extends Annotation> annotationType,
            final Callback callback) {
        //        ConfigurationBuilder configurationBuilder = configurationBuilder();
        //        Set<URL> computeUrls = computeUrls();
        //        Reflections reflections = new Reflections(configurationBuilder.addUrls(computeUrls).setScanners(new TypesScanner()));

        queue(new ScannerCommand() {
            @Override
            public void execute(Reflections reflections) {
                Multimap<String, String> multimap = reflections.getStore().get(TypesScanner.class);
                Collection<Class<?>> typesAnnotatedWith = Sets.newHashSet();
                for (String className : multimap.keys()) {
                    Class<?> klass = toClass(className);
                    if (annotationType != null && klass != null
                            && AssertUtils.hasAnnotationDeep(klass, annotationType) && !klass.isAnnotation()) {
                        typesAnnotatedWith.add(klass);
                    }
                }
                callback.callback((Collection) postTreatment((Collection) typesAnnotatedWith));
            }

            @Override
            public Scanner scanner() {
                return new TypesScanner();
            }

        });

    }

    @Override
    public void scanClasspathForMetaAnnotationRegex(final String metaAnnotationRegex, final Callback callback) {
        //        ConfigurationBuilder configurationBuilder = configurationBuilder();
        //        Set<URL> computeUrls = computeUrls();
        //        Reflections reflections = new Reflections(configurationBuilder.addUrls(computeUrls).setScanners(new TypesScanner()));

        queue(new ScannerCommand() {
            @Override
            public void execute(Reflections reflections) {
                Multimap<String, String> multimap = reflections.getStore().get(TypesScanner.class);

                Collection<Class<?>> typesAnnotatedWith = Sets.newHashSet();

                for (String className : multimap.keys()) {
                    Class<?> klass = toClass(className);
                    if (metaAnnotationRegex != null && klass != null
                            && AssertUtils.hasAnnotationDeepRegex(klass, metaAnnotationRegex)
                            && !klass.isAnnotation())
                    //            if ( annotationType != null && klass != null &&  AssertUtils.hasAnnotationDeep(klass, annotationType) && ! klass.isAnnotation() )
                    {
                        typesAnnotatedWith.add(klass);
                    }
                }
                callback.callback((Collection) postTreatment((Collection) typesAnnotatedWith));
            }

            @Override
            public Scanner scanner() {
                return new TypesScanner();
            }
        });
    }

    private Class<?> toClass(String candidate) {
        try {
            return Class.forName(candidate);
        } catch (Throwable e) {
            logger.debug("String to Class : " + e.getMessage());

        }
        return null;
    }

    static class IgnorePredicate implements Predicate<Class<?>> {

        Logger logger = LoggerFactory.getLogger(ClasspathScannerInternal.IgnorePredicate.class);

        private final boolean reachAbstractClass;

        public IgnorePredicate(boolean reachAbstractClass) {
            this.reachAbstractClass = reachAbstractClass;
        }

        @Override
        public boolean apply(Class<?> clazz) {

            logger.trace("Checking {} for Ignore", clazz.getName());

            boolean toKeep = true;

            if ((Modifier.isAbstract(clazz.getModifiers()) && !reachAbstractClass) && (!clazz.isInterface())) {
                toKeep = false;
            }

            for (Annotation annotation : clazz.getAnnotations()) {
                logger.trace("Checking annotation {} for Ignore", annotation.annotationType().getName());
                if (annotation.annotationType().equals(Ignore.class)
                        || annotation.annotationType().getName().endsWith("Ignore")) {
                    toKeep = false;
                }
                logger.trace("Result tokeep = {}.", toKeep);
                if (!toKeep) {
                    break;
                }
            }
            return toKeep;
        }
    }

    private Collection<Class<?>> postTreatment(Collection<Class<?>> set) {

        // Sanity Check : throw a KernelException if one of the returned classes is null
        for (Class<?> class1 : set) {
            if (null == class1) {
                throw new KernelException(
                        "Scanned classes results can not be null. Please check Integrity of the classes.");
            }
        }

        Collection<Class<?>> filtered = Collections2.filter(set, new IgnorePredicate(reachAbstractClass));

        return filtered;

    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    public void scanClasspathForAnnotationRegex(final String annotationTypeRegex, final Callback callback) {
        //        Reflections reflections = new Reflections(configurationBuilder().addUrls(computeUrls()).setScanners(new TypeAnnotationsScanner()));

        queue(new ScannerCommand() {
            @Override
            public void execute(Reflections reflections) {
                Store store = reflections.getStore();

                Multimap<String, String> multimap = store.get(TypeAnnotationsScanner.class);

                List<String> key = new ArrayList<String>();
                for (String loopKey : multimap.keySet()) {
                    if (loopKey.matches(annotationTypeRegex)) {
                        key.add(loopKey);
                    }
                }

                Collection<Class<?>> typesAnnotatedWith = new HashSet<Class<?>>();

                for (String k : key) {
                    Collection<String> collectionOfString = multimap.get(k);
                    typesAnnotatedWith.addAll(toClasses(collectionOfString));
                }
                callback.callback((Collection) postTreatment((Collection) typesAnnotatedWith));
            }

            @Override
            public Scanner scanner() {
                return new TypeAnnotationsScanner();
            }

        });
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    public void scanClasspathForTypeRegex(final String typeName, final Callback callback) {
        //        Reflections reflections = new Reflections(configurationBuilder().addUrls(computeUrls()).setScanners(new TypesScanner()));
        queue(new ScannerCommand() {
            @Override
            public void execute(Reflections reflections) {
                Store store = reflections.getStore();
                Multimap<String, String> multimap = store.get(TypesScanner.class);
                Collection<String> collectionOfString = new HashSet<String>();
                for (String loopKey : multimap.keySet()) {
                    if (loopKey.matches(typeName)) {
                        collectionOfString.add(loopKey);
                    }
                }

                Collection<Class<?>> types = null;

                if (collectionOfString.size() > 0) {
                    types = toClasses(collectionOfString);
                } else {
                    types = Collections.emptySet();
                }
                callback.callback((Collection) postTreatment((Collection) types));

            }

            @Override
            public Scanner scanner() {
                return new TypesScanner();
            }

        });

    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    public void scanClasspathForSpecification(final Specification<Class<?>> specification,
            final Callback callback) {
        //        Reflections reflections = new Reflections(configurationBuilder().addUrls(computeUrls()).setScanners(new TypesScanner()));
        queue(new ScannerCommand() {
            @Override
            public void execute(Reflections reflections) {
                Store store = reflections.getStore();
                Multimap<String, String> multimap = store.get(TypesScanner.class);
                Collection<String> collectionOfString = multimap.keySet();

                Collection<Class<?>> types = null;
                Collection<Class<?>> filteredTypes = new HashSet<Class<?>>();

                // Convert String to classes
                if (collectionOfString.size() > 0) {
                    types = toClasses(collectionOfString);
                } else {
                    types = Collections.emptySet();
                }

                // Filter via specification
                for (Class<?> candidate : types) {
                    if (specification.isSatisfiedBy(candidate)) {
                        filteredTypes.add(candidate);
                    }
                }

                callback.callback((Collection) postTreatment((Collection) filteredTypes));

            }

            @Override
            public Scanner scanner() {
                return new TypesScanner();
            }

        });

    }

    @SuppressWarnings({})
    @Override
    public void scanClasspathForSubTypeRegex(final String subTypeName, final Callback callback) {

        //        Reflections reflections = new Reflections(configurationBuilder().addUrls(computeUrls()).setScanners(new TypesScanner()));

        queue(new ScannerCommand() {
            @Override
            public void execute(Reflections reflections) {
                // empty just add 
            }

            @Override
            public Scanner scanner() {
                return new SubTypesScanner();
            }
        });
        queue(new ScannerCommand() {
            @SuppressWarnings({ "rawtypes", "unchecked" })
            @Override
            public void execute(Reflections reflections) {
                Store store = reflections.getStore();
                Multimap<String, String> multimap = store.get(TypesScanner.class);

                Collection<String> collectionOfString = new HashSet<String>();

                for (String loopKey : multimap.keySet()) {

                    if (loopKey.matches(subTypeName)) {
                        collectionOfString.add(loopKey);
                    }
                }

                Collection<Class<?>> types = null;

                if (collectionOfString.size() > 0) {
                    types = toClasses(collectionOfString);
                } else {
                    types = Collections.emptySet();
                }

                // Then find subclasses of types
                Collection<Class<?>> finalClasses = new HashSet<Class<?>>();
                for (Class<?> subType : types) {
                    // Collection<Class<?>> scanClasspathForSubTypeClass =
                    // scanClasspathForSubTypeClass(class1);
                    // ///////////////////////////////
                    Collection<?> typesAnnotatedWith = (Collection<?>) reflections.getSubTypesOf(subType);

                    if (typesAnnotatedWith == null) {
                        typesAnnotatedWith = Collections.emptySet();
                    }
                    // ///////////////////////////////
                    finalClasses.addAll((Collection) postTreatment((Collection) typesAnnotatedWith));
                }

                // removed ignored already done
                callback.callback(finalClasses);
                // return (Collection) removeIgnore((Collection)types);

            }

            @Override
            public Scanner scanner() {
                return new TypesScanner();

            }
        });

    }

    @Override
    public void scanClasspathForResource(final String pattern, final CallbackResources callback) {
        //        Reflections reflections = new Reflections(new ConfigurationBuilder().addUrls(computeUrls()).setScanners(new ResourcesScanner()));
        queue(new ScannerCommand() {
            @Override
            public void execute(Reflections reflections) {
                Set<String> resources = reflections.getResources(Pattern.compile(pattern));
                callback.callback(resources);

            }

            @Override
            public Scanner scanner() {
                return new ResourcesScanner();
            }

        });
    }
    //  queue(new ScannerCommand()
    //  {
    //      @Override
    //      public void execute(Reflections reflections)
    //      {
    //          
    //      }
    //      
    //      @Override
    //      public Scanner scanner()
    //      {
    //          return
    //      }
    //      
    //  });

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    public void scanClasspathForSubTypeClass(final Class<?> subType, final Callback callback) {
        //        Reflections reflections = new Reflections(configurationBuilder().addUrls(computeUrls()).setScanners(new SubTypesScanner()));
        queue(new ScannerCommand() {
            @Override
            public void execute(Reflections reflections) {
                Collection<?> typesAnnotatedWith = (Collection<?>) reflections.getSubTypesOf(subType);

                if (typesAnnotatedWith == null) {
                    typesAnnotatedWith = Collections.emptySet();
                }

                callback.callback((Collection) postTreatment((Collection) typesAnnotatedWith));

            }

            @Override
            public Scanner scanner() {
                return new SubTypesScanner();
            }

        });

    }

    /**
     * Unique reflections object. Unique scan
     */
    @Override
    public void doClasspathScan() {

        Scanner[] scanners = getScanners();

        ConfigurationBuilder configurationBuilder = configurationBuilder().addUrls(computeUrls())
                .setScanners(scanners);

        Reflections reflections = new Reflections(configurationBuilder);

        for (ScannerCommand command : commands) {
            command.execute(reflections);
        }
    }

    private Scanner[] getScanners() {
        Map<Class<?>, Scanner> scannersByClass = Maps.newHashMap();
        for (ScannerCommand command : commands) {
            Scanner scanner = command.scanner();

            if (!scannersByClass.containsKey(scanner.getClass())) {
                scannersByClass.put(scanner.getClass(), scanner);
            }
        }

        Collection<Scanner> scanners = scannersByClass.values();
        int size = scanners.size();

        Scanner[] arrayOfScanners = new Scanner[size];

        scanners.toArray(arrayOfScanners);

        return arrayOfScanners;
    }

    public void setAdditionalClasspath(Set<URL> additionalClasspath) {
        this.additionalClasspath = additionalClasspath;
    }

    private ConfigurationBuilder configurationBuilder() {
        ConfigurationBuilder cb = new ConfigurationBuilder();
        FilterBuilder fb = new FilterBuilder();

        for (String packageRoot : packageRoots) {
            fb.include(prefix(packageRoot));
        }

        cb.filterInputsBy(fb);

        return cb;
    }

    private Set<URL> computeUrls() {
        if (urls == null) {
            urls = new HashSet<URL>();

            switch (classpathStrategy.getStrategy()) {
            case SYSTEM:
                urls.addAll(ClasspathHelper.forJavaClassPath());
                break;
            case CLASSLOADER:
                urls.addAll(ClasspathHelper.forClassLoader());
                break;
            case ALL:
                urls.addAll(ClasspathHelper.forJavaClassPath());
                urls.addAll(ClasspathHelper.forClassLoader());
                break;
            case NONE:
                break;
            default:
                throw new IllegalArgumentException(
                        "Unsupported classpath strategy " + classpathStrategy.toString());
            }

            if (classpathStrategy.isAdditional() && this.additionalClasspath != null) {
                urls.addAll(this.additionalClasspath);
            }
        }

        urls.addAll(ClasspathHelper.forManifest(urls));

        return urls;
    }

    private <T> Collection<Class<?>> toClasses2(Collection<String> names) {
        Collection<Class<?>> classes = new HashSet();

        for (String name : names) {
            try {
                classes.add((Class<T>) Class.forName(name));
            } catch (Exception e) {
                logger.warn("Error when converting " + name + " to class.", e);
            }
        }

        return classes;
    }

    private <T> Collection<Class<? extends T>> toClasses(Collection<String> names) {
        return ReflectionUtils.<T>forNames(names, this.getClass().getClassLoader());
    }
}