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.jcr.node2bean.impl;
35
36 import static info.magnolia.objectfactory.Components.getComponentProvider;
37 import static info.magnolia.transformer.TransformationProblem.*;
38 import static java.util.stream.Collectors.toSet;
39
40 import info.magnolia.jcr.decoration.ContentDecorator;
41 import info.magnolia.jcr.iterator.FilteringPropertyIterator;
42 import info.magnolia.jcr.node2bean.Node2BeanException;
43 import info.magnolia.jcr.node2bean.Node2BeanProcessor;
44 import info.magnolia.jcr.node2bean.Node2BeanTransformer;
45 import info.magnolia.jcr.node2bean.PropertyTypeDescriptor;
46 import info.magnolia.jcr.node2bean.TransformationState;
47 import info.magnolia.jcr.node2bean.TypeDescriptor;
48 import info.magnolia.jcr.node2bean.TypeMapping;
49 import info.magnolia.jcr.predicate.AbstractPredicate;
50 import info.magnolia.jcr.util.NodeTypes;
51 import info.magnolia.jcr.util.NodeUtil;
52 import info.magnolia.jcr.util.PropertyUtil;
53 import info.magnolia.jcr.wrapper.ExtendingNodeWrapper;
54 import info.magnolia.objectfactory.ComponentProvider;
55 import info.magnolia.transformer.TransformationProblem;
56 import info.magnolia.transformer.TransformationResult;
57
58 import java.util.Collection;
59 import java.util.LinkedHashMap;
60 import java.util.Map;
61 import java.util.Set;
62
63 import javax.inject.Inject;
64 import javax.inject.Singleton;
65 import javax.jcr.Node;
66 import javax.jcr.NodeIterator;
67 import javax.jcr.Property;
68 import javax.jcr.PropertyIterator;
69 import javax.jcr.RepositoryException;
70
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
73
74
75
76
77 @Singleton
78 public class Node2BeanProcessorImpl implements Node2BeanProcessor {
79
80 private static final Logger log = LoggerFactory.getLogger(Node2BeanProcessorImpl.class);
81
82 private final TypeMapping typeMapping;
83
84 private final Node2BeanTransformer defaultTransformer;
85
86 private boolean forceCreation = true;
87
88 @Inject
89 public Node2BeanProcessorImpl(TypeMapping typeMapping, Node2BeanTransformer transformer) {
90 this.typeMapping = typeMapping;
91 this.defaultTransformer = transformer;
92 }
93
94 @Override
95 public <T> TransformationResult<T> transform(Node source, Class<T> type) {
96 return transform(source, type, defaultTransformer);
97 }
98
99 @Override
100 public <T> TransformationResult<T> transform(Node source, Class<T> type, Node2BeanTransformer transformer) {
101 final TransformationState state = transformer.newState();
102
103 T bean = null;
104 try {
105
106 bean = (T) toBean(source, true, transformer, state, getComponentProvider());
107 } catch (RepositoryException e) {
108 state.trackProblem(error("failed to instantiate a bean due to a JCR operation problem: [%s]", e.getMessage()).withException(e));
109 } catch (Node2BeanException e) {
110 state.trackProblem(error("failed to instantiate a bean due to a Node2Bean operation problem: [%s]", e.getMessage()).withException(e));
111 } catch (Exception e) {
112 state.trackProblem(error("failed to instantiate a bean due to an un-expected problem: [%s]", e.getMessage()).withException(e));
113 }
114
115 return TransformationResult.transformationResult(bean, state.getProblems());
116 }
117
118 @Override
119 public Object toBean(Node node) throws Node2BeanException, RepositoryException {
120 return transformAndLogProblems(node, true, defaultTransformer, defaultTransformer.newState(), getComponentProvider());
121 }
122
123 @Override
124 public Object toBean(Node node, final Class<?> defaultClass) throws Node2BeanException, RepositoryException {
125 return transformAndLogProblems(node, true, new Node2BeanTransformerImpl() {
126 @Override
127 protected TypeDescriptor onResolveType(TypeMapping mapping, TransformationState state, TypeDescriptor resolvedType, ComponentProvider componentProvider) {
128 if (resolvedType == null && state.getLevel() == 1) {
129 return mapping.getTypeDescriptor(defaultClass);
130 }
131 return resolvedType;
132 }
133 }, defaultTransformer.newState(), getComponentProvider());
134 }
135
136 @Override
137 public Object toBean(Node node, boolean recursive, final Node2BeanTransformer transformer, ComponentProvider componentProvider) throws Node2BeanException, RepositoryException {
138 return transformAndLogProblems(node, recursive, transformer, transformer.newState(), componentProvider);
139 }
140
141
142
143
144
145
146
147 private Object transformAndLogProblems(Node source, boolean recursive, Node2BeanTransformer transformer, TransformationState state, ComponentProvider componentProvider) throws Node2BeanException, RepositoryException {
148
149 try {
150 return toBean(source, recursive, transformer, state, componentProvider);
151 } finally {
152
153
154
155
156 for (final TransformationProblem problem : state.getProblems()) {
157 switch (problem.getSeverityType()) {
158 case ERROR:
159 log.warn(problem.getMessage(), problem.getException());
160 break;
161 case WARNING:
162 log.debug(problem.getMessage());
163 break;
164 }
165 }
166 }
167 }
168
169 protected Object toBean(Node node, boolean recursive, Node2BeanTransformer transformer, TransformationState state, ComponentProvider componentProvider) throws Node2BeanException, RepositoryException {
170 if (!NodeUtil.isWrappedWith(node, ExtendingNodeWrapper.class)) {
171 node = new ExtendingNodeWrapper(node);
172 }
173 state.pushNode(node);
174 TypeDescriptor type = null;
175 try {
176 type = transformer.resolveType(typeMapping, state, componentProvider);
177 } catch (Exception e) {
178 final String errorMessage = String.format("Bean type resolution failed for node [%s] due to: %s", node.getPath(), e.getMessage());
179 if (isForceCreation()) {
180 state.trackProblem(error(errorMessage).withException(e));
181 } else {
182 throw new Node2BeanException(errorMessage, e);
183 }
184 }
185
186 Object bean = null;
187 if (type != null) {
188 state.pushType(type);
189
190 transformer = resolveTransformer(type, transformer);
191
192 Map<String, Object> values = toMap(node, recursive, transformer, state, componentProvider);
193
194 try {
195 bean = transformer.newBeanInstance(state, values, componentProvider);
196 } catch (Exception e) {
197 final String errorMessage = String.format("Failed to instantiate an object of type [%s] due to [%s], node-to-bean transformation will is aborted",
198 state.getCurrentType().getType(),
199 e.getMessage());
200
201 if (!isForceCreation()) {
202 state.trackProblem(error(errorMessage).withException(e));
203 }
204
205 if (!isForceCreation()) {
206 throw new Node2BeanException(errorMessage, e);
207 }
208 }
209
210 if (bean != null) {
211 state.pushBean(bean);
212
213 setProperties(values, transformer, state);
214
215 transformer.initBean(state, values);
216
217 bean = state.getCurrentBean();
218
219 state.popBean();
220 } else {
221 if (forceCreation) {
222 state.trackProblem(error("Did not manage to create a bean for node [%s], this part of configuration will be skipped", node.getPath()));
223 } else {
224 throw new Node2BeanException("can't instantiate bean of type " + type.getType().getName());
225 }
226 }
227
228 state.popType();
229 }
230 state.popNode();
231
232 return bean;
233 }
234
235 @Override
236 public Object setProperties(final Object bean, Node node, boolean recursive, Node2BeanTransformer transformer, ComponentProvider componentProvider) throws Node2BeanException, RepositoryException {
237
238 node = new ExtendingNodeWrapper(node);
239
240 TransformationState state = transformer.newState();
241 state.pushBean(bean);
242 state.pushNode(node);
243
244 TypeDescriptor type = typeMapping.getTypeDescriptor(bean.getClass());
245
246 state.pushType(type);
247
248 transformer = resolveTransformer(type, transformer);
249
250 Map<String, Object> values = toMap(node, recursive, transformer, state, componentProvider);
251
252 setProperties(values, transformer, state);
253
254 transformer.initBean(state, values);
255
256 state.popBean();
257 state.popType();
258 state.popNode();
259
260 return bean;
261 }
262
263
264
265
266 protected Map<String, Object> toMap(Node node, boolean recursive, Node2BeanTransformer transformer, TransformationState state, ComponentProvider componentProvider) throws Node2BeanException, RepositoryException {
267 Map<String, Object> map = new LinkedHashMap<String, Object>();
268 PropertyIterator it = new FilteringPropertyIterator(node.getProperties(), new AbstractPredicate<Property>() {
269 @Override
270 public boolean evaluateTyped(Property t) {
271 try {
272 return !(t.getName().startsWith(NodeTypes.JCR_PREFIX) || t.getName().startsWith(NodeTypes.MGNL_PREFIX));
273 } catch (RepositoryException e) {
274 return false;
275 }
276 }
277 }, (ContentDecorator) null);
278 while (it.hasNext()) {
279 Property p = it.nextProperty();
280 Object val = PropertyUtil.getValueObject(p.getValue());
281 if (val != null) {
282 map.put(p.getName(), val);
283 }
284 }
285 if (recursive) {
286 final NodeIterator children = transformer.getChildren(node);
287
288 while (children.hasNext()) {
289 Node childNode = (Node) children.next();
290
291
292
293 Object childBean = null;
294
295 try {
296 childBean = toBean(childNode, true, transformer, state, componentProvider);
297 } catch (Exception e) {
298 state.trackProblem(error("Failed to resolve child bean due to: %s", e.getMessage()).withException(e));
299 }
300
301
302 if (childBean != null) {
303 String name = childNode.getName();
304 try {
305 if (childNode.getIndex() > 1) {
306 name += childNode.getIndex();
307 }
308 } catch (RepositoryException e) {
309 log.error("can't read index of the node [{}]", childNode, e);
310 }
311 map.put(name, childBean);
312 }
313 }
314 }
315
316 return map;
317 }
318
319
320
321
322
323
324 protected void setProperties(Map<String, Object> values, final Node2BeanTransformer transformer, TransformationState state) throws Node2BeanException, RepositoryException {
325 Object bean = state.getCurrentBean();
326 log.debug("will populate bean {} with the values {}", bean.getClass().getName(), values);
327
328 if (bean instanceof Map) {
329 ((Map<String, Object>) bean).putAll(values);
330 }
331
332 if (bean instanceof Collection) {
333 ((Collection<Object>) bean).addAll(values.values());
334 } else {
335 TypeDescriptor beanTypeDescriptor = typeMapping.getTypeDescriptor(bean.getClass());
336 final Collection<PropertyTypeDescriptor> dscrs = beanTypeDescriptor.getPropertyDescriptors(typeMapping).values();
337
338 if (!(bean instanceof Map) && !state.getCurrentType().isArray()) {
339 final Set<String> classPropertyNames = dscrs.stream().map(PropertyTypeDescriptor::getName).collect(toSet());
340 values.keySet()
341 .stream()
342 .filter(p -> !classPropertyNames.contains(p))
343 .forEach(p -> state.trackProblem(warning("Property [%s] not found in class [%s], property is not assigned", p, state.getCurrentType().getType().getName())));
344 }
345
346 for (PropertyTypeDescriptor descriptor : dscrs) {
347 transformer.setProperty(typeMapping, state, descriptor, values);
348 }
349 }
350 }
351
352 protected Node2BeanTransformer resolveTransformer(TypeDescriptor type, Node2BeanTransformer transformer) {
353 Node2BeanTransformer customTransformer = type.getTransformer();
354 if (customTransformer != null) {
355 transformer = customTransformer;
356 }
357 return transformer;
358 }
359
360 public boolean isForceCreation() {
361 return this.forceCreation;
362 }
363
364 public void setForceCreation(boolean forceCreation) {
365 this.forceCreation = forceCreation;
366 }
367 }