Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions exist-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,9 @@
<include>src/test/java/org/exist/xquery/functions/fn/transform/FunTransformITTest.java</include>
<include>src/test/java/org/exist/xquery/functions/securitymanager/AccountMetadataFunctionsTest.java</include>
<include>src/test/java/org/exist/xquery/functions/securitymanager/SecurityManagerTestUtil.java</include>
<include>src/test/java/org/exist/xquery/functions/system/GetMainModuleLoadPathTest.java</include>
<include>src/main/java/org/exist/xquery/functions/system/GetModuleLoadPath.java</include>
<include>src/test/java/org/exist/xquery/functions/system/GetModuleLoadPathTest.java</include>
<include>src/main/java/org/exist/xquery/functions/system/FunctionAvailable.java</include>
<include>src/test/java/org/exist/xquery/functions/xmldb/XMLDBStoreTest.java</include>
<include>src/test/java/org/exist/xquery/functions/xquery3/SerializeTest.java</include>
Expand Down Expand Up @@ -2445,6 +2448,9 @@
<exclude>src/test/java/org/exist/xquery/functions/session/AbstractSessionTest.java</exclude>
<exclude>src/test/java/org/exist/xquery/functions/session/AttributeTest.java</exclude>
<exclude>src/main/java/org/exist/xquery/functions/system/FunctionAvailable.java</exclude>
<exclude>src/test/java/org/exist/xquery/functions/system/GetMainModuleLoadPathTest.java</exclude>
<exclude>src/main/java/org/exist/xquery/functions/system/GetModuleLoadPath.java</exclude>
<exclude>src/test/java/org/exist/xquery/functions/system/GetModuleLoadPathTest.java</exclude>
<exclude>src/test/java/org/exist/xquery/functions/system/GetRunningXQueriesTest.java</exclude>
<exclude>src/main/java/org/exist/xquery/functions/system/GetUptime.java</exclude>
<exclude>src/main/java/org/exist/xquery/functions/system/Restore.java</exclude>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
/*
* eXist-db Open Source Native XML Database
* Copyright (C) 2001 The eXist-db Authors
* Elemental
* Copyright (C) 2024, Evolved Binary Ltd
*
* info@exist-db.org
* http://www.exist-db.org
* admin@evolvedbinary.com
* https://www.evolvedbinary.com | https://www.elemental.xyz
*
* 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.
* License as published by the Free Software Foundation; version 2.1.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
Expand All @@ -21,41 +20,52 @@
*/
package org.exist.xquery.functions.system;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.exist.dom.QName;
import org.exist.xquery.BasicFunction;
import org.exist.xquery.Cardinality;
import org.exist.xquery.FunctionSignature;
import org.exist.xquery.XPathException;
import org.exist.xquery.XQueryContext;
import org.exist.xquery.value.FunctionReturnSequenceType;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.StringValue;
import org.exist.xquery.value.Type;

