org.exist.xmldb.LocalXPathQueryService Maven / Gradle / Ivy
/*
* eXist-db Open Source Native XML Database
* Copyright (C) 2001 The eXist-db Authors
*
* [email protected]
* http://www.exist-db.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.exist.xmldb;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.exist.EXistException;
import org.exist.debuggee.Debuggee;
import org.exist.security.PermissionDeniedException;
import org.exist.security.Subject;
import org.exist.source.DBSource;
import org.exist.source.FileSource;
import org.exist.source.Source;
import org.exist.storage.BrokerPool;
import org.exist.storage.DBBroker;
import org.exist.storage.XQueryPool;
import org.exist.storage.lock.Lock.LockMode;
import org.exist.storage.lock.LockedDocumentMap;
import org.exist.storage.serializers.EXistOutputKeys;
import org.exist.storage.txn.Txn;
import org.exist.util.LockException;
import org.exist.xmldb.function.LocalXmldbFunction;
import org.exist.xquery.value.AnyURIValue;
import org.exist.xquery.value.BinaryValue;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.SequenceIterator;
import org.w3c.dom.Node;
import org.xmldb.api.base.*;
import org.xmldb.api.base.Collection;
import org.xmldb.api.modules.XMLResource;
import java.io.Writer;
import java.util.*;
import org.exist.dom.persistent.BinaryDocument;
import org.exist.dom.persistent.DefaultDocumentSet;
import org.exist.dom.persistent.DocumentImpl;
import org.exist.dom.persistent.ExtArrayNodeSet;
import org.exist.dom.persistent.MutableDocumentSet;
import org.exist.dom.persistent.NodeProxy;
import org.exist.dom.persistent.NodeSet;
import org.exist.security.Permission;
import com.evolvedbinary.j8fu.Either;
import org.exist.xquery.CompiledXQuery;
import org.exist.xquery.XPathException;
import org.exist.xquery.XQuery;
import org.exist.xquery.XQueryContext;
public class LocalXPathQueryService extends AbstractLocalService implements EXistXPathQueryService, EXistXQueryService {
private final static Logger LOG = LogManager.getLogger(LocalXPathQueryService.class);
private final TreeMap namespaceDecls = new TreeMap<>();
private final TreeMap variableDecls = new TreeMap<>();
private boolean xpathCompatible = true;
private String moduleLoadPath = null;
private final Properties properties;
private boolean lockDocuments = false;
private LockedDocumentMap lockedDocuments = null;
private DBBroker reservedBroker = null;
public LocalXPathQueryService(final Subject user, final BrokerPool pool, final LocalCollection collection) {
super(user, pool, collection);
this.properties = new Properties(collection.getProperties());
}
@Override
public String getName() throws XMLDBException {
return "XPathQueryService";
}
@Override
public String getVersion() throws XMLDBException {
return "1.0";
}
@Override
public void clearNamespaces() throws XMLDBException {
namespaceDecls.clear();
}
@Override
public String getNamespace(final String prefix) throws XMLDBException {
return namespaceDecls.get(prefix);
}
@Override
public String getProperty(final String property) throws XMLDBException {
return properties.getProperty(property);
}
@Override
public ResourceSet query(final String query) throws XMLDBException {
return query(query, null);
}
@Override
public ResourceSet query(final XMLResource res, final String query) throws XMLDBException {
return query(res, query, null);
}
@Override
public ResourceSet query(final String query, final String sortBy) throws XMLDBException {
return withDb((broker, transaction) -> {
final XmldbURI[] docs = new XmldbURI[] { XmldbURI.create(collection.getName(broker, transaction)) };
return doQuery(broker, transaction, query, docs, null, sortBy);
});
}
@Override
public ResourceSet query(final XMLResource res, final String query, final String sortBy) throws XMLDBException {
final Node n = ((LocalXMLResource) res).root;
return withDb((broker, transaction) -> {
if (n != null && n instanceof org.exist.dom.memtree.NodeImpl) {
final XmldbURI[] docs = new XmldbURI[]{ getCollectionUri(broker, transaction, res.getParentCollection()) };
return doQuery(broker, transaction, query, docs, (org.exist.dom.memtree.NodeImpl) n, sortBy);
}
final NodeProxy node = ((LocalXMLResource) res).getNode(broker, transaction);
if (node == null) {
// resource is a document
//TODO : use dedicated function in XmldbURI
final XmldbURI[] docs = new XmldbURI[]{ getCollectionUri(broker, transaction, res.getParentCollection()).append(res.getDocumentId()) };
return doQuery(broker, transaction, query, docs, null, sortBy);
} else {
final NodeSet set = new ExtArrayNodeSet(1);
set.add(node);
final XmldbURI[] docs = new XmldbURI[]{node.getOwnerDocument().getURI()};
return doQuery(broker, transaction, query, docs, set, sortBy);
}
});
}
private ResourceSet doQuery(final DBBroker broker, final Txn transaction, final String query, final XmldbURI[] docs, final Sequence contextSet, final String sortExpr) throws XMLDBException {
final Either maybeExpr = compileAndCheck(broker, transaction, query);
if(maybeExpr.isLeft()) {
final XPathException e = maybeExpr.left().get();
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, e.getMessage(), e);
} else {
return execute(broker, transaction, docs, contextSet, maybeExpr.right().get(), sortExpr);
}
}
@Override
public ResourceSet execute(final CompiledExpression expression) throws XMLDBException {
return withDb((broker, transaction) ->
execute(broker, transaction, null, null, expression, null));
}
@Override
public ResourceSet execute(final XMLResource res, final CompiledExpression expression) throws XMLDBException {
return withDb((broker, transaction) -> {
final NodeProxy node = ((LocalXMLResource) res).getNode(broker, transaction);
if (node == null) {
// resource is a document
final XmldbURI[] docs = new XmldbURI[]{ getCollectionUri(broker, transaction, res.getParentCollection()).append(res.getDocumentId()) };
return execute(broker, transaction, docs, null, expression, null);
} else {
final NodeSet set = new ExtArrayNodeSet(1);
set.add(node);
final XmldbURI[] docs = new XmldbURI[]{node.getOwnerDocument().getURI()};
return execute(broker, transaction, docs, set, expression, null);
}
});
}
private ResourceSet execute(final DBBroker broker, final Txn transaction, XmldbURI[] docs, final Sequence contextSet, final CompiledExpression expression, final String sortExpr) throws XMLDBException {
final long start = System.currentTimeMillis();
final CompiledXQuery expr = (CompiledXQuery)expression;
Sequence result = null;
final XQueryContext context = expr.getContext();
try {
context.setStaticallyKnownDocuments(docs);
if (lockedDocuments != null) {
context.setProtectedDocs(lockedDocuments);
}
setupContext(null, context);
final XQuery xquery = brokerPool.getXQueryService();
result = xquery.execute(broker, expr, contextSet, properties);
} catch (final Exception e) {
// need to catch all runtime exceptions here to be able to release locked documents
throw new XMLDBException(ErrorCodes.VENDOR_ERROR, e.getMessage(), e);
} finally {
/*
* Run the cleanup tasks, but don't close BinaryValues which
* are in the result set as the user has not yet accessed them.
*
* Final cleanup of those BinaryValues is done by the user
* calling EXistResource#close(), ResourceSet#clear() or CompiledExpression#reset().
*/
final Sequence resSeq = result;
context.runCleanupTasks(o -> {
if(resSeq != null && o instanceof BinaryValue) {
for(int i = 0; i < resSeq.getItemCount(); i++) {
if (resSeq.itemAt(i) == o) {
return false;
}
}
}
return true;
});
}
LOG.debug("query took {} ms.", System.currentTimeMillis() - start);
if(result != null) {
final Properties resourceSetProperties = new Properties(properties);
resourceSetProperties.setProperty(EXistOutputKeys.XDM_SERIALIZATION, "yes");
return new LocalResourceSet(user, brokerPool, collection, resourceSetProperties, result, sortExpr);
} else {
return null;
}
}
@Override
public ResourceSet execute(final Source source) throws XMLDBException {
return execute((broker, transaction) -> source);
}
@Override
public ResourceSet executeStoredQuery(final String uri) throws XMLDBException {
return execute((broker, transaction) -> {
final DocumentImpl resource = broker.getResource(new XmldbURI(uri), Permission.READ | Permission.EXECUTE);
if (resource == null) {
throw new XMLDBException(ErrorCodes.INVALID_URI, "No stored XQuery exists at: " + uri);
}
return new DBSource(broker, (BinaryDocument) resource, false);
});
}
private ResourceSet execute(final LocalXmldbFunction
© 2015 - 2025 Weber Informatics LLC | Privacy Policy