diff --git a/.idea/libraries/ant.xml b/.idea/libraries/ant.xml deleted file mode 100644 index f614c1b1066..00000000000 --- a/.idea/libraries/ant.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/extensions.xml b/.idea/libraries/extensions.xml index fbf504ce55a..b5beaf6428e 100644 --- a/.idea/libraries/extensions.xml +++ b/.idea/libraries/extensions.xml @@ -6,11 +6,13 @@ + + diff --git a/eXist-db.iml b/eXist-db.iml index 4665b0bb15c..475b9a704e5 100644 --- a/eXist-db.iml +++ b/eXist-db.iml @@ -67,7 +67,6 @@ - diff --git a/src/org/exist/http/urlrewrite/XQueryURLRewrite.java b/src/org/exist/http/urlrewrite/XQueryURLRewrite.java index dffcbd06950..18311d9b1e7 100644 --- a/src/org/exist/http/urlrewrite/XQueryURLRewrite.java +++ b/src/org/exist/http/urlrewrite/XQueryURLRewrite.java @@ -723,8 +723,11 @@ SourceInfo findSourceFromDb(final DBBroker broker, final String basePath, final DocumentImpl controllerDoc = null; try { final XmldbURI locationUri = XmldbURI.xmldbUriFor(basePath); - - controllerDoc = findDbControllerXql(broker, locationUri, components); + XmldbURI resourceUri = locationUri; + for(final String component : components) { + resourceUri = resourceUri.append(component); + } + controllerDoc = findDbControllerXql(broker, locationUri, resourceUri); if (controllerDoc == null) { LOG.warn("XQueryURLRewrite controller could not be found for path: " + path); @@ -757,32 +760,35 @@ SourceInfo findSourceFromDb(final DBBroker broker, final String basePath, final /** * Finds a `controller.xql` file within a Collection hierarchy. * Most specific collections are considered first. - *

+ * * For example, given the collectionUri `/db/apps` - * and the pathPomponents `['myapp', 'data']`, the + * and the resourceUri /db/apps/myapp/data, the * order or search will be: - *

- * /db/apps/myapp/data/collection.xconf - * /db/apps/myapp/collection.xconf - * /db/apps/collection.xconf + * + * /db/apps/myapp/data/controller.xql + * /db/apps/myapp/controller.xql + * /db/apps/controller.xql * * @param broker The database broker * @param collectionUri The root collection URI, below which we should not descend - * @param pathComponents The path within the collectionUri to the most specific Collection + * @param resourceUri The path to the most specific document or collection for which we should find a controller * @return The most relevant controller.xql document (with a READ_LOCK), or null if it could not be found. */ //@tailrec private @Nullable - DocumentImpl findDbControllerXql(final DBBroker broker, final XmldbURI collectionUri, final String[] pathComponents) { + DocumentImpl findDbControllerXql(final DBBroker broker, final XmldbURI collectionUri, final XmldbURI resourceUri) { + if (collectionUri.compareTo(resourceUri) > 0) { + return null; + } + Collection collection = null; try { - collection = broker.openCollection(collectionUri, LockMode.READ_LOCK); - if (collection == null) { - return null; - } - - if (pathComponents.length == 0 || !collection.hasChildCollection(broker, XmldbURI.createInternal(pathComponents[0]))) { - return collection.getDocumentWithLock(broker, XQUERY_CONTROLLER_URI, LockMode.READ_LOCK); + collection = broker.openCollection(resourceUri, LockMode.READ_LOCK); + if (collection != null) { + final DocumentImpl doc = collection.getDocumentWithLock(broker, XQUERY_CONTROLLER_URI, LockMode.READ_LOCK); + if (doc != null) { + return doc; + } } } catch (final PermissionDeniedException e) { if (LOG.isDebugEnabled()) { @@ -800,12 +806,12 @@ DocumentImpl findDbControllerXql(final DBBroker broker, final XmldbURI collectio } } - final XmldbURI subCollectionUri = collectionUri.append(pathComponents[0]); - final String[] subPathComponents = new String[pathComponents.length - 1]; - if (subPathComponents.length > 0) { - System.arraycopy(pathComponents, 1, subPathComponents, 0, subPathComponents.length); + if(resourceUri.numSegments() == 2) { + return null; } - return findDbControllerXql(broker, subCollectionUri, subPathComponents); + final XmldbURI subResourceUri = resourceUri.removeLastSegment(); + + return findDbControllerXql(broker, collectionUri, subResourceUri); } private SourceInfo findSourceFromFs(final String basePath, final String[] components) { diff --git a/src/org/exist/jetty/JettyStart.java b/src/org/exist/jetty/JettyStart.java index e29a4861f6b..e6c24551e1f 100644 --- a/src/org/exist/jetty/JettyStart.java +++ b/src/org/exist/jetty/JettyStart.java @@ -101,6 +101,10 @@ public JettyStart() { } public synchronized void run() { + run(true); + } + + public synchronized void run(final boolean standalone) { final String jettyProperty = Optional.ofNullable(System.getProperty(JETTY_HOME_PROP)) .orElseGet(() -> { final Optional home = ConfigurationHelper.getExistHome(); @@ -110,8 +114,13 @@ public synchronized void run() { return jettyPath; }); - final Path standaloneFile = Paths.get(jettyProperty).resolve("etc").resolve(Main.STANDALONE_ENABLED_JETTY_CONFIGS); - run(new String[] { standaloneFile.toAbsolutePath().toString() }, null); + final Path jettyConfig; + if (standalone) { + jettyConfig = Paths.get(jettyProperty).resolve("etc").resolve(Main.STANDALONE_ENABLED_JETTY_CONFIGS); + } else { + jettyConfig = Paths.get(jettyProperty).resolve("etc").resolve(Main.STANDARD_ENABLED_JETTY_CONFIGS); + } + run(new String[] { jettyConfig.toAbsolutePath().toString() }, null); } public synchronized void run(final String[] args, final Observer observer) { diff --git a/test/src/org/exist/http/urlrewrite/URLRewritingTest.java b/test/src/org/exist/http/urlrewrite/URLRewritingTest.java new file mode 100644 index 00000000000..4b61eaad65f --- /dev/null +++ b/test/src/org/exist/http/urlrewrite/URLRewritingTest.java @@ -0,0 +1,114 @@ +/* + * eXist Open Source Native XML Database + * Copyright (C) 2001-2018 The eXist Project + * http://exist-db.org + * + * This program 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 + * of the License, or (at your option) any later version. + * + * This program 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 St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package org.exist.http.urlrewrite; + +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.fluent.Executor; +import org.apache.http.client.fluent.Request; +import org.apache.http.entity.ContentType; +import org.exist.TestUtils; +import org.exist.test.ExistWebServer; +import org.exist.util.io.FastByteArrayOutputStream; +import org.exist.xmldb.XmldbURI; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; + +import java.io.IOException; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.exist.http.urlrewrite.XQueryURLRewrite.XQUERY_CONTROLLER_FILENAME; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class URLRewritingTest { + + private static final XmldbURI TEST_COLLECTION_NAME = XmldbURI.create("controller-test"); + private static final XmldbURI TEST_COLLECTION = XmldbURI.create("/db/apps").append(TEST_COLLECTION_NAME); + + private static final String TEST_CONTROLLER = "xquery version \"3.1\";\n{fn:current-dateTime()}"; + private static Executor executor = null; + + @ClassRule + public static final ExistWebServer existWebServer = new ExistWebServer(true, false, true, true, false); + + @Test + public void findsParentController() throws IOException { + final XmldbURI nestedCollectionName = XmldbURI.create("nested"); + final XmldbURI docName = XmldbURI.create("test.xml"); + final String testDocument = "world"; + + final String storeDocUri = getRestUri() + TEST_COLLECTION.append(nestedCollectionName).append(docName); + HttpResponse response = executor.execute(Request + .Put(storeDocUri) + .bodyString(testDocument, ContentType.APPLICATION_XML) + ).returnResponse(); + assertEquals(HttpStatus.SC_CREATED, response.getStatusLine().getStatusCode()); + + final String retrieveDocUri = getAppsUri() + "/" + TEST_COLLECTION_NAME.append(nestedCollectionName).append(docName); + response = executor.execute(Request + .Get(retrieveDocUri) + ).returnResponse(); + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + final String responseBody; + try (final FastByteArrayOutputStream baos = new FastByteArrayOutputStream((int)response.getEntity().getContentLength())) { + response.getEntity().writeTo(baos); + responseBody = baos.toString(UTF_8); + } + assertTrue(responseBody.matches(".+")); + } + + @BeforeClass + public static void setup() throws IOException { + executor = Executor.newInstance() + .auth(TestUtils.ADMIN_DB_USER, TestUtils.ADMIN_DB_PWD) + .authPreemptive("localhost"); + + final HttpResponse response = executor.execute(Request + .Put(getRestUri() + TEST_COLLECTION + "/" + XQUERY_CONTROLLER_FILENAME) + .bodyString(TEST_CONTROLLER, ContentType.create("application/xquery")) + ).returnResponse(); + assertEquals(HttpStatus.SC_CREATED, response.getStatusLine().getStatusCode()); + } + + @AfterClass + public static void cleanup() throws IOException { + + final HttpResponse response = executor.execute(Request + .Delete(getRestUri() + TEST_COLLECTION) + ).returnResponse(); + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + } + + private static String getServerUri() { + return "http://localhost:" + existWebServer.getPort() + "/exist"; + } + + private static String getRestUri() { + return getServerUri() + "/rest"; + } + + private static String getAppsUri() { + return getServerUri() + "/apps"; + } +} diff --git a/test/src/org/exist/test/ExistWebServer.java b/test/src/org/exist/test/ExistWebServer.java index 1c37febfbb5..2aed2273921 100644 --- a/test/src/org/exist/test/ExistWebServer.java +++ b/test/src/org/exist/test/ExistWebServer.java @@ -39,6 +39,7 @@ public class ExistWebServer extends ExternalResource { private final boolean disableAutoDeploy; private final boolean useTemporaryStorage; private Optional temporaryStorage = Optional.empty(); + private final boolean jettyStandaloneMode; public ExistWebServer() { this(false); @@ -57,10 +58,15 @@ public ExistWebServer(final boolean useRandomPort, final boolean cleanupDbOnShut } public ExistWebServer(final boolean useRandomPort, final boolean cleanupDbOnShutdown, final boolean disableAutoDeploy, final boolean useTemporaryStorage) { + this(useRandomPort, cleanupDbOnShutdown, disableAutoDeploy, useTemporaryStorage, true); + } + + public ExistWebServer(final boolean useRandomPort, final boolean cleanupDbOnShutdown, final boolean disableAutoDeploy, final boolean useTemporaryStorage, final boolean jettyStandaloneMode) { this.useRandomPort = useRandomPort; this.cleanupDbOnShutdown = cleanupDbOnShutdown; this.disableAutoDeploy = disableAutoDeploy; this.useTemporaryStorage = useTemporaryStorage; + this.jettyStandaloneMode = jettyStandaloneMode; } public final int getPort() { @@ -94,7 +100,7 @@ protected void before() throws Throwable { System.setProperty(PROP_JETTY_SSL_PORT, Integer.toString(nextFreePort(MIN_RANDOM_PORT, MAX_RANDOM_PORT))); server = new JettyStart(); - server.run(); + server.run(jettyStandaloneMode); } } else { server = new JettyStart();