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.rest.springboot;
018
019import static org.apache.juneau.commons.utils.Utils.*;
020
021import java.util.*;
022import java.util.stream.*;
023
024import org.apache.juneau.cp.*;
025import org.springframework.context.*;
026
027/**
028 * A bean store that uses Spring bean resolution to find beans if they're not already in this store.
029 *
030 * <h5 class='section'>See Also:</h5><ul>
031 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestServerSpringbootBasics">juneau-rest-server-springboot Basics</a>
032 * </ul>
033 */
034public class SpringBeanStore extends BeanStore {
035
036   private final Optional<ApplicationContext> appContext;
037
038   /**
039    * Constructor.
040    *
041    * @param appContext The Spring application context used to resolve beans.
042    * @param parent The parent REST object bean store.  Can be <jk>null</jk>.
043    * @param resource The REST object.  Can be <jk>null</jk>.
044    */
045   public SpringBeanStore(Optional<ApplicationContext> appContext, Optional<BeanStore> parent, Object resource) {
046      super(create().parent(parent.orElse(null)).outer(resource));
047      this.appContext = appContext;
048   }
049
050   @Override /* Overridden from BeanStore */
051   public SpringBeanStore clear() {
052      super.clear();
053      return this;
054   }
055
056   @Override
057   public <T> Optional<T> getBean(Class<T> c) {
058      try {
059         var o = super.getBean(c);
060         if (o.isPresent())
061            return o;
062         if (appContext.isPresent()) {
063            return opt(appContext.get().getBeanProvider(c).getIfAvailable());
064         }
065      } catch (Exception e) {
066         e.printStackTrace();
067      }
068      return opte();
069   }
070
071   @Override
072   public <T> Optional<T> getBean(Class<T> c, String name) {
073      try {
074         var o = super.getBean(c, name);
075         if (o.isPresent())
076            return o;
077         if (appContext.isPresent()) {
078            var ctx = appContext.get();
079            if (nn(name))
080               return opt(ctx.containsBean(name) ? appContext.get().getBean(name, c) : null);
081            return opt(appContext.get().getBean(c));
082         }
083      } catch (Exception e) {
084         e.printStackTrace();
085      }
086      return opte();
087   }
088
089   @Override /* Overridden from BeanStore */
090   public SpringBeanStore removeBean(Class<?> beanType) {
091      super.removeBean(beanType);
092      return this;
093   }
094
095   @Override /* Overridden from BeanStore */
096   public SpringBeanStore removeBean(Class<?> beanType, String name) {
097      super.removeBean(beanType, name);
098      return this;
099   }
100
101   @SuppressWarnings("unchecked")
102   @Override
103   public <T> Stream<BeanStoreEntry<T>> stream(Class<T> c) {
104      try {
105         var o = super.stream(c);
106         if (appContext.isPresent())
107            o = Stream.concat(o, appContext.get().getBeansOfType(c).entrySet().stream().map(x -> BeanStoreEntry.create(c, () -> x.getValue(), x.getKey())));
108         return o;
109      } catch (Exception e) {
110         e.printStackTrace();
111      }
112      return Collections.emptyList().stream().map(x -> (BeanStoreEntry<T>)x);
113   }
114}