1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.ui.framework.ioc;
35
36 import info.magnolia.event.EventBus;
37 import info.magnolia.event.ResettableEventBus;
38 import info.magnolia.event.SimpleEventBus;
39 import info.magnolia.objectfactory.ComponentProvider;
40 import info.magnolia.objectfactory.guice.GuiceComponentProvider;
41 import info.magnolia.ui.api.app.AppContext;
42 import info.magnolia.ui.api.app.AppDescriptor;
43 import info.magnolia.ui.api.app.AppEventBus;
44 import info.magnolia.ui.api.app.SubAppContext;
45 import info.magnolia.ui.api.app.SubAppDescriptor;
46 import info.magnolia.ui.api.app.SubAppEventBus;
47 import info.magnolia.ui.api.context.UiContext;
48 import info.magnolia.ui.api.event.AdmincentralEventBus;
49 import info.magnolia.ui.api.event.ChooseDialogEventBus;
50 import info.magnolia.ui.api.ioc.AdmincentralScoped;
51 import info.magnolia.ui.api.ioc.AdmincentralScopedEager;
52 import info.magnolia.ui.api.ioc.AppScoped;
53 import info.magnolia.ui.api.ioc.SubAppScoped;
54 import info.magnolia.ui.api.ioc.UiContextScoped;
55 import info.magnolia.ui.api.shell.Shell;
56 import info.magnolia.ui.datasource.WithDatasource;
57 import info.magnolia.ui.datasource.DatasourceDefinition;
58
59 import java.lang.annotation.Annotation;
60 import java.util.HashMap;
61 import java.util.Map;
62 import java.util.Objects;
63 import java.util.Optional;
64 import java.util.function.Consumer;
65 import java.util.function.Function;
66 import java.util.stream.Stream;
67
68 import javax.inject.Named;
69
70 import com.google.inject.AbstractModule;
71 import com.google.inject.Key;
72 import com.google.inject.Provider;
73 import com.google.inject.Provides;
74 import com.google.inject.Scope;
75 import com.google.inject.binder.LinkedBindingBuilder;
76 import com.google.inject.name.Names;
77 import com.vaadin.server.Page;
78 import com.vaadin.server.VaadinServlet;
79 import com.vaadin.server.VaadinSession;
80 import com.vaadin.ui.UI;
81
82
83
84
85
86 public class UiBaseModule extends AbstractModule {
87
88 private final Map<String, EventBusProxyScope> eventBusScopes = new HashMap<>();
89
90 private final EventBus systemEventBus;
91
92 private final UiScopes uiScopes;
93
94 UiBaseModule(EventBus systemEventBus, UiScopes uiScopes) {
95 this.systemEventBus = systemEventBus;
96 this.uiScopes = uiScopes;
97
98 eventBusScopes.put(AdmincentralEventBus.NAME, new EventBusProxyScope(this.uiScopes.eagerAdmincentralScope(), systemEventBus, this.uiScopes));
99 eventBusScopes.put(AppEventBus.NAME, new EventBusProxyScope(this.uiScopes.eagerAppScope(), systemEventBus, this.uiScopes));
100 eventBusScopes.put(SubAppEventBus.NAME, new EventBusProxyScope(this.uiScopes.eagerSubAppScope(), systemEventBus, this.uiScopes));
101 eventBusScopes.put(ChooseDialogEventBus.NAME, new EventBusProxyScope(this.uiScopes.eagerViewScope(), systemEventBus, this.uiScopes));
102
103
104
105 eventBusScopes.put("mediaeditor", new EventBusProxyScope(this.uiScopes.eagerViewScope(), systemEventBus, this.uiScopes));
106 }
107
108 public UiBaseModule(EventBus systemEventBus) {
109 this(systemEventBus, new UiScopes(systemEventBus));
110 }
111
112
113
114
115
116
117 @Provides
118 @SubAppScoped
119 SubAppContext getSubAppCtx(CurrentUiContextReference currentUiContextReference) {
120 Optional<UiContextReference> currentSubAppReference = currentUiContextReference.getSubAppReference();
121 return currentSubAppReference
122 .map(reference -> SessionStore.access().getBeanStore(reference).get(SubAppContext.class))
123 .orElseThrow(IllegalStateException::new);
124 }
125
126
127
128
129
130
131 @Provides
132 @AppScoped
133 AppContext getAppCtx(CurrentUiContextReference currentUiContextReference) {
134 Optional<UiContextReference> currentAppKey = currentUiContextReference.getAppReference();
135 return currentAppKey
136 .map(key -> SessionStore.access().getBeanStore(key).get(AppContext.class))
137 .orElseThrow(IllegalStateException::new);
138 }
139
140 @Provides
141 @AppScoped
142 AppDescriptor getAppDescriptor(AppContext appContext) {
143 return appContext.getAppDescriptor();
144 }
145
146 @Provides
147 @SubAppScoped
148 SubAppDescriptor getSubAppDescriptor(SubAppContext subAppContext) {
149 return subAppContext.getSubAppDescriptor();
150 }
151
152 @Provides
153 @AdmincentralScopedEager
154 CurrentUiContextReference getCurrentUiContextReference() {
155 return SessionStore.access().getBeanStore(UiContextReference.ofCurrentUi()).get(CurrentUiContextReference.class);
156 }
157
158 @Provides
159 @UiContextScoped
160 DatasourceDefinition getDatasourceDefinition(UiContextReference uiContextReference) {
161 Stream<UiContextReference> reachableUiContexts = Stream.concat(Stream.of(uiContextReference), uiContextReference.getParentReferences().stream());
162 Function<UiContextReference, DatasourceDefinition> locateDatasourceDefinition = ref -> Optional.ofNullable(SessionStore.access().getBeanStore(ref))
163 .flatMap(beanStore -> {
164 if (beanStore.hasInstanceOf(DatasourceDefinition.class)) {
165 return beanStore.getInstance(DatasourceDefinition.class);
166 } else {
167 return beanStore.getInstance(WithDatasource.class).map(WithDatasource::getDatasource);
168 }
169 }).orElse(null);
170
171 return reachableUiContexts
172 .map(locateDatasourceDefinition)
173 .filter(Objects::nonNull)
174 .findFirst()
175 .orElse(null);
176 }
177
178 @Provides
179 @AdmincentralScoped
180 SessionStore getSessionStore() {
181 return new SessionStore(VaadinSession.getCurrent(), this.systemEventBus);
182 }
183
184
185
186
187 @Provides
188 GuiceComponentProvider getUiGuiceComponentProvider() {
189 return (GuiceComponentProvider) VaadinServlet.getCurrent().getServletContext().getAttribute("componentProvider");
190 }
191
192
193
194
195
196
197 @Provides
198 @Named("main") GuiceComponentProvider getMainGuiceComponentProvider(GuiceComponentProvider guiceComponentProvider) {
199 return guiceComponentProvider.getParent();
200 }
201
202 @Provides
203 @UiContextScoped
204 ComponentProvider getComponentProvider(GuiceComponentProvider parentComponentProvider, CurrentUiContextReference currentUiContextReference) {
205 return new UiContextBoundComponentProvider(currentUiContextReference.getUiContextReference(), parentComponentProvider, currentUiContextReference);
206 }
207
208 @Provides
209 @UiContextScoped
210 UiComponentProvider getComponentProvider(ComponentProvider componentProvider) {
211 return new UiComponentProvider/../../../info/magnolia/ui/framework/ioc/UiComponentProvider.html#UiComponentProvider">UiComponentProvider(((UiComponentProvider) componentProvider).getUiContextReference());
212 }
213
214 @Provides
215 @UiContextScoped
216 UiContextReference getCurrentContextReference(CurrentUiContextReference currentUiContextReference) {
217 return currentUiContextReference.getUiContextReference();
218 }
219
220 @Override
221 protected void configure() {
222
223 uiScopes.bind(binder());
224
225
226 bindEventBuses();
227
228
229 bindUiContext();
230
231
232 bind(UI.class).toProvider(UI::getCurrent);
233 bind(VaadinSession.class).toProvider(VaadinSession::getCurrent);
234 bind(Page.class).toProvider(Page::getCurrent);
235
236 bind(UiScopes.class).toInstance(this.uiScopes);
237 }
238
239 private void bindUiContext() {
240 final Function<Annotation, LinkedBindingBuilder<UiContext>> uiContextKey = annotation -> bind(UiContext.class).annotatedWith(annotation);
241
242 uiContextKey.apply(UiAnnotations.forAdmincentral()).to(Shell.class);
243 uiContextKey.apply(UiAnnotations.forM5Admincentral()).to(Shell.class);
244 uiContextKey.apply(UiAnnotations.forApps()).to(AppContext.class);
245 uiContextKey.apply(UiAnnotations.forSubApps()).to(SubAppContext.class);
246 uiContextKey.apply(UiAnnotations.forViews()).toProvider(() ->
247 CurrentUiContextReference.get().getViewReference()
248 .map(viewContextKey -> SessionStore.access().getBeanStore(viewContextKey).get(Key.get(UiContext.class, viewContextKey.getAnnotation())))
249 .orElseThrow(() -> new IllegalStateException("Not in view context, cannot resolve view UiContext")));
250
251 bind(UiContext.class).toProvider(new UiContextApplyingProvider<>(UiContext.class));
252 }
253
254 private void bindEventBuses() {
255
256
257 final Provider<EventBus> eventBusProvider = () -> new ResettableEventBus(new SimpleEventBus());
258 final Function<String, Key<EventBus>> createEventBusKey = (name) -> Key.get(EventBus.class, Names.named(name));
259
260 final Consumer<String> bindEventBus = name ->
261 bind(createEventBusKey.apply(name))
262 .toProvider(eventBusProvider)
263 .in(eventBusScopes.get(name));
264
265 bindEventBus.accept(AdmincentralEventBus.NAME);
266 bindEventBus.accept(AppEventBus.NAME);
267 bindEventBus.accept(SubAppEventBus.NAME);
268 bindEventBus.accept(ChooseDialogEventBus.NAME);
269 bindEventBus.accept("mediaeditor");
270 }
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293 @SuppressWarnings("unchecked")
294 private static class EventBusProxyScope extends ProxyScope {
295
296 EventBusProxyScope(SessionStoreScope originalScope, EventBus systemEventBus, UiScopes allUiScopes) {
297 super(originalScope, allUiScopes);
298 systemEventBus.addHandler(BeanStoreLifecycleEvent.BeforeDestroy.class, event -> this.releaseEventHandlers(event.getRelatedContextKey(), event.getStore()));
299 }
300
301
302
303 private void releaseEventHandlers(UiContextReference relatedContextReference, BeanStore store) {
304
305 final Scope destroyedScope = getAllUiScopes().getScope(relatedContextReference.getAnnotation().getRelatedScopeAnnotation(true));
306
307
308
309 if (Objects.equals(getOriginalScope(), destroyedScope)) {
310 store.forEach((key, value) -> {
311 if (value instanceof ResettableEventBus) {
312 ((ResettableEventBus) value).reset();
313 }
314 });
315 }
316 }
317
318 @Override
319 protected <T> Provider<T> proxyScope(Key<T> key, Provider<T> scopedProvider, SessionStoreScope currentScope) {
320 if (key.getTypeLiteral().getRawType().isAssignableFrom(EventBus.class)) {
321 return () -> {
322 final BeanStore relatedStore = currentScope.getStore();
323 EventBus protectedEventBus = relatedStore.get((Key<EventBus>) key);
324 if (protectedEventBus == null) {
325 protectedEventBus = new ResettableEventBus((EventBus) scopedProvider.get());
326 relatedStore.put(key, protectedEventBus);
327 }
328 return (T) protectedEventBus;
329
330 };
331 }
332
333 throw new IllegalArgumentException("Can only proxy EventBus objects, do not know what to do with [" + key + "]");
334 }
335 }
336 }