diff --git a/exist-core/src/main/java/org/exist/source/SourceFactory.java b/exist-core/src/main/java/org/exist/source/SourceFactory.java
index 378710be7cf..1efc60ad01a 100644
--- a/exist-core/src/main/java/org/exist/source/SourceFactory.java
+++ b/exist-core/src/main/java/org/exist/source/SourceFactory.java
@@ -111,7 +111,7 @@ public class SourceFactory {
&& ((location.startsWith("/db") && !Files.exists(Paths.get(firstPathSegment(location))))
|| (contextPath != null && contextPath.startsWith("/db") && !Files.exists(Paths.get(firstPathSegment(contextPath)))))) {
final XmldbURI pathUri;
- if (contextPath == null) {
+ if (contextPath == null || ".".equals(contextPath)) {
pathUri = XmldbURI.create(location);
} else {
pathUri = XmldbURI.create(contextPath).append(location);
diff --git a/exist-core/src/main/java/org/exist/test/XQueryCompilationTest.java b/exist-core/src/main/java/org/exist/test/XQueryCompilationTest.java
index 637670a2123..b4505830646 100644
--- a/exist-core/src/main/java/org/exist/test/XQueryCompilationTest.java
+++ b/exist-core/src/main/java/org/exist/test/XQueryCompilationTest.java
@@ -33,17 +33,22 @@
import org.exist.xquery.value.Sequence;
import org.junit.ClassRule;
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
import static com.evolvedbinary.j8fu.Either.Left;
import static com.evolvedbinary.j8fu.Either.Right;
+import static com.ibm.icu.impl.Assert.fail;
/**
* Base class for test suites testing XQuery compilation
* @author Juri Leino
*/
public abstract class XQueryCompilationTest {
- @ClassRule
- public static final ExistEmbeddedServer server = new ExistEmbeddedServer(true, true);
+ @ClassRule
+ public static ExistEmbeddedServer server = new ExistEmbeddedServer(true, true);
protected static Either compileQuery(final String string) throws EXistException, PermissionDeniedException {
final BrokerPool pool = server.getBrokerPool();
final XQuery xqueryService = pool.getXQueryService();
diff --git a/exist-core/src/main/java/org/exist/xquery/XQueryContext.java b/exist-core/src/main/java/org/exist/xquery/XQueryContext.java
index 8bac2341af5..b3721c34179 100644
--- a/exist-core/src/main/java/org/exist/xquery/XQueryContext.java
+++ b/exist-core/src/main/java/org/exist/xquery/XQueryContext.java
@@ -587,33 +587,34 @@ public Optional getRepository() {
return null;
}
- // use the resolved file or return null
- if (resolved != null) {
-
- String location = "";
-
- try {
-
- // see if the src exists in the database and if so, use that instead
- Source src = repo.get().resolveStoredXQueryModuleFromDb(getBroker(), resolved);
- if (src != null) {
- // NOTE(AR) set the location of the module to import relative to this module's load path - so that transient imports of the imported module will resolve correctly!
- location = Paths.get(XmldbURI.create(moduleLoadPath).getCollectionPath()).relativize(Paths.get(((DBSource)src).getDocumentPath().getCollectionPath())).toString();
+ // use the resolved file
+ String location = "";
+ try {
+ // see if the src exists in the database and if so, use that instead
+ Source src = repo.get().resolveStoredXQueryModuleFromDb(getBroker(), resolved);
+ if (src == null) {
+ // fallback to load the source from the filesystem
+ src = new FileSource(resolved, false);
+ } else {
+ final String sourceCollection = ((DBSource)src).getDocumentPath().getCollectionPath();
+ if (".".equals(moduleLoadPath)) {
+ // module is a string passed to the xquery context, has therefore no location of its own
+ location = sourceCollection;
} else {
- // else, fallback to the one from the filesystem
- src = new FileSource(resolved, false);
+ // NOTE(AR) set the location of the module to import relative to this module's load path
+ // - so that transient imports of the imported module will resolve correctly!
+ final Path collectionPath = Paths.get(XmldbURI.create(moduleLoadPath).getCollectionPath());
+ final Path sourcePath = Paths.get(sourceCollection);
+ location = collectionPath.relativize(sourcePath).toString();
}
+ }
- // build a module object from the source
- final ExternalModule module = compileOrBorrowModule(namespace, prefix, location, src);
- return module;
+ // build a module object from the source
+ return compileOrBorrowModule(namespace, prefix, location, src);
- } catch (final PermissionDeniedException e) {
- throw new XPathException(e.getMessage(), e);
- }
+ } catch (final PermissionDeniedException | IllegalArgumentException e) {
+ throw new XPathException(e.getMessage(), e);
}
-
- return null;
}
/**
diff --git a/exist-core/src/test/java/org/exist/xquery/AbsolutePathTests.java b/exist-core/src/test/java/org/exist/xquery/AbsolutePathTests.java
index d551d2582c6..6e6d85849a0 100644
--- a/exist-core/src/test/java/org/exist/xquery/AbsolutePathTests.java
+++ b/exist-core/src/test/java/org/exist/xquery/AbsolutePathTests.java
@@ -27,6 +27,7 @@
import org.exist.test.XQueryCompilationTest;
import org.exist.xquery.value.IntegerValue;
import org.exist.xquery.value.Sequence;
+import org.exist.xquery.value.StringValue;
import org.junit.Test;
import javax.xml.transform.Source;
@@ -133,4 +134,5 @@ public void topLevelAbsolutePath() throws EXistException, PermissionDeniedExcept
assertThatXQResult(actual, equalTo(expected));
}
+
}
diff --git a/exist-core/src/test/java/org/exist/xquery/ModuleImportTest.java b/exist-core/src/test/java/org/exist/xquery/ModuleImportTest.java
new file mode 100644
index 00000000000..9095e79e2db
--- /dev/null
+++ b/exist-core/src/test/java/org/exist/xquery/ModuleImportTest.java
@@ -0,0 +1,162 @@
+/*
+ * eXist-db Open Source Native XML Database
+ * Copyright (C) 2001 The eXist-db Authors
+ *
+ * info@exist-db.org
+ * 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.xquery;
+
+import com.evolvedbinary.j8fu.Either;
+import org.exist.EXistException;
+import org.exist.security.PermissionDeniedException;
+import org.exist.storage.BrokerPool;
+import org.exist.storage.DBBroker;
+import org.exist.test.ExistEmbeddedServer;
+import org.exist.test.ExistXmldbEmbeddedServer;
+import org.exist.test.XQueryCompilationTest;
+import org.exist.xquery.value.Sequence;
+import org.exist.xquery.value.StringValue;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import static com.evolvedbinary.j8fu.Either.Left;
+import static com.evolvedbinary.j8fu.Either.Right;
+import static com.ibm.icu.impl.Assert.fail;
+import static org.exist.test.XQueryAssertions.assertThatXQResult;
+import static org.exist.test.XQueryAssertions.assertXQStaticError;
+import static org.hamcrest.Matchers.equalTo;
+
+/**
+ * Ensure library module imports work in one-off queries
+ * needs functx to be installed => conf.xml => triggers => autodeploy
+ *
+ * @author Juri Leino
+ */
+public class ModuleImportTest {
+ protected static Either compileQuery(final String string) throws EXistException, PermissionDeniedException {
+ final BrokerPool pool = server.getBrokerPool();
+ final XQuery xqueryService = pool.getXQueryService();
+ try (final DBBroker broker = pool.getBroker()) {
+ try {
+ return Right(xqueryService.compile(new XQueryContext(broker.getDatabase()), string));
+ } catch (final XPathException e) {
+ return Left(e);
+ }
+ }
+ }
+
+ protected static Either executeQuery(final String string) throws EXistException, PermissionDeniedException {
+ final BrokerPool pool = server.getBrokerPool();
+ final XQuery xqueryService = pool.getXQueryService();
+ try (final DBBroker broker = pool.getBroker()) {
+ try {
+ return Right(xqueryService.execute(broker, string, null));
+ } catch (final XPathException e) {
+ return Left(e);
+ }
+ }
+ }
+
+ private static Path getConfigFile() {
+ final ClassLoader loader = ModuleImportTest.class.getClassLoader();
+ final char separator = System.getProperty("file.separator").charAt(0);
+ final String packagePath = ModuleImportTest.class.getPackage().getName().replace('.', separator);
+
+ try {
+ return Paths.get(loader.getResource(packagePath + separator + "conf.xml").toURI());
+ } catch (final URISyntaxException e) {
+ fail(e);
+ return null;
+ }
+ }
+
+ @ClassRule
+ public static ExistEmbeddedServer server = new ExistEmbeddedServer(null, getConfigFile(), null, false, true);
+
+ @Test
+ public void importLibraryWithoutLocation() throws EXistException, PermissionDeniedException {
+ final Sequence expected = new StringValue("xs:integer");
+
+ final String query = "import module namespace functx='http://www.functx.com';" +
+ "functx:atomic-type(4)";
+ final Either actual = executeQuery(query);
+
+ assertThatXQResult(actual, equalTo(expected));
+ }
+ @Test
+ public void importLibraryFromDbLocation() throws EXistException, PermissionDeniedException {
+ final Sequence expected = new StringValue("xs:integer");
+
+ final String query = "import module namespace functx='http://www.functx.com'" +
+ " at '/db/system/repo/functx-1.0.1/functx/functx.xq';" +
+ "functx:atomic-type(4)";
+ final Either actual = executeQuery(query);
+
+ assertThatXQResult(actual, equalTo(expected));
+ }
+
+ @Test
+ public void importLibraryFromXMLDBLocation() throws EXistException, PermissionDeniedException {
+ final Sequence expected = new StringValue("xs:integer");
+
+ final String query = "import module namespace functx='http://www.functx.com'" +
+ " at 'xmldb:///db/system/repo/functx-1.0.1/functx/functx.xq';" +
+ "functx:atomic-type(4)";
+ final Either actual = executeQuery(query);
+
+ assertThatXQResult(actual, equalTo(expected));
+ }
+
+ @Test
+ public void importLibraryFromExistXMLDBLocation() throws EXistException, PermissionDeniedException {
+ final Sequence expected = new StringValue("xs:integer");
+
+ final String query = "import module namespace functx='http://www.functx.com'" +
+ " at 'xmldb:exist:///db/system/repo/functx-1.0.1/functx/functx.xq';" +
+ "functx:atomic-type(4)";
+ final Either actual = executeQuery(query);
+
+ assertThatXQResult(actual, equalTo(expected));
+ }
+
+ @Test
+ public void importLibraryFromUnknownLocation() throws EXistException, PermissionDeniedException {
+
+ final String query = "import module namespace functx='http://www.functx.com'" +
+ " at 'unknown:///db/system/repo/functx-1.0.1/functx/functx.xq';" +
+ "functx:atomic-type(4)";
+ final String expectedMessage = "error found while loading module functx: Source for module 'http://www.functx.com' not found module location hint URI 'unknown:///db/system/repo/functx-1.0.1/functx/functx.xq'.";
+
+ assertXQStaticError(ErrorCodes.XQST0059, -1,-1, expectedMessage, compileQuery(query));
+ }
+
+ @Test
+ public void importLibraryFromRelativeLocation() throws EXistException, PermissionDeniedException {
+ final String query = "import module namespace functx='http://www.functx.com'" +
+ " at './functx.xq';" +
+ "functx:atomic-type(4)";
+ final String expectedMessage = "error found while loading module functx: Source for module 'http://www.functx.com' not found module location hint URI './functx.xq'.";
+
+ assertXQStaticError(ErrorCodes.XQST0059, -1,-1, expectedMessage, compileQuery(query));
+ }
+
+}
diff --git a/exist-core/src/test/resources-filtered/conf.xml b/exist-core/src/test/resources-filtered/conf.xml
index 9a76f8c79a5..f93577a35d6 100644
--- a/exist-core/src/test/resources-filtered/conf.xml
+++ b/exist-core/src/test/resources-filtered/conf.xml
@@ -231,6 +231,15 @@
+
+
+
+
+
@@ -768,7 +777,7 @@
Set the parameter to "yes" to enable this feature.
-->
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+