View Javadoc

1   /*
2    * Copyright 2000-2005 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
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 			// Add mixin types
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 			// Add mixin types defined in the associated interfaces 
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 			// If required, add the discriminator node type 
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 //		} catch (ClassNotFoundException clnf) {
283 //			throw new PersistenceException("Impossible to instantiate the object at " + path, clnf);
284 		} catch (PathNotFoundException pnfe) {
285 			// HINT should never get here
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 //		} catch (ClassNotFoundException clnf) {
338 //			throw new PersistenceException("Impossible to instantiate the object at " + path, clnf);
339 		} catch (PathNotFoundException pnfe) {
340 			// HINT should never get here
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 			// Check if the attribute is a collection
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 			//Don't check the primary node type for interfaces. They are only associated to mixin node type
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 		//return true if node type is not used
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 					// HINT: lazy initialize target bean - The bean can be null
552 					// when it is inline
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 						// HINT: lazy initialize target bean - The bean can be
575 						// null when it is inline
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 				// if the bean is null, remove existing node
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 				// Of course, Path field is not stored as property
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) { // DO NOT TRY TO WRITE PROTECTED  PROPERTIES
817 					Object fieldValue = ReflectionUtils.getNestedProperty(object, fieldName);
818 					AtomicTypeConverter converter = getAtomicTypeConverter(fieldDescriptor, object, fieldName);
819 					Value value = converter.getValue(valueFactory, fieldValue);
820 
821 					// Check if the node property is "autocreated"
822 					boolean autoCreated = fieldDescriptor.isJcrAutoCreated();
823 
824 					if (objectNode.hasProperty(jcrName)) {
825 						autoCreated = objectNode.getProperty(jcrName).getDefinition().isAutoCreated();
826 					}
827 
828 					if (!autoCreated) {
829 						// Check if mandatory property are not null
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 }