Change all the package names from infrautils to odlguice
[odlguice.git] / inject / inject / src / main / java / org / opendaylight / odlguice / inject / ClassPathScanner.java
1 /*
2  * Copyright © 2018 Red Hat, Inc. and others.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.odlguice.inject;
9
10 import io.github.classgraph.ClassGraph;
11 import io.github.classgraph.ClassInfo;
12 import io.github.classgraph.ClassInfoList;
13 import io.github.classgraph.ScanResult;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.Map;
17 import java.util.Set;
18 import java.util.function.BiConsumer;
19 import java.util.function.Consumer;
20 import javax.inject.Singleton;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 /**
25  * Class path scanner designed to be used with Guice. This provides a way for modules to request the bindings they
26  * need by scanning the class path.
27  */
28 @SuppressWarnings("rawtypes")
29 public class ClassPathScanner {
30     private static final Logger LOG = LoggerFactory.getLogger(ClassPathScanner.class);
31
32     private final Map<String, Class> implementations = new HashMap<>();
33     private final Set<Class<?>> singletons = new HashSet<>();
34
35     /**
36      * Create a class path scanner, scanning packages with the given prefix for {@literal @}Singleton annotated classes.
37      *
38      * @param prefix The package prefix.
39      */
40     public ClassPathScanner(String prefix) {
41         try (ScanResult scanResult =
42                  new ClassGraph()
43                      .enableClassInfo()
44                      .enableAnnotationInfo()
45                      .whitelistPackages(prefix)
46                      .scan()) {
47             Set<String> duplicateInterfaces = new HashSet<>();
48             for (ClassInfo singletonInfo : scanResult.getClassesWithAnnotation(Singleton.class.getName())) {
49                 ClassInfoList interfaces = singletonInfo.getInterfaces();
50                 if (interfaces.isEmpty()) {
51                     singletons.add(singletonInfo.loadClass());
52                 } else {
53                     for (ClassInfo interfaceInfo : interfaces) {
54                         String interfaceName = interfaceInfo.getName();
55                         if (!duplicateInterfaces.contains(interfaceName)) {
56                             if (implementations.put(interfaceName, singletonInfo.loadClass()) != null) {
57                                 LOG.debug("{} is declared multiple times, ignoring it", interfaceName);
58                                 implementations.remove(interfaceName);
59                                 duplicateInterfaces.add(interfaceName);
60                             }
61                         }
62                     }
63                 }
64             }
65         }
66     }
67
68     /**
69      * Binds all {@link Singleton} annotated classes discovered by scanning the class path to all their interfaces.
70      *
71      * @param prefix the package prefix of Singleton implementations to consider
72      * @param binder The binder (modeled as a generic consumer)
73      */
74     public void bindAllSingletons(String prefix, BiConsumer<Class, Class> binder, Consumer<Class> singletonConsumer) {
75         implementations.forEach((interfaceName, singletonClass) -> {
76             if (singletonClass.getName().startsWith(prefix)) {
77                 try {
78                     Class interfaceClass = Class.forName(interfaceName);
79                     binder.accept(interfaceClass, singletonClass);
80                     // TODO later probably lower this info to debug, but for now it's very useful..
81                     LOG.info("Bound {} to {}", interfaceClass, singletonClass);
82                 } catch (ClassNotFoundException e) {
83                     LOG.warn("ClassNotFoundException on Class.forName: {}", interfaceName, e);
84                 }
85             }
86         });
87         singletons.stream().filter(singletonClass -> singletonClass.getName().startsWith(prefix))
88                 .forEach(singletonClass -> singletonConsumer.accept(singletonClass));
89     }
90 }