Property changes on: . ___________________________________________________________________ Modified: svn:ignore - derby.log target *.iws *.ipr *.iml .* jcoverage* junit*.properties + derby.log target *.iws *.ipr *.iml .* jcoverage* junit*.properties target-eclipse Index: src/main/java/org/apache/jackrabbit/ocm/manager/collectionconverter/impl/DefaultCollectionConverterImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/ocm/manager/collectionconverter/impl/DefaultCollectionConverterImpl.java (revision 701632) +++ src/main/java/org/apache/jackrabbit/ocm/manager/collectionconverter/impl/DefaultCollectionConverterImpl.java (working copy) @@ -206,7 +206,7 @@ Node collectionNode = parentNode.getNode(jcrName); // If the collection elements have not an id, it is not possible to find the matching JCR nodes // => delete the complete collection - if (!elementClassDescriptor.hasIdField()) { + if (!elementClassDescriptor.hasIdField() && !elementClassDescriptor.hasUUIdField()) { collectionNode.remove(); collectionNode = parentNode.addNode(jcrName); } @@ -214,13 +214,32 @@ Iterator collectionIterator = objects.getIterator(); Map updatedItems = new HashMap(); + List validUuidsForTheNode = new ArrayList(); while (collectionIterator.hasNext()) { Object item = collectionIterator.next(); - String elementJcrName = null; + + if (elementClassDescriptor.hasUUIdField()){ + elementJcrName = collectionDescriptor.getJcrElementName(); + elementJcrName = (elementJcrName == null)? COLLECTION_ELEMENT_NAME : elementJcrName; + String uuidFieldName = elementClassDescriptor.getUuidFieldDescriptor().getFieldName(); + Object objUuid = ReflectionUtils.getNestedProperty(item, uuidFieldName); + String currentItemUuid = (objUuid == null) ? null : objUuid.toString(); + if (currentItemUuid != null){ + //The Node already exists so we need to update the existing node + //rather than to replace it. + Node nodeToUpdate = collectionNode.getSession().getNodeByUUID(currentItemUuid); + objectConverter.update(session, currentItemUuid, item); + validUuidsForTheNode.add(currentItemUuid); + } + else{ + objectConverter.insert(session, collectionNode, elementJcrName, item); + validUuidsForTheNode.add(ReflectionUtils.getNestedProperty(item, uuidFieldName).toString()); + } + + } + else if (elementClassDescriptor.hasIdField()) { - if (elementClassDescriptor.hasIdField()) { - String idFieldName = elementClassDescriptor.getIdFieldDescriptor().getFieldName(); elementJcrName = ReflectionUtils.getNestedProperty(item, idFieldName).toString(); @@ -245,6 +264,22 @@ } // Delete JCR nodes that are not present in the collection + if (elementClassDescriptor.hasUUIdField()) { + NodeIterator nodeIterator = collectionNode.getNodes(); + List removeNodes = new ArrayList(); + while (nodeIterator.hasNext()) { + Node currentNode = nodeIterator.nextNode(); + if (!validUuidsForTheNode.contains(currentNode.getUUID())) { + removeNodes.add(currentNode); + } + } + for (Node aNode : removeNodes){ + aNode.remove(); + } + return; + } + + // Delete JCR nodes that are not present in the collection if (elementClassDescriptor.hasIdField()) { NodeIterator nodeIterator = collectionNode.getNodes(); List removeNodes = new ArrayList(); @@ -348,4 +383,4 @@ } return false; } -} \ No newline at end of file +} Index: src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/impl/ObjectConverterImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/impl/ObjectConverterImpl.java (revision 701632) +++ src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/impl/ObjectConverterImpl.java (working copy) @@ -271,6 +271,32 @@ * @see org.apache.jackrabbit.ocm.manager.objectconverter.ObjectConverter#update(javax.jcr.Session, * javax.jcr.Node, java.lang.String, java.lang.Object) */ + public void update(Session session, String uuId, Object object) { + try { + ClassDescriptor classDescriptor = mapper.getClassDescriptorByClass(ReflectionUtils.getBeanClass(object)); + Node objectNode = session.getNodeByUUID(uuId); + + checkNodeType(session, classDescriptor); + + checkCompatiblePrimaryNodeTypes(session, objectNode, classDescriptor, false); + + simpleFieldsHelp.storeSimpleFields(session, object, classDescriptor, objectNode); + updateBeanFields(session, object, classDescriptor, objectNode); + updateCollectionFields(session, object, classDescriptor, objectNode); + simpleFieldsHelp.refreshUuidPath(session, classDescriptor, objectNode, object); + } catch (PathNotFoundException pnfe) { + throw new ObjectContentManagerException("Impossible to update the object with UUID: " + uuId , pnfe); + } catch (RepositoryException re) { + throw new org.apache.jackrabbit.ocm.exception.RepositoryException("Impossible to update the object with UUID: " + uuId, re); + } + } + + + /** + * + * @see org.apache.jackrabbit.ocm.manager.objectconverter.ObjectConverter#update(javax.jcr.Session, + * javax.jcr.Node, java.lang.String, java.lang.Object) + */ public void update(Session session, Node parentNode, String nodeName, Object object) { try { ClassDescriptor classDescriptor = mapper.getClassDescriptorByClass(ReflectionUtils.getBeanClass(object)); Index: src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/ObjectConverter.java =================================================================== --- src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/ObjectConverter.java (revision 701632) +++ src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/ObjectConverter.java (working copy) @@ -115,7 +115,16 @@ */ public void update(Session session, Node parentNode, String nodeName, Object object) throws ObjectContentManagerException; - + /** + * Update the object + * + * @param session the JCR session + * @param uuId The UUID of the node to get updated. + * @param object the object to update + * @throws ObjectContentManagerException when it is not possible to update the object + */ + public void update(Session session, String uuId, Object object); + /** * Get the object JCR path * Index: src/main/java/org/apache/jackrabbit/ocm/mapper/impl/AbstractMapperImpl.java =================================================================== --- src/main/java/org/apache/jackrabbit/ocm/mapper/impl/AbstractMapperImpl.java (revision 701632) +++ src/main/java/org/apache/jackrabbit/ocm/mapper/impl/AbstractMapperImpl.java (working copy) @@ -200,7 +200,7 @@ public ClassDescriptor getClassDescriptorByClass(Class clazz) { ClassDescriptor descriptor = mappingDescriptor.getClassDescriptorByName(clazz.getName()); if (descriptor==null) { - throw new IncorrectPersistentClassException("Class of type: " + clazz.getName() + " has no descriptor."); + //throw new IncorrectPersistentClassException("Class of type: " + clazz.getName() + " has no descriptor."); } return descriptor ; } Index: src/main/java/org/apache/jackrabbit/ocm/mapper/model/ClassDescriptor.java =================================================================== --- src/main/java/org/apache/jackrabbit/ocm/mapper/model/ClassDescriptor.java (revision 701632) +++ src/main/java/org/apache/jackrabbit/ocm/mapper/model/ClassDescriptor.java (working copy) @@ -296,6 +296,16 @@ } /** + * Check if this class has an UUID + * @return true if the class has an UUID + */ + public boolean hasUUIdField(){ + return (this.getUuidFieldDescriptor() != null + && this.getUuidFieldDescriptor().isUuid()); + + } + + /** * Get the JCR name used for one of the object attributes * @param fieldName the object attribute name (can be an atomic field, bean field or a collection field) * @return the JCR name found @@ -589,4 +599,4 @@ public String toString() { return "Class Descriptor : " + this.getClassName(); } -} \ No newline at end of file +} Index: src/test/java/org/apache/jackrabbit/ocm/DigesterTestBase.java =================================================================== --- src/test/java/org/apache/jackrabbit/ocm/DigesterTestBase.java (revision 701632) +++ src/test/java/org/apache/jackrabbit/ocm/DigesterTestBase.java (working copy) @@ -57,10 +57,11 @@ "./src/test/test-config/jcrmapping-beandescriptor.xml", "./src/test/test-config/jcrmapping-inheritance.xml", "./src/test/test-config/jcrmapping-jcrnodetypes.xml", - "./src/test/test-config/jcrmapping-uuid.xml"}; + "./src/test/test-config/jcrmapping-uuid.xml", + "./src/test/test-config/jcrmapping-complex-collections.xml"}; session = RepositoryUtil.login(repository, "superuser", "superuser"); ocm = new ObjectContentManagerImpl(session, files); } -} \ No newline at end of file +} Index: src/test/java/org/apache/jackrabbit/ocm/manager/collectionconverter/DigesterPersonListTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/ocm/manager/collectionconverter/DigesterPersonListTest.java (revision 0) +++ src/test/java/org/apache/jackrabbit/ocm/manager/collectionconverter/DigesterPersonListTest.java (revision 0) @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.ocm.manager.collectionconverter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jackrabbit.ocm.DigesterTestBase; +import org.apache.jackrabbit.ocm.RepositoryLifecycleTestSetup; +import org.apache.jackrabbit.ocm.manager.ObjectContentManager; +import org.apache.jackrabbit.ocm.testmodel.collection.ArrayListElement; +import org.apache.jackrabbit.ocm.testmodel.collection.Element; +import org.apache.jackrabbit.ocm.testmodel.collection.Main; +import org.apache.jackrabbit.ocm.testmodel.collection.Person; + +/** + * @author Boni Gopalan + */ +public class DigesterPersonListTest extends DigesterTestBase +{ + private final static Log log = LogFactory.getLog(DigesterPersonListTest.class); + + /** + *

Defines the test case name for junit.

+ * @param testName The test case name. + */ + public DigesterPersonListTest(String testName) throws Exception + { + super(testName); + } + + public static Test suite() + { + // All methods starting with "test" will be executed in the test suite. + return new RepositoryLifecycleTestSetup(new TestSuite(DigesterPersonListTest.class)); + } + + public void testPersonList() + { + try + { + ObjectContentManager ocm = getObjectContentManager(); + Person aPerson = buildPerson("PERSON1"); + aPerson.setPath("/person"); + ocm.insert(aPerson); + ocm.save(); + assertNotNull(aPerson.getId()); + String oldParentId = new String(aPerson.getId().toCharArray()); + List childIds = new ArrayList(); + for (Person p : aPerson.getChildren()){ + assertNotNull(p.getId()); + childIds.add(new String(p.getId().toCharArray())); + } + aPerson.setName("UPDATED1"); + ocm.update(aPerson); + Person fb1Person = (Person)ocm.getObject("/person"); + assertNotNull(fb1Person); + assertEquals("UPDATED1", fb1Person.getName()); + assertEquals(oldParentId, fb1Person.getId()); + + //To assert that the ids of the objects in the + //collection has not changed during update. + for (Person p : fb1Person.getChildren()){ + assertTrue(childIds.contains(p.getId())); + } + + Person newChild = new Person(); + newChild.setName("CHILD2"); + + fb1Person.getChildren().add(newChild); + ocm.update(fb1Person); + + Person fb2Person = (Person)ocm.getObject("/person"); + assertNotNull(fb2Person); + assertEquals("UPDATED1", fb2Person.getName()); + assertEquals(oldParentId, fb2Person.getId()); + + //To assert that the ids of the objects in the + //collection has not changed during update. + String child2Id = null; + for (Person p : fb2Person.getChildren()){ + if (!"CHILD2".equals(p.getName())) + assertTrue(childIds.contains(p.getId())); + else{ + assertNotNull(p.getId()); + child2Id = new String(p.getId().toCharArray()); + assertFalse(childIds.contains(p.getId())); + } + } + + //Now remove everyone but CHILD2 and do the update once again + List peopleToRemove = new ArrayList(); + for (Person p : fb2Person.getChildren()){ + if (!"CHILD2".equals(p.getName())) + peopleToRemove.add(p); + } + + for (Person p : peopleToRemove){ + fb2Person.getChildren().remove(p); + } + + ocm.update(fb2Person); + + Person fb3Person = (Person)ocm.getObject("/person"); + assertNotNull(fb3Person); + assertEquals(1, fb3Person.getChildren().size()); + assertEquals(child2Id, fb3Person.getChildren().get(0).getId()); + + } + catch (Exception e) + { + e.printStackTrace(); + fail("Exception occurs during the unit test : " + e); + } + } + + public Person buildPerson(String name){ + Person p = new Person(); + p.setName(name); + Person aChild = new Person(); + aChild.setName("CHILD1"); + List children = new ArrayList(); + children.add(aChild); + p.setChildren(children); + return p; + } + + + +} Index: src/test/java/org/apache/jackrabbit/ocm/testmodel/collection/Person.java =================================================================== --- src/test/java/org/apache/jackrabbit/ocm/testmodel/collection/Person.java (revision 0) +++ src/test/java/org/apache/jackrabbit/ocm/testmodel/collection/Person.java (revision 0) @@ -0,0 +1,65 @@ +package org.apache.jackrabbit.ocm.testmodel.collection; + +import java.util.List; +import java.util.Map; + +public class Person { + String id; + String name; + String path; + List children; + Map friends; + public String getId() { + return id; + } + public void setId(String id) { + this.id = id; + } + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public List getChildren() { + return children; + } + public void setChildren(List children) { + this.children = children; + } + public Map getFriends() { + return friends; + } + public void setFriends(Map friends) { + this.friends = friends; + } + public String getPath() { + return path; + } + public void setPath(String path) { + this.path = path; + } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.hashCode()); + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final Person other = (Person) obj; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + return true; + } +} Index: src/test/test-config/jcrmapping-complex-collections.xml =================================================================== --- src/test/test-config/jcrmapping-complex-collections.xml (revision 0) +++ src/test/test-config/jcrmapping-complex-collections.xml (revision 0) @@ -0,0 +1,30 @@ + + + + + + + + + + + +