vgritsenko 2005/05/25 18:17:11
Modified: . status.xml java/src/org/apache/xindice/core/xupdate XUpdateImpl.java java/tests/src/org/apache/xindice/integration/client/services XUpdateQueryTest.java Log: apply patch from Todd Byrne, fix xupdate bug #30878 Revision Changes Path 1.52 +8 -1 xml-xindice/status.xml Index: status.xml =================================================================== RCS file: /home/cvs/xml-xindice/status.xml,v retrieving revision 1.51 retrieving revision 1.52 diff -u -r1.51 -r1.52 --- status.xml 4 Mar 2005 03:38:14 -0000 1.51 +++ status.xml 26 May 2005 01:17:11 -0000 1.52 @@ -73,7 +73,14 @@ </todo> <changes> - <release version="1.1b5-dev" date="March 3 2005"> + <release version="1.1b5-dev" date="May 25 2005"> + <action dev="VG" type="fix" fixes-bug="30878" due-to="Todd Byrne"> + Make sure that XUpdate commands are run only once per document. + In case XUpdate command does not complete successfully, revert + all affected documents to the original state. This requires + buffering of modified documents - so XUpdate on large collection + might require lots of memory. + </action> <action dev="VG" type="update" fixes-bug="33657" due-to="Dave Brosius"> Added support for current Xalan CVS (post 2.6.0 release). </action> 1.13 +34 -21 xml-xindice/java/src/org/apache/xindice/core/xupdate/XUpdateImpl.java Index: XUpdateImpl.java =================================================================== RCS file: /home/cvs/xml-xindice/java/src/org/apache/xindice/core/xupdate/XUpdateImpl.java,v retrieving revision 1.12 retrieving revision 1.13 diff -u -r1.12 -r1.13 --- XUpdateImpl.java 8 Feb 2004 02:50:54 -0000 1.12 +++ XUpdateImpl.java 26 May 2005 01:17:11 -0000 1.13 @@ -1,5 +1,5 @@ /* - * Copyright 1999-2004 The Apache Software Foundation. + * Copyright 1999-2005 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,27 +34,30 @@ import org.xmldb.xupdate.lexus.commands.DefaultCommand; import java.util.Enumeration; +import java.util.HashMap; import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; /** * Provides Collection and document based XUpdate capabilities. * * For more detail about XUpdate look at the - * <a href="http://www.xmldb.org/xupdate/xupdate-wd.html">XUpdate Working Draft</a>. + * <a href="http://xmldb-org.sourceforge.net/xupdate/xupdate-wd.html">XUpdate Working Draft</a>. * * @version CVS $Revision$, $Date$ */ public class XUpdateImpl extends XUpdateQueryImpl { - protected int nodesModified = 0; - protected NamespaceMap nsMap; - /** * If set to true, then namespaces set explicitly via an API call will take precendence. * If set to false, then namespaces set implicitly within query string will take precedence. */ private static final boolean API_NS_PRECEDENCE = true; + protected int nodesModified; + protected NamespaceMap nsMap; + /** * Set the namespace map to be used when resolving queries */ @@ -148,44 +151,54 @@ * @exception Exception Description of Exception */ public void execute(Collection col) throws Exception { - int attribIndex = 0; + + // TODO: Don't cache all the documents in memory. + // Need to keep updated documents in memory so that can + // 'rollback' all the changes in case of failure. + // Won't need this in case underlying collection supports + // transaction. + HashMap docsUpdated = new HashMap(); for (int i = 0; i < super.query[0].size(); i++) { int cmdID = ((Integer) super.query[0].elementAt(i)).intValue(); if (cmdID == CommandConstants.ATTRIBUTES) { Hashtable attribs = (Hashtable) super.query[1].elementAt(attribIndex); - attribIndex++; String selector = (String) attribs.get("select"); + attribIndex++; - // If we found an XPath selector we need to execute the commands. - if (selector != null) { + // If we found an XPath selector we need to execute the commands, + // but we can not execute xupdate variables again. + // all variables start with a '$' + if (selector != null && !selector.startsWith("$")) { NodeSet ns = col.queryCollection("XPath", selector, nsMap); - Document lastDoc = null; while (ns != null && ns.hasMoreNodes()) { DBNode node = (DBNode) ns.getNextNode(); Document doc = node.getOwnerDocument(); + NodeSource source = node.getSource(); - if (doc == lastDoc) { + if (docsUpdated.containsKey(source.getKey())) { continue; // We only have to process it once } else { - lastDoc = doc; + docsUpdated.put(source.getKey(), doc); } - NodeSource source = node.getSource(); - - Node contextNode = doc.getDocumentElement(); - execute(contextNode); - - col.setDocument(source.getKey(), doc); + execute(doc.getDocumentElement()); } } } } + + // Update all documents at once + // this way we don't get any half run xupdate commands. + Iterator i = docsUpdated.entrySet().iterator(); + while (i.hasNext()) { + Map.Entry set = (Map.Entry) i.next(); + col.setDocument(set.getKey(), (Document) set.getValue()); + } } public int getModifiedCount() { return nodesModified; } } - 1.8 +44 -7 xml-xindice/java/tests/src/org/apache/xindice/integration/client/services/XUpdateQueryTest.java Index: XUpdateQueryTest.java =================================================================== RCS file: /home/cvs/xml-xindice/java/tests/src/org/apache/xindice/integration/client/services/XUpdateQueryTest.java,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- XUpdateQueryTest.java 1 Apr 2005 02:46:43 -0000 1.7 +++ XUpdateQueryTest.java 26 May 2005 01:17:11 -0000 1.8 @@ -22,6 +22,9 @@ import org.xmldb.api.modules.XUpdateQueryService; import org.xmldb.api.base.Collection; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.custommonkey.xmlunit.XMLAssert; /** * @version CVS $Revision$, $Date$ @@ -29,7 +32,7 @@ */ public class XUpdateQueryTest extends AbstractXmlDbClientTest { - + private static final Log log = LogFactory.getLog(XUpdateQueryTest.class); public void setUp() throws Exception { super.setUp(); @@ -96,6 +99,7 @@ "<last>Smith</last>" + "<phone type=\"work\">480-300-3003</phone>" + "</person>"; + String updatedDocument2 = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<person status=\"divorced\">" + "<first>Ben</first>" + @@ -103,18 +107,51 @@ "<phone type=\"work\">480-300-3003</phone>" + "</person>"; + String query2 = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + + "<xupdate:modifications version=\"1.0\" xmlns:xupdate=\"http://www.xmldb.org/xupdate\">" + + "<xupdate:append select=\"/person/first\">" + + "<xupdate:element name=\"test2\"/>" + + "</xupdate:append>" + + "<xupdate:append select=\"/person/last\">" + + "<xupdate:element name=\"test1\"/>" + + "</xupdate:append>" + + "</xupdate:modifications>"; + + String updatedDocument3 = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<person status=\"divorced\">" + + "<first>Ben" + + "<test2 />" + + "</first>" + + "<last>Benton"+ + "<test1 />" + + "</last>" + + "<phone type=\"work\">480-300-3003</phone>" + + "</person>"; + Collection col = this.client.getCollection(TEST_COLLECTION_PATH); XUpdateQueryService service = (XUpdateQueryService) col.getService("XUpdateQueryService", "1.0"); - long count = service.update(query); - assertEquals(6, count); + // Fixed: was 6 should be 2. The old code would cause multiple updates. + assertEquals(2, count); String doc = this.client.getDocument(TEST_COLLECTION_PATH, "doc1"); assertNotNull(doc); - assertEquals(updatedDocument1, doc); + XMLAssert.assertXMLEqual(updatedDocument1, doc); doc = this.client.getDocument(TEST_COLLECTION_PATH, "doc2"); assertNotNull(doc); - assertEquals(updatedDocument2, doc); + XMLAssert.assertXMLEqual(updatedDocument2, doc); + + // test the second query should update 2 docs + count = service.update(query2); + assertEquals(2,count); + + // check the second doc to make sure its correct + doc = this.client.getDocument(TEST_COLLECTION_PATH, "doc2"); + + log.debug("Doc2:=\n " + doc); + log.debug("UpdatedDoc3:\n " + updatedDocument3); + XMLAssert.assertXMLEqual(updatedDocument3,doc); } }