Merge "Add Mycilla code to odlguice"
[odlguice.git] / inject / inject-guice-testutils / src / main / java / org / opendaylight / infrautils / inject / guice / testutils / GuiceRule.java
1 /*
2  * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
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.infrautils.inject.guice.testutils;
9
10 import com.google.inject.ConfigurationException;
11 import com.google.inject.Guice;
12 import com.google.inject.Injector;
13 import com.google.inject.Module;
14 import com.google.inject.Stage;
15 import java.util.Arrays;
16 import java.util.List;
17 import java.util.stream.Collectors;
18 import org.junit.rules.MethodRule;
19 import org.junit.runners.model.FrameworkMethod;
20 import org.junit.runners.model.Statement;
21 import org.opendaylight.infrautils.inject.PostFullSystemInjectionListener;
22 import org.opendaylight.odlguice.inject.guice.extensions.closeable.CloseableInjector;
23
24 /**
25  * JUnit Rule which initializes Guice {@link Injector} for tests.
26  *
27  * <p>Usage:
28  * <pre>
29  *   public {@literal @}Rule GuiceRule guice = new GuiceRule(YourGuiceModule.class);
30  *
31  *   {@literal @}Inject SomeClass someClass;
32  * </pre>
33  *
34  * @author Michael Vorburger.ch
35  */
36 public class GuiceRule implements MethodRule {
37
38     /**
39      * Default Stage PRODUCTION.
40      * Note that this is different from Guice's DEVELOPMENT default.
41      * We do this to avoid having to declare bindings of Listeners asEagerSingleton(),
42      * because in typical OpenDaylight projects there are Listener classes which are not @Inject,
43      * but must still be created (so that they're registered).
44      * See <a href="https://github.com/google/guice/wiki/Bootstrap">Guice documentation</a>.
45      */
46     protected static final Stage DEFAULT_STAGE = Stage.PRODUCTION;
47
48     protected final Iterable<? extends Module> modules;
49     protected final Stage stage;
50
51     protected Injector injector;
52
53     public GuiceRule(Module... modules) {
54         this(DEFAULT_STAGE, modules);
55     }
56
57     protected GuiceRule(Stage stage, Module... modules) {
58         this.modules = Arrays.asList(modules);
59         this.stage = stage;
60     }
61
62     @SafeVarargs
63     public GuiceRule(Class<? extends Module>... moduleClasses) {
64         this.modules = createModules(Arrays.asList(moduleClasses));
65         this.stage = DEFAULT_STAGE;
66     }
67
68     private static Iterable<? extends Module> createModules(List<Class<? extends Module>> moduleClasses) {
69         return moduleClasses.stream().map(klass -> {
70             try {
71                 return klass.getConstructor().newInstance();
72             } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) {
73                 throw new IllegalArgumentException("newInstance() failed: " + klass.getName(), e);
74             }
75         }).collect(Collectors.toList());
76     }
77
78     @Override
79     public Statement apply(Statement base, FrameworkMethod method, Object target) {
80         return new Statement() {
81             @Override
82             public void evaluate() throws Throwable {
83                 try {
84                     setUpGuice(target);
85                     base.evaluate();
86                 } finally {
87                     tearDownGuice();
88                 }
89             }
90         };
91     }
92
93     protected void setUpGuice(Object target) {
94         injector = Guice.createInjector(stage, modules);
95         injector.injectMembers(target);
96
97         try {
98             injector.getInstance(PostFullSystemInjectionListener.class).onFullSystemInjected();
99         } catch (ConfigurationException e) {
100             // It's OK if we didn't bind a PostFullSystemInjectionListener.
101         }
102     }
103
104     protected void tearDownGuice() {
105         if (injector != null) {
106             // http://code.mycila.com/guice/#3-jsr-250
107             try {
108                 injector.getInstance(CloseableInjector.class).close();
109             } catch (ConfigurationException e) {
110                 throw new IllegalStateException("You forgot to either add GuiceRule(..., AnnotationsModule.class), "
111                         + "or in your Module use an install(new AnnotationsModule()) with "
112                         + AnnotationsModule.class.getName(), e);
113             }
114         }
115     }
116
117 }