Skip to content

Commit aa4a41d

Browse files
committed
[featue] Add a new xmldb:update#3 function that allows to update a single document
1 parent af7da3c commit aa4a41d

4 files changed

Lines changed: 107 additions & 68 deletions

File tree

exist-core/pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,6 +1285,7 @@
12851285
<include>src/test/java/org/exist/xquery/functions/validate/JingXsdTest.java</include>
12861286
<include>src/main/java/org/exist/xquery/functions/validation/Jaxp.java</include>
12871287
<include>src/test/java/org/exist/xquery/functions/xmldb/DbStore2Test.java</include>
1288+
<include>src/main/java/org/exist/xquery/functions/xmldb/XMLDBModule.java</include>
12881289
<include>src/main/java/org/exist/xquery/functions/xmldb/XMLDBStore.java</include>
12891290
<include>src/main/java/org/exist/xquery/functions/xmldb/XMLDBXUpdate.java</include>
12901291
<include>src/test/java/org/exist/xquery/functions/xquery3/TryCatchTest.java</include>
@@ -1983,6 +1984,7 @@
19831984
<exclude>src/test/java/org/exist/xquery/functions/xmldb/AbstractXMLDBTest.java</exclude>
19841985
<exclude>src/test/java/org/exist/xquery/functions/xmldb/DbStore2Test.java</exclude>
19851986
<exclude>src/test/java/org/exist/xquery/functions/xmldb/XMLDBAuthenticateTest.java</exclude>
1987+
<exclude>src/main/java/org/exist/xquery/functions/xmldb/XMLDBModule.java</exclude>
19861988
<exclude>src/main/java/org/exist/xquery/functions/xmldb/XMLDBStore.java</exclude>
19871989
<exclude>src/test/java/org/exist/xquery/functions/xmldb/XMLDBStoreTest.java</exclude>
19881990
<exclude>src/main/java/org/exist/xquery/functions/xmldb/XMLDBXUpdate.java</exclude>