public class GetModuleLoadPath extends BasicFunction {
import javax.annotation.Nullable;

import static org.exist.xquery.FunctionDSL.returns;
import static org.exist.xquery.functions.system.SystemModule.functionSignature;

protected final static Logger logger = LogManager.getLogger(GetModuleLoadPath.class);
public class GetModuleLoadPath extends BasicFunction {

public final static FunctionSignature signature =
new FunctionSignature(
new QName("get-module-load-path", SystemModule.NAMESPACE_URI, SystemModule.PREFIX),
"Returns the module load path from the current query context. The module load path " +
"corresponds to the location on the file system from where modules are loaded " +
"into an XQuery. This is usually the directory from which the main XQuery was " +
"compiled, or - when executing a stored XQuery - the collection in which the main " +
"query resides. The module load path " +
"is also used to resolve relative XInclude paths.",
FunctionSignature.NO_ARGS,
new FunctionReturnSequenceType(Type.STRING, Cardinality.EXACTLY_ONE, "the load path"));
private static final String FS_GET_MODULE_LOAD_PATH_NAME = "get-module-load-path";
public static final FunctionSignature FS_GET_MODULE_LOAD_PATH = functionSignature(
FS_GET_MODULE_LOAD_PATH_NAME,
"Returns the path from which the module was loaded. Either a filesystem directory, or database collection. If the path cannot be determined, for example because the query module is in-memory then '.' is returned.",
returns(Type.STRING, "The path from which the module was loaded")
);

private static final String FS_GET_MAIN_MODULE_LOAD_PATH_NAME = "get-main-module-load-path";
public static final FunctionSignature FS_GET_MAIN_MODULE_LOAD_PATH = functionSignature(
FS_GET_MAIN_MODULE_LOAD_PATH_NAME,
"Returns the path from which the main module was loaded. If called from a library module, it finds the path of the ancestor main module. Either a filesystem directory, or database collection. If the path cannot be determined, for example because the query module is in-memory then '.' is returned.",
returns(Type.STRING, "The path from which the main module was loaded")
);

public GetModuleLoadPath(XQueryContext context) {
public GetModuleLoadPath(final XQueryContext context, final FunctionSignature signature) {
super(context, signature);
}

public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathException {
return new StringValue(this, context.getModuleLoadPath());
@Override
public Sequence eval(final Sequence[] args, final Sequence contextSequence) throws XPathException {
final String moduleLoadPath;
if (isCalledAs(FS_GET_MODULE_LOAD_PATH_NAME)) {
moduleLoadPath = context.getModuleLoadPath();
} else {
@Nullable final XQueryContext rootContext = context.getRootContext();
if (rootContext == null) {
// there may not be a main module, e.g. RESTXQ resource functions
throw new XPathException(this, "There is no root context as the library module was executed via a named function");
}
moduleLoadPath = rootContext.getModuleLoadPath();
}
return new StringValue(this, moduleLoadPath);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ public class SystemModule extends AbstractInternalModule {
new FunctionDef(GetExistHome.signature, GetExistHome.class),
new FunctionDef(Shutdown.signatures[0], Shutdown.class),
new FunctionDef(Shutdown.signatures[1], Shutdown.class),
new FunctionDef(GetModuleLoadPath.signature, GetModuleLoadPath.class),
new FunctionDef(GetModuleLoadPath.FS_GET_MODULE_LOAD_PATH, GetModuleLoadPath.class),
new FunctionDef(GetModuleLoadPath.FS_GET_MAIN_MODULE_LOAD_PATH, GetModuleLoadPath.class),
new FunctionDef(TriggerSystemTask.signature, TriggerSystemTask.class),
new FunctionDef(AsUser.FS_AS_USER, AsUser.class),
new FunctionDef(AsUser.FS_FUNCTION_AS_USER, AsUser.class),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
/*
* Elemental
* Copyright (C) 2024, Evolved Binary Ltd
*
* admin@evolvedbinary.com
* https://www.evolvedbinary.com | https://www.elemental.xyz
*
* 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; version 2.1.
*
* 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.xquery.functions.system;

import com.evolvedbinary.j8fu.function.ConsumerE;
import org.exist.EXistException;
import org.exist.collections.Collection;
import org.exist.security.PermissionDeniedException;
import org.exist.source.DbUriSource;
import org.exist.source.StringSource;
import org.exist.storage.BrokerPool;
import org.exist.storage.DBBroker;
import org.exist.storage.txn.Txn;
import org.exist.test.ExistEmbeddedServer;
import org.exist.util.LockException;
import org.exist.util.StringInputSource;
import org.exist.xmldb.XmldbURI;
import org.exist.xquery.XPathException;
import org.exist.xquery.XQueryContext;
import org.exist.xquery.XQueryUtil;
import org.exist.xquery.value.Item;
import org.exist.xquery.value.Type;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import org.xmlunit.builder.DiffBuilder;
import org.xmlunit.builder.Input;
import org.xmlunit.diff.Diff;

import javax.xml.transform.Source;
import java.io.IOException;
import java.util.Optional;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.exist.test.Util.storeQuery;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;

public class GetMainModuleLoadPathTest {

private static XmldbURI TEST_COLLECTION_URI = XmldbURI.create("/db/get-main-module-load-path-test");
private static XmldbURI TEST_SUB_COLLECTION_URI = TEST_COLLECTION_URI.append("sub1");

private static XmldbURI STANDALONE_MAIN_MODULE_URI = TEST_COLLECTION_URI.append("standalone-main.xq");
private static String STANDALONE_MAIN_MODULE_XQ =
"import module namespace system = \"http://exist-db.org/xquery/system\";\n" +
"document {\n" +
" <path>{system:get-main-module-load-path()}</path>\n" +
"}";

private static XmldbURI IMPORTED_LIBRARY_MODULE_URI = TEST_SUB_COLLECTION_URI.append("imported-library.xqm");
private static String IMPORTED_LIBRARY_MODULE_XQ =
"module namespace ilm = \"http://ilm\";\n" +
"import module namespace system = \"http://exist-db.org/xquery/system\";\n" +
"declare function ilm:main-module-load-path() {\n" +
" system:get-main-module-load-path()\n" +
"};";

private static XmldbURI IMPORTING_MAIN_MODULE_URI = TEST_COLLECTION_URI.append("importing-main.xq");
private static String IMPORTING_MAIN_MODULE_XQ =
"import module namespace system = \"http://exist-db.org/xquery/system\";\n" +
"import module namespace ilm = \"http://ilm\" at \"xmldb:exist://" + IMPORTED_LIBRARY_MODULE_URI.getXmldbURI().toString() + "\";\n" +
"document {\n" +
" <paths>\n" +
" <path>{system:get-main-module-load-path()}</path>\n" +
" <library-path>{ilm:main-module-load-path()}</library-path>\n" +
" </paths>\n" +
"}";

@ClassRule
public static ExistEmbeddedServer EXIST_EMBEDDED_SERVER = new ExistEmbeddedServer(true, true);

@BeforeClass
public static void setup() throws EXistException, PermissionDeniedException, IOException, SAXException, LockException {
final BrokerPool brokerPool = EXIST_EMBEDDED_SERVER.getBrokerPool();
try (final DBBroker broker = brokerPool.get(Optional.of(brokerPool.getSecurityManager().getSystemSubject()));
final Txn transaction = brokerPool.getTransactionManager().beginTransaction()) {

// store xquery documents
try (final Collection testCollection = broker.getOrCreateCollection(transaction, TEST_COLLECTION_URI)) {
storeQuery(broker, transaction, new StringInputSource(STANDALONE_MAIN_MODULE_XQ.getBytes(UTF_8)), testCollection, STANDALONE_MAIN_MODULE_URI);

try (final Collection testSubCollection = broker.getOrCreateCollection(transaction, TEST_SUB_COLLECTION_URI)) {
storeQuery(broker, transaction, new StringInputSource(IMPORTED_LIBRARY_MODULE_XQ.getBytes(UTF_8)), testSubCollection, IMPORTED_LIBRARY_MODULE_URI);
}

storeQuery(broker, transaction, new StringInputSource(IMPORTING_MAIN_MODULE_XQ.getBytes(UTF_8)), testCollection, IMPORTING_MAIN_MODULE_URI);
}

transaction.commit();
}
}

@Test
public void standaloneMainModule() throws EXistException, PermissionDeniedException, XPathException, IOException {
final String expected = "<path>.</path>";

final BrokerPool brokerPool = EXIST_EMBEDDED_SERVER.getBrokerPool();
try (final DBBroker broker = brokerPool.get(Optional.of(brokerPool.getSecurityManager().getSystemSubject()));
final Txn transaction = brokerPool.getTransactionManager().beginTransaction()) {

try (final XQueryUtil.QueryResult queryResult = XQueryUtil.query(broker, new StringSource(STANDALONE_MAIN_MODULE_XQ), false, null, null, null, null, null)) {
assertNotNull(queryResult.result);
assertEquals(1, queryResult.result.getItemCount());
final Item item = queryResult.result.itemAt(0);
assertEquals(Type.DOCUMENT, item.getType());

final Source expectedSrc = Input.fromString(expected).build();
final Source actualSrc = Input.fromNode((Document) item).build();

final Diff diff = DiffBuilder.compare(expectedSrc)
.withTest(actualSrc)
.checkForSimilar()
.build();

assertFalse(diff.toString(), diff.hasDifferences());
}

transaction.commit();
}
}

@Test
public void importingMainModule() throws EXistException, PermissionDeniedException, XPathException, IOException {
final String expected =
"<paths>" +
"<path>.</path>" +
"<library-path>.</library-path>" +
"</paths>";

final BrokerPool brokerPool = EXIST_EMBEDDED_SERVER.getBrokerPool();
try (final DBBroker broker = brokerPool.get(Optional.of(brokerPool.getSecurityManager().getSystemSubject()));
final Txn transaction = brokerPool.getTransactionManager().beginTransaction()) {

try (final XQueryUtil.QueryResult queryResult = XQueryUtil.query(broker, new StringSource(IMPORTING_MAIN_MODULE_XQ), false, null, null, null, null, null)) {
assertNotNull(queryResult.result);
assertEquals(1, queryResult.result.getItemCount());
final Item item = queryResult.result.itemAt(0);
assertEquals(Type.DOCUMENT, item.getType());

final Source expectedSrc = Input.fromString(expected).build();
final Source actualSrc = Input.fromNode((Document) item).build();

final Diff diff = DiffBuilder.compare(expectedSrc)
.withTest(actualSrc)
.checkForSimilar()
.build();

assertFalse(diff.toString(), diff.hasDifferences());
}

transaction.commit();
}
}


@Test
public void storedStandaloneMainModule() throws EXistException, PermissionDeniedException, XPathException, IOException {
final String expected = "<path>" + TEST_COLLECTION_URI.getCollectionPath() + "</path>";

final BrokerPool brokerPool = EXIST_EMBEDDED_SERVER.getBrokerPool();
try (final DBBroker broker = brokerPool.get(Optional.of(brokerPool.getSecurityManager().getSystemSubject()));
final Txn transaction = brokerPool.getTransactionManager().beginTransaction()) {

final ConsumerE<XQueryContext, XPathException> preCompilationContextSetup = xqueryContext -> xqueryContext.setModuleLoadPath(STANDALONE_MAIN_MODULE_URI.removeLastSegment().getCollectionPath());

try (final XQueryUtil.QueryResult queryResult = XQueryUtil.query(broker, DbUriSource.from(brokerPool, STANDALONE_MAIN_MODULE_URI, false, false), false, null, null, preCompilationContextSetup, null, null)) {
assertNotNull(queryResult.result);
assertEquals(1, queryResult.result.getItemCount());
final Item item = queryResult.result.itemAt(0);
assertEquals(Type.DOCUMENT, item.getType());

final Source expectedSrc = Input.fromString(expected).build();
final Source actualSrc = Input.fromNode((Document) item).build();

final Diff diff = DiffBuilder.compare(expectedSrc)
.withTest(actualSrc)
.checkForSimilar()
.build();

assertFalse(diff.toString(), diff.hasDifferences());
}

transaction.commit();
}
}

@Test
public void storedImportingMainModule() throws EXistException, PermissionDeniedException, XPathException, IOException {
final String expected =
"<paths>" +
"<path>" + TEST_COLLECTION_URI.getCollectionPath() + "</path>" +
"<library-path>" + TEST_COLLECTION_URI.getCollectionPath() + "</library-path>" +
"</paths>";

final BrokerPool brokerPool = EXIST_EMBEDDED_SERVER.getBrokerPool();
try (final DBBroker broker = brokerPool.get(Optional.of(brokerPool.getSecurityManager().getSystemSubject()));
final Txn transaction = brokerPool.getTransactionManager().beginTransaction()) {

final ConsumerE<XQueryContext, XPathException> preCompilationContextSetup = xqueryContext -> xqueryContext.setModuleLoadPath(IMPORTING_MAIN_MODULE_URI.removeLastSegment().getCollectionPath());


try (final XQueryUtil.QueryResult queryResult = XQueryUtil.query(broker, DbUriSource.from(brokerPool, IMPORTING_MAIN_MODULE_URI, false, false), false, null, null, preCompilationContextSetup, null, null)) {
assertNotNull(queryResult.result);
assertEquals(1, queryResult.result.getItemCount());
final Item item = queryResult.result.itemAt(0);
assertEquals(Type.DOCUMENT, item.getType());

final Source expectedSrc = Input.fromString(expected).build();
final Source actualSrc = Input.fromNode((Document) item).build();

final Diff diff = DiffBuilder.compare(expectedSrc)
.withTest(actualSrc)
.checkForSimilar()
.build();

assertFalse(diff.toString(), diff.hasDifferences());
}

transaction.commit();
}
}
}
Loading
Loading