View Javadoc
1   /**
2    * This file Copyright (c) 2016-2018 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.ui.framework.ioc;
35  
36  import info.magnolia.objectfactory.guice.GuiceComponentProvider;
37  import info.magnolia.objectfactory.guice.GuiceUtils;
38  
39  import java.util.Optional;
40  
41  import javax.inject.Inject;
42  
43  import com.google.inject.Injector;
44  import com.google.inject.Key;
45  import com.google.inject.Provider;
46  import com.google.inject.name.Names;
47  
48  /**
49   * <p>
50   * Special {@link Provider} implementation which is used
51   * when a certain type is bound differently in different contexts.
52   * </p>
53   * <p>
54   * This would be the case when e.g. app <b>bar</b> configures a type
55   * {@code Foo} to be implemented by {@code FooBarImpl} and app
56   * <b>baz</b> has a binding of {@code Foo} to {@code FooBazImpl}.
57   * </p>
58   * <p>
59   * In order to support such cases we assume that {@code Foo} is bound
60   * in a special way. It is expected that two concrete implementations
61   * of it are bound using a {@link com.google.inject.BindingAnnotation
62   * binding annotation} such as e.g.
63   * {@link info.magnolia.ui.api.ioc.App App}.
64   * </p>
65   * <p>
66   * By binding the un-annotated type to this provider we allow the developers
67   * to inject types like {@code Foo} without explicitly
68   * mentioning the binding annotations.
69   * This provider will use {@link CurrentUiContextReference} to fetch all the
70   * relevant UI contexts and will produce the possible Guice {@link Key keys}.
71   * The most suitable key is then picked, i.e. we pick the binding
72   * which is closest to the current UI context.
73   * </p>
74   *
75   * <p>
76   * For more information on binding annotations, see
77   * <a href="https://github.com/google/guice/wiki/BindingAnnotations">Guice Binding Annotations tutorial</a>.
78   * </p>
79   *
80   * @param <T> type of the provided instance.
81   *
82   * @see UiContextReference
83   * @see UiContextBoundComponentProvider
84   */
85  class UiContextApplyingProvider<T> implements Provider<T> {
86  
87      public static <U> UiContextApplyingProvider<U> of(Class<U> type) {
88          return new UiContextApplyingProvider<>(type);
89      }
90  
91      private @Inject Injector injector;
92  
93      private final Class<T> type;
94  
95      UiContextApplyingProvider(Class<T> type) {
96          this.type = type;
97      }
98  
99      @Override
100     public T get() {
101         final CurrentUiContextReference currentUiContext = injector.getInstance(CurrentUiContextReference.class);
102 
103         // get all the UI contexts that are reachable from the current one
104         return currentUiContext.getAvailableContextReferences()
105                 .stream()
106                 // produce a Guice type Key relevant to the UI context
107                 .map(uiContextReference -> Key.get(type, uiContextReference.getAnnotation()))
108                 // check whether such Key is bound to anything
109                 .filter(key -> GuiceUtils.hasExplicitBindingFor(injector, key))
110                 .findFirst().map(injector::getInstance)
111                 // in most of the cases - there should be a UI-annotated variation to be used
112                 .orElseGet(() -> getDefault(currentUiContext));
113     }
114 
115     private T getDefault(CurrentUiContextReference context) {
116         return Optional.ofNullable(
117                 SessionStore.access().getBeanStore(context.getUiContextReference()).get(type))
118                 .orElseGet(() -> {
119                     try {
120                         return this.injector.getInstance(GuiceComponentProvider.class).newInstance(type);
121                     } catch (Exception e) {
122                         return this.injector.getInstance(Key.get(GuiceComponentProvider.class, Names.named("main"))).getComponent(type);
123                     }
124                 });
125     }
126 }