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.cp;
018
019import static java.util.stream.Collectors.*;
020import static java.util.stream.Collectors.toList;
021import static org.apache.juneau.commons.reflect.ReflectionUtils.*;
022import static org.apache.juneau.commons.utils.CollectionUtils.*;
023import static org.apache.juneau.commons.utils.ThrowableUtils.*;
024import static org.apache.juneau.commons.utils.Utils.*;
025
026import java.util.*;
027import java.util.concurrent.*;
028
029import java.util.function.*;
030import java.util.stream.*;
031
032import org.apache.juneau.commons.collections.*;
033import org.apache.juneau.commons.concurrent.*;
034import org.apache.juneau.commons.reflect.*;
035
036/**
037 * Java bean store.
038 *
039 * <p>
040 * A simple storage database for beans keyed by type and name.
041 * Used to retrieve and instantiate beans using an injection-like API.
042 * 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.
043 *
044 * <p>
045 * Beans can be stored with or without names.  Named beans are typically resolved using
046 * the <ja>@Name</ja> or <ja>@Qualified</ja> annotations on constructor or method parameters.
047 *
048 * <p>
049 * Beans are added through the following methods:
050 * <ul class='javatreec'>
051 *    <li class='jm'>{@link #add(Class,Object) add(Class,Object)}
052 *    <li class='jm'>{@link #add(Class,Object,String) add(Class,Object,String)}
053 *    <li class='jm'>{@link #addBean(Class,Object) addBean(Class,Object)}
054 *    <li class='jm'>{@link #addBean(Class,Object,String) addBean(Class,Object,String)}
055 *    <li class='jm'>{@link #addSupplier(Class,Supplier) addSupplier(Class,Supplier)}
056 *    <li class='jm'>{@link #addSupplier(Class,Supplier,String) addSupplier(Class,Supplier,String)}
057 * </ul>
058 *
059 * <p>
060 * Beans are retrieved through the following methods:
061 * <ul class='javatreec'>
062 *    <li class='jm'>{@link #getBean(Class) getBean(Class)}
063 *    <li class='jm'>{@link #getBean(Class,String) getBean(Class,String)}
064 *    <li class='jm'>{@link #stream(Class) stream(Class)}
065 * </ul>
066 *
067 * <p>
068 * Beans are created through the following methods:
069 * <ul class='javatreec'>
070 *    <li class='jm'>{@link #createBean(Class) createBean(Class)}
071 *    <li class='jm'>{@link #createMethodFinder(Class) createMethodFinder(Class)}
072 *    <li class='jm'>{@link #createMethodFinder(Class,Class) createMethodFinder(Class,Class)}
073 *    <li class='jm'>{@link #createMethodFinder(Class,Object) createMethodFinder(Class,Object)}
074 * </ul>
075 *
076 * <h5 class='section'>Notes:</h5><ul>
077 *    <li class='note'>Bean stores can be nested using {@link Builder#parent(BeanStore)}.
078 *    <li class='note'>Bean stores can be made read-only using {@link Builder#readOnly()}.
079 *    <li class='note'>Bean stores can be made thread-safe using {@link Builder#threadSafe()}.
080 * </ul>
081 *
082 */
083public class BeanStore {
084   /**
085    * Builder class.
086    */
087   public static class Builder {
088
089      BeanStore parent;
090      boolean readOnly, threadSafe;
091      Object outer;
092      Class<? extends BeanStore> type;
093      BeanStore impl;
094
095      /**
096       * Constructor.
097       */
098      protected Builder() {}
099
100      /**
101       * Instantiates this bean store.
102       *
103       * @return A new bean store.
104       */
105      public BeanStore build() {
106         if (nn(impl))
107            return impl;
108         if (type == null || type == BeanStore.class)
109            return new BeanStore(this);
110
111         var c = info(type);
112
113         // @formatter:off
114         var result = c.getDeclaredMethod(
115            x -> x.isPublic()
116            && x.getParameterCount() == 0
117            && x.isStatic()
118            && x.hasName("getInstance")
119         ).map(m -> m.<BeanStore>invoke(null));
120         // @formatter:on
121         if (result.isPresent())
122            return result.get();
123
124         result = c.getPublicConstructor(x -> x.canAccept(this)).map(ci -> ci.<BeanStore>newInstance(this));
125         if (result.isPresent())
126            return result.get();
127
128         result = c.getDeclaredConstructor(x -> x.isProtected() && x.canAccept(this)).map(ci -> ci.accessible().<BeanStore>newInstance(this));
129         if (result.isPresent())
130            return result.get();
131
132         throw rex("Could not find a way to instantiate class {0}", cn(type));
133      }
134
135      /**
136       * Overrides the bean to return from the {@link #build()} method.
137       *
138       * @param value The bean to return from the {@link #build()} method.
139       * @return This object.
140       */
141      public Builder impl(BeanStore value) {
142         impl = value;
143         return this;
144      }
145
146      /**
147       * Specifies the outer bean context.
148       *
149       * <p>
150       * The outer context bean to use when calling constructors on inner classes.
151       *
152       * @param value The outer bean context.  Can be <jk>null</jk>.
153       * @return  This object.
154       */
155      public Builder outer(Object value) {
156         outer = value;
157         return this;
158      }
159
160      /**
161       * Specifies the parent bean store.
162       *
163       * <p>
164       * Bean searches are performed recursively up this parent chain.
165       *
166       * @param value The setting value.
167       * @return  This object.
168       */
169      public Builder parent(BeanStore value) {
170         parent = value;
171         return this;
172      }
173
174      /**
175       * Specifies that the bean store is read-only.
176       *
177       * <p>
178       * This means methods such as {@link BeanStore#addBean(Class, Object)} cannot be used.
179       *
180       * @return  This object.
181       */
182      public Builder readOnly() {
183         readOnly = true;
184         return this;
185      }
186
187      /**
188       * Specifies that the bean store being created should be thread-safe.
189       *
190       * @return  This object.
191       */
192      public Builder threadSafe() {
193         threadSafe = true;
194         return this;
195      }
196
197      /**
198       * Overrides the bean store type.
199       *
200       * <p>
201       * The specified type must have one of the following:
202       * <ul>
203       *    <li>A static <c>getInstance()</c> method.
204       *    <li>A public constructor that takes in this builder.
205       *    <li>A protected constructor that takes in this builder.
206       * </ul>
207       *
208       * @param value The bean store type.
209       * @return This object.
210       */
211      public Builder type(Class<? extends BeanStore> value) {
212         type = value;
213         return this;
214      }
215   }
216
217   /**
218    * Non-existent bean store.
219    */
220   public static final class Void extends BeanStore {}
221
222   /**
223    * Static read-only reusable instance.
224    */
225   public static final BeanStore INSTANCE = create().readOnly().build();
226
227   /**
228    * Static creator.
229    *
230    * @return A new {@link Builder} object.
231    */
232   public static Builder create() {
233      return new Builder();
234   }
235
236   /**
237    * Static creator.
238    *
239    * @param parent Parent bean store.  Can be <jk>null</jk> if this is the root resource.
240    * @return A new {@link BeanStore} object.
241    */
242   public static BeanStore of(BeanStore parent) {
243      return create().parent(parent).build();
244   }
245
246   /**
247    * Static creator.
248    *
249    * @param parent Parent bean store.  Can be <jk>null</jk> if this is the root resource.
250    * @param outer The outer bean used when instantiating inner classes.  Can be <jk>null</jk>.
251    * @return A new {@link BeanStore} object.
252    */
253   public static BeanStore of(BeanStore parent, Object outer) {
254      return create().parent(parent).outer(outer).build();
255   }
256
257   private final Deque<BeanStoreEntry<?>> entries;
258   private final Map<Class<?>,BeanStoreEntry<?>> unnamedEntries;
259
260   final Optional<BeanStore> parent;
261   final Optional<Object> outer;
262   final boolean readOnly, threadSafe;
263   final SimpleReadWriteLock lock;
264
265   /**
266    * Constructor.
267    *
268    * @param builder The builder containing the settings for this bean.
269    */
270   protected BeanStore(Builder builder) {
271      parent = opt(builder.parent);
272      outer = opt(builder.outer);
273      readOnly = builder.readOnly;
274      threadSafe = builder.threadSafe;
275      lock = threadSafe ? new SimpleReadWriteLock() : SimpleReadWriteLock.NO_OP;
276      entries = threadSafe ? new ConcurrentLinkedDeque<>() : new LinkedList<>();
277      unnamedEntries = threadSafe ? new ConcurrentHashMap<>() : map();
278   }
279
280   BeanStore() {
281      this(create());
282   }
283
284   /**
285    * Same as {@link #addBean(Class,Object)} but returns the bean instead of this object for fluent calls.
286    *
287    * @param <T> The class to associate this bean with.
288    * @param beanType The class to associate this bean with.
289    * @param bean The bean.  Can be <jk>null</jk>.
290    * @return The bean.
291    */
292   public <T> T add(Class<T> beanType, T bean) {
293      add(beanType, bean, null);
294      return bean;
295   }
296
297   /**
298    * Same as {@link #addBean(Class,Object,String)} but returns the bean instead of this object for fluent calls.
299    *
300    * @param <T> The class to associate this bean with.
301    * @param beanType The class to associate this bean with.
302    * @param bean The bean.  Can be <jk>null</jk>.
303    * @param name The bean name if this is a named bean.  Can be <jk>null</jk>.
304    * @return The bean.
305    */
306   public <T> T add(Class<T> beanType, T bean, String name) {
307      addBean(beanType, bean, name);
308      return bean;
309   }
310
311   /**
312    * Adds an unnamed bean of the specified type to this factory.
313    *
314    * @param <T> The class to associate this bean with.
315    * @param beanType The class to associate this bean with.
316    * @param bean The bean.  Can be <jk>null</jk>.
317    * @return This object.
318    */
319   public <T> BeanStore addBean(Class<T> beanType, T bean) {
320      return addBean(beanType, bean, null);
321   }
322
323   /**
324    * Adds a named bean of the specified type to this factory.
325    *
326    * @param <T> The class to associate this bean with.
327    * @param beanType The class to associate this bean with.
328    * @param bean The bean.  Can be <jk>null</jk>.
329    * @param name The bean name if this is a named bean.  Can be <jk>null</jk>.
330    * @return This object.
331    */
332   public <T> BeanStore addBean(Class<T> beanType, T bean, String name) {
333      return addSupplier(beanType, () -> bean, name);
334   }
335
336   /**
337    * Adds a supplier for an unnamed bean of the specified type to this factory.
338    *
339    * @param <T> The class to associate this bean with.
340    * @param beanType The class to associate this bean with.
341    * @param bean The bean supplier.
342    * @return This object.
343    */
344   public <T> BeanStore addSupplier(Class<T> beanType, Supplier<T> bean) {
345      return addSupplier(beanType, bean, null);
346   }
347
348   /**
349    * Adds a supplier for a named bean of the specified type to this factory.
350    *
351    * @param <T> The class to associate this bean with.
352    * @param beanType The class to associate this bean with.
353    * @param bean The bean supplier.
354    * @param name The bean name if this is a named bean.  Can be <jk>null</jk>.
355    * @return This object.
356    */
357   public <T> BeanStore addSupplier(Class<T> beanType, Supplier<T> bean, String name) {
358      assertCanWrite();
359      var e = createEntry(beanType, bean, name);
360      try (var x = lock.write()) {
361         entries.addFirst(e);
362         if (e(name))
363            unnamedEntries.put(beanType, e);
364      }
365      return this;
366   }
367
368   /**
369    * Clears out all bean in this bean store.
370    *
371    * <p>
372    * Does not affect the parent bean store.
373    *
374    * @return This object.
375    */
376   public BeanStore clear() {
377      assertCanWrite();
378      try (var x = lock.write()) {
379         unnamedEntries.clear();
380         entries.clear();
381      }
382      return this;
383   }
384
385   /**
386    * Instantiates a bean creator.
387    *
388    * <h5 class='section'>See Also:</h5><ul>
389    *    <li class='jc'>{@link BeanCreator} for usage.
390    * </ul>
391    *
392    * @param <T> The bean type to create.
393    * @param beanType The bean type to create.
394    * @return A new bean creator.
395    */
396   public <T> BeanCreator<T> createBean(Class<T> beanType) {
397      return new BeanCreator<>(beanType, this);
398   }
399
400   /**
401    * Create a method finder for finding bean creation methods.
402    *
403    * <p>
404    * Same as {@link #createMethodFinder(Class,Object)} but uses {@link Builder#outer(Object)} as the resource bean.
405    *
406    * <h5 class='section'>See Also:</h5><ul>
407    *    <li class='jc'>{@link BeanCreateMethodFinder} for usage.
408    * </ul>
409    *
410    * @param <T> The bean type to create.
411    * @param beanType The bean type to create.
412    * @return The method finder.  Never <jk>null</jk>.
413    */
414   public <T> BeanCreateMethodFinder<T> createMethodFinder(Class<T> beanType) {
415      return new BeanCreateMethodFinder<>(beanType, outer.orElseThrow(() -> new IllegalArgumentException("Method cannot be used without outer bean definition.")), this);
416   }
417
418   /**
419    * Create a method finder for finding bean creation methods.
420    *
421    * <p>
422    * Same as {@link #createMethodFinder(Class,Class)} but looks for only static methods on the specified resource class
423    * and not also instance methods within the context of a bean.
424    *
425    * <h5 class='section'>See Also:</h5><ul>
426    *    <li class='jc'>{@link BeanCreateMethodFinder} for usage.
427    * </ul>
428    *
429    * @param <T> The bean type to create.
430    * @param beanType The bean type to create.
431    * @param resourceClass The class containing the bean creator method.
432    * @return The method finder.  Never <jk>null</jk>.
433    */
434   public <T> BeanCreateMethodFinder<T> createMethodFinder(Class<T> beanType, Class<?> resourceClass) {
435      return new BeanCreateMethodFinder<>(beanType, resourceClass, this);
436   }
437
438   /**
439    * Create a method finder for finding bean creation methods.
440    *
441    * <h5 class='section'>See Also:</h5><ul>
442    *    <li class='jc'>{@link BeanCreateMethodFinder} for usage.
443    * </ul>
444    *
445    * @param <T> The bean type to create.
446    * @param beanType The bean type to create.
447    * @param resource The class containing the bean creator method.
448    * @return The method finder.  Never <jk>null</jk>.
449    */
450   public <T> BeanCreateMethodFinder<T> createMethodFinder(Class<T> beanType, Object resource) {
451      return new BeanCreateMethodFinder<>(beanType, resource, this);
452   }
453
454   /**
455    * Returns the unnamed bean of the specified type.
456    *
457    * @param <T> The type of bean to return.
458    * @param beanType The type of bean to return.
459    * @return The bean.
460    */
461   @SuppressWarnings("unchecked")
462   public <T> Optional<T> getBean(Class<T> beanType) {
463      try (var x = lock.read()) {
464         var e = (BeanStoreEntry<T>)unnamedEntries.get(beanType);
465         if (nn(e))
466            return opt(e.get());
467         if (parent.isPresent())
468            return parent.get().getBean(beanType);
469         return opte();
470      }
471   }
472
473   /**
474    * Returns the named bean of the specified type.
475    *
476    * @param <T> The type of bean to return.
477    * @param beanType The type of bean to return.
478    * @param name The bean name.  Can be <jk>null</jk>.
479    * @return The bean.
480    */
481   @SuppressWarnings("unchecked")
482   public <T> Optional<T> getBean(Class<T> beanType, String name) {
483      try (var x = lock.read()) {
484         var e = (BeanStoreEntry<T>)entries.stream().filter(x2 -> x2.matches(beanType, name)).findFirst().orElse(null);
485         if (nn(e))
486            return opt(e.get());
487         if (parent.isPresent())
488            return parent.get().getBean(beanType, name);
489         return opte();
490      }
491   }
492
493   /**
494    * Given an executable, returns a list of types that are missing from this factory.
495    *
496    * @param executable The constructor or method to get the params for.
497    * @return A comma-delimited list of types that are missing from this factory, or <jk>null</jk> if none are missing.
498    */
499   public String getMissingParams(ExecutableInfo executable) {
500      var params = executable.getParameters();
501      List<String> l = list();
502      loop: for (int i = 0; i < params.size(); i++) {
503         var pi = params.get(i);
504         var pt = pi.getParameterType();
505         if (i == 0 && outer.isPresent() && pt.isInstance(outer.get()))
506            continue loop;
507         if (pt.is(Optional.class) || pt.is(BeanStore.class))
508            continue loop;
509         var beanName = pi.getResolvedQualifier();  // Use @Named for bean injection
510         var ptc = pt.inner();
511         if (beanName == null && ! hasBean(ptc))
512            l.add(pt.getNameSimple());
513         if (nn(beanName) && ! hasBean(ptc, beanName))
514            l.add(pt.getNameSimple() + '@' + beanName);
515      }
516      return l.isEmpty() ? null : l.stream().sorted().collect(joining(","));
517   }
518
519   /**
520    * Returns the corresponding beans in this factory for the specified param types.
521    *
522    * @param executable The constructor or method to get the params for.
523    * @return The corresponding beans in this factory for the specified param types.
524    */
525   public Object[] getParams(ExecutableInfo executable) {
526      var o = new Object[executable.getParameterCount()];
527      for (var i = 0; i < executable.getParameterCount(); i++) {
528         var pi = executable.getParameter(i);
529         var pt = pi.getParameterType();
530         if (i == 0 && outer.isPresent() && pt.isInstance(outer.get())) {
531            o[i] = outer.get();
532         } else if (pt.is(BeanStore.class)) {
533            o[i] = this;
534         } else {
535            var beanQualifier = pi.getResolvedQualifier();
536            var ptc = pt.unwrap(Optional.class).inner();
537            var o2 = beanQualifier == null ? getBean(ptc) : getBean(ptc, beanQualifier);
538            o[i] = pt.is(Optional.class) ? o2 : o2.orElse(null);
539         }
540      }
541      return o;
542   }
543
544   /**
545    * Given the list of param types, returns <jk>true</jk> if this factory has all the parameters for the specified executable.
546    *
547    * @param executable The constructor or method to get the params for.
548    * @return A comma-delimited list of types that are missing from this factory.
549    */
550   public boolean hasAllParams(ExecutableInfo executable) {
551      loop: for (int i = 0; i < executable.getParameterCount(); i++) {
552         var pi = executable.getParameter(i);
553         var pt = pi.getParameterType();
554         if (i == 0 && outer.isPresent() && pt.isInstance(outer.get()))
555            continue loop;
556         if (pt.is(Optional.class) || pt.is(BeanStore.class))
557            continue loop;
558         var beanQualifier = pi.getResolvedQualifier();
559         var ptc = pt.inner();
560         if ((beanQualifier == null && ! hasBean(ptc)) || (nn(beanQualifier) && ! hasBean(ptc, beanQualifier)))
561            return false;
562      }
563      return true;
564   }
565
566   /**
567    * Returns <jk>true</jk> if this store contains the specified unnamed bean type.
568    *
569    * @param beanType The bean type to check.
570    * @return <jk>true</jk> if this store contains the specified unnamed bean type.
571    */
572   public boolean hasBean(Class<?> beanType) {
573      return unnamedEntries.containsKey(beanType) || parent.map(x -> x.hasBean(beanType)).orElse(false);
574   }
575
576   /**
577    * Returns <jk>true</jk> if this store contains the specified named bean type.
578    *
579    * @param beanType The bean type to check.
580    * @param name The bean name.
581    * @return <jk>true</jk> if this store contains the specified named bean type.
582    */
583   public boolean hasBean(Class<?> beanType, String name) {
584      return entries.stream().anyMatch(x -> x.matches(beanType, name)) || parent.map(x -> x.hasBean(beanType, name)).orElse(false);
585   }
586
587   /**
588    * Removes an unnamed bean from this store.
589    *
590    * @param beanType The bean type being removed.
591    * @return This object.
592    */
593   public BeanStore removeBean(Class<?> beanType) {
594      return removeBean(beanType, null);
595   }
596
597   /**
598    * Removes a named bean from this store.
599    *
600    * @param beanType The bean type being removed.
601    * @param name The bean name to remove.
602    * @return This object.
603    */
604   public BeanStore removeBean(Class<?> beanType, String name) {
605      assertCanWrite();
606      try (var x = lock.write()) {
607         if (name == null)
608            unnamedEntries.remove(beanType);
609         entries.removeIf(y -> y.matches(beanType, name));
610      }
611      return this;
612   }
613
614   /**
615    * Returns all the beans in this store of the specified type.
616    *
617    * <p>
618    * Returns both named and unnamed beans.
619    *
620    * <p>
621    * The results from the parent bean store are appended to the list of beans from this beans store.
622    *
623    * @param <T> The bean type to return.
624    * @param beanType The bean type to return.
625    * @return The bean entries.  Never <jk>null</jk>.
626    */
627   public <T> Stream<BeanStoreEntry<T>> stream(Class<T> beanType) {
628      @SuppressWarnings("unchecked")
629      var s = entries.stream().filter(x -> x.matches(beanType)).map(x -> (BeanStoreEntry<T>)x);
630      if (parent.isPresent())
631         s = Stream.concat(s, parent.get().stream(beanType));
632      return s;
633   }
634
635   protected FluentMap<String,Object> properties() {
636      // @formatter:off
637      return filteredBeanPropertyMap()
638         .a("entries", entries.stream().map(BeanStoreEntry::properties).collect(toList()))
639         .a("identity", id(this))
640         .a("outer", id(outer.orElse(null)))
641         .a("parent", parent.map(BeanStore::properties).orElse(null))
642         .ai(readOnly, "readOnly", readOnly)
643         .ai(threadSafe, "threadSafe", threadSafe);
644      // @formatter:on
645   }
646
647   @Override /* Overridden from Object */
648   public String toString() {
649      return r(properties());
650   }
651
652   private void assertCanWrite() {
653      if (readOnly)
654         throw new IllegalStateException("Method cannot be used because BeanStore is read-only.");
655   }
656
657   /**
658    * Creates an entry in this store for the specified bean.
659    *
660    * <p>
661    * Subclasses can override this method to create their own entry subtypes.
662    *
663    * @param <T> The class type to associate with the bean.
664    * @param type The class type to associate with the bean.
665    * @param bean The bean supplier.
666    * @param name Optional name to associate with the bean.  Can be <jk>null</jk>.
667    * @return A new bean store entry.
668    */
669   protected <T> BeanStoreEntry<T> createEntry(Class<T> type, Supplier<T> bean, String name) {
670      return BeanStoreEntry.create(type, bean, name);
671   }
672}