001package io.avaje.inject;
002
003import io.avaje.lang.NonNullApi;
004import io.avaje.lang.Nullable;
005
006import java.lang.annotation.Annotation;
007import java.lang.reflect.Type;
008import java.util.List;
009import java.util.Map;
010import java.util.Optional;
011
012/**
013 * Holds beans created by dependency injection.
014 * <p>
015 * The beans have singleton scope, support lifecycle methods for postConstruct and
016 * preDestroy and are created (wired) via dependency injection.
017 * </p>
018 *
019 * <h3>Create a BeanScope</h3>
020 * <p>
021 * We can programmatically create a BeanScope via {@code BeanScope.builder()}.
022 * </p>
023 * <pre>{@code
024 *
025 *   // create a BeanScope ...
026 *
027 *   try (BeanScope scope = BeanScope.builder()
028 *     .build()) {
029 *
030 *     CoffeeMaker coffeeMaker = context.get(CoffeeMaker.class);
031 *     coffeeMaker.makeIt()
032 *   }
033 *
034 * }</pre>
035 *
036 * <h3>External dependencies</h3>
037 * <p>
038 * We can supporting external dependencies when creating the BeanScope. We need to do 2 things.
039 * we need to specify these via
040 * </p>
041 * <ul>
042 *   <li>
043 *       1. Specify the external dependency via {@code @InjectModule(requires=...)}.
044 *       Otherwise at compile time the annotation processor detects it as a missing dependency and we can't compile.
045 *   </li>
046 *   <li>
047 *       2. Provide the dependency when creating the BeanScope
048 *   </li>
049 * </ul>
050 * <p>
051 * For example, given we have Pump as an externally provided dependency.
052 *
053 * <pre>{@code
054 *
055 *   // tell the annotation processor Pump is provided externally
056 *   // otherwise it thinks we have a missing dependency
057 *
058 *   @InjectModule(requires=Pump.class)
059 *
060 * }</pre>
061 * <p>
062 * When we build the BeanScope provide the dependency via {@link BeanScopeBuilder#bean(Class, Object)}.
063 *
064 * <pre>{@code
065 *
066 *   // provide external dependencies ...
067 *   Pump pump = ...
068 *
069 *   try (BeanScope scope = BeanScope.builder()
070 *     .bean(Pump.class, pump)
071 *     .build()) {
072 *
073 *     CoffeeMaker coffeeMaker = context.get(CoffeeMaker.class);
074 *     coffeeMaker.makeIt()
075 *   }
076 *
077 * }</pre>
078 */
079@NonNullApi
080public interface BeanScope extends AutoCloseable {
081
082  /**
083   * Build a bean scope with options for shutdown hook and supplying external dependencies.
084   * <p>
085   * We can optionally:
086   * <ul>
087   *   <li>Provide external dependencies</li>
088   *   <li>Specify a parent BeanScope</li>
089   *   <li>Specify specific modules to wire</li>
090   *   <li>Specify to include a shutdown hook (to fire preDestroy lifecycle methods)</li>
091   *   <li>Use {@code forTesting()} to specify mocks and spies to use when wiring tests</li>
092   * </ul>
093   *
094   * <pre>{@code
095   *
096   *   // create a BeanScope ...
097   *
098   *   try (BeanScope scope = BeanScope.builder()
099   *     .build()) {
100   *
101   *     CoffeeMaker coffeeMaker = context.get(CoffeeMaker.class);
102   *     coffeeMaker.makeIt()
103   *   }
104   *
105   * }</pre>
106   */
107  static BeanScopeBuilder builder() {
108    return new DBeanScopeBuilder();
109  }
110
111  /**
112   * Return a single bean given the type.
113   *
114   * <pre>{@code
115   *
116   *   CoffeeMaker coffeeMaker = beanScope.get(CoffeeMaker.class);
117   *   coffeeMaker.brew();
118   *
119   * }</pre>
120   *
121   * @param type an interface or bean type
122   * @throws java.util.NoSuchElementException When no matching bean is found
123   */
124  <T> T get(Class<T> type);
125
126  /**
127   * Return a single bean given the type and name.
128   *
129   * <pre>{@code
130   *
131   *   Heater heater = beanScope.get(Heater.class, "electric");
132   *   heater.heat();
133   *
134   * }</pre>
135   *
136   * @param type an interface or bean type
137   * @param name the name qualifier of a specific bean
138   * @throws java.util.NoSuchElementException When no matching bean is found
139   */
140  <T> T get(Class<T> type, @Nullable String name);
141
142  /**
143   * Return a single bean given the generic type and name.
144   *
145   * @param type The generic type
146   * @param name the name qualifier of a specific bean
147   * @throws java.util.NoSuchElementException When no matching bean is found
148   */
149  <T> T get(Type type, @Nullable String name);
150
151  /**
152   * Optionally return a single bean given the type and empty if it is not found.
153   *
154   * @param type an interface or bean type
155   */
156  <T> Optional<T> getOptional(Class<T> type);
157
158  /**
159   * Optionally return a single bean given the type and name and empty if it is not found.
160   *
161   * @param type an interface or bean type
162   * @param name the name qualifier of a specific bean
163   */
164  <T> Optional<T> getOptional(Type type, @Nullable String name);
165
166  /**
167   * Return the list of beans that have an annotation. The annotation must have a @Retention policy of RUNTIME
168   *
169   * <pre>{@code
170   *
171   *   // e.g. register all controllers with web a framework
172   *   // .. where Controller is an annotation on the beans
173   *
174   *   List<Object> controllers = beanScope.listByAnnotation(Controller.class);
175   *
176   * }</pre>
177   *
178   * @param annotation An annotation class.
179   */
180  List<Object> listByAnnotation(Class<? extends Annotation> annotation);
181
182  /**
183   * Return the list of beans for a given type.
184   *
185   * <pre>{@code
186   *
187   *   // e.g. register all routes for a web framework
188   *
189   *   List<WebRoute> routes = beanScope.list(WebRoute.class);
190   *
191   * }</pre>
192   *
193   * @param type The type of beans to return.
194   */
195  <T> List<T> list(Class<T> type);
196
197  /**
198   * Return the list of beans that implement the given type.
199   */
200  <T> List<T> list(Type type);
201
202  /**
203   * Return the list of beans that implement the interface sorting by priority.
204   */
205  <T> List<T> listByPriority(Class<T> type);
206
207  /**
208   * Return the beans that implement the interface sorting by the priority annotation used.
209   * <p>
210   * The priority annotation will typically be either <code>javax.annotation.Priority</code>
211   * or <code>jakarta.annotation.Priority</code>.
212   *
213   * @param type     The interface type of the beans to return
214   * @param priority The priority annotation used to sort the beans
215   */
216  <T> List<T> listByPriority(Class<T> type, Class<? extends Annotation> priority);
217
218  /**
219   * Return the beans for this type mapped by their qualifier name.
220   * <p>
221   * Beans with no qualifier name get a generated unique key to use instead.
222   */
223  <T> Map<String, T> map(Type type);
224
225  /**
226   * Return all the bean entries from the scope.
227   * <p>
228   * The bean entries include entries from the parent scope if it has one.
229   *
230   * @return All bean entries from the scope.
231   */
232  List<BeanEntry> all();
233
234  /**
235   * Return true if the bean scope contains the given type.
236   */
237  boolean contains(Type type);
238
239  /**
240   * Return true if the bean scope contains the given type.
241   */
242  boolean contains(String type);
243
244  /**
245   * Close the scope firing any <code>@PreDestroy</code> lifecycle methods.
246   */
247  @Override
248  void close();
249}