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 static java.util.stream.Collectors.toMap;
37
38 import info.magnolia.config.NamedDefinition;
39 import info.magnolia.i18nsystem.util.AnnotationUtils;
40 import info.magnolia.objectfactory.CandidateParameterResolver;
41 import info.magnolia.objectfactory.ComponentProvider;
42 import info.magnolia.objectfactory.ParameterResolver;
43 import info.magnolia.objectfactory.guice.GuiceComponentProvider;
44 import info.magnolia.ui.UIComponent;
45 import info.magnolia.ui.api.ioc.ViewScoped;
46 import info.magnolia.ui.datasource.DatasourceDefinition;
47 import info.magnolia.ui.datasource.WithDatasource;
48 import info.magnolia.util.Util;
49
50 import java.io.Serializable;
51 import java.util.Arrays;
52 import java.util.HashMap;
53 import java.util.Map;
54 import java.util.Optional;
55 import java.util.function.Function;
56 import java.util.function.Supplier;
57 import java.util.stream.Stream;
58
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 import com.google.common.base.Suppliers;
63 import com.google.inject.Key;
64 import com.vaadin.server.VaadinServlet;
65
66
67
68
69
70
71
72 public class UiComponentProvider implements ComponentProvider, Serializable {
73
74 private static final Logger log = LoggerFactory.getLogger(UiComponentProvider.class);
75
76 private final transient GuiceComponentProvider parentComponentProvider;
77 private final CurrentUiContextReference currentUiContextReference = CurrentUiContextReference.get();
78 private final UiContextReference viewContextReference;
79 private final Supplier<DatasourceDefinition> datasourceDefinition;
80
81 public UiComponentProvider(UiContextReference viewContextReference, GuiceComponentProvider parentComponentProvider) {
82 this.parentComponentProvider = parentComponentProvider;
83 this.viewContextReference = viewContextReference;
84 this.datasourceDefinition = Suppliers.memoize(() -> this.getComponent(DatasourceDefinition.class));
85 }
86
87 public UiComponentProvider(UiContextReference viewContextReference) {
88 this(viewContextReference, (GuiceComponentProvider) VaadinServlet.getCurrent().getServletContext().getAttribute("componentProvider"));
89 }
90
91 public UiContextReference getUiContextReference() {
92 return viewContextReference;
93 }
94
95 public UiComponentProvider inChildContext(String name, Object definition) {
96 Builder builder = builder(name, viewContextReference).withInstances(definition);
97 if (definition instanceof WithDatasource) {
98 Map<Class, Object> instances = new HashMap<>();
99 Optional.ofNullable(((WithDatasource) definition).getDatasource())
100 .ifPresent(datasourceDefinition -> instances.put(DatasourceDefinition.class, datasourceDefinition));
101 builder = builder.withInstances(instances);
102 }
103
104 return builder.build();
105 }
106
107 public UiComponentProvider inChildContext(NamedDefinition definition) {
108 return inChildContext(definition.getName(), definition);
109 }
110
111 @Override
112 public <T> T getComponent(Class<T> type) {
113 return provideInCurrentScope(() -> getParent().getComponent(type));
114 }
115
116 @Override
117 public <T> Class<? extends T> getImplementation(Class<T> type) {
118 return getParent().getImplementation(getClosestMappedType(type));
119 }
120
121 @Override
122 public <T> T getSingleton(Class<T> type) {
123 return getParent().getSingleton(type);
124 }
125
126 @Override
127 public <T> T newInstance(Class<T> type, Object... parameters) {
128 return newInstanceWithParameterResolvers(type, new CandidateParameterResolver(parameters));
129 }
130
131 @Override
132 public <T> T newInstanceWithParameterResolvers(Class<T> type, ParameterResolver... parameters) {
133 ParameterResolver[] combinedParameters = new ParameterResolver[parameters.length + 1];
134 combinedParameters[parameters.length] = new UiComponentContextParameterResolver(viewContextReference);
135 System.arraycopy(parameters, 0, combinedParameters, 0, parameters.length);
136
137 T instance = provideInCurrentScope(() -> getParent().newInstanceWithParameterResolvers(getClosestMappedType(type), combinedParameters));
138
139 if (instance instanceof UIComponent) {
140 BeanStore beanStore = SessionStore.access().getBeanStore(getUiContextReference());
141 if (beanStore.hasInstanceOf(UIComponent.class)) {
142 log.warn("Attempted to cache a UIComponent ({}) instance in bean store containing another UIComponent ({})",
143 type.getSimpleName(), beanStore.get(UIComponent.class).getClass().getSimpleName());
144 }
145
146 beanStore.put(type, instance);
147 }
148
149
150 if (AnnotationUtils.hasAnnotationOn(type, ViewScoped.class)) {
151
152 SessionStore.access().getBeanStore(getUiContextReference()).put(type, instance);
153 }
154
155 return instance;
156 }
157
158 @Override
159 public GuiceComponentProvider getParent() {
160 return this.parentComponentProvider;
161 }
162
163 protected Stream<UiContextReference> getReachableUiContextReferences() {
164 Stream<UiContextReference> datasourceReference = Optional.ofNullable(datasourceDefinition.get())
165 .map(DatasourceDefinition::getName)
166 .map(UiContextReference::ofDatasourceComponent)
167 .map(Stream::of)
168 .orElse(Stream.empty());
169
170 return Stream.concat(currentUiContextReference.getAvailableContextReferences().stream(), datasourceReference);
171 }
172
173 private <T> T provideInCurrentScope(Supplier<T> provider) {
174 final UiContextReference previousUiContextReference = currentUiContextReference.getUiContextReference();
175 currentUiContextReference.setUiContextReference(viewContextReference);
176 try {
177 return provider.get();
178 } finally {
179 currentUiContextReference.setUiContextReference(previousUiContextReference);
180 }
181 }
182
183 private <T> Key<T> getClosestMappedType(Class<T> type) {
184 return getReachableUiContextReferences()
185 .map(key -> Key.get(type, key.getAnnotation()))
186
187 .filter(key -> getParent().getTypeMappings().containsKey(key))
188 .findFirst()
189
190 .orElseGet(() -> Key.get(type));
191 }
192
193 public static Builder builder(String name, UiContextReference parentReference) {
194 String id = name;
195 UiContextReference viewContextReference;
196 do {
197 viewContextReference = UiContextReference.ofView(id, parentReference);
198 id = Util.createUniqueName(id);
199 } while (SessionStore.access().getBeanStore(viewContextReference) != null);
200
201 return new Builder().withUiContextReference(viewContextReference);
202 }
203
204 public static class Builder {
205
206 private Builder() {
207 }
208
209 private UiContextReference uiContextReference;
210 private Map<Class, Object> instances = new HashMap<>();
211
212 public Builder withUiContextReference(UiContextReference uiContextReference) {
213 this.uiContextReference = uiContextReference;
214 return this;
215 }
216
217 public Builder withInstances(Object... instances) {
218 this.instances.putAll(Arrays.stream(instances)
219 .collect(toMap(Object::getClass, Function.identity(), (first, second) -> second)));
220 return this;
221 }
222
223 public Builder withInstances(Map<Class, Object> instances) {
224 this.instances.putAll(instances);
225 return this;
226 }
227
228 public UiComponentProvider build() {
229 BeanStore beanStore = SessionStore.access().createBeanStore(uiContextReference);
230 instances.forEach(beanStore::put);
231
232 UiContextBoundComponentProviderhtml#UiContextBoundComponentProvider">UiContextBoundComponentProvider uiComponentProvider = new UiContextBoundComponentProvider(uiContextReference);
233 beanStore.put(UiContextBoundComponentProvider.class, uiComponentProvider);
234 beanStore.put(ComponentProvider.class, uiComponentProvider);
235
236 return uiComponentProvider;
237 }
238 }
239 }