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.AppEventBus;
43 import info.magnolia.ui.api.app.SubAppContext;
44 import info.magnolia.ui.api.app.SubAppEventBus;
45 import info.magnolia.ui.api.context.UiContext;
46 import info.magnolia.ui.api.event.AdmincentralEventBus;
47 import info.magnolia.ui.api.event.ChooseDialogEventBus;
48 import info.magnolia.ui.api.ioc.AdmincentralScoped;
49 import info.magnolia.ui.api.ioc.AdmincentralScopedEager;
50 import info.magnolia.ui.api.ioc.AppScoped;
51 import info.magnolia.ui.api.ioc.SubAppScoped;
52 import info.magnolia.ui.api.ioc.UiContextScoped;
53 import info.magnolia.ui.api.shell.Shell;
54
55 import java.lang.annotation.Annotation;
56 import java.util.HashMap;
57 import java.util.Map;
58 import java.util.Objects;
59 import java.util.Optional;
60 import java.util.function.Consumer;
61 import java.util.function.Function;
62
63 import javax.inject.Named;
64
65 import com.google.inject.AbstractModule;
66 import com.google.inject.Key;
67 import com.google.inject.Provider;
68 import com.google.inject.Provides;
69 import com.google.inject.Scope;
70 import com.google.inject.binder.LinkedBindingBuilder;
71 import com.google.inject.name.Names;
72 import com.vaadin.server.Page;
73 import com.vaadin.server.VaadinServlet;
74 import com.vaadin.server.VaadinSession;
75 import com.vaadin.ui.UI;
76
77
78
79
80
81 public class UiBaseModule extends AbstractModule {
82
83 private final Map<String, EventBusProxyScope> eventBusScopes = new HashMap<>();
84
85 private final EventBus systemEventBus;
86
87 private final UiScopes uiScopes;
88
89 UiBaseModule(EventBus systemEventBus, UiScopes uiScopes) {
90 this.systemEventBus = systemEventBus;
91 this.uiScopes = uiScopes;
92
93 eventBusScopes.put(AdmincentralEventBus.NAME, new EventBusProxyScope(this.uiScopes.eagerAdmincentralScope(), systemEventBus, this.uiScopes));
94 eventBusScopes.put(AppEventBus.NAME, new EventBusProxyScope(this.uiScopes.eagerAppScope(), systemEventBus, this.uiScopes));
95 eventBusScopes.put(SubAppEventBus.NAME, new EventBusProxyScope(this.uiScopes.eagerSubAppScope(), systemEventBus, this.uiScopes));
96 eventBusScopes.put(ChooseDialogEventBus.NAME, new EventBusProxyScope(this.uiScopes.eagerViewScope(), systemEventBus, this.uiScopes));
97
98
99
100 eventBusScopes.put("mediaeditor", new EventBusProxyScope(this.uiScopes.eagerViewScope(), systemEventBus, this.uiScopes));
101 }
102
103 public UiBaseModule(EventBus systemEventBus) {
104 this(systemEventBus, new UiScopes(systemEventBus));
105 }
106
107
108
109
110
111
112 @Provides
113 @SubAppScoped
114 SubAppContext getSubAppCtx(CurrentUiContextReference currentUiContextReference) {
115 Optional<UiContextReference> currentSubAppReference = currentUiContextReference.getSubAppReference();
116 return currentSubAppReference
117 .map(reference -> SessionStore.access().getBeanStore(reference).get(SubAppContext.class))
118 .orElseThrow(IllegalStateException::new);
119 }
120
121
122
123
124
125
126 @Provides
127 @AppScoped
128 AppContext getAppCtx(CurrentUiContextReference currentUiContextReference) {
129 Optional<UiContextReference> currentAppKey = currentUiContextReference.getAppReference();
130 return currentAppKey
131 .map(key -> SessionStore.access().getBeanStore(key).get(AppContext.class))
132 .orElseThrow(IllegalStateException::new);
133 }
134
135 @Provides
136 @AdmincentralScopedEager
137 CurrentUiContextReference getCurrentUiContextReference() {
138 return SessionStore.access().getBeanStore(UiContextReference.ofCurrentUi()).get(CurrentUiContextReference.class);
139 }
140
141 @Provides
142 @AdmincentralScoped
143 SessionStore getSessionStore() {
144 return new SessionStore(VaadinSession.getCurrent(), this.systemEventBus);
145 }
146
147
148
149
150 @Provides
151 GuiceComponentProvider getUiGuiceComponentProvider() {
152 return (GuiceComponentProvider) VaadinServlet.getCurrent().getServletContext().getAttribute("componentProvider");
153 }
154
155
156
157
158
159
160 @Provides
161 @Named("main") GuiceComponentProvider getMainGuiceComponentProvider(GuiceComponentProvider guiceComponentProvider) {
162 return guiceComponentProvider.getParent();
163 }
164
165 @Provides
166 @UiContextScoped
167 ComponentProvider getComponentProvider(GuiceComponentProvider parentComponentProvider, CurrentUiContextReference currentUiContextReference) {
168 return new UiContextBoundComponentProvider(currentUiContextReference.getUiContextReference(), parentComponentProvider, currentUiContextReference);
169 }
170
171 @Override
172 protected void configure() {
173
174 uiScopes.bind(binder());
175
176
177 bindEventBuses();
178
179
180 bindUiContext();
181
182
183 bind(UI.class).toProvider(UI::getCurrent);
184 bind(VaadinSession.class).toProvider(VaadinSession::getCurrent);
185 bind(Page.class).toProvider(Page::getCurrent);
186
187 bind(UiScopes.class).toInstance(this.uiScopes);
188 }
189
190 private void bindUiContext() {
191 final Function<Annotation, LinkedBindingBuilder<UiContext>> uiContextKey = annotation -> bind(UiContext.class).annotatedWith(annotation);
192
193 uiContextKey.apply(UiAnnotations.forAdmincentral()).to(Shell.class);
194 uiContextKey.apply(UiAnnotations.forApps()).to(AppContext.class);
195 uiContextKey.apply(UiAnnotations.forSubApps()).to(SubAppContext.class);
196 uiContextKey.apply(UiAnnotations.forViews()).toProvider(() ->
197 CurrentUiContextReference.get().getViewReference()
198 .map(viewContextKey -> SessionStore.access().getBeanStore(viewContextKey).get(Key.get(UiContext.class, viewContextKey.getAnnotation())))
199 .orElseThrow(() -> new IllegalStateException("Not in view context, cannot resolve view UiContext")));
200
201 bind(UiContext.class).toProvider(new UiContextApplyingProvider<>(UiContext.class));
202 }
203
204 private void bindEventBuses() {
205
206
207 final Provider<EventBus> eventBusProvider = () -> new ResettableEventBus(new SimpleEventBus());
208 final Function<String, Key<EventBus>> createEventBusKey = (name) -> Key.get(EventBus.class, Names.named(name));
209
210 final Consumer<String> bindEventBus = name ->
211 bind(createEventBusKey.apply(name))
212 .toProvider(eventBusProvider)
213 .in(eventBusScopes.get(name));
214
215 bindEventBus.accept(AdmincentralEventBus.NAME);
216 bindEventBus.accept(AppEventBus.NAME);
217 bindEventBus.accept(SubAppEventBus.NAME);
218 bindEventBus.accept(ChooseDialogEventBus.NAME);
219 bindEventBus.accept("mediaeditor");
220 }
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243 @SuppressWarnings("unchecked")
244 private static class EventBusProxyScope extends ProxyScope {
245
246 EventBusProxyScope(SessionStoreScope originalScope, EventBus systemEventBus, UiScopes allUiScopes) {
247 super(originalScope, allUiScopes);
248 systemEventBus.addHandler(BeanStoreLifecycleEvent.BeforeDestroy.class, event -> this.releaseEventHandlers(event.getRelatedContextKey(), event.getStore()));
249 }
250
251
252
253 private void releaseEventHandlers(UiContextReference relatedContextReference, BeanStore store) {
254
255 final Scope destroyedScope = getAllUiScopes().getScope(relatedContextReference.getAnnotation().getRelatedScopeAnnotation(true));
256
257
258
259 if (Objects.equals(getOriginalScope(), destroyedScope)) {
260 store.forEach((key, value) -> {
261 if (value instanceof ResettableEventBus) {
262 ((ResettableEventBus) value).reset();
263 }
264 });
265 }
266 }
267
268 @Override
269 protected <T> Provider<T> proxyScope(Key<T> key, Provider<T> scopedProvider, SessionStoreScope currentScope) {
270 if (key.getTypeLiteral().getRawType().isAssignableFrom(EventBus.class)) {
271 return () -> {
272 final BeanStore relatedStore = currentScope.getStore();
273 EventBus protectedEventBus = relatedStore.get((Key<EventBus>) key);
274 if (protectedEventBus == null) {
275 protectedEventBus = new ResettableEventBus((EventBus) scopedProvider.get());
276 relatedStore.put(key, protectedEventBus);
277 }
278 return (T) protectedEventBus;
279
280 };
281 }
282
283 throw new IllegalArgumentException("Can only proxy EventBus objects, do not know what to do with [" + key + "]");
284 }
285 }
286 }