1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.portals.graffito.jcr.persistence.objectconverter.impl;
17
18 import java.util.Iterator;
19 import java.util.Map;
20
21 import javax.jcr.Node;
22 import javax.jcr.PathNotFoundException;
23 import javax.jcr.RepositoryException;
24 import javax.jcr.Session;
25 import javax.jcr.Value;
26 import javax.jcr.ValueFactory;
27 import javax.jcr.ValueFormatException;
28 import javax.jcr.lock.LockException;
29 import javax.jcr.nodetype.ConstraintViolationException;
30 import javax.jcr.nodetype.NoSuchNodeTypeException;
31 import javax.jcr.nodetype.NodeType;
32 import javax.jcr.nodetype.NodeTypeManager;
33 import javax.jcr.nodetype.PropertyDefinition;
34 import javax.jcr.version.VersionException;
35
36 import org.apache.commons.logging.Log;
37 import org.apache.commons.logging.LogFactory;
38 import org.apache.portals.graffito.jcr.exception.JcrMappingException;
39 import org.apache.portals.graffito.jcr.exception.PersistenceException;
40 import org.apache.portals.graffito.jcr.mapper.Mapper;
41 import org.apache.portals.graffito.jcr.mapper.model.BeanDescriptor;
42 import org.apache.portals.graffito.jcr.mapper.model.ClassDescriptor;
43 import org.apache.portals.graffito.jcr.mapper.model.CollectionDescriptor;
44 import org.apache.portals.graffito.jcr.mapper.model.FieldDescriptor;
45 import org.apache.portals.graffito.jcr.persistence.PersistenceConstant;
46 import org.apache.portals.graffito.jcr.persistence.atomictypeconverter.AtomicTypeConverter;
47 import org.apache.portals.graffito.jcr.persistence.atomictypeconverter.AtomicTypeConverterProvider;
48 import org.apache.portals.graffito.jcr.persistence.atomictypeconverter.impl.NullTypeConverterImpl;
49 import org.apache.portals.graffito.jcr.persistence.collectionconverter.CollectionConverter;
50 import org.apache.portals.graffito.jcr.persistence.collectionconverter.ManageableCollection;
51 import org.apache.portals.graffito.jcr.persistence.collectionconverter.ManageableCollectionUtil;
52 import org.apache.portals.graffito.jcr.persistence.collectionconverter.impl.DefaultCollectionConverterImpl;
53 import org.apache.portals.graffito.jcr.persistence.objectconverter.BeanConverter;
54 import org.apache.portals.graffito.jcr.persistence.objectconverter.ObjectConverter;
55 import org.apache.portals.graffito.jcr.reflection.ReflectionUtils;
56 import org.apache.portals.graffito.jcr.repository.RepositoryUtil;
57
58 /***
59 * Default implementation for {@link ObjectConverterImpl}
60 *
61 * @author <a href="mailto:christophe.lombart@gmail.com">Lombart Christophe </a>
62 * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
63 */
64 public class ObjectConverterImpl implements ObjectConverter {
65
66 private final static Log log = LogFactory.getLog(ObjectConverterImpl.class);
67
68 private static final AtomicTypeConverter NULL_CONVERTER = new NullTypeConverterImpl();
69
70 private Mapper mapper;
71
72 private AtomicTypeConverterProvider atomicTypeConverterProvider;
73
74 private ProxyManager proxyManager;
75
76 /***
77 * No-arg constructor.
78 */
79 public ObjectConverterImpl() {
80 }
81
82 /***
83 * Constructor
84 *
85 * @param mapper
86 * The mapper to used
87 * @param converterProvider
88 * The atomic type converter provider
89 *
90 */
91 public ObjectConverterImpl(Mapper mapper, AtomicTypeConverterProvider converterProvider) {
92 this.mapper = mapper;
93 this.atomicTypeConverterProvider = converterProvider;
94 this.proxyManager = new ProxyManager();
95 }
96
97 /***
98 * Set the <code>Mapper</code> used to solve mappings.
99 *
100 * @param mapper
101 * a <code>Mapper</code>
102 */
103 public void setMapper(Mapper mapper) {
104 this.mapper = mapper;
105 }
106
107 /***
108 * Sets the converter provider.
109 *
110 * @param converterProvider
111 * an <code>AtomicTypeConverterProvider</code>
112 */
113 public void setAtomicTypeConverterProvider(AtomicTypeConverterProvider converterProvider) {
114 this.atomicTypeConverterProvider = converterProvider;
115 }
116
117 /***
118 * @see org.apache.portals.graffito.jcr.persistence.objectconverter.ObjectConverter#insert(javax.jcr.Session,
119 * java.lang.Object)
120 */
121 public void insert(Session session, Object object) {
122 String path = this.getPath(session, object);
123 try {
124 String parentPath = RepositoryUtil.getParentPath(path);
125 String nodeName = RepositoryUtil.getNodeName(path);
126 Node parentNode = (Node) session.getItem(parentPath);
127 this.insert(session, parentNode, nodeName, object);
128
129 } catch (PathNotFoundException pnfe) {
130 throw new PersistenceException("Impossible to insert the object at '" + path + "'", pnfe);
131 } catch (RepositoryException re) {
132 throw new org.apache.portals.graffito.jcr.exception.RepositoryException("Impossible to insert the object at '" + path
133 + "'", re);
134 }
135 }
136
137 /***
138 *
139 * @see org.apache.portals.graffito.jcr.persistence.objectconverter.ObjectConverter#insert(javax.jcr.Session,
140 * javax.jcr.Node, java.lang.String, java.lang.Object)
141 */
142 public void insert(Session session, Node parentNode, String nodeName, Object object) {
143 ClassDescriptor classDescriptor = mapper.getClassDescriptorByClass(object.getClass());
144
145 String jcrNodeType = classDescriptor.getJcrNodeType();
146 if ((jcrNodeType == null) || jcrNodeType.equals("")) {
147 jcrNodeType = PersistenceConstant.NT_UNSTRUCTURED;
148 }
149
150 Node objectNode = null;
151 try {
152 objectNode = parentNode.addNode(nodeName, jcrNodeType);
153
154 } catch (NoSuchNodeTypeException nsnte) {
155 throw new JcrMappingException("Unknown node type " + jcrNodeType + " for mapped class " + object.getClass(), nsnte);
156 } catch (RepositoryException re) {
157 throw new PersistenceException("Cannot create new node of type " + jcrNodeType + " from mapped class "
158 + object.getClass(), re);
159 }
160
161 String[] mixinTypes = classDescriptor.getJcrMixinTypes();
162 String mixinTypeName = null;
163 try {
164
165
166 if (null != classDescriptor.getJcrMixinTypes()) {
167 for (int i = 0; i < mixinTypes.length; i++) {
168 mixinTypeName = mixinTypes[i].trim();
169 objectNode.addMixin(mixinTypeName);
170 }
171 }
172
173
174 if (!classDescriptor.hasDiscriminator() && classDescriptor.hasInterfaces()) {
175 Iterator interfacesIterator = classDescriptor.getImplements().iterator();
176 while (interfacesIterator.hasNext()) {
177 String interfaceName = (String) interfacesIterator.next();
178 ClassDescriptor interfaceDescriptor = mapper
179 .getClassDescriptorByClass(ReflectionUtils.forName(interfaceName));
180 objectNode.addMixin(interfaceDescriptor.getJcrNodeType().trim());
181 }
182 }
183
184
185 if (classDescriptor.hasDiscriminator()) {
186 mixinTypeName = PersistenceConstant.DISCRIMINATOR_NODE_TYPE;
187 objectNode.addMixin(mixinTypeName);
188 objectNode.setProperty(PersistenceConstant.DISCRIMINATOR_PROPERTY_NAME, ReflectionUtils.getBeanClass(object)
189 .getName());
190 }
191 } catch (NoSuchNodeTypeException nsnte) {
192 throw new JcrMappingException("Unknown mixin type " + mixinTypeName + " for mapped class " + object.getClass(), nsnte);
193 } catch (RepositoryException re) {
194 throw new PersistenceException("Cannot create new node of type " + jcrNodeType + " from mapped class "
195 + object.getClass(), re);
196 }
197
198 storeSimpleFields(session, object, classDescriptor, objectNode);
199 insertBeanFields(session, object, classDescriptor, objectNode);
200 insertCollectionFields(session, object, classDescriptor, objectNode);
201 }
202
203 /***
204 * @see org.apache.portals.graffito.jcr.persistence.objectconverter.ObjectConverter#update(javax.jcr.Session,
205 * java.lang.Object)
206 */
207 public void update(Session session, Object object) {
208 String path = this.getPath(session, object);
209 try {
210 String parentPath = RepositoryUtil.getParentPath(path);
211 String nodeName = RepositoryUtil.getNodeName(path);
212 Node parentNode = (Node) session.getItem(parentPath);
213 this.update(session, parentNode, nodeName, object);
214 } catch (PathNotFoundException pnfe) {
215 throw new PersistenceException("Impossible to update the object at '" + path + "'", pnfe);
216 } catch (RepositoryException re) {
217 throw new org.apache.portals.graffito.jcr.exception.RepositoryException("Impossible to update the object at '" + path
218 + "'", re);
219 }
220 }
221
222 /***
223 *
224 * @see org.apache.portals.graffito.jcr.persistence.objectconverter.ObjectConverter#update(javax.jcr.Session,
225 * javax.jcr.Node, java.lang.String, java.lang.Object)
226 */
227 public void update(Session session, Node parentNode, String nodeName, Object object) {
228 try {
229 ClassDescriptor classDescriptor = mapper.getClassDescriptorByClass(ReflectionUtils.getBeanClass(object));
230 Node objectNode = parentNode.getNode(nodeName);
231
232 checkNodeType(session, classDescriptor);
233
234 checkCompatiblePrimaryNodeTypes(session, objectNode, classDescriptor, false);
235
236 storeSimpleFields(session, object, classDescriptor, objectNode);
237 updateBeanFields(session, object, classDescriptor, objectNode);
238 updateCollectionFields(session, object, classDescriptor, objectNode);
239 } catch (PathNotFoundException pnfe) {
240 throw new PersistenceException("Impossible to update the object: " + nodeName + " at node : " + parentNode, pnfe);
241 } catch (RepositoryException re) {
242 throw new org.apache.portals.graffito.jcr.exception.RepositoryException("Impossible to update the object: "
243 + nodeName + " at node : " + parentNode, re);
244 }
245 }
246
247 /***
248 * @see org.apache.portals.graffito.jcr.persistence.objectconverter.ObjectConverter#getObject(javax.jcr.Session,
249 * java.lang.Class, java.lang.String)
250 */
251 public Object getObject(Session session, String path) {
252 try {
253 if (!session.itemExists(path)) {
254 return null;
255 }
256
257 ClassDescriptor classDescriptor = null;
258 Node node = (Node) session.getItem(path);
259 if (node.hasProperty(PersistenceConstant.DISCRIMINATOR_PROPERTY_NAME)) {
260 String className = node.getProperty(PersistenceConstant.DISCRIMINATOR_PROPERTY_NAME).getValue().getString();
261 classDescriptor = mapper.getClassDescriptorByClass(ReflectionUtils.forName(className));
262 } else {
263 String nodeType = node.getPrimaryNodeType().getName();
264 if (nodeType.equals(PersistenceConstant.FROZEN_NODE_TYPE)) {
265 nodeType = node.getProperty(PersistenceConstant.FROZEN_PRIMARY_TYPE_PROPERTY).getString();
266 }
267 classDescriptor = mapper.getClassDescriptorByNodeType(nodeType);
268 }
269
270 if (null == classDescriptor) {
271 throw new JcrMappingException("Impossible to find the classdescriptor for " + path
272 + ". There is no discriminator and associated JCR node type");
273 }
274
275 Object object = ReflectionUtils.newInstance(classDescriptor.getClassName());
276
277 retrieveSimpleFields(session, classDescriptor, node, object);
278 retrieveBeanFields(session, classDescriptor, node, path, object, false);
279 retrieveCollectionFields(session, classDescriptor, node, object, false);
280
281 return object;
282
283
284 } catch (PathNotFoundException pnfe) {
285
286 throw new PersistenceException("Impossible to get the object at " + path, pnfe);
287 } catch (RepositoryException re) {
288 throw new org.apache.portals.graffito.jcr.exception.RepositoryException("Impossible to get the object at " + path, re);
289 }
290 }
291
292 /***
293 * @see org.apache.portals.graffito.jcr.persistence.objectconverter.ObjectConverter#getObject(javax.jcr.Session,
294 * java.lang.Class, java.lang.String)
295 */
296 public Object getObject(Session session, Class clazz, String path) {
297 try {
298 if (!session.itemExists(path)) {
299 return null;
300 }
301
302 ClassDescriptor classDescriptor = getClassDescriptor(clazz);
303
304 checkNodeType(session, classDescriptor);
305
306 Node node = (Node) session.getItem(path);
307 if (!classDescriptor.isInterface()) {
308 checkCompatiblePrimaryNodeTypes(session, node, classDescriptor, true);
309 }
310
311 Object object = null;
312 if (classDescriptor.usesNodeTypePerHierarchyStrategy()) {
313 if (!node.hasProperty(PersistenceConstant.DISCRIMINATOR_PROPERTY_NAME)) {
314 throw new PersistenceException("Cannot fetch object of type '" + clazz.getName()
315 + "' using NODETYPE_PER_HIERARCHY. Discriminator property is not present.");
316 }
317
318 String className = node.getProperty(PersistenceConstant.DISCRIMINATOR_PROPERTY_NAME).getValue().getString();
319 classDescriptor = getClassDescriptor(ReflectionUtils.forName(className));
320 object = ReflectionUtils.newInstance(className);
321 } else {
322 if (classDescriptor.usesNodeTypePerConcreteClassStrategy()) {
323 String nodeType = node.getPrimaryNodeType().getName();
324 if (!nodeType.equals(classDescriptor.getJcrNodeType())) {
325 classDescriptor = classDescriptor.getDescendantClassDescriptor(nodeType);
326 }
327 }
328 object = ReflectionUtils.newInstance(classDescriptor.getClassName());
329
330 }
331
332 retrieveSimpleFields(session, classDescriptor, node, object);
333 retrieveBeanFields(session, classDescriptor, node, path, object, false);
334 retrieveCollectionFields(session, classDescriptor, node, object, false);
335
336 return object;
337
338
339 } catch (PathNotFoundException pnfe) {
340
341 throw new PersistenceException("Impossible to get the object at " + path, pnfe);
342 } catch (RepositoryException re) {
343 throw new org.apache.portals.graffito.jcr.exception.RepositoryException("Impossible to get the object at " + path, re);
344 }
345 }
346
347 public void retrieveAllMappedAttributes(Session session, Object object) {
348 String path = null;
349 try {
350 ClassDescriptor classDescriptor = getClassDescriptor(object.getClass());
351 String pathFieldName = classDescriptor.getPathFieldDescriptor().getFieldName();
352 path = (String) ReflectionUtils.getNestedProperty(object, pathFieldName);
353 Node node = (Node) session.getItem(path);
354 retrieveBeanFields(session, classDescriptor, node, path, object, true);
355 retrieveCollectionFields(session, classDescriptor, node, object, true);
356
357 } catch (PathNotFoundException pnfe) {
358
359 throw new PersistenceException("Impossible to get the object at " + path, pnfe);
360 } catch (RepositoryException re) {
361 throw new org.apache.portals.graffito.jcr.exception.RepositoryException("Impossible to get the object at " + path, re);
362 }
363 }
364
365 public void retrieveMappedAttribute(Session session, Object object, String attributeName) {
366 String path = null;
367 ClassDescriptor classDescriptor = null;
368 try {
369 classDescriptor = getClassDescriptor(object.getClass());
370 String pathFieldName = classDescriptor.getPathFieldDescriptor().getFieldName();
371 path = (String) ReflectionUtils.getNestedProperty(object, pathFieldName);
372 Node node = (Node) session.getItem(path);
373 BeanDescriptor beanDescriptor = classDescriptor.getBeanDescriptor(attributeName);
374 if (beanDescriptor != null)
375 {
376 this.retrieveBeanField(session, beanDescriptor, node, path, object, true);
377 }
378
379 else
380 {
381 CollectionDescriptor collectionDescriptor = classDescriptor.getCollectionDescriptor(attributeName);
382 if (collectionDescriptor != null)
383 {
384 this.retrieveCollectionField(session, collectionDescriptor, node, object, true);
385 }
386 else
387 {
388 throw new PersistenceException("Impossible to retrieve the mapped attribute. The attribute '" +
389 attributeName + "' is not a bean or a collection for the class : " + classDescriptor.getClassName());
390 }
391 }
392
393 } catch (PathNotFoundException pnfe) {
394
395 throw new PersistenceException("Impossible to get the object at " + path, pnfe);
396 } catch (RepositoryException re) {
397 throw new org.apache.portals.graffito.jcr.exception.RepositoryException("Impossible to get the object at " + path, re);
398 }
399 }
400
401 /***
402 * Validates the node type used by the class descriptor.
403 *
404 * @param session
405 * the current session
406 * @param classDescriptor
407 * descriptor
408 * @throws JcrMappingException
409 * thrown if the node type is unknown
410 * @throws org.apache.portals.graffito.jcr.exception.RepositoryException
411 * thrown if an error occured in the underlying repository
412 */
413 private void checkNodeType(Session session, ClassDescriptor classDescriptor) {
414 String jcrTypeName = null;
415 try {
416
417
418 if (classDescriptor.isInterface()) {
419 String[] mixinTypes = classDescriptor.getJcrMixinTypes();
420 for (int i = 0; i < mixinTypes.length; i++) {
421 jcrTypeName = mixinTypes[i];
422 session.getWorkspace().getNodeTypeManager().getNodeType(jcrTypeName);
423 }
424 } else {
425 jcrTypeName = classDescriptor.getJcrNodeType();
426 if (jcrTypeName != null && !jcrTypeName.equals("")) {
427 session.getWorkspace().getNodeTypeManager().getNodeType(jcrTypeName);
428 }
429 }
430 } catch (NoSuchNodeTypeException nsnte) {
431 throw new JcrMappingException("Mapping for class '" + classDescriptor.getClassName()
432 + "' use unknown primary or mixin node type '" + jcrTypeName + "'");
433 } catch (RepositoryException re) {
434 throw new org.apache.portals.graffito.jcr.exception.RepositoryException(re);
435 }
436 }
437
438 /***
439 * Checks if the node type in the class descriptor is compatible with the
440 * specified node node type.
441 *
442 * @param session
443 * the current session
444 * @param node
445 * node against whose node type the compatibility is checked
446 * @param classDescriptor
447 * class descriptor
448 * @param checkVersionNode
449 * <tt>true</tt> if the check should continue in case the
450 * <tt>node</tt> is a version node, <tt>false</tt> if no
451 * check against version node should be performed
452 *
453 * @throws PersistenceException
454 * thrown if node types are incompatible
455 * @throws org.apache.portals.graffito.jcr.exception.RepositoryException
456 * thrown if an error occured in the underlying repository
457 */
458 private void checkCompatiblePrimaryNodeTypes(Session session, Node node, ClassDescriptor classDescriptor,
459 boolean checkVersionNode) {
460 try {
461 NodeType nodeType = node.getPrimaryNodeType();
462
463 boolean compatible = checkCompatibleNodeTypes(nodeType, classDescriptor);
464
465 if (!compatible && checkVersionNode && PersistenceConstant.FROZEN_NODE_TYPE.equals(nodeType.getName())) {
466 NodeTypeManager ntMgr = session.getWorkspace().getNodeTypeManager();
467 nodeType = ntMgr.getNodeType(node.getProperty(PersistenceConstant.FROZEN_PRIMARY_TYPE_PROPERTY).getString());
468
469 compatible = checkCompatibleNodeTypes(nodeType, classDescriptor);
470 }
471
472 if (!compatible) {
473 throw new PersistenceException("Cannot map object of type '" + classDescriptor.getClassName() + "'. Node type '"
474 + node.getPrimaryNodeType().getName() + "' does not match descriptor node type '"
475 + classDescriptor.getJcrNodeType() + "'");
476 }
477 } catch (RepositoryException re) {
478 throw new org.apache.portals.graffito.jcr.exception.RepositoryException(re);
479 }
480 }
481
482 /***
483 * Node types compatibility check.
484 *
485 * @param nodeType
486 * target node type
487 * @param descriptor
488 * descriptor containing source node type
489 * @return <tt>true</tt> if nodes are considered compatible,
490 * <tt>false</tt> otherwise
491 */
492 private boolean checkCompatibleNodeTypes(NodeType nodeType, ClassDescriptor descriptor) {
493
494
495 if (descriptor.getJcrNodeType() == null || descriptor.getJcrNodeType().equals("")) {
496 return true;
497 }
498
499 if (nodeType.getName().equals(descriptor.getJcrNodeType())) {
500 return true;
501 }
502
503 NodeType[] superTypes = nodeType.getSupertypes();
504 for (int i = 0; i < superTypes.length; i++) {
505 if (superTypes[i].getName().equals(descriptor.getJcrNodeType())) {
506 return true;
507 }
508 }
509
510 return false;
511 }
512
513 /***
514 * @see org.apache.portals.graffito.jcr.persistence.objectconverter.ObjectConverter#getPath(javax.jcr.Session,
515 * java.lang.Object)
516 * @throws JcrMappingException
517 */
518 public String getPath(Session session, Object object) {
519 ClassDescriptor classDescriptor = mapper.getClassDescriptorByClass(object.getClass());
520
521 final FieldDescriptor pathFieldDescriptor = classDescriptor.getPathFieldDescriptor();
522 if (pathFieldDescriptor == null) {
523 throw new JcrMappingException(
524 "Class of type: "
525 + object.getClass().getName()
526 + " has no path mapping. Maybe attribute path=\"true\" for a field element of this class in jcrmapping.xml is missing?");
527 }
528 String pathField = pathFieldDescriptor.getFieldName();
529
530 return (String) ReflectionUtils.getNestedProperty(object, pathField);
531 }
532
533 /***
534 * Retrieve simple fields (atomic fields)
535 *
536 * @throws JcrMappingException
537 * @throws org.apache.portals.graffito.jcr.exception.RepositoryException
538 */
539 private Object retrieveSimpleFields(Session session, ClassDescriptor classDescriptor, Node node, Object object) {
540 Object initializedBean = object;
541 try {
542 Iterator fieldDescriptorIterator = classDescriptor.getFieldDescriptors().iterator();
543
544 while (fieldDescriptorIterator.hasNext()) {
545 FieldDescriptor fieldDescriptor = (FieldDescriptor) fieldDescriptorIterator.next();
546
547 String fieldName = fieldDescriptor.getFieldName();
548 String propertyName = fieldDescriptor.getJcrName();
549
550 if (fieldDescriptor.isPath()) {
551
552
553 if (null == initializedBean) {
554 initializedBean = ReflectionUtils.newInstance(classDescriptor.getClassName());
555 }
556
557 ReflectionUtils.setNestedProperty(initializedBean, fieldName, node.getPath());
558
559 } else if (classDescriptor.usesNodeTypePerHierarchyStrategy() && classDescriptor.hasDiscriminator()) {
560
561 if (node.hasProperty(PersistenceConstant.DISCRIMINATOR_PROPERTY_NAME)) {
562 if (null == initializedBean) {
563 initializedBean = ReflectionUtils.newInstance(classDescriptor.getClassName());
564 }
565 String value = node.getProperty(propertyName).getValue().getString();
566 ReflectionUtils.setNestedProperty(initializedBean, fieldName, value);
567 } else {
568 throw new PersistenceException("Class '" + classDescriptor.getClassName()
569 + "' have not a discriminator property.");
570 }
571 } else {
572 if (node.hasProperty(propertyName)) {
573 Value propValue = node.getProperty(propertyName).getValue();
574
575
576 if (null != propValue && null == initializedBean) {
577 initializedBean = ReflectionUtils.newInstance(classDescriptor.getClassName());
578 }
579
580 AtomicTypeConverter converter = getAtomicTypeConverter(fieldDescriptor, initializedBean, fieldName);
581
582 Object fieldValue = converter.getObject(propValue);
583 ReflectionUtils.setNestedProperty(initializedBean, fieldName, fieldValue);
584 } else {
585 log.warn("Class '" + classDescriptor.getClassName() + "' has an unmapped property : " + propertyName);
586 }
587 }
588 }
589 } catch (ValueFormatException vfe) {
590 throw new PersistenceException("Cannot retrieve properties of object " + object + " from node " + node, vfe);
591 } catch (RepositoryException re) {
592 throw new org.apache.portals.graffito.jcr.exception.RepositoryException("Cannot retrieve properties of object "
593 + object + " from node " + node, re);
594 }
595
596 return initializedBean;
597 }
598
599 /***
600 * Retrieve bean fields
601 */
602 private void retrieveBeanFields(Session session, ClassDescriptor classDescriptor, Node node, String path, Object object,
603 boolean forceToRetrieve) {
604 Iterator beanDescriptorIterator = classDescriptor.getBeanDescriptors().iterator();
605 while (beanDescriptorIterator.hasNext()) {
606 BeanDescriptor beanDescriptor = (BeanDescriptor) beanDescriptorIterator.next();
607 this.retrieveBeanField(session, beanDescriptor, node, path, object, forceToRetrieve);
608 }
609 }
610
611
612 private void retrieveBeanField(Session session,BeanDescriptor beanDescriptor, Node node, String path, Object object, boolean forceToRetrieve )
613 {
614 if (!beanDescriptor.isAutoRetrieve() && !forceToRetrieve) {
615 return;
616 }
617
618 String beanName = beanDescriptor.getFieldName();
619 Class beanClass = ReflectionUtils.getPropertyType(object, beanName);
620 Object bean = null;
621 if (beanDescriptor.isProxy()) {
622 bean = proxyManager.createBeanProxy(session, this, beanClass, path + "/" + beanDescriptor.getJcrName());
623
624 } else {
625 if (beanDescriptor.isInline()) {
626 bean = this.retrieveSimpleFields(session, mapper.getClassDescriptorByClass(beanClass), node, bean);
627 } else if (null != beanDescriptor.getConverter() && !"".equals(beanDescriptor.getConverter())) {
628 String converterClassName = beanDescriptor.getConverter();
629 Object[] param = {this};
630 BeanConverter beanConverter = (BeanConverter) ReflectionUtils.invokeConstructor(converterClassName, param);
631 bean = beanConverter.getObject(session, node, beanDescriptor, beanClass);
632 } else {
633 bean = this.getObject(session, path + "/" + beanDescriptor.getJcrName());
634 }
635 }
636 ReflectionUtils.setNestedProperty(object, beanName, bean);
637 }
638
639
640
641 /***
642 * Retrieve Collection fields
643 */
644 private void retrieveCollectionFields(Session session, ClassDescriptor classDescriptor, Node parentNode, Object object,
645 boolean forceToRetrieve) {
646 Iterator collectionDescriptorIterator = classDescriptor.getCollectionDescriptors().iterator();
647 while (collectionDescriptorIterator.hasNext()) {
648 CollectionDescriptor collectionDescriptor = (CollectionDescriptor) collectionDescriptorIterator.next();
649 this.retrieveCollectionField(session, collectionDescriptor, parentNode, object, forceToRetrieve);
650 }
651 }
652
653 private void retrieveCollectionField(Session session, CollectionDescriptor collectionDescriptor, Node parentNode, Object object, boolean forceToRetrieve)
654 {
655 if (!collectionDescriptor.isAutoRetrieve() && !forceToRetrieve) {
656 return;
657 }
658
659 CollectionConverter collectionConverter = this.getCollectionConverter(session, collectionDescriptor);
660 Class collectionFieldClass = ReflectionUtils.getPropertyType(object, collectionDescriptor.getFieldName());
661 ManageableCollection collection = null;
662 if (collectionDescriptor.isProxy()) {
663 collection = (ManageableCollection) proxyManager.createCollectionProxy(session, collectionConverter, parentNode,
664 collectionDescriptor, collectionFieldClass);
665
666 } else {
667 collection = collectionConverter.getCollection(session, parentNode, collectionDescriptor, collectionFieldClass);
668 }
669
670 ReflectionUtils.setNestedProperty(object, collectionDescriptor.getFieldName(), collection);
671 }
672
673 /***
674 * Insert Bean fields
675 */
676 private void insertBeanFields(Session session, Object object, ClassDescriptor classDescriptor, Node objectNode) {
677 Iterator beanDescriptorIterator = classDescriptor.getBeanDescriptors().iterator();
678 while (beanDescriptorIterator.hasNext()) {
679 BeanDescriptor beanDescriptor = (BeanDescriptor) beanDescriptorIterator.next();
680
681 if (!beanDescriptor.isAutoInsert()) {
682 continue;
683 }
684
685 String jcrName = beanDescriptor.getJcrName();
686 Object bean = ReflectionUtils.getNestedProperty(object, beanDescriptor.getFieldName());
687 if (bean != null) {
688 if (beanDescriptor.isInline()) {
689 this.storeSimpleFields(session, bean, mapper.getClassDescriptorByClass(bean.getClass()), objectNode);
690 } else if (null != beanDescriptor.getConverter() && !"".equals(beanDescriptor.getConverter())) {
691 String converterClassName = beanDescriptor.getConverter();
692 Object[] param = {this};
693 BeanConverter beanConverter = (BeanConverter) ReflectionUtils.invokeConstructor(converterClassName, param);
694 beanConverter.insert(session, objectNode, beanDescriptor, object);
695 } else {
696 this.insert(session, objectNode, jcrName, bean);
697 }
698 }
699 }
700 }
701
702 /***
703 * Update Bean fields
704 */
705 private void updateBeanFields(Session session, Object object, ClassDescriptor classDescriptor, Node objectNode) {
706 String jcrName = null;
707 try {
708 Iterator beanDescriptorIterator = classDescriptor.getBeanDescriptors().iterator();
709 while (beanDescriptorIterator.hasNext()) {
710 BeanDescriptor beanDescriptor = (BeanDescriptor) beanDescriptorIterator.next();
711 if (!beanDescriptor.isAutoUpdate()) {
712 continue;
713 }
714
715 jcrName = beanDescriptor.getJcrName();
716 Object bean = ReflectionUtils.getNestedProperty(object, beanDescriptor.getFieldName());
717
718
719 if ((bean == null)) {
720 if (beanDescriptor.isInline()) {
721 Class beanClass = ReflectionUtils.getPropertyType(object, beanDescriptor.getFieldName());
722 this.storeSimpleFields(session, bean, mapper.getClassDescriptorByClass(beanClass), objectNode);
723 } else if (null != beanDescriptor.getConverter() && !"".equals(beanDescriptor.getConverter())) {
724 String converterClassName = beanDescriptor.getConverter();
725 Object[] param = {this};
726 BeanConverter beanConverter = (BeanConverter) ReflectionUtils
727 .invokeConstructor(converterClassName, param);
728 beanConverter.remove(session, objectNode, beanDescriptor);
729 } else {
730 if (objectNode.hasNode(jcrName)) {
731 objectNode.getNode(jcrName).remove();
732 }
733 }
734 } else {
735 if (beanDescriptor.isInline()) {
736 this.storeSimpleFields(session, bean, mapper.getClassDescriptorByClass(bean.getClass()), objectNode);
737 } else if (null != beanDescriptor.getConverter() && !"".equals(beanDescriptor.getConverter())) {
738 String converterClassName = beanDescriptor.getConverter();
739 Object[] param = {this};
740 BeanConverter beanConverter = (BeanConverter) ReflectionUtils
741 .invokeConstructor(converterClassName, param);
742 beanConverter.update(session, objectNode, beanDescriptor, bean);
743 } else {
744 this.update(session, objectNode, jcrName, bean);
745 }
746 }
747 }
748 } catch (VersionException ve) {
749 throw new PersistenceException("Cannot remove bean at path " + jcrName, ve);
750 } catch (LockException le) {
751 throw new PersistenceException("Cannot remove bean at path " + jcrName + ". Item is locked.", le);
752 } catch (ConstraintViolationException cve) {
753 throw new PersistenceException("Cannot remove bean at path " + jcrName + ". Contraint violation.", cve);
754 } catch (RepositoryException re) {
755 throw new org.apache.portals.graffito.jcr.exception.RepositoryException("Cannot remove bean at path " + jcrName, re);
756 }
757 }
758
759 private void insertCollectionFields(Session session, Object object, ClassDescriptor classDescriptor, Node objectNode) {
760 Iterator collectionDescriptorIterator = classDescriptor.getCollectionDescriptors().iterator();
761
762 while (collectionDescriptorIterator.hasNext()) {
763 CollectionDescriptor collectionDescriptor = (CollectionDescriptor) collectionDescriptorIterator.next();
764
765 if (!collectionDescriptor.isAutoInsert()) {
766 continue;
767 }
768
769 CollectionConverter collectionConverter = this.getCollectionConverter(session, collectionDescriptor);
770 Object collection = ReflectionUtils.getNestedProperty(object, collectionDescriptor.getFieldName());
771 ManageableCollection manageableCollection = ManageableCollectionUtil.getManageableCollection(collection);
772
773 collectionConverter.insertCollection(session, objectNode, collectionDescriptor, manageableCollection);
774 }
775 }
776
777 private void updateCollectionFields(Session session, Object object, ClassDescriptor classDescriptor, Node objectNode) {
778 Iterator collectionDescriptorIterator = classDescriptor.getCollectionDescriptors().iterator();
779
780 while (collectionDescriptorIterator.hasNext()) {
781 CollectionDescriptor collectionDescriptor = (CollectionDescriptor) collectionDescriptorIterator.next();
782 if (!collectionDescriptor.isAutoUpdate()) {
783 continue;
784 }
785
786 CollectionConverter collectionConverter = this.getCollectionConverter(session, collectionDescriptor);
787 Object collection = ReflectionUtils.getNestedProperty(object, collectionDescriptor.getFieldName());
788 ManageableCollection manageableCollection = ManageableCollectionUtil.getManageableCollection(collection);
789
790 collectionConverter.updateCollection(session, objectNode, collectionDescriptor, manageableCollection);
791 }
792 }
793
794 private void storeSimpleFields(Session session, Object object, ClassDescriptor classDescriptor, Node objectNode) {
795 try {
796 ValueFactory valueFactory = session.getValueFactory();
797
798 Iterator fieldDescriptorIterator = classDescriptor.getFieldDescriptors().iterator();
799 while (fieldDescriptorIterator.hasNext()) {
800 FieldDescriptor fieldDescriptor = (FieldDescriptor) fieldDescriptorIterator.next();
801
802 String fieldName = fieldDescriptor.getFieldName();
803 String jcrName = fieldDescriptor.getJcrName();
804
805
806 if (fieldDescriptor.isPath()) {
807 continue;
808 }
809
810 boolean protectedProperty = fieldDescriptor.isJcrProtected();
811
812 if (objectNode.hasProperty(jcrName)) {
813 protectedProperty = objectNode.getProperty(jcrName).getDefinition().isProtected();
814 }
815
816 if (!protectedProperty) {
817 Object fieldValue = ReflectionUtils.getNestedProperty(object, fieldName);
818 AtomicTypeConverter converter = getAtomicTypeConverter(fieldDescriptor, object, fieldName);
819 Value value = converter.getValue(valueFactory, fieldValue);
820
821
822 boolean autoCreated = fieldDescriptor.isJcrAutoCreated();
823
824 if (objectNode.hasProperty(jcrName)) {
825 autoCreated = objectNode.getProperty(jcrName).getDefinition().isAutoCreated();
826 }
827
828 if (!autoCreated) {
829
830 checkMandatoryProperty(objectNode, fieldDescriptor, value);
831 }
832
833 objectNode.setProperty(jcrName, value);
834 }
835 }
836 } catch (ValueFormatException vfe) {
837 throw new PersistenceException("Cannot persist properties of object " + object + ". Value format exception.", vfe);
838 } catch (VersionException ve) {
839 throw new PersistenceException("Cannot persist properties of object " + object + ". Versioning exception.", ve);
840 } catch (LockException le) {
841 throw new PersistenceException("Cannot persist properties of object " + object + " on locked node.", le);
842 } catch (ConstraintViolationException cve) {
843 throw new PersistenceException("Cannot persist properties of object " + object + ". Constraint violation.", cve);
844 } catch (RepositoryException re) {
845 throw new org.apache.portals.graffito.jcr.exception.RepositoryException("Cannot persist properties of object "
846 + object, re);
847 }
848 }
849
850 private CollectionConverter getCollectionConverter(Session session, CollectionDescriptor collectionDescriptor) {
851 String className = collectionDescriptor.getCollectionConverter();
852 Map atomicTypeConverters = this.atomicTypeConverterProvider.getAtomicTypeConverters();
853 if (className == null) {
854 return new DefaultCollectionConverterImpl(atomicTypeConverters, this, this.mapper);
855 } else {
856 return (CollectionConverter) ReflectionUtils.invokeConstructor(className, new Object[]{atomicTypeConverters, this,
857 this.mapper});
858 }
859
860 }
861
862 private void checkMandatoryProperty(Node objectNode, FieldDescriptor fieldDescriptor, Value value) throws RepositoryException {
863 PropertyDefinition[] propertyDefinitions = objectNode.getPrimaryNodeType().getDeclaredPropertyDefinitions();
864 for (int i = 0; i < propertyDefinitions.length; i++) {
865 PropertyDefinition definition = propertyDefinitions[i];
866 if (definition.getName().equals(fieldDescriptor.getJcrName()) && definition.isMandatory()
867 && (definition.isAutoCreated() == false) && (value == null)) {
868 throw new PersistenceException("Class of type:" + fieldDescriptor.getClassDescriptor().getClassName()
869 + " has property: " + fieldDescriptor.getFieldName() + " declared as JCR property: "
870 + fieldDescriptor.getJcrName() + " This property is mandatory but property in bean has value null");
871 }
872 }
873 }
874
875 private AtomicTypeConverter getAtomicTypeConverter(FieldDescriptor fd, Object object, String fieldName) {
876 Class fieldTypeClass = null;
877 if (null != fd.getFieldTypeClass()) {
878 fieldTypeClass = fd.getFieldTypeClass();
879 } else if (null != object) {
880 fieldTypeClass = ReflectionUtils.getPropertyType(object, fieldName);
881 }
882
883 if (null != fieldTypeClass) {
884 return this.atomicTypeConverterProvider.getAtomicTypeConverter(fieldTypeClass);
885 } else {
886 return NULL_CONVERTER;
887 }
888 }
889
890 private ClassDescriptor getClassDescriptor(Class beanClass) {
891 ClassDescriptor classDescriptor = mapper.getClassDescriptorByClass(beanClass);
892 if (null == classDescriptor) {
893 throw new JcrMappingException("Class of type: " + beanClass.getName()
894 + " is not JCR persistable. Maybe element 'class-descriptor' for this type in mapping file is missing");
895 }
896
897 return classDescriptor;
898 }
899
900 }