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  
17  package org.apache.portals.graffito.jcr.persistence.collectionconverter.impl;
18  
19  
20  import java.util.ArrayList;
21  import java.util.Collection;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  
27  import javax.jcr.Node;
28  import javax.jcr.NodeIterator;
29  import javax.jcr.PathNotFoundException;
30  import javax.jcr.RepositoryException;
31  import javax.jcr.Session;
32  import javax.jcr.ValueFormatException;
33  import javax.jcr.lock.LockException;
34  import javax.jcr.nodetype.ConstraintViolationException;
35  import javax.jcr.version.VersionException;
36  
37  import org.apache.commons.logging.Log;
38  import org.apache.commons.logging.LogFactory;
39  import org.apache.portals.graffito.jcr.mapper.Mapper;
40  import org.apache.portals.graffito.jcr.mapper.model.ClassDescriptor;
41  import org.apache.portals.graffito.jcr.mapper.model.CollectionDescriptor;
42  import org.apache.portals.graffito.jcr.persistence.collectionconverter.ManageableCollection;
43  import org.apache.portals.graffito.jcr.persistence.collectionconverter.ManageableCollectionUtil;
44  import org.apache.portals.graffito.jcr.persistence.objectconverter.ObjectConverter;
45  import org.apache.portals.graffito.jcr.reflection.ReflectionUtils;
46  
47  /***
48   * Collection Mapping/convertion based on node type.
49   *
50   * This collection mapping strategy maps a collection into several nodes based on specific node type.
51   *
52   *
53   * If the collection element class contains an id (see the FieldDescriptor definition), this id value is used to build the collection element node.
54   * Otherwise, the element node name is a simple constant (collection-element)
55   *
56   * Example - without an id attribute:
57   *   /test (Main object containing the collection field )
58   *          /collection-element (node used to store the first collection element)
59   *                /item-prop
60   *                ....
61   *          /collection-element (node used to store the second collection element)
62   *          ...
63   *
64   * Example - with an id attribute:
65   *   /test (Main object containing the collection field )
66   *          /aValue (id value assigned to the first element)
67   *                /item-prop
68   *                ....
69   *          /anotherValue (id value assigned to the first element)
70   *          ...
71   *
72   * @author <a href="mailto:christophe.lombart@gmail.com">Christophe Lombart</a>
73   *
74   */
75  public class NTCollectionConverterImpl extends AbstractCollectionConverterImpl {
76  
77      private final static Log log = LogFactory.getLog(NTCollectionConverterImpl.class);
78  
79      private static final String COLLECTION_ELEMENT_NAME = "collection-element";
80  
81      /***
82       * Constructor
83       *
84       * @param atomicTypeConverters
85       * @param objectConverter
86       * @param mapper
87       */
88      public NTCollectionConverterImpl(Map atomicTypeConverters,
89                                       ObjectConverter objectConverter,
90                                       Mapper mapper) {
91          super(atomicTypeConverters, objectConverter, mapper);
92      }
93  
94      /***
95       * @see AbstractCollectionConverterImpl#doInsertCollection(Session, Node, CollectionDescriptor, ManageableCollection)
96       */
97      protected void doInsertCollection(Session session,
98                                        Node parentNode,
99                                        CollectionDescriptor collectionDescriptor,
100                                       ManageableCollection collection) {
101         if (collection == null) {
102             return;
103         }
104         
105         ClassDescriptor elementClassDescriptor = mapper.getClassDescriptorByClass( ReflectionUtils.forName(collectionDescriptor.getElementClassName()));
106 
107         Iterator collectionIterator = collection.getIterator();        
108         while (collectionIterator.hasNext()) {
109             Object item = collectionIterator.next();
110             String elementJcrName = null;
111 
112             // If the element object has a unique id => the element jcr node name = the id value
113             if (elementClassDescriptor.hasIdField()) {
114                 String idFieldName = elementClassDescriptor.getIdFieldDescriptor().getFieldName();
115                 elementJcrName = ReflectionUtils.getNestedProperty(item, idFieldName).toString();
116             }
117             else {                
118                 elementJcrName = COLLECTION_ELEMENT_NAME;
119             }
120 
121             objectConverter.insert(session, parentNode, elementJcrName, item);
122         }
123     }
124 
125     /***
126      *
127      * @see org.apache.portals.graffito.jcr.persistence.collectionconverter.CollectionConverter#updateCollection(javax.jcr.Session, javax.jcr.Node, org.apache.portals.graffito.jcr.mapper.model.CollectionDescriptor, org.apache.portals.graffito.jcr.persistence.collectionconverter.ManageableCollection)
128      */
129     protected void doUpdateCollection(Session session,
130                                       Node parentNode,
131                                       CollectionDescriptor collectionDescriptor,
132                                       ManageableCollection collection) throws RepositoryException {
133         Mapper mapper = collectionDescriptor.getClassDescriptor().getMappingDescriptor().getMapper();
134         ClassDescriptor elementClassDescriptor = mapper.getClassDescriptorByClass(
135                 ReflectionUtils.forName(collectionDescriptor.getElementClassName()));
136 
137         if (collection == null || !elementClassDescriptor.hasIdField()) {
138             this.deleteCollectionItems(session,
139                                        parentNode,
140                                        elementClassDescriptor.getJcrNodeType());
141         }
142 
143         if (collection == null) {
144             return;
145         }
146 
147         Iterator collectionIterator = collection.getIterator();
148         Map updatedItems = new HashMap();
149         while (collectionIterator.hasNext()) {
150             Object item = collectionIterator.next();
151         
152             String elementJcrName = null;
153 
154             if (elementClassDescriptor.hasIdField()) {
155                 String idFieldName = elementClassDescriptor.getIdFieldDescriptor().getFieldName();
156                 elementJcrName = ReflectionUtils.getNestedProperty(item, idFieldName).toString();
157 
158                 // Update existing JCR Nodes
159                 if (parentNode.hasNode(elementJcrName)) {
160                     objectConverter.update(session, parentNode, elementJcrName, item);
161                 }
162                 else {
163                     // Add new collection elements
164                     objectConverter.insert(session, parentNode, elementJcrName, item);
165                 }
166 
167                 updatedItems.put(elementJcrName, item);
168             }
169             else {
170                 elementJcrName = COLLECTION_ELEMENT_NAME;
171                 objectConverter.insert(session, parentNode, elementJcrName, item);
172             }
173         }
174 
175         // Delete JCR nodes that are not present in the collection
176         if (elementClassDescriptor.hasIdField()) {
177             Iterator nodeIterator = this.getCollectionNodes(session, parentNode,
178                     elementClassDescriptor.getJcrNodeType()).iterator();
179 
180             while (nodeIterator.hasNext()) {
181                 Node child = (Node) nodeIterator.next();
182                 
183                 if (!updatedItems.containsKey(child.getName())) {
184                     child.remove();
185                 }
186             }
187         }
188     }
189 
190     /***
191      * @see org.apache.portals.graffito.jcr.persistence.collectionconverter.CollectionConverter#getCollection(javax.jcr.Session, javax.jcr.Node, org.apache.portals.graffito.jcr.mapper.model.CollectionDescriptor, java.lang.Class)
192      */
193     protected ManageableCollection doGetCollection(Session session,
194                                                    Node parentNode,
195                                                    CollectionDescriptor collectionDescriptor,
196                                                    Class collectionFieldClass) throws RepositoryException {
197 	    ClassDescriptor elementClassDescriptor = mapper.getClassDescriptorByClass( ReflectionUtils.forName(collectionDescriptor.getElementClassName())); 
198         ManageableCollection collection = ManageableCollectionUtil.getManageableCollection(collectionFieldClass);
199         Class elementClass = ReflectionUtils.forName(collectionDescriptor.getElementClassName());
200         Iterator children = this.getCollectionNodes(session, parentNode,
201                 elementClassDescriptor.getJcrNodeType()).iterator();
202 
203         while (children.hasNext()) {
204             Node itemNode = (Node) children.next();
205             log.debug("Collection node found : " + itemNode.getPath());
206             Object item = objectConverter.getObject(session,  itemNode.getPath());
207             collection.addObject(item);
208         }
209 
210         return collection;
211     }
212     
213     /***
214      * @see AbstractCollectionConverterImpl#doIsNull(Session, Node, CollectionDescriptor, Class)
215      */
216     protected boolean doIsNull(Session session,
217                                               Node parentNode,
218                                               CollectionDescriptor collectionDescriptor,
219                                               Class collectionFieldClass) throws RepositoryException {
220 
221     	    // This collection converter returns at least a empty collection (see in doGetCollection) 
222         return false;
223     }         
224 
225     private Collection getCollectionNodes(Session session, Node parentNode, String itemNodeType)
226     throws PathNotFoundException, ValueFormatException, RepositoryException {
227 
228         List collectionNodes = new ArrayList();
229 
230         // TODO : review this workaround used to support version nodes
231         // Searching on the version storage has some bugs => loop on all child noded and check the property jcr:frozenPrimaryType
232         // I have to investigate in more detail what's happen exactly
233         if (!parentNode.getPath().startsWith("/jcr:system/jcr:versionStorage")) {
234             NodeIterator nodeIterator = parentNode.getNodes();
235             while (nodeIterator.hasNext()) {
236                 Node child = nodeIterator.nextNode();
237 
238                 if (child.isNodeType(itemNodeType)) {
239                     collectionNodes.add(child);
240                 }
241             }
242         }
243         else {
244             NodeIterator nodeIterator = parentNode.getNodes();
245             while (nodeIterator.hasNext()) {
246                 Node child = nodeIterator.nextNode();
247 
248                 if (child.getProperty("jcr:frozenPrimaryType").getString().equals(itemNodeType)) {
249                     collectionNodes.add(child);
250                 }
251             }
252 
253         }
254 
255         return collectionNodes;
256     }
257 
258     private void deleteCollectionItems(Session session, Node parentNode, String itemNodeType) 
259     throws VersionException, 
260            LockException, 
261            ConstraintViolationException, 
262            PathNotFoundException, 
263            ValueFormatException, 
264            RepositoryException
265     {
266         Iterator nodeIterator = this.getCollectionNodes(session, parentNode, itemNodeType).iterator();
267         while (nodeIterator.hasNext()) {
268             Node node = (Node) nodeIterator.next();
269             node.remove();
270         }
271     }
272 }