001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.commons.inject;
018
019import static org.apache.juneau.commons.reflect.ReflectionUtils.*;
020import static org.apache.juneau.commons.utils.CollectionUtils.*;
021import static org.apache.juneau.commons.utils.Shorts.*;
022import static org.apache.juneau.commons.utils.StringUtils.*;
023
024import java.util.*;
025import java.util.concurrent.*;
026import java.util.function.*;
027import org.apache.juneau.commons.collections.*;
028import org.apache.juneau.commons.logging.*;
029import org.apache.juneau.commons.reflect.*;
030import org.apache.juneau.commons.settings.*;
031
032/**
033 * Basic implementation of {@link WritableBeanStore}.
034 *
035 * <p>
036 * A simple storage database for beans keyed by type and name.
037 * Used to retrieve and instantiate beans using an injection-like API.
038 * It's similar in concept to the injection framework of Spring but greatly simplified in function and not intended to implement a full-fledged injection framework.
039 *
040 * <p>
041 * Beans can be stored with or without names.  Named beans are typically resolved using
042 * the <ja>@Name</ja> or <ja>@Qualified</ja> annotations on constructor or method parameters.
043 *
044 * <p>
045 * This implementation is thread-safe and supports parent bean stores for hierarchical bean resolution.
046 *
047 * <h5 class='section'>Resolution order:</h5>
048 * <p>
049 * Bean lookups consult the following sources in this order, returning the first match:
050 * <ol>
051 *    <li>{@linkplain #BasicBeanStore(BeanStore,BeanStore) Overriding parent} (e.g. a Spring application context bridge), if non-{@code null}.
052 *    <li>Local entries added via {@link #addBean(Class,Object) addBean} / {@link #addSupplier(Class,Supplier) addSupplier}.
053 *    <li>Regular {@linkplain #BasicBeanStore(BeanStore) parent}, if non-{@code null}.
054 *    <li>Local default suppliers added via {@link #addDefaultSupplier(Class,Supplier) addDefaultSupplier} (memoizer-backed framework defaults).
055 * </ol>
056 *
057 * <h5 class='section'>See Also:</h5><ul>
058 *    <li class='jc'>{@link BeanStore} - Read-only bean lookup interface
059 *    <li class='jc'>{@link WritableBeanStore} - Writable bean store interface
060 * </ul>
061 */
062@SuppressWarnings({
063   "java:S135", // @PreDestroy/@Bean discovery loops use per-element continue guards for clarity; refactoring obscures the per-annotation filter chain
064   "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants
065   "java:S3011", // setAccessible(true) is required to invoke private/package-private @Bean members and @PreDestroy methods via reflection
066   "java:S3776", // matchesConditions and registerConfiguration intentionally centralize the @Conditional/@Bean discovery state machine; splitting hurts cohesion
067   "resource", // BeanStore is a fluent AutoCloseable; self-returns and owned/sentinel stores are not new resources to close.
068   "unchecked" // Bean maps are keyed by Class<T>, so (T)/(Supplier<T>)/(Class<? extends T>) casts on stored suppliers and bindings match the requested bean type
069})
070public class BasicBeanStore implements WritableBeanStore {
071   private static final RichLogger LOGGER = RichLogger.getLogger(BasicBeanStore.class.getName());
072
073   /**
074    * Static reusable empty instance.
075    *
076    * <p>
077    * Useful as a placeholder when an API requires a {@link BeanStore} but the caller has no beans to
078    * register &mdash; e.g. as the parent argument to a builder that only needs the bean store for
079    * downstream parameter resolution.
080    *
081    * <p>
082    * <b>Treat this instance as read-only.</b>  It's typed as the concrete class so it can be passed
083    * to APIs that take {@link WritableBeanStore} or {@code BasicBeanStore}, but callers should not
084    * call any mutating methods on it &mdash; doing so would leak state between unrelated callers.
085    * Code that legitimately needs to add beans should construct its own {@code new BasicBeanStore()}.
086    */
087   public static final BasicBeanStore INSTANCE = new BasicBeanStore();
088
089   private final ConcurrentHashMap<Class<?>,ConcurrentHashMap<String,Supplier<?>>> entries;
090   private final BeanStore parent;
091   private final BeanStore overridingParent;
092   @SuppressWarnings({
093      "java:S3077" // volatile is required here for correct double-checked-locking safe-publication of the lazily-created StackOverlay in pushOverlay(); the reference is publish-once and never compound-mutated.
094   })
095   private volatile StackOverlay overlayStack;
096   @SuppressWarnings({
097      "java:S3077" // volatile is required for safe publication of the lazily-created holder; the reference is publish-once and never compound-mutated.
098   })
099   private volatile Extras extras;
100   @SuppressWarnings({
101      "java:S3077" // volatile is required for safe publication of the lazily-created holder; the reference is publish-once and never compound-mutated.
102   })
103   private volatile Tracking tracking;
104   private volatile boolean closed;
105
106   /**
107    * Rarely-used state, created on first write so a short-lived store that only holds a few entries (e.g. one per
108    * request) doesn't allocate it.
109    */
110   private static final class Extras {
111      final ConcurrentHashMap<Class<?>,ConcurrentHashMap<String,Supplier<?>>> defaults = new ConcurrentHashMap<>();
112      // Holds only non-default metadata (@Primary / @Order / priority); an absent entry means BeanSourceMeta.DEFAULT.
113      final ConcurrentHashMap<Class<?>,ConcurrentHashMap<String,BeanSourceMeta>> entryMetadata = new ConcurrentHashMap<>();
114      final ConcurrentHashMap<Class<?>,Class<?>> typeBindings = new ConcurrentHashMap<>();
115      final Set<Class<?>> registeredConfigurations = Collections.synchronizedSet(new HashSet<>());
116   }
117
118   /** Beans handed out by this store, in resolution order, for {@link #close()} callbacks.  Guarded by itself. */
119   private static final class Tracking {
120      final List<Object> beans = new ArrayList<>(4);
121      final Set<Object> identities = Collections.newSetFromMap(new IdentityHashMap<>(8));
122   }
123
124   private Extras extras() {
125      var x = extras;
126      if (x == null) {
127         synchronized (this) {
128            x = extras;
129            if (x == null) {
130               x = new Extras();
131               extras = x;
132            }
133         }
134      }
135      return x;
136   }
137
138   private ConcurrentHashMap<String,Supplier<?>> defaultTypeMap(Class<?> beanType) {
139      var x = extras;
140      return x == null ? null : x.defaults.get(beanType);
141   }
142
143   private void putEntryMeta(Class<?> beanType, String key, BeanSourceMeta meta) {
144      if (meta == BeanSourceMeta.DEFAULT) {
145         // Default metadata is represented by absence; drop any non-default value this key previously had.
146         var x = extras;
147         if (x != null) {
148            var m = x.entryMetadata.get(beanType);
149            if (m != null)
150               m.remove(key);
151         }
152      } else {
153         extras().entryMetadata.computeIfAbsent(beanType, k -> new ConcurrentHashMap<>()).put(key, meta);
154      }
155   }
156
157   /**
158    * No-arg constructor.  Equivalent to {@code new BasicBeanStore(null)}.
159    *
160    * <p>
161    * Creates a standalone, parent-less bean store.  All lookups consult only locally-registered beans and
162    * default suppliers.
163    */
164   public BasicBeanStore() {
165      this(null, null);
166   }
167
168   /**
169    * Constructor.
170    *
171    * @param parent The parent bean store.  Can be <jk>null</jk>.  Bean searches are performed recursively up this parent chain
172    *    <i>after</i> local entries are checked.
173    */
174   public BasicBeanStore(BeanStore parent) {
175      this(parent, null);
176   }
177
178   /**
179    * Constructor that accepts an overriding parent bean store.
180    *
181    * <p>
182    * The {@code overridingParent} is consulted <i>before</i> any local entries during bean lookup, allowing an outer
183    * scope (typically a Spring {@code ApplicationContext} bridge) to take precedence over local registrations.  The
184    * regular {@code parent} continues to be consulted as a fallback after local entries.
185    *
186    * <p>
187    * Final resolution order:
188    * <ol>
189    *    <li>{@code overridingParent} (if non-{@code null})
190    *    <li>local entries (added via {@link #addBean(Class,Object) addBean} / {@link #addSupplier(Class,Supplier) addSupplier})
191    *    <li>regular {@code parent} (if non-{@code null})
192    *    <li>local default suppliers (added via {@link #addDefaultSupplier(Class,Supplier) addDefaultSupplier})
193    * </ol>
194    *
195    * @param parent The parent bean store, used as a fallback after local entries.  Can be <jk>null</jk> (this store then has no parent fallback).
196    * @param overridingParent The overriding parent bean store, consulted before local entries.  Can be <jk>null</jk> (no overriding parent is consulted).
197    */
198   public BasicBeanStore(BeanStore parent, BeanStore overridingParent) {
199      this.parent = parent;
200      this.overridingParent = overridingParent;
201      entries = new ConcurrentHashMap<>();
202      addSupplier(BasicBeanStore.class, ()->this, null);
203      addSupplier(BeanStore.class, ()->this, null);
204      addSupplier(WritableBeanStore.class, ()->this, null);
205   }
206
207   /**
208    * Same as {@link #addBean(Class,Object)} but returns the bean instead of this object for fluent calls.
209    *
210    * @param <T> The class to associate this bean with.
211    * @param beanType The class to associate this bean with.  Must not be <jk>null</jk>.
212    * @param bean The bean.  Can be <jk>null</jk> (stored as an explicit <jk>null</jk> binding, distinct from no binding at all).
213    * @return The bean.
214    */
215   @Override
216   public <T> T add(Class<T> beanType, T bean) {
217      add(beanType, bean, null);
218      return bean;
219   }
220
221   /**
222    * Same as {@link #addBean(Class,Object,String)} but returns the bean instead of this object for fluent calls.
223    *
224    * @param <T> The class to associate this bean with.
225    * @param beanType The class to associate this bean with.  Must not be <jk>null</jk>.
226    * @param bean The bean.  Can be <jk>null</jk> (stored as an explicit <jk>null</jk> binding, distinct from no binding at all).
227    * @param name The bean name if this is a named bean.  Can be <jk>null</jk> for unnamed beans.
228    * @return The bean.
229    */
230   @Override
231   public <T> T add(Class<T> beanType, T bean, String name) {
232      addBean(beanType, bean, name);
233      return bean;
234   }
235
236   /**
237    * Adds an unnamed bean of the specified type to this store.
238    *
239    * @param <T> The class to associate this bean with.
240    * @param beanType The class to associate this bean with.  Must not be <jk>null</jk>.
241    * @param bean The bean.  Can be <jk>null</jk> (stored as an explicit <jk>null</jk> binding, distinct from no binding at all).
242    * @return This object.
243    */
244   @Override
245   public <T> BasicBeanStore addBean(Class<T> beanType, T bean) {
246      return addBean(beanType, bean, null);
247   }
248
249   /**
250    * Adds a named bean of the specified type to this store.
251    *
252    * @param <T> The class to associate this bean with.
253    * @param beanType The class to associate this bean with.  Must not be <jk>null</jk>.
254    * @param bean The bean.  Can be <jk>null</jk> (stored as an explicit <jk>null</jk> binding, distinct from no binding at all).
255    * @param name The bean name if this is a named bean.  Can be <jk>null</jk> for unnamed beans.
256    * @return This object.
257    */
258   @Override
259   public <T> BasicBeanStore addBean(Class<T> beanType, T bean, String name) {
260      return addSupplier(beanType, () -> bean, name);
261   }
262
263   /**
264    * Adds a supplier for an unnamed bean of the specified type to this store.
265    *
266    * <p>
267    * The supplier will be invoked lazily when the bean is first requested.
268    *
269    * @param <T> The class to associate this bean with.
270    * @param beanType The class to associate this bean with.  Must not be <jk>null</jk>.
271    * @param bean The bean supplier.  Must not be <jk>null</jk>.
272    * @return This object.
273    */
274   @Override
275   public <T> BasicBeanStore addSupplier(Class<T> beanType, Supplier<T> bean) {
276      return addSupplier(beanType, bean, null);
277   }
278
279   /**
280    * Adds a supplier for a named bean of the specified type to this store.
281    *
282    * <p>
283    * The supplier will be invoked lazily when the bean is first requested.
284    *
285    * @param <T> The class to associate this bean with.
286    * @param beanType The class to associate this bean with.  Must not be <jk>null</jk>.
287    * @param bean The bean supplier.  Must not be <jk>null</jk>.
288    * @param name The bean name if this is a named bean.  Can be <jk>null</jk> for unnamed beans.
289    * @return This object.
290    */
291   @Override
292   public <T> BasicBeanStore addSupplier(Class<T> beanType, Supplier<T> bean, String name) {
293      checkOpen();
294      var typeMap = entries.computeIfAbsent(beanType, k -> new ConcurrentHashMap<>());
295      var key = emptyIfNull(name);
296      typeMap.put(key, bean);
297      putEntryMeta(beanType, key, BeanSourceMeta.DEFAULT);
298      return this;
299   }
300
301   /**
302    * Adds a fallback supplier for an unnamed bean of the specified type to this store.
303    *
304    * <p>
305    * Default suppliers are consulted only after local {@linkplain #addBean(Class,Object) entries} and the regular
306    * {@linkplain #BasicBeanStore(BeanStore) parent} have been searched.  They are intended for memoizer-backed
307    * framework defaults that should not shadow explicit user registrations or beans inherited from an
308    * {@linkplain #BasicBeanStore(BeanStore,BeanStore) overriding parent} (e.g. Spring).
309    *
310    * @param <T> The bean type.
311    * @param beanType The bean type.  Must not be <jk>null</jk>.
312    * @param supplier The bean supplier.  Must not be <jk>null</jk>.
313    * @return This object.
314    */
315   @Override
316   public <T> BasicBeanStore addDefaultSupplier(Class<T> beanType, Supplier<T> supplier) {
317      return addDefaultSupplier(beanType, supplier, null);
318   }
319
320   /**
321    * Adds a fallback supplier for a named bean of the specified type to this store.
322    *
323    * <p>
324    * See {@link #addDefaultSupplier(Class,Supplier)} for ordering semantics.
325    *
326    * @param <T> The bean type.
327    * @param beanType The bean type.  Must not be <jk>null</jk>.
328    * @param supplier The bean supplier.  Must not be <jk>null</jk>.
329    * @param name The bean name.  Can be <jk>null</jk> for unnamed beans.
330    * @return This object.
331    */
332   @Override
333   public <T> BasicBeanStore addDefaultSupplier(Class<T> beanType, Supplier<T> supplier, String name) {
334      checkOpen();
335      var typeMap = extras().defaults.computeIfAbsent(beanType, k -> new ConcurrentHashMap<>());
336      var key = emptyIfNull(name);
337      typeMap.put(key, supplier);
338      return this;
339   }
340
341   /**
342    * Promotes every locally-registered default supplier to a local entry supplier of the same name and type.
343    *
344    * <p>
345    * Default suppliers normally resolve at tier 4 (after the regular parent walk), which means a parent
346    * store's default supplier wins over this store's default supplier when both are registered for the same
347    * type.  Promoting moves each default into the tier-2 local-entry slot so that this store's framework
348    * defaults take precedence over the parent's same-type defaults &mdash; useful for embedded sub-contexts
349    * (e.g. per-mixin {@code RestContext}s) that need their own framework objects (serializer set, parser set,
350    * call logger, etc.) even though the parent is parent-linked for non-framework bean lookups.
351    *
352    * <p>
353    * The original default-supplier registrations are left in place; promoted entries that already exist as
354    * local entries are not overwritten.  Promotion is idempotent and only affects suppliers registered prior
355    * to the call &mdash; later {@code addDefaultSupplier(...)} calls do not retroactively promote.
356    *
357    * @return This object.
358    * @since 10.0.0
359    */
360   public BasicBeanStore promoteDefaultsToLocalSuppliers() {
361      checkOpen();
362      var x = extras;
363      if (x != null)
364         x.defaults.forEach((beanType, typeMap) -> typeMap.forEach((name, supplier) ->
365            entries.computeIfAbsent(beanType, k -> new ConcurrentHashMap<>()).putIfAbsent(name, supplier)));
366      return this;
367   }
368
369   @Override
370   public Snapshot pushOverlay(BeanStore overlay) {
371      Objects.requireNonNull(overlay, "overlay must not be null");
372      checkOpen();
373      var stack = overlayStack;
374      if (stack == null) {
375         synchronized (this) {
376            stack = overlayStack;
377            if (stack == null) {
378               stack = new StackOverlay();
379               overlayStack = stack;
380            }
381         }
382      }
383      stack.push(overlay);
384      return new Snapshot(this, overlay);
385   }
386
387   @Override
388   public void popOverlay(Snapshot snapshot) {
389      Objects.requireNonNull(snapshot, "snapshot must not be null");
390      checkOpen();
391      if (snapshot.owner() != this)
392         throw new IllegalStateException("Snapshot does not belong to this BeanStore (foreign-snapshot pop).");
393      var stack = overlayStack;
394      if (stack == null || stack.depth() == 0)
395         throw new IllegalStateException("popOverlay invoked on an empty overlay stack.");
396      // LIFO discipline — the snapshot must identify the current top-of-stack frame.
397      var top = stack.peek();
398      if (top != snapshot.frame())
399         throw new IllegalStateException(
400            "Out-of-order popOverlay: supplied snapshot does not identify the top-of-stack overlay frame.");
401      stack.pop();
402   }
403
404   /**
405    * Removes all beans from this store.
406    *
407    * <p>
408    * This operation only affects this store and does not affect the parent bean store.
409    *
410    * @return This object.
411    */
412   @Override
413   public BasicBeanStore clear() {
414      checkOpen();
415      entries.clear();
416      var x = extras;
417      if (x != null) {
418         x.defaults.clear();
419         x.entryMetadata.clear();
420         x.registeredConfigurations.clear();
421      }
422      return this;
423   }
424
425   /**
426    * Returns the unnamed bean of the specified type.
427    *
428    * <p>
429    * If no unnamed bean is found in this store, searches the parent store recursively.
430    *
431    * @param <T> The type of bean to return.
432    * @param beanType The type of bean to return.  Must not be <jk>null</jk>.
433    * @return The bean, or {@link Optional#empty()} if not found.
434    */
435   @Override
436   public <T> Optional<T> getBean(Class<T> beanType) {
437      return getBean(beanType, null);
438   }
439
440
441   /**
442    * Returns the named bean of the specified type.
443    *
444    * <p>
445    * If no bean with the specified name is found in this store, searches the parent store recursively.
446    *
447    * @param <T> The type of bean to return.
448    * @param beanType The type of bean to return.  Must not be <jk>null</jk>.
449    * @param name The bean name.  Can be <jk>null</jk> for unnamed beans.
450    * @return The bean, or {@link Optional#empty()} if not found.
451    */
452   @Override
453   public <T> Optional<T> getBean(Class<T> beanType, String name) {
454      checkOpen();
455      if (name == null) {
456         var unnamed = resolve(beanType, null).map(Supplier::get);
457         if (unnamed.isPresent())
458            return unnamed.map(this::trackResolved);
459         return selectPrimary(beanType).map(this::trackResolved);
460      }
461      return resolve(beanType, name).map(Supplier::get).map(this::trackResolved);
462   }
463
464   /**
465    * Returns all beans of the specified type, keyed by bean name.
466    *
467    * <p>
468    * The map keys are the bean names (empty string for unnamed beans), and the values are the bean instances.
469    * Results from the parent store are included first, then beans from this store (which override parent beans with the same name).
470    *
471    * @param <T> The bean type.
472    * @param beanType The bean type.  Must not be <jk>null</jk>.
473    * @return A map of bean names to bean instances.  Never <jk>null</jk>.
474    */
475   @Override
476   public <T> Map<String,T> getBeansOfType(Class<T> beanType) {
477      checkOpen();
478      // Build the result respecting the priority order used by getBean / resolve:
479      //   defaults (lowest) < parent < entries (local) < overridingParent < overlayStack (highest)
480      // Higher-priority maps overwrite lower-priority ones with the same name.
481      Map<String,T> result = map();
482      var defaultMap = defaultTypeMap(beanType);
483      if (nn(defaultMap))
484         defaultMap.forEach((name, supplier) -> result.put(name, (T)supplier.get()));
485      if (nn(parent))
486         parent.getBeansOfType(beanType).forEach(result::put);
487      var typeMap = entries.get(beanType);
488      if (nn(typeMap))
489         typeMap.forEach((name, supplier) -> result.put(name, (T)supplier.get()));
490      if (nn(overridingParent))
491         overridingParent.getBeansOfType(beanType).forEach(result::put);
492      var stack = overlayStack;
493      if (nn(stack) && stack.depth() > 0)
494         stack.getBeansOfType(beanType).forEach(result::put);
495      if (result.isEmpty())
496         return result;
497      return result.entrySet().stream()
498         .sorted(Comparator.comparingInt(e -> beanOrder(beanType, e.getKey())))
499         .collect(LinkedHashMap::new, (m, e) -> m.put(e.getKey(), trackResolved(e.getValue())), Map::putAll);
500   }
501
502   /**
503    * Returns <jk>true</jk> if this store has a default supplier registered locally for the specified unnamed bean type.
504    *
505    * <p>
506    * Parent and overriding-parent stores are <i>not</i> consulted.
507    *
508    * @param beanType The bean type to check.  Must not be <jk>null</jk>.
509    * @return <jk>true</jk> if a default supplier for the unnamed bean type is registered on this store.
510    */
511   @Override
512   public boolean hasDefaultSupplier(Class<?> beanType) {
513      return hasDefaultSupplier(beanType, null);
514   }
515
516   /**
517    * Returns <jk>true</jk> if this store has a default supplier registered locally for the specified bean type and name.
518    *
519    * <p>
520    * Parent and overriding-parent stores are <i>not</i> consulted.
521    *
522    * @param beanType The bean type to check.  Must not be <jk>null</jk>.
523    * @param name The bean name.  Can be <jk>null</jk> for unnamed beans.
524    * @return <jk>true</jk> if a default supplier for the bean type and name is registered on this store.
525    */
526   @Override
527   public boolean hasDefaultSupplier(Class<?> beanType, String name) {
528      var typeMap = defaultTypeMap(beanType);
529      return nn(typeMap) && typeMap.containsKey(emptyIfNull(name));
530   }
531
532   /**
533    * Returns the default supplier registered locally for the specified unnamed bean type, or empty if none.
534    *
535    * <p>
536    * Parent and overriding-parent stores are <i>not</i> consulted.  This returns the supplier itself,
537    * unwrapped from any resolution chain &mdash; it lets callers promote a memoizer-backed default
538    * supplier into a higher-precedence layer (e.g. a local entry) without re-invoking the underlying
539    * factory.
540    *
541    * @param <T> The bean type.
542    * @param beanType The bean type to look up.  Must not be <jk>null</jk>.
543    * @return The locally-registered default supplier, or {@link Optional#empty()} if not present.
544    */
545   public <T> Optional<Supplier<T>> getDefaultSupplier(Class<T> beanType) {
546      return getDefaultSupplier(beanType, null);
547   }
548
549   /**
550    * Returns the default supplier registered locally for the specified bean type and name, or empty if none.
551    *
552    * <p>
553    * Parent and overriding-parent stores are <i>not</i> consulted.  This returns the supplier itself,
554    * unwrapped from any resolution chain &mdash; it lets callers promote a memoizer-backed default
555    * supplier into a higher-precedence layer (e.g. a local entry) without re-invoking the underlying
556    * factory.
557    *
558    * @param <T> The bean type.
559    * @param beanType The bean type to look up.  Must not be <jk>null</jk>.
560    * @param name The bean name.  Can be <jk>null</jk> for unnamed beans.
561    * @return The locally-registered default supplier, or {@link Optional#empty()} if not present.
562    */
563   public <T> Optional<Supplier<T>> getDefaultSupplier(Class<T> beanType, String name) {
564      var typeMap = defaultTypeMap(beanType);
565      if (typeMap == null)
566         return oe();
567      var supplier = typeMap.get(emptyIfNull(name));
568      return supplier == null ? oe() : o((Supplier<T>) supplier);
569   }
570
571   /**
572    * Returns <jk>true</jk> if this store contains at least one unnamed bean of the specified type.
573    *
574    * <p>
575    * If not found in this store, searches the parent store recursively.
576    *
577    * @param beanType The bean type to check.  Must not be <jk>null</jk>.
578    * @return <jk>true</jk> if this store contains at least one unnamed bean of the specified type.
579    */
580   @Override
581   public boolean hasBean(Class<?> beanType) {
582      return hasBean(beanType, null);
583   }
584
585   /**
586    * Returns <jk>true</jk> if this store contains a bean of the specified type and name.
587    *
588    * <p>
589    * If not found in this store, searches the parent store recursively.
590    *
591    * @param beanType The bean type to check.  Must not be <jk>null</jk>.
592    * @param name The bean name.  Can be <jk>null</jk> for unnamed beans.
593    * @return <jk>true</jk> if this store contains a bean of the specified type and name.
594    */
595   @Override
596   public boolean hasBean(Class<?> beanType, String name) {
597      return resolve(beanType, name).isPresent();
598   }
599
600   @Override
601   public <T> WritableBeanStore addBeanType(Class<T> beanType, Class<? extends T> implType) {
602      checkOpen();
603      extras().typeBindings.put(beanType, implType);
604      return this;
605   }
606
607   @Override
608   public <T> Optional<Class<? extends T>> getBeanType(Class<T> beanType) {
609      var x = extras;
610      var v = x == null ? null : (Class<? extends T>) x.typeBindings.get(beanType);
611      if (nn(v))
612         return o(v);
613      if (nn(parent))
614         return parent.getBeanType(beanType);
615      return oe();
616   }
617
618   /**
619    * Returns the supplier for an unnamed bean of the specified type.
620    *
621    * <p>
622    * If no supplier is found in this store, searches the parent store recursively.
623    *
624    * @param <T> The bean type.
625    * @param beanType The bean type.  Must not be <jk>null</jk>.
626    * @return The supplier, or {@link Optional#empty()} if no supplier of the specified type exists.
627    */
628   @Override
629   public <T> Optional<Supplier<T>> getBeanSupplier(Class<T> beanType) {
630      return getBeanSupplier(beanType, null);
631   }
632
633   /**
634    * Resolves a supplier for a bean of the specified type and name.
635    *
636    * <p>
637    * If no supplier with the specified name is found in this store, searches the parent store recursively.
638    *
639    * @param <T> The bean type.
640    * @param beanType The bean type.  Must not be <jk>null</jk>.
641    * @param name The bean name.  Can be <jk>null</jk> for unnamed beans.
642    * @return The supplier, or {@link Optional#empty()} if no supplier of the specified type and name exists.
643    */
644   protected <T> Optional<Supplier<T>> resolve(Class<T> beanType, String name) {
645      // (0) Push/pop overlay stack — wins over the construction-time overridingParent slot because pushed
646      // frames are "more recent".  Lazily initialized; null until the first pushOverlay() call.
647      var stack = overlayStack;
648      if (nn(stack) && stack.depth() > 0) {
649         var fromStack = stack.getBeanSupplier(beanType, name);
650         if (fromStack.isPresent())
651            return fromStack;
652      }
653      // (1) Overriding parent (e.g. Spring) — wins over local entries.
654      if (nn(overridingParent)) {
655         var fromOverriding = overridingParent.getBeanSupplier(beanType, name);
656         if (fromOverriding.isPresent())
657            return fromOverriding;
658      }
659      // (2) Local regular entries.
660      var typeMap = entries.get(beanType);
661      if (nn(typeMap)) {
662         var key = emptyIfNull(name);
663         var supplier = typeMap.get(key);
664         if (nn(supplier))
665            return o((Supplier<T>)supplier);
666      }
667      // (3) Regular parent fallback.
668      if (nn(parent)) {
669         var fromParent = parent.getBeanSupplier(beanType, name);
670         if (fromParent.isPresent())
671            return fromParent;
672      }
673      // (4) Local default suppliers (memoizer-backed framework defaults).
674      var defaultMap = defaultTypeMap(beanType);
675      if (nn(defaultMap)) {
676         var key = emptyIfNull(name);
677         var supplier = defaultMap.get(key);
678         if (nn(supplier))
679            return o((Supplier<T>)supplier);
680      }
681      return oe();
682   }
683
684   /**
685    * Returns the supplier for a named bean of the specified type.
686    *
687    * <p>
688    * If no supplier with the specified name is found in this store, searches the parent store recursively.
689    *
690    * @param <T> The bean type.
691    * @param beanType The bean type.  Must not be <jk>null</jk>.
692    * @param name The bean name.  Can be <jk>null</jk> for unnamed beans.
693    * @return The supplier, or {@link Optional#empty()} if no supplier of the specified type and name exists.
694    */
695   @Override
696   public <T> Optional<Supplier<T>> getBeanSupplier(Class<T> beanType, String name) {
697      checkOpen();
698      return resolve(beanType, name);
699   }
700
701   @Override
702   public WritableBeanStore registerConfiguration(Class<?> configType) {
703      checkOpen();
704      if (configType == null)
705         return this;
706      registerConfiguration(configType, extras().registeredConfigurations);
707      return this;
708   }
709
710   /**
711    * Finds and invokes a factory method that produces a bean of type <c>beanType</c>.
712    *
713    * <p>
714    * If <c>onClassOrObject</c> is a {@link Class}, only static methods are eligible.
715    * Otherwise, both instance and static methods on the object's class are eligible.
716    * A <jk>null</jk> <c>filter</c> accepts any qualifying method.
717    *
718    * <p>
719    * The search has two passes.  The first pass considers public methods only (unchanged behavior).  If it finds
720    * nothing, the second pass considers <em>non-public</em> methods across the class hierarchy, child class first,
721    * but only those carrying {@link Bean @Bean} (declared directly or inherited from a method they override; see
722    * {@link BeanAnnotation#find(MethodInfo)}).  Bridge/synthetic methods, interface static methods and deprecated
723    * methods are skipped, as are parent methods overridden by an already-seen child method.  A non-public method
724    * without {@link Bean @Bean} is never invoked, even when <c>filter</c> is <jk>null</jk>.
725    * Non-public methods are made accessible before invocation.
726    *
727    * <h5 class='section'>Example:</h5>
728    * <p class='bjava'>
729    *    <jk>public class</jk> MyResource {
730    *       <ja>@Bean</ja>
731    *       RichLogger logger() { <jk>return new</jk> RichLogger(); }  <jc>// Package-private, found by the second pass.</jc>
732    *    }
733    *
734    *    Optional&lt;RichLogger&gt; <jv>logger</jv> = <jv>beanStore</jv>.createBeanFromMethod(RichLogger.<jk>class</jk>, <jk>new</jk> MyResource());
735    * </p>
736    *
737    * @param <T> The bean type.
738    * @param beanType The type of bean to create.  Must not be <jk>null</jk>.
739    * @param onClassOrObject The object instance or {@link Class} whose methods are searched.
740    *    Must not be <jk>null</jk>.
741    * @param filter Optional predicate restricting which methods are eligible.  Can be <jk>null</jk> (any method qualifies).
742    * @param extraBeans Optional bean instances visible to parameter resolution for this call only.
743    * @return The created bean wrapped in an {@link Optional}, or {@link Optional#empty()} if no matching
744    *    factory method was found.
745    * @throws BeanCreationException If a matching method was found but threw an exception during invocation.
746    */
747   @Override
748   public <T> Optional<T> createBeanFromMethod(Class<T> beanType, Object onClassOrObject, Predicate<MethodInfo> filter, Object... extraBeans) {
749      reqnn("beanType", beanType);
750      reqnn("onClassOrObject", onClassOrObject);
751      Object resource = onClassOrObject instanceof Class ? null : onClassOrObject;
752      Class<?> resourceClass = onClassOrObject instanceof Class<?> onClassOrObject2 ? onClassOrObject2 : onClassOrObject.getClass();
753      var first = info(resourceClass)
754         .getPublicMethod(m ->
755            m.isNotDeprecated()
756            && m.hasReturnType(beanType)
757            && (filter == null || filter.test(m))
758            && (m.isStatic() || nn(resource))
759            && m.canResolveAllParameters(this, extraBeans))
760         .map(m -> {
761            try {
762               return m.<T>inject(this, resource, extraBeans);
763            } catch (Exception e) {
764               throw new BeanCreationException("Failed to create bean of type [" + beanType.getSimpleName() + "] via method [" + m.getName() + "]", e);
765            }
766         });
767      if (first.isPresent())
768         return first;
769      return createBeanFromNonPublicMethod(beanType, resourceClass, resource, filter, extraBeans);
770   }
771
772   /**
773    * Second pass of {@link #createBeanFromMethod}: non-public {@link Bean @Bean}-annotated methods, child class first.
774    */
775   private <T> Optional<T> createBeanFromNonPublicMethod(Class<T> beanType, Class<?> resourceClass, Object resource, Predicate<MethodInfo> filter, Object[] extraBeans) {
776      var seen = new HashSet<String>();  // Signatures of non-private, non-static methods already visited (child-first).
777      for (var m : info(resourceClass).getAllMethods()) {
778         if (m.isBridge() || m.isSynthetic())
779            continue;
780         if (m.isNotPrivate() && m.isNotStatic() && ! seen.add(m.getSignature()))
781            continue;
782         if (m.isPublic() || (m.isStatic() && m.getDeclaringClass().isInterface()))
783            continue;
784         if (BeanAnnotation.find(m).isEmpty())
785            continue;
786         if (m.isNotDeprecated()
787            && m.hasReturnType(beanType)
788            && (filter == null || filter.test(m))
789            && (m.isStatic() || nn(resource))
790            && m.canResolveAllParameters(this, extraBeans))
791            return Optional.ofNullable(BeanMethodInvoker.invoke(this, beanType, m, m.isStatic() ? null : resource, extraBeans));
792      }
793      return Optional.empty();
794   }
795
796   @Override /* Overridden from Object */
797   public String toString() {
798      return r(properties());
799   }
800
801   protected FluentMap<String,Object> properties() {
802      // @formatter:off
803      var entryList = list();
804      entries.forEach((type, typeMap) -> typeMap.forEach((name, supplier) -> entryList.add(filteredBeanPropertyMap()
805         .a("type", cns(type))
806         .a("bean", id(supplier.get()))
807         .a("name", name))));
808      var defaultList = list();
809      var xd = extras;
810      if (xd != null)
811         xd.defaults.forEach((type, typeMap) -> typeMap.forEach((name, supplier) -> defaultList.add(filteredBeanPropertyMap()
812            .a("type", cns(type))
813            .a("bean", id(supplier.get()))
814            .a("name", name))));
815      Object overridingParentValue = null;
816      if (nn(overridingParent)) {
817         if (overridingParent instanceof BasicBeanStore overridingParent2)
818            overridingParentValue = overridingParent2.properties();
819         else
820            overridingParentValue = s(overridingParent);
821      }
822      var stack = overlayStack;
823      Object overlayStackValue = (nn(stack) && stack.depth() > 0) ? s(stack) : null;
824      return filteredBeanPropertyMap()
825         .a("entries", entryList)
826         .a("defaults", defaultList.isEmpty() ? null : defaultList)
827         .a("overlayStack", overlayStackValue)
828         .a("overridingParent", overridingParentValue)
829         .a("identity", id(this))
830         .a("parent", parent instanceof BasicBeanStore parent2 ? parent2.properties() : s(parent));
831      // @formatter:on
832   }
833
834   @Override
835   public void close() throws BeanCreationException {
836      if (closed)
837         return;
838      closed = true;
839      var errors = new BeanCreationException("Errors while invoking @PreDestroy callbacks.");
840      boolean hasErrors = false;
841      var t = tracking;
842      List<Object> beans = new ArrayList<>();
843      if (t != null)
844         synchronized (t) {
845            beans.addAll(t.beans);
846         }
847      Collections.reverse(beans);
848      for (var bean : beans) {
849         for (var method : ClassInfo.of(bean).getAllMethods()) {
850            if (!method.getReturnType().is(void.class) || method.getParameterCount() != 0 || method.isAbstract() || method.isStatic())
851               continue;
852            if (method.getAnnotations().stream().noneMatch(JsrSupport::isPreDestroyAnnotation))
853               continue;
854            try {
855               method.invoke(bean);
856            } catch (Exception e) {
857               hasErrors = true;
858               errors.addSuppressed(e);
859            }
860         }
861      }
862      if (hasErrors)
863         throw errors;
864   }
865
866   private void registerConfiguration(Class<?> configType, Set<Class<?>> visited) {
867      if (!visited.add(configType))
868         return;
869      var cfg = configType.getAnnotation(Configuration.class);
870      if (cfg == null)
871         throw new BeanCreationException("Type is not @Configuration: " + configType.getName());
872      if (!matchesConditions(configType))
873         return;
874      // Annotation arrays cannot contain null elements at runtime — Java rejects null defaults at
875      // declaration time and the compiler refuses null literals in annotation values.  No defensive
876      // null check is needed here.
877      for (var imported : cfg.imports())
878         registerConfiguration(imported, visited);
879
880      // Walk the type and its superclasses; collect @Bean members on each level.  This honours
881      // the plan's "superclass inheritance of @Bean members" requirement.  Order matters here:
882      // process the highest ancestor first so subclass @Bean methods can depend on superclass
883      // beans (analogous to Spring's @Configuration inheritance).
884      Object instance = null;
885      var hierarchy = new ArrayList<Class<?>>();
886      for (var c = configType; c != null && c != Object.class; c = c.getSuperclass())
887         hierarchy.add(c);
888      Collections.reverse(hierarchy);
889      for (var c : hierarchy) {
890         for (var field : c.getDeclaredFields()) {
891            var beanAnn = field.getAnnotation(Bean.class);
892            if (beanAnn == null)
893               continue;
894            if (!matchesConditions(field))
895               continue;
896            try {
897               if (!java.lang.reflect.Modifier.isStatic(field.getModifiers()) && instance == null)
898                  instance = BeanInstantiator.of(configType, this).run();
899               field.setAccessible(true);
900               var value = field.get(java.lang.reflect.Modifier.isStatic(field.getModifiers()) ? null : instance);
901               addBeanWithMeta((Class<Object>)field.getType(), value, emptyIfNull(beanName(beanAnn)), BeanSourceMeta.from(field, beanAnn));
902            } catch (Exception e) {
903               throw new BeanCreationException("Failed to register @Bean field: " + field, e);
904            }
905         }
906         for (var method : c.getDeclaredMethods()) {
907            var beanAnn = method.getAnnotation(Bean.class);
908            if (beanAnn == null)
909               continue;
910            if (!matchesConditions(method))
911               continue;
912            try {
913               if (!java.lang.reflect.Modifier.isStatic(method.getModifiers()) && instance == null)
914                  instance = BeanInstantiator.of(configType, this).run();
915               method.setAccessible(true);
916               var value = MethodInfo.of(method).inject(this, java.lang.reflect.Modifier.isStatic(method.getModifiers()) ? null : instance);
917               addBeanWithMeta((Class<Object>)method.getReturnType(), value, emptyIfNull(beanName(beanAnn)), BeanSourceMeta.from(method, beanAnn));
918            } catch (Exception e) {
919               throw new BeanCreationException("Failed to register @Bean method: " + method, e);
920            }
921         }
922      }
923   }
924
925   private boolean matchesConditions(java.lang.reflect.AnnotatedElement element) {
926      var classLoader = element instanceof Class<?> element2 ? element2.getClassLoader() : element.getClass().getClassLoader();
927      var ctx = new ConditionContext(this, Settings.get(), classLoader, element);
928      for (var c : element.getAnnotationsByType(Conditional.class)) {
929         if (!BeanInstantiator.of(c.value(), this).run().matches(ctx)) {
930            LOGGER.fine(() -> "Skipping conditional element due to @Conditional: " + element);
931            return false;
932         }
933      }
934      for (var c : element.getAnnotationsByType(ConditionalOnClass.class)) {
935         try {
936            Class.forName(c.value(), false, classLoader);
937         } catch (@SuppressWarnings("unused") ClassNotFoundException e) {
938            LOGGER.fine(() -> "Skipping conditional element due to missing class " + c.value() + ": " + element);
939            return false;
940         }
941      }
942      for (var c : element.getAnnotationsByType(ConditionalOnMissingBean.class)) {
943         var type = c.value();
944         var name = c.name().isEmpty() ? null : c.name();
945         if (type == Object.class) {
946            if (name != null && anyLocalBeanNamed(name))
947               return false;
948         } else if (hasBean(type, name)) {
949            return false;
950         }
951      }
952      for (var c : element.getAnnotationsByType(ConditionalOnProperty.class)) {
953         var value = Settings.get().get(c.name()).orElse(null);
954         if (value == null && !c.matchIfMissing())
955            return false;
956         if (value != null && !c.havingValue().isEmpty() && !c.havingValue().equals(value))
957            return false;
958      }
959      return true;
960   }
961
962   private boolean anyLocalBeanNamed(String name) {
963      var key = emptyIfNull(name);
964      var x = extras;
965      return entries.values().stream().anyMatch(m -> m.containsKey(key))
966         || (x != null && x.defaults.values().stream().anyMatch(m -> m.containsKey(key)));
967   }
968
969   private String beanName(Bean bean) {
970      return bean.name().isEmpty() ? bean.value() : bean.name();
971   }
972
973   private <T> void addBeanWithMeta(Class<T> beanType, T bean, String name, BeanSourceMeta meta) {
974      var key = emptyIfNull(name);
975      // Duplicate-check against LOCAL entries only.  Beans present via the overridingParent or parent
976      // chain are allowed to coexist with a local registration here — the resolution order
977      // (overridingParent > local > parent > defaults) ensures the right entry wins at lookup time.
978      // This is what lets a test-time overlay installed in the overridingParent slot coexist with a
979      // local @Bean factory of the same (type, name) declared on a @Configuration class without
980      // triggering a spurious duplicate-bean error.
981      var typeMap = entries.computeIfAbsent(beanType, k -> new ConcurrentHashMap<>());
982      if (typeMap.containsKey(key))
983         throw new BeanCreationException("Duplicate bean: type=" + beanType.getName() + ", name=" + key);
984      typeMap.put(key, () -> bean);
985      putEntryMeta(beanType, key, meta);
986   }
987
988   private <T> T trackResolved(T bean) {
989      if (bean == null)
990         return null;
991      var t = tracking;
992      if (t == null) {
993         synchronized (this) {
994            t = tracking;
995            if (t == null) {
996               t = new Tracking();
997               tracking = t;
998            }
999         }
1000      }
1001      synchronized (t) {
1002         if (t.identities.add(bean))
1003            t.beans.add(bean);
1004      }
1005      return bean;
1006   }
1007
1008   private <T> Optional<T> selectPrimary(Class<T> beanType) {
1009      // @Primary is a local-entry-only signal.  Default suppliers added via addDefaultSupplier()
1010      // always carry BeanSourceMeta.DEFAULT (primary = false), so default suppliers never
1011      // contributes a primary candidate — only entryMetadata is consulted here.  Parent and
1012      // overridingParent stores (including Spring-backed delegates) are similarly excluded because
1013      // @Primary metadata only exists on beans this store registered itself.
1014      Set<String> primaryNames = st();
1015      var x = extras;
1016      var em = x == null ? null : x.entryMetadata.get(beanType);
1017      if (nn(em))
1018         em.forEach((name, meta) -> { if (meta.primary()) primaryNames.add(name); });
1019      if (primaryNames.isEmpty())
1020         return oe();
1021      if (primaryNames.size() > 1)
1022         throw new BeanCreationException("Multiple @Primary candidates of type " + beanType.getName());
1023      var primaryName = primaryNames.iterator().next();
1024      return resolve(beanType, primaryName).map(Supplier::get);
1025   }
1026
1027   private int beanOrder(Class<?> type, String name) {
1028      // @Order / priority is sourced from registered entries only.  Default suppliers always carry
1029      // BeanSourceMeta.DEFAULT (which already returns Integer.MAX_VALUE/2), so we fall through to
1030      // the same constant rather than tracking default-supplier metadata.
1031      var key = emptyIfNull(name);
1032      var x = extras;
1033      var m = x == null ? null : x.entryMetadata.get(type);
1034      if (m != null)
1035         return m.getOrDefault(key, BeanSourceMeta.DEFAULT).orderValue();
1036      return Integer.MAX_VALUE / 2;
1037   }
1038
1039   private void checkOpen() {
1040      if (closed)
1041         throw new IllegalStateException("BeanStore has been closed.");
1042   }
1043
1044   private record BeanSourceMeta(boolean primary, Integer order, int priority) {
1045      static final BeanSourceMeta DEFAULT = new BeanSourceMeta(false, null, Integer.MAX_VALUE / 2);
1046
1047      int orderValue() {
1048         return order == null ? priority : order.intValue();
1049      }
1050
1051      static BeanSourceMeta from(java.lang.reflect.AnnotatedElement element, Bean bean) {
1052         var order = element.getAnnotation(Order.class);
1053         return new BeanSourceMeta(element.getAnnotation(Primary.class) != null, order == null ? null : order.value(), bean.priority());
1054      }
1055   }
1056}