Skip to content

Commit e5f9271

Browse files
authored
Merge pull request #231 from evolvedbinary/7.x.x/feature/exist-compat-get-main-module-load-path
[7.x.x] Added a new system:get-main-module-load-path() XPath function
2 parents 7c2ccd7 + d04c0a4 commit e5f9271

5 files changed

Lines changed: 533 additions & 28 deletions

File tree

exist-core/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,9 @@
778778
<include>src/test/java/org/exist/xquery/functions/fn/transform/FunTransformITTest.java</include>
779779
<include>src/test/java/org/exist/xquery/functions/securitymanager/AccountMetadataFunctionsTest.java</include>
780780
<include>src/test/java/org/exist/xquery/functions/securitymanager/SecurityManagerTestUtil.java</include>
781+
<include>src/test/java/org/exist/xquery/functions/system/GetMainModuleLoadPathTest.java</include>
782+
<include>src/main/java/org/exist/xquery/functions/system/GetModuleLoadPath.java</include>
783+
<include>src/test/java/org/exist/xquery/functions/system/GetModuleLoadPathTest.java</include>
781784
<include>src/main/java/org/exist/xquery/functions/system/FunctionAvailable.java</include>
782785
<include>src/test/java/org/exist/xquery/functions/xmldb/XMLDBStoreTest.java</include>
783786
<include>src/test/java/org/exist/xquery/functions/xquery3/SerializeTest.java</include>
@@ -2445,6 +2448,9 @@
24452448
<exclude>src/test/java/org/exist/xquery/functions/session/AbstractSessionTest.java</exclude>
24462449
<exclude>src/test/java/org/exist/xquery/functions/session/AttributeTest.java</exclude>
24472450
<exclude>src/main/java/org/exist/xquery/functions/system/FunctionAvailable.java</exclude>
2451+
<exclude>src/test/java/org/exist/xquery/functions/system/GetMainModuleLoadPathTest.java</exclude>
2452+
<exclude>src/main/java/org/exist/xquery/functions/system/GetModuleLoadPath.java</exclude>
2453+
<exclude>src/test/java/org/exist/xquery/functions/system/GetModuleLoadPathTest.java</exclude>
24482454
<exclude>src/test/java/org/exist/xquery/functions/system/GetRunningXQueriesTest.java</exclude>
24492455
<exclude>src/main/java/org/exist/xquery/functions/system/GetUptime.java</exclude>
24502456
<exclude>src/main/java/org/exist/xquery/functions/system/Restore.java</exclude>
Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
/*
2-
* eXist-db Open Source Native XML Database
3-
* Copyright (C) 2001 The eXist-db Authors
2+
* Elemental
3+
* Copyright (C) 2024, Evolved Binary Ltd
44
*
5-
* info@exist-db.org
6-
* http://www.exist-db.org
5+
* admin@evolvedbinary.com
6+
* https://www.evolvedbinary.com | https://www.elemental.xyz
77
*
88
* This library is free software; you can redistribute it and/or
99
* modify it under the terms of the GNU Lesser General Public
10-
* License as published by the Free Software Foundation; either
11-
* version 2.1 of the License, or (at your option) any later version.
10+
* License as published by the Free Software Foundation; version 2.1.
1211
*
1312
* This library is distributed in the hope that it will be useful,
1413
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -21,41 +20,52 @@
2120
*/
2221
package org.exist.xquery.functions.system;
2322

24-
import org.apache.logging.log4j.LogManager;
25-
import org.apache.logging.log4j.Logger;
26-
import org.exist.dom.QName;
2723
import org.exist.xquery.BasicFunction;
28-
import org.exist.xquery.Cardinality;
2924
import org.exist.xquery.FunctionSignature;
3025
import org.exist.xquery.XPathException;
3126
import org.exist.xquery.XQueryContext;
32-
import org.exist.xquery.value.FunctionReturnSequenceType;
3327
import org.exist.xquery.value.Sequence;
3428
import org.exist.xquery.value.StringValue;
3529
import org.exist.xquery.value.Type;
3630

37-
public class GetModuleLoadPath extends BasicFunction {
31+
import javax.annotation.Nullable;
32+
33+
import static org.exist.xquery.FunctionDSL.returns;
34+
import static org.exist.xquery.functions.system.SystemModule.functionSignature;
3835

39-
protected final static Logger logger = LogManager.getLogger(GetModuleLoadPath.class);
36+
public class GetModuleLoadPath extends BasicFunction {
4037

41-
public final static FunctionSignature signature =
42-
new FunctionSignature(
43-
new QName("get-module-load-path", SystemModule.NAMESPACE_URI, SystemModule.PREFIX),
44-
"Returns the module load path from the current query context. The module load path " +
45-
"corresponds to the location on the file system from where modules are loaded " +
46-
"into an XQuery. This is usually the directory from which the main XQuery was " +
47-
"compiled, or - when executing a stored XQuery - the collection in which the main " +
48-
"query resides. The module load path " +
49-
"is also used to resolve relative XInclude paths.",
50-
FunctionSignature.NO_ARGS,
51-
new FunctionReturnSequenceType(Type.STRING, Cardinality.EXACTLY_ONE, "the load path"));
38+
private static final String FS_GET_MODULE_LOAD_PATH_NAME = "get-module-load-path";
39+
public static final FunctionSignature FS_GET_MODULE_LOAD_PATH = functionSignature(
40+
FS_GET_MODULE_LOAD_PATH_NAME,
41+
"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.",
42+
returns(Type.STRING, "The path from which the module was loaded")
43+
);
5244

45+
private static final String FS_GET_MAIN_MODULE_LOAD_PATH_NAME = "get-main-module-load-path";
46+
public static final FunctionSignature FS_GET_MAIN_MODULE_LOAD_PATH = functionSignature(
47+
FS_GET_MAIN_MODULE_LOAD_PATH_NAME,
48+
"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.",
49+
returns(Type.STRING, "The path from which the main module was loaded")
50+
);
5351

54-
public GetModuleLoadPath(XQueryContext context) {
52+
public GetModuleLoadPath(final XQueryContext context, final FunctionSignature signature) {
5553
super(context, signature);
5654
}
5755

58-
public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathException {
59-
return new StringValue(this, context.getModuleLoadPath());
56+
@Override
57+
public Sequence eval(final Sequence[] args, final Sequence contextSequence) throws XPathException {
58+
final String moduleLoadPath;
59+
if (isCalledAs(FS_GET_MODULE_LOAD_PATH_NAME)) {
60+
moduleLoadPath = context.getModuleLoadPath();
61+
} else {
62+
@Nullable final XQueryContext rootContext = context.getRootContext();
63+
if (rootContext == null) {
64+
// there may not be a main module, e.g. RESTXQ resource functions
65+
throw new XPathException(this, "There is no root context as the library module was executed via a named function");
66+
}
67+
moduleLoadPath = rootContext.getModuleLoadPath();
68+
}
69+
return new StringValue(this, moduleLoadPath);
6070
}
6171
}

exist-core/src/main/java/org/exist/xquery/functions/system/SystemModule.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ public class SystemModule extends AbstractInternalModule {
8888
new FunctionDef(GetExistHome.signature, GetExistHome.class),
8989
new FunctionDef(Shutdown.signatures[0], Shutdown.class),
9090
new FunctionDef(Shutdown.signatures[1], Shutdown.class),
91-
new FunctionDef(GetModuleLoadPath.signature, GetModuleLoadPath.class),
91+
new FunctionDef(GetModuleLoadPath.FS_GET_MODULE_LOAD_PATH, GetModuleLoadPath.class),
92+
new FunctionDef(GetModuleLoadPath.FS_GET_MAIN_MODULE_LOAD_PATH, GetModuleLoadPath.class),
9293
new FunctionDef(TriggerSystemTask.signature, TriggerSystemTask.class),
9394
new FunctionDef(AsUser.FS_AS_USER, AsUser.class),
9495
new FunctionDef(AsUser.FS_FUNCTION_AS_USER, AsUser.class),
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
/*
2+
* Elemental
3+
* Copyright (C) 2024, Evolved Binary Ltd
4+
*
5+
* admin@evolvedbinary.com
6+
* https://www.evolvedbinary.com | https://www.elemental.xyz
7+
*
8+
* This library is free software; you can redistribute it and/or
9+
* modify it under the terms of the GNU Lesser General Public
10+
* License as published by the Free Software Foundation; version 2.1.
11+
*
12+
* This library is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
* Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public
18+
* License along with this library; if not, write to the Free Software
19+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20+
*/
21+
package org.exist.xquery.functions.system;
22+
23+
import com.evolvedbinary.j8fu.function.ConsumerE;
24+
import org.exist.EXistException;
25+
import org.exist.collections.Collection;
26+
import org.exist.security.PermissionDeniedException;
27+
import org.exist.source.DbUriSource;
28+
import org.exist.source.StringSource;
29+
import org.exist.storage.BrokerPool;
30+
import org.exist.storage.DBBroker;
31+
import org.exist.storage.txn.Txn;
32+
import org.exist.test.ExistEmbeddedServer;
33+
import org.exist.util.LockException;
34+
import org.exist.util.StringInputSource;
35+
import org.exist.xmldb.XmldbURI;
36+
import org.exist.xquery.XPathException;
37+
import org.exist.xquery.XQueryContext;
38+
import org.exist.xquery.XQueryUtil;
39+
import org.exist.xquery.value.Item;
40+
import org.exist.xquery.value.Type;
41+
import org.junit.BeforeClass;
42+
import org.junit.ClassRule;
43+
import org.junit.Test;
44+
import org.w3c.dom.Document;
45+
import org.xml.sax.SAXException;
46+
import org.xmlunit.builder.DiffBuilder;
47+
import org.xmlunit.builder.Input;
48+
import org.xmlunit.diff.Diff;
49+
50+
import javax.xml.transform.Source;
51+
import java.io.IOException;
52+
import java.util.Optional;
53+
54+
import static java.nio.charset.StandardCharsets.UTF_8;
55+
import static org.exist.test.Util.storeQuery;
56+
import static org.junit.Assert.assertEquals;
57+
import static org.junit.Assert.assertFalse;
58+
import static org.junit.Assert.assertNotNull;
59+
60+
public class GetMainModuleLoadPathTest {
61+
62+
private static XmldbURI TEST_COLLECTION_URI = XmldbURI.create("/db/get-main-module-load-path-test");
63+
private static XmldbURI TEST_SUB_COLLECTION_URI = TEST_COLLECTION_URI.append("sub1");
64+
65+
private static XmldbURI STANDALONE_MAIN_MODULE_URI = TEST_COLLECTION_URI.append("standalone-main.xq");
66+
private static String STANDALONE_MAIN_MODULE_XQ =
67+
"import module namespace system = \"http://exist-db.org/xquery/system\";\n" +
68+
"document {\n" +
69+
" <path>{system:get-main-module-load-path()}</path>\n" +
70+
"}";
71+
72+
private static XmldbURI IMPORTED_LIBRARY_MODULE_URI = TEST_SUB_COLLECTION_URI.append("imported-library.xqm");
73+
private static String IMPORTED_LIBRARY_MODULE_XQ =
74+
"module namespace ilm = \"http://ilm\";\n" +
75+
"import module namespace system = \"http://exist-db.org/xquery/system\";\n" +
76+
"declare function ilm:main-module-load-path() {\n" +
77+
" system:get-main-module-load-path()\n" +
78+
"};";
79+
80+
private static XmldbURI IMPORTING_MAIN_MODULE_URI = TEST_COLLECTION_URI.append("importing-main.xq");
81+
private static String IMPORTING_MAIN_MODULE_XQ =
82+
"import module namespace system = \"http://exist-db.org/xquery/system\";\n" +
83+
"import module namespace ilm = \"http://ilm\" at \"xmldb:exist://" + IMPORTED_LIBRARY_MODULE_URI.getXmldbURI().toString() + "\";\n" +
84+
"document {\n" +
85+
" <paths>\n" +
86+
" <path>{system:get-main-module-load-path()}</path>\n" +
87+
" <library-path>{ilm:main-module-load-path()}</library-path>\n" +
88+
" </paths>\n" +
89+
"}";
90+
91+
@ClassRule
92+
public static ExistEmbeddedServer EXIST_EMBEDDED_SERVER = new ExistEmbeddedServer(true, true);
93+
94+
@BeforeClass
95+
public static void setup() throws EXistException, PermissionDeniedException, IOException, SAXException, LockException {
96+
final BrokerPool brokerPool = EXIST_EMBEDDED_SERVER.getBrokerPool();
97+
try (final DBBroker broker = brokerPool.get(Optional.of(brokerPool.getSecurityManager().getSystemSubject()));
98+
final Txn transaction = brokerPool.getTransactionManager().beginTransaction()) {
99+
100+
// store xquery documents
101+
try (final Collection testCollection = broker.getOrCreateCollection(transaction, TEST_COLLECTION_URI)) {
102+
storeQuery(broker, transaction, new StringInputSource(STANDALONE_MAIN_MODULE_XQ.getBytes(UTF_8)), testCollection, STANDALONE_MAIN_MODULE_URI);
103+
104+
try (final Collection testSubCollection = broker.getOrCreateCollection(transaction, TEST_SUB_COLLECTION_URI)) {
105+
storeQuery(broker, transaction, new StringInputSource(IMPORTED_LIBRARY_MODULE_XQ.getBytes(UTF_8)), testSubCollection, IMPORTED_LIBRARY_MODULE_URI);
106+
}
107+
108+
storeQuery(broker, transaction, new StringInputSource(IMPORTING_MAIN_MODULE_XQ.getBytes(UTF_8)), testCollection, IMPORTING_MAIN_MODULE_URI);
109+
}
110+
111+
transaction.commit();
112+
}
113+
}
114+
115+
@Test
116+
public void standaloneMainModule() throws EXistException, PermissionDeniedException, XPathException, IOException {
117+
final String expected = "<path>.</path>";
118+
119+
final BrokerPool brokerPool = EXIST_EMBEDDED_SERVER.getBrokerPool();
120+
try (final DBBroker broker = brokerPool.get(Optional.of(brokerPool.getSecurityManager().getSystemSubject()));
121+
final Txn transaction = brokerPool.getTransactionManager().beginTransaction()) {
122+
123+
try (final XQueryUtil.QueryResult queryResult = XQueryUtil.query(broker, new StringSource(STANDALONE_MAIN_MODULE_XQ), false, null, null, null, null, null)) {
124+
assertNotNull(queryResult.result);
125+
assertEquals(1, queryResult.result.getItemCount());
126+
final Item item = queryResult.result.itemAt(0);
127+
assertEquals(Type.DOCUMENT, item.getType());
128+
129+
final Source expectedSrc = Input.fromString(expected).build();
130+
final Source actualSrc = Input.fromNode((Document) item).build();
131+
132+
final Diff diff = DiffBuilder.compare(expectedSrc)
133+
.withTest(actualSrc)
134+
.checkForSimilar()
135+
.build();
136+
137+
assertFalse(diff.toString(), diff.hasDifferences());
138+
}
139+
140+
transaction.commit();
141+
}
142+
}
143+
144+
@Test
145+
public void importingMainModule() throws EXistException, PermissionDeniedException, XPathException, IOException {
146+
final String expected =
147+
"<paths>" +
148+
"<path>.</path>" +
149+
"<library-path>.</library-path>" +
150+
"</paths>";
151+
152+
final BrokerPool brokerPool = EXIST_EMBEDDED_SERVER.getBrokerPool();
153+
try (final DBBroker broker = brokerPool.get(Optional.of(brokerPool.getSecurityManager().getSystemSubject()));
154+
final Txn transaction = brokerPool.getTransactionManager().beginTransaction()) {
155+
156+
try (final XQueryUtil.QueryResult queryResult = XQueryUtil.query(broker, new StringSource(IMPORTING_MAIN_MODULE_XQ), false, null, null, null, null, null)) {
157+
assertNotNull(queryResult.result);
158+
assertEquals(1, queryResult.result.getItemCount());
159+
final Item item = queryResult.result.itemAt(0);
160+
assertEquals(Type.DOCUMENT, item.getType());
161+
162+
final Source expectedSrc = Input.fromString(expected).build();
163+
final Source actualSrc = Input.fromNode((Document) item).build();
164+
165+
final Diff diff = DiffBuilder.compare(expectedSrc)
166+
.withTest(actualSrc)
167+
.checkForSimilar()
168+
.build();
169+
170+
assertFalse(diff.toString(), diff.hasDifferences());
171+
}
172+
173+
transaction.commit();
174+
}
175+
}
176+
177+
178+
@Test
179+
public void storedStandaloneMainModule() throws EXistException, PermissionDeniedException, XPathException, IOException {
180+
final String expected = "<path>" + TEST_COLLECTION_URI.getCollectionPath() + "</path>";
181+
182+
final BrokerPool brokerPool = EXIST_EMBEDDED_SERVER.getBrokerPool();
183+
try (final DBBroker broker = brokerPool.get(Optional.of(brokerPool.getSecurityManager().getSystemSubject()));
184+
final Txn transaction = brokerPool.getTransactionManager().beginTransaction()) {
185+
186+
final ConsumerE<XQueryContext, XPathException> preCompilationContextSetup = xqueryContext -> xqueryContext.setModuleLoadPath(STANDALONE_MAIN_MODULE_URI.removeLastSegment().getCollectionPath());
187+
188+
try (final XQueryUtil.QueryResult queryResult = XQueryUtil.query(broker, DbUriSource.from(brokerPool, STANDALONE_MAIN_MODULE_URI, false, false), false, null, null, preCompilationContextSetup, null, null)) {
189+
assertNotNull(queryResult.result);
190+
assertEquals(1, queryResult.result.getItemCount());
191+
final Item item = queryResult.result.itemAt(0);
192+
assertEquals(Type.DOCUMENT, item.getType());
193+
194+
final Source expectedSrc = Input.fromString(expected).build();
195+
final Source actualSrc = Input.fromNode((Document) item).build();
196+
197+
final Diff diff = DiffBuilder.compare(expectedSrc)
198+
.withTest(actualSrc)
199+
.checkForSimilar()
200+
.build();
201+
202+
assertFalse(diff.toString(), diff.hasDifferences());
203+
}
204+
205+
transaction.commit();
206+
}
207+
}
208+
209+
@Test
210+
public void storedImportingMainModule() throws EXistException, PermissionDeniedException, XPathException, IOException {
211+
final String expected =
212+
"<paths>" +
213+
"<path>" + TEST_COLLECTION_URI.getCollectionPath() + "</path>" +
214+
"<library-path>" + TEST_COLLECTION_URI.getCollectionPath() + "</library-path>" +
215+
"</paths>";
216+
217+
final BrokerPool brokerPool = EXIST_EMBEDDED_SERVER.getBrokerPool();
218+
try (final DBBroker broker = brokerPool.get(Optional.of(brokerPool.getSecurityManager().getSystemSubject()));
219+
final Txn transaction = brokerPool.getTransactionManager().beginTransaction()) {
220+
221+
final ConsumerE<XQueryContext, XPathException> preCompilationContextSetup = xqueryContext -> xqueryContext.setModuleLoadPath(IMPORTING_MAIN_MODULE_URI.removeLastSegment().getCollectionPath());
222+
223+
224+
try (final XQueryUtil.QueryResult queryResult = XQueryUtil.query(broker, DbUriSource.from(brokerPool, IMPORTING_MAIN_MODULE_URI, false, false), false, null, null, preCompilationContextSetup, null, null)) {
225+
assertNotNull(queryResult.result);
226+
assertEquals(1, queryResult.result.getItemCount());
227+
final Item item = queryResult.result.itemAt(0);
228+
assertEquals(Type.DOCUMENT, item.getType());
229+
230+
final Source expectedSrc = Input.fromString(expected).build();
231+
final Source actualSrc = Input.fromNode((Document) item).build();
232+
233+
final Diff diff = DiffBuilder.compare(expectedSrc)
234+
.withTest(actualSrc)
235+
.checkForSimilar()
236+
.build();
237+
238+
assertFalse(diff.toString(), diff.hasDifferences());
239+
}
240+
241+
transaction.commit();
242+
}
243+
}
244+
}

0 commit comments

Comments
 (0)