exist-core/src/main/java/org/exist/xquery/functions/xmldb/XMLDBModule.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ public class XMLDBModule extends AbstractInternalModule {
6464
new FunctionDef(XMLDBLoadFromPattern.signatures[1], XMLDBLoadFromPattern.class),
6565
new FunctionDef(XMLDBLoadFromPattern.signatures[2], XMLDBLoadFromPattern.class),
6666
new FunctionDef(XMLDBLoadFromPattern.signatures[3], XMLDBLoadFromPattern.class),
67-
new FunctionDef(XMLDBXUpdate.signature, XMLDBXUpdate.class),
67+
new FunctionDef(XMLDBXUpdate.FS_UPDATE[0], XMLDBXUpdate.class),
68+
new FunctionDef(XMLDBXUpdate.FS_UPDATE[1], XMLDBXUpdate.class),
6869
new FunctionDef(XMLDBCopy.FS_COPY_COLLECTION[0], XMLDBCopy.class),
6970
new FunctionDef(XMLDBCopy.FS_COPY_COLLECTION[1], XMLDBCopy.class),
7071
new FunctionDef(XMLDBCopy.FS_COPY_RESOURCE[0], XMLDBCopy.class),

exist-core/src/main/java/org/exist/xquery/functions/xmldb/XMLDBXUpdate.java

Lines changed: 98 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -45,85 +45,117 @@
4545
*/
4646
package org.exist.xquery.functions.xmldb;
4747

48-
import org.apache.commons.io.output.StringBuilderWriter;
49-
import org.apache.logging.log4j.LogManager;
50-
import org.apache.logging.log4j.Logger;
51-
52-
import org.exist.dom.QName;
53-
import org.exist.util.serializer.DOMSerializer;
54-
import org.exist.xquery.Cardinality;
48+
import org.exist.EXistException;
49+
import org.exist.collections.Collection;
50+
import org.exist.dom.persistent.*;
51+
import org.exist.security.PermissionDeniedException;
52+
import org.exist.storage.lock.Lock;
53+
import org.exist.util.LockException;
54+
import org.exist.xmldb.XmldbURI;
55+
import org.exist.xquery.BasicFunction;
5556
import org.exist.xquery.FunctionSignature;
5657
import org.exist.xquery.XPathException;
5758
import org.exist.xquery.XQueryContext;
58-
import org.exist.xquery.value.FunctionReturnSequenceType;
5959
import org.exist.xquery.value.FunctionParameterSequenceType;
6060
import org.exist.xquery.value.IntegerValue;
61-
import org.exist.xquery.value.NodeValue;
61+
import org.exist.xquery.value.Item;
6262
import org.exist.xquery.value.Sequence;
63-
import org.exist.xquery.value.SequenceType;
6463
import org.exist.xquery.value.Type;
65-
import org.xmldb.api.base.Collection;
66-
import org.xmldb.api.base.XMLDBException;
67-
import org.xmldb.api.modules.XUpdateQueryService;
64+
import org.exist.xupdate.Modification;
65+
import org.exist.xupdate.XUpdateProcessor;
66+
import org.xml.sax.SAXException;
67+
68+
import javax.annotation.Nullable;
69+
import javax.xml.parsers.ParserConfigurationException;
6870

69-
import javax.xml.transform.OutputKeys;
70-
import javax.xml.transform.TransformerException;
71+
import java.util.List;
7172
import java.util.Properties;
7273

74+
import static org.exist.xquery.FunctionDSL.*;
75+
import static org.exist.xquery.functions.xmldb.XMLDBModule.functionSignatures;
76+
7377
/**
74-
*
75-
* @author wolf
76-
*
78+
* @author <a href="mailto:adam@evolvedbinary.com">Adam Retter</a>
7779
*/
78-
public class XMLDBXUpdate extends XMLDBAbstractCollectionManipulator
79-
{
80-
protected static final Logger logger = LogManager.getLogger(XMLDBXUpdate.class);
81-
82-
public final static FunctionSignature signature = new FunctionSignature(
83-
new QName("update", XMLDBModule.NAMESPACE_URI, XMLDBModule.PREFIX),
84-
"Processes an XUpdate request, $modifications, against a collection $collection-uri. "
85-
+ XMLDBModule.COLLECTION_URI
86-
+ "The modifications are passed in a "
87-
+ "document conforming to the XUpdate specification. "
88-
+ "http://rx4rdf.liminalzone.org/xupdate-wd.html#N1a32e0"
89-
+ "The function returns the number of modifications caused by the XUpdate.",
90-
new SequenceType[]{
91-
new FunctionParameterSequenceType("collection-uri", Type.STRING, Cardinality.EXACTLY_ONE, "The collection URI"),
92-
new FunctionParameterSequenceType("modifications", Type.NODE, Cardinality.EXACTLY_ONE, "The XUpdate modifications to be processed")},
93-
new FunctionReturnSequenceType(Type.INTEGER, Cardinality.EXACTLY_ONE, "the number of modifications, as xs:integer, caused by the XUpdate"));
94-
95-
public XMLDBXUpdate(XQueryContext context) {
96-
super(context, signature);
80+
public class XMLDBXUpdate extends BasicFunction {
81+
private static final FunctionParameterSequenceType FS_PARAM_COLLECTION_URI = param("collection-uri", Type.STRING, "The collection URI");
82+
private static final FunctionParameterSequenceType FS_PARAM_DOCUMENT_URI = param("document-uri", Type.STRING, "The document URI");
83+
private static final FunctionParameterSequenceType FS_PARAM_MODIFICATIONS = param("modifications", Type.NODE, "The XUpdate modifications to be processed");
84+
85+
private static final String FS_UPDATE_NAME = "update";
86+
static final FunctionSignature[] FS_UPDATE = functionSignatures(
87+
FS_UPDATE_NAME,
88+
"Processes an XUpdate request, $modifications, against a collection $collection-uri. "
89+
+ XMLDBModule.COLLECTION_URI
90+
+ "The modifications are passed in a "
91+
+ "document conforming to the XUpdate specification. "
92+
+ "https://xmldb-org.sourceforge.net/xupdate/xupdate-wd.html"
93+
+ "The function returns the number of modifications caused by the XUpdate.",
94+
returns(Type.INTEGER, "The number of modifications, as an xs:integer, caused by the XUpdate"),
95+
arities(
96+
arity(
97+
FS_PARAM_COLLECTION_URI,
98+
FS_PARAM_MODIFICATIONS
99+
),
100+
arity(
101+
FS_PARAM_COLLECTION_URI,
102+
FS_PARAM_DOCUMENT_URI,
103+
FS_PARAM_MODIFICATIONS
104+
)
105+
)
106+
);
107+
108+
public XMLDBXUpdate(final XQueryContext context, final FunctionSignature functionSignature) {
109+
super(context, functionSignature);
97110
}
98111

99-
/* (non-Javadoc)
100-
* @see org.exist.xquery.BasicFunction#eval(org.exist.xquery.value.Sequence[], org.exist.xquery.value.Sequence)
101-
*/
102-
public Sequence evalWithCollection(Collection c, Sequence[] args, Sequence contextSequence)
103-
throws XPathException {
104-
final NodeValue data = (NodeValue) args[1].itemAt(0);
105-
final String xupdate;
106-
try (final StringBuilderWriter writer = new StringBuilderWriter()) {
107-
final Properties properties = new Properties();
108-
properties.setProperty(OutputKeys.INDENT, "yes");
109-
final DOMSerializer serializer = new DOMSerializer(writer, properties);
110-
serializer.serialize(data.getNode());
111-
xupdate = writer.toString();
112-
} catch(final TransformerException e) {
113-
logger.debug("Exception while serializing XUpdate document", e);
114-
throw new XPathException(this, "Exception while serializing XUpdate document: " + e.getMessage(), e);
115-
}
116-
117-
long modifications = 0;
118-
try {
119-
final XUpdateQueryService service = c.getService(XUpdateQueryService.class);
120-
logger.debug("Processing XUpdate request: {}", xupdate);
121-
modifications = service.update(xupdate);
122-
} catch(final XMLDBException e) {
123-
throw new XPathException(this, "Exception while processing xupdate: " + e.getMessage(), e);
124-
}
125-
126-
context.getRootExpression().resetState(false);
127-
return new IntegerValue(this, modifications);
112+
@Override
113+
public Sequence eval(final Sequence[] args, final Sequence contextSequence) throws XPathException {
114+
final String collectionUri = args[0].itemAt(0).getStringValue();
115+
@Nullable final String documentUri;
116+
final Item modifications;
117+
if (args.length == 3) {
118+
documentUri = args[1].itemAt(0).getStringValue();
119+
modifications = args[2].itemAt(0);
120+
} else {
121+
documentUri = null;
122+
modifications = args[1].itemAt(0);
123+
}
124+
125+
final MutableDocumentSet documentSet = new DefaultDocumentSet();
126+
try (final Collection collection = context.getBroker().openCollection(XmldbURI.create(collectionUri), Lock.LockMode.READ_LOCK)) {
127+
if (documentUri == null) {
128+
collection.allDocs(context.getBroker(), documentSet, true);
129+
130+
} else {
131+
try (final LockedDocument lockedDocument = collection.getDocumentWithLock(context.getBroker(), XmldbURI.create(documentUri), Lock.LockMode.READ_LOCK)) {
132+
133+
// NOTE: early release of Collection lock inline with Asymmetrical Locking scheme
134+
collection.close();
135+
136+
final DocumentImpl doc = lockedDocument == null ? null : lockedDocument.getDocument();
137+
if (doc == null) {
138+
throw new XPathException(this, "Resource not found: " + documentUri);
139+
}
140+
documentSet.add(doc);
141+
}
142+
}
143+
144+
final XUpdateProcessor processor = new XUpdateProcessor(context.getBroker(), documentSet);
145+
modifications.toSAX(context.getBroker(), processor, new Properties());
146+
final List<Modification> modificationList = processor.getModifications();
147+
long mods = 0;
148+
for (final Modification modification : modificationList) {
149+
mods += modification.process(context.getBroker().getCurrentTransaction());
150+
context.getBroker().flush();
151+
}
152+
153+
context.getRootExpression().resetState(false);
154+
155+
return new IntegerValue(this, mods);
156+
157+
} catch (final PermissionDeniedException | LockException | EXistException | ParserConfigurationException | SAXException e) {
158+
throw new XPathException(this, e);
159+
}
128160
}
129161
}

exist-core/src/main/java/org/exist/xupdate/XUpdateProcessor.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -868,7 +868,11 @@ public void startDTD(String name, String publicId, String systemId)
868868
public void startEntity(String name) throws SAXException {
869869
}
870870

871-
public void reset() {
871+
public List<Modification> getModifications() {
872+
return modifications;
873+
}
874+
875+
public void reset() {
872876
this.preserveWhitespace = false;
873877
this.spaceStack = null;
874878

0 commit comments

Comments
 (0)