001package io.avaje.inject;
002
003import java.io.Closeable;
004import java.lang.annotation.Annotation;
005import java.util.List;
006
007/**
008 * Holds beans created by dependency injection.
009 * <p>
010 * The beans have singleton scope, support lifecycle methods for postConstruct and
011 * preDestroy and are created (wired) via dependency injection.
012 * </p>
013 *
014 * <h3>Create a BeanContext</h3>
015 * <p>
016 * We can programmatically create a BeanContext via BeanContextBuilder.
017 * </p>
018 * <pre>{@code
019 *
020 *   // create a BeanContext ...
021 *
022 *   try (BeanContext context = new BeanContextBuilder()
023 *     .build()) {
024 *
025 *     CoffeeMaker coffeeMaker = context.getBean(CoffeeMaker.class);
026 *     coffeeMaker.makeIt()
027 *   }
028 *
029 * }</pre>
030 *
031 * <h3>Implicitly used</h3>
032 * <p>
033 * The BeanContext is implicitly used by SystemContext.  It will be created as needed and
034 * a shutdown hook will close the underlying BeanContext on JVM shutdown.
035 * </p>
036 * <pre>{@code
037 *
038 *   // BeanContext created as needed under the hood
039 *
040 *   CoffeeMaker coffeeMaker = SystemContext.getBean(CoffeeMaker.class);
041 *   coffeeMaker.brew();
042 *
043 * }</pre>
044 */
045public interface BeanContext extends Closeable {
046
047  /**
048   * Build a bean context with options for shutdown hook and supplying test doubles.
049   * <p>
050   * We would choose to use BeanContextBuilder in test code (for component testing)
051   * as it gives us the ability to inject test doubles, mocks, spy's etc.
052   * </p>
053   *
054   * <pre>{@code
055   *
056   *   @Test
057   *   public void someComponentTest() {
058   *
059   *     MyRedisApi mockRedis = mock(MyRedisApi.class);
060   *     MyDbApi mockDatabase = mock(MyDbApi.class);
061   *
062   *     try (BeanContext context = BeanContext.newBuilder()
063   *       .withBeans(mockRedis, mockDatabase)
064   *       .build()) {
065   *
066   *       // built with test doubles injected ...
067   *       CoffeeMaker coffeeMaker = context.getBean(CoffeeMaker.class);
068   *       coffeeMaker.makeIt();
069   *
070   *       assertThat(...
071   *     }
072   *   }
073   *
074   * }</pre>
075   */
076  static BeanContextBuilder newBuilder() {
077    return new DBeanContextBuilder();
078  }
079
080  /**
081   * Return the module name of the bean context.
082   *
083   * @see ContextModule
084   */
085  String getName();
086
087  /**
088   * Return the names of module features this bean context provides.
089   *
090   * @see ContextModule
091   */
092  String[] getProvides();
093
094  /**
095   * Return the names of modules this bean context depends on.
096   *
097   * @see ContextModule
098   */
099  String[] getDependsOn();
100
101  /**
102   * Return a single bean given the type.
103   *
104   * <pre>{@code
105   *
106   *   CoffeeMaker coffeeMaker = beanContext.getBean(CoffeeMaker.class);
107   *   coffeeMaker.brew();
108   *
109   * }</pre>
110   *
111   * @param type an interface or bean type
112   */
113  <T> T getBean(Class<T> type);
114
115  /**
116   * Return a single bean given the type and name.
117   *
118   * <pre>{@code
119   *
120   *   Heater heater = beanContext.getBean(Heater.class, "electric");
121   *   heater.heat();
122   *
123   * }</pre>
124   *
125   * @param type an interface or bean type
126   * @param name the name qualifier of a specific bean
127   */
128  <T> T getBean(Class<T> type, String name);
129
130  /**
131   * Return the wiring candidate bean with name and priority.
132   */
133  <T> BeanEntry<T> candidate(Class<T> type, String name);
134
135  /**
136   * Return the list of beans that have an annotation.
137   *
138   * <pre>{@code
139   *
140   *   // e.g. register all controllers with web a framework
141   *   // .. where Controller is an annotation on the beans
142   *
143   *   List<Object> controllers = SystemContext.getBeansWithAnnotation(Controller.class);
144   *
145   * }</pre>
146   *
147   * <p>
148   * The classic use case for this is registering controllers or routes to
149   * web frameworks like Sparkjava, Javlin, Rapidoid etc.
150   *
151   * @param annotation An annotation class.
152   */
153  List<Object> getBeansWithAnnotation(Class<?> annotation);
154
155  /**
156   * Return the list of beans that implement the interface.
157   *
158   * <pre>{@code
159   *
160   *   // e.g. register all routes for a web framework
161   *
162   *   List<WebRoute> routes = SystemContext.getBeans(WebRoute.class);
163   *
164   * }</pre>
165   *
166   * @param interfaceType An interface class.
167   */
168  <T> List<T> getBeans(Class<T> interfaceType);
169
170  /**
171   * Return the list of beans that implement the interface sorting by priority.
172   */
173  <T> List<T> getBeansByPriority(Class<T> interfaceType);
174
175  /**
176   * Return the beans that implement the interface sorting by the priority annotation used.
177   * <p>
178   * The priority annotation will typically be either <code>javax.annotation.Priority</code>
179   * or <code>jakarta.annotation.Priority</code>.
180   *
181   * @param interfaceType The interface type of the beans to return
182   * @param priority      The priority annotation used to sort the beans
183   */
184  <T> List<T> getBeansByPriority(Class<T> interfaceType, Class<? extends Annotation> priority);
185
186  /**
187   * Sort the beans by javax.annotation.Priority annotation.
188   *
189   * @param list The beans to sort by priority
190   * @return A new list of beans sorted by priority
191   */
192  <T> List<T> sortByPriority(List<T> list);
193
194  /**
195   * Sort the beans using the given Priority annotation.
196   * <p>
197   * The priority annotation will typically be either <code>javax.annotation.Priority</code>
198   * or <code>jakarta.annotation.Priority</code>.
199   *
200   * @param list     The beans to sort by priority
201   * @param priority The priority annotation used to sort the beans
202   * @return A new list of beans sorted by priority
203   */
204  <T> List<T> sortByPriority(List<T> list, final Class<? extends Annotation> priority);
205
206  /**
207   * Start the context firing any <code>@PostConstruct</code> methods.
208   */
209  void start();
210
211  /**
212   * Close the context firing any <code>@PreDestroy</code> methods.
213   */
214  void close();
215}