View Javadoc
1   /**
2    * This file Copyright (c) 2017-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.event.EventBus;
37  import info.magnolia.ui.api.ioc.AdmincentralScoped;
38  import info.magnolia.ui.api.ioc.AdmincentralScopedEager;
39  import info.magnolia.ui.api.ioc.AppScoped;
40  import info.magnolia.ui.api.ioc.AppScopedEager;
41  import info.magnolia.ui.api.ioc.SubAppScoped;
42  import info.magnolia.ui.api.ioc.SubAppScopedEager;
43  import info.magnolia.ui.api.ioc.UiContextScoped;
44  import info.magnolia.ui.api.ioc.ViewScoped;
45  import info.magnolia.ui.api.ioc.ViewScopedEager;
46  
47  import java.lang.annotation.Annotation;
48  import java.util.HashMap;
49  import java.util.Map;
50  
51  import com.google.inject.Binder;
52  
53  /**
54   * <p>
55   * Magnolia UI-related IoC scopes.
56   * </p>
57   * <p>
58   * Currently all the Magnolia-specific scopes are bound
59   * to http session (or {@link com.vaadin.server.VaadinSession Vaadin Session} which wraps it).
60   * This means that all the instances bound to the scopes will reside in session
61   * unless manually cleared-up.
62   * </p>
63   * <p>
64   * Effectively with this approach the http session can be treated as a map
65   * of {@link BeanStore bean stores} each of which corresponds to a
66   * certain admincentral instance, app, sub-app, or a view. Currently provided
67   * scopes are capable of automatically finding the appropriate store and look up and save
68   * instances in it.
69   * </p>
70   * <p>
71   * There are four UI scopes currently defined:
72   * <ul>
73   * <li>Admincentral</li>
74   * <li>App</li>
75   * <li>Sub-app</li>
76   * <li>View</li>
77   * </ul>
78   * </p>
79   * <p>{@link CurrentUiContextReference} is used to help scopes to find the bean stores.
80   * E.g. when there's an app-scoped {@code Foo} instance is to be injected the
81   * {@code UiScopes#appScope} will look up the {@link UiContextReference} of the
82   * app involved in injection via {@link CurrentUiContextReference} and will fetch
83   * the corresponding bean store from the session (via {@link SessionStore} utility).
84   * </p>
85   * <p>
86   * For more information on the Guice scoping,
87   * see <a href="https://github.com/google/guice/wiki/Scopes">Guice Scopes manual</a>
88   * or <a href="https://github.com/google/guice/wiki/CustomScopes">Guice Custom Scopes tutorial</a>.
89   * </p>
90   *
91   * @see SessionStore
92   * @see SessionStoreScope
93   * @see BeanStore
94   * @see ProxyScope
95   */
96  class UiScopes {
97  
98      private final Map<Class<? extends Annotation>, SessionStoreScope> scopes = new HashMap<>();
99  
100     UiScopes(EventBus systemEventBus) {
101         this.eagerAppScope = new EagerSessionStoreScope(appScope, systemEventBus, this);
102         this.eagerAdmincentralScope = new EagerSessionStoreScope(admincentralScope, systemEventBus, this);
103         this.eagerSubAppScope = new EagerSessionStoreScope(subAppScope, systemEventBus, this);
104         this.eagerViewScope = new EagerSessionStoreScope(viewScope, systemEventBus, this);
105         this.eagerCurrentUiContextScope = new EagerSessionStoreScope(uiContextScope, systemEventBus, this);
106 
107         scopes.put(AppScoped.class, this.appScope());
108         scopes.put(SubAppScoped.class, this.subAppScope());
109         scopes.put(AdmincentralScoped.class, this.admincentralScope());
110         scopes.put(ViewScoped.class, this.viewScope());
111         scopes.put(UiContextScoped.class, this.currentUiContextScope());
112 
113         scopes.put(AppScopedEager.class, this.eagerAppScope());
114         scopes.put(AdmincentralScopedEager.class, this.eagerAdmincentralScope());
115         scopes.put(SubAppScopedEager.class, this.eagerSubAppScope());
116         scopes.put(ViewScopedEager.class, this.eagerViewScope());
117     }
118     
119     void bind(Binder binder) {
120         scopes.forEach(binder::bindScope);
121     }
122 
123     SessionStoreScope getScope(Class<? extends Annotation> annotation) {
124         return this.scopes.get(annotation);
125     }
126 
127     SessionStoreScope getScope(Annotation annotation) {
128         return this.getScope(annotation.annotationType());
129     }
130 
131     /**
132      * {@link SessionStoreScope} corresponding to the current Admincentral instance.
133      * Resolves the bean store by the current AdminCentral context reference produced from
134      * the current UI instance.
135      */
136     private final SessionStoreScopee.html#SessionStoreScope">SessionStoreScope admincentralScope = new SessionStoreScope() {
137         @Override
138         protected UiContextReference getCurrentContextKey() {
139             return UiContextReference.ofCurrentUi();
140         }
141 
142         @Override
143         public String toString() {
144             return "AdminCentral scope";
145         }
146     };
147 
148     /**
149      * {@link SessionStoreScope} corresponding to the current Admincentral instance.
150      * Resolves the bean store the current app context reference.
151      */
152     private final SessionStoreScopeStoreScope.html#SessionStoreScope">SessionStoreScope appScope = new SessionStoreScope() {
153         @Override
154         protected UiContextReference getCurrentContextKey() {
155             return CurrentUiContextReference.get().getAppReference().orElseThrow(() -> new IllegalStateException("Not in app scope, can't resolve current app store!"));
156         }
157 
158         @Override
159         public String toString() {
160             return "App scope";
161         }
162     };
163 
164     /**
165      * {@link SessionStoreScope} corresponding to the current Admincentral instance.
166      * Resolves the bean store the current view context reference.
167      */
168     private final SessionStoreScopetoreScope.html#SessionStoreScope">SessionStoreScope viewScope = new SessionStoreScope() {
169         @Override
170         protected UiContextReference getCurrentContextKey() {
171             return CurrentUiContextReference.get().getViewReference().orElseThrow(() -> new IllegalStateException("Not in a view scope, cannot resolve the corresponding bean store!"));
172         }
173 
174         @Override
175         public String toString() {
176             return "View scope";
177         }
178     };
179 
180     /**
181      * {@link SessionStoreScope} corresponding to the current Admincentral instance.
182      * Resolves the bean store the current sub-app context reference.
183      */
184     private final SessionStoreScopereScope.html#SessionStoreScope">SessionStoreScope subAppScope = new SessionStoreScope() {
185 
186         @Override
187         protected UiContextReference getCurrentContextKey() {
188             return CurrentUiContextReference.get().getSubAppReference().orElseThrow(() -> new IllegalStateException("Not in sub-app scope, can't resolve current sub-app store!"));
189         }
190 
191         @Override
192         public String toString() {
193             return "Sub-app scope";
194         }
195     };
196 
197     private final SessionStoreScopecope.html#SessionStoreScope">SessionStoreScope uiContextScope = new SessionStoreScope() {
198         @Override
199         protected UiContextReference getCurrentContextKey() {
200             return CurrentUiContextReference.get().getUiContextReference();
201         }
202 
203         @Override
204         public String toString() {
205             return "Current UI context scope";
206         }
207     };
208     
209     private final EagerSessionStoreScope eagerAdmincentralScope;
210 
211     private final EagerSessionStoreScope eagerAppScope;
212 
213     private final EagerSessionStoreScope eagerSubAppScope;
214 
215     private final EagerSessionStoreScope eagerViewScope;
216 
217     private final SessionStoreScope eagerCurrentUiContextScope;
218 
219     SessionStoreScope appScope() {
220         return this.appScope;
221     }
222 
223     SessionStoreScope admincentralScope() {
224         return this.admincentralScope;
225     }
226 
227     SessionStoreScope subAppScope() {
228         return this.subAppScope;
229     }
230 
231     SessionStoreScope viewScope() {
232         return this.viewScope;
233     }
234 
235     SessionStoreScope currentUiContextScope() {
236         return this.uiContextScope;
237     }
238 
239     SessionStoreScope eagerAppScope() {
240         return this.eagerAppScope;
241     }
242 
243     SessionStoreScope eagerSubAppScope() {
244         return this.eagerSubAppScope;
245     }
246 
247     SessionStoreScope eagerAdmincentralScope() {
248         return this.eagerAdmincentralScope;
249     }
250 
251     SessionStoreScope eagerViewScope() {
252         return this.eagerViewScope;
253     }
254 
255     SessionStoreScope eagerCurrentUiContextScope() {
256         return this.eagerCurrentUiContextScope;
257     }
258 }