2 * Copyright © 2018 Red Hat, Inc. and others.
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
8 package org.opendaylight.odlguice.inject;
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;
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;
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.
28 @SuppressWarnings("rawtypes")
29 public class ClassPathScanner {
30 private static final Logger LOG = LoggerFactory.getLogger(ClassPathScanner.class);
32 private final Map<String, Class> implementations = new HashMap<>();
33 private final Set<Class<?>> singletons = new HashSet<>();
36 * Create a class path scanner, scanning packages with the given prefix for {@literal @}Singleton annotated classes.
38 * @param prefix The package prefix.
40 public ClassPathScanner(String prefix) {
41 try (ScanResult scanResult =
44 .enableAnnotationInfo()
45 .whitelistPackages(prefix)
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());
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);
69 * Binds all {@link Singleton} annotated classes discovered by scanning the class path to all their interfaces.
71 * @param prefix the package prefix of Singleton implementations to consider
72 * @param binder The binder (modeled as a generic consumer)
74 public void bindAllSingletons(String prefix, BiConsumer<Class, Class> binder, Consumer<Class> singletonConsumer) {
75 implementations.forEach((interfaceName, singletonClass) -> {
76 if (singletonClass.getName().startsWith(prefix)) {
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);
87 singletons.stream().filter(singletonClass -> singletonClass.getName().startsWith(prefix))
88 .forEach(singletonClass -> singletonConsumer.accept(singletonClass));