From 23c5a865f25ae3923f2fc153cf2703d7d750a75b Mon Sep 17 00:00:00 2001 From: Otmar Humbel Date: Fri, 22 Sep 2023 16:19:12 +0200 Subject: [PATCH] [feature] Provide mime type on store XMLResource The mime type that is available on the remote resource was not provided when calling the parse() procedure on the backend and has now been added. When calculating the mime type being stored the following order will be used: - mime type as given by the method call - mime type caclulated based on the file name extension - default mime type 'application/octet-stream' is used Co-authored-by: Otmar Humbel Signed-off-by: Patrick Reinhart --- .../org/exist/xmldb/RemoteCollection.java | 1 + .../main/java/org/exist/xmlrpc/RpcAPI.java | 11 +- .../java/org/exist/xmlrpc/RpcConnection.java | 28 +++-- .../java/org/exist/xmlrpc/MimeTypeTest.java | 101 ++++++++++++++++++ 4 files changed, 133 insertions(+), 8 deletions(-) create mode 100644 exist-core/src/test/java/org/exist/xmlrpc/MimeTypeTest.java diff --git a/exist-core/src/main/java/org/exist/xmldb/RemoteCollection.java b/exist-core/src/main/java/org/exist/xmldb/RemoteCollection.java index ba0f47cf9c3..f2a871297f6 100644 --- a/exist-core/src/main/java/org/exist/xmldb/RemoteCollection.java +++ b/exist-core/src/main/java/org/exist/xmldb/RemoteCollection.java @@ -519,6 +519,7 @@ private void store(final RemoteXMLResource res) throws XMLDBException { } catch (final URISyntaxException e) { throw new XMLDBException(ErrorCodes.INVALID_URI, e); } + params.add(res.getMimeType()); params.add(1); if (res.getCreationTime() != null) { params.add(res.getCreationTime()); diff --git a/exist-core/src/main/java/org/exist/xmlrpc/RpcAPI.java b/exist-core/src/main/java/org/exist/xmlrpc/RpcAPI.java index c486274f3f0..c6fe9fd6444 100644 --- a/exist-core/src/main/java/org/exist/xmlrpc/RpcAPI.java +++ b/exist-core/src/main/java/org/exist/xmlrpc/RpcAPI.java @@ -545,18 +545,27 @@ boolean parse(byte[] xmlData, String docName) * * @param xmlData The document data * @param docName The path where the document will be stored + * @param mimeType the mimeType to check for * @param overwrite Overwrite an existing document with the same path? + * @param created Specifies the creattion date + * @param modified Specifies the last modification date * @return true, if the document is valid XML * @throws EXistException If an internal error occurs * @throws PermissionDeniedException If the current user is not allowed to perform this action * @throws URISyntaxException If the URI contains syntax errors */ - boolean parse(byte[] xmlData, String docName, int overwrite) + boolean parse(byte[] xmlData, String docName, String mimeType, int overwrite, Date created, Date modified) throws EXistException, PermissionDeniedException, URISyntaxException; boolean parse(byte[] xmlData, String docName, int overwrite, Date created, Date modified) throws EXistException, PermissionDeniedException, URISyntaxException; + boolean parse(byte[] xmlData, String docName, String mimeType, int overwrite) + throws EXistException, PermissionDeniedException, URISyntaxException; + + boolean parse(byte[] xmlData, String docName, int overwrite) + throws EXistException, PermissionDeniedException, URISyntaxException; + boolean parse(String xml, String docName, int overwrite) throws EXistException, PermissionDeniedException, URISyntaxException; diff --git a/exist-core/src/main/java/org/exist/xmlrpc/RpcConnection.java b/exist-core/src/main/java/org/exist/xmlrpc/RpcConnection.java index 6e3b5b45c9a..e12993f1da3 100644 --- a/exist-core/src/main/java/org/exist/xmlrpc/RpcConnection.java +++ b/exist-core/src/main/java/org/exist/xmlrpc/RpcConnection.java @@ -1336,18 +1336,24 @@ private boolean hasCollection(final XmldbURI collUri) throws EXistException, Per @Override public boolean parse(byte[] xml, String documentPath, int overwrite) throws URISyntaxException, EXistException, PermissionDeniedException { - return parse(xml, documentPath, overwrite, null, null); + return parse(xml, documentPath, null, overwrite, null, null); + } + + @Override + public boolean parse(final byte[] xml, final String documentPath, final String mimeType, final int overwrite) throws URISyntaxException, EXistException, PermissionDeniedException { + return parse(xml, documentPath, mimeType, overwrite, null, null); } @Override public boolean parse(final byte[] xml, final String documentPath, final int overwrite, final Date created, final Date modified) throws URISyntaxException, EXistException, PermissionDeniedException { - return parse(xml, XmldbURI.xmldbUriFor(documentPath), overwrite, created, modified); + return parse(xml, documentPath, null, overwrite, created, modified); } - private boolean parse(final byte[] xml, final XmldbURI docUri, - final int overwrite, @Nullable final Date created, @Nullable final Date modified) throws EXistException, PermissionDeniedException { - + @Override + public boolean parse(final byte[] xml, final String documentPath, @Nullable String mimeType, + final int overwrite, @Nullable final Date created, @Nullable final Date modified) throws URISyntaxException, EXistException, PermissionDeniedException { + final XmldbURI docUri = XmldbURI.xmldbUriFor(documentPath); return this.writeCollection(docUri.removeLastSegment()).apply((collection, broker, transaction) -> { try(final ManagedDocumentLock lockedDocument = broker.getBrokerPool().getLockManager().acquireDocumentWriteLock(docUri)) { @@ -1366,7 +1372,7 @@ private boolean parse(final byte[] xml, final XmldbURI docUri, final long startTime = System.currentTimeMillis(); - final MimeType mime = MimeTable.getInstance().getContentTypeFor(docUri.lastSegment()); + final MimeType mime = lookupMimeType(mimeType, docUri.lastSegment()); broker.storeDocument(transaction, docUri.lastSegment(), source, mime, created, modified, null, null, null, collection); // NOTE: early release of Collection lock inline with Asymmetrical Locking scheme @@ -1379,6 +1385,14 @@ private boolean parse(final byte[] xml, final XmldbURI docUri, }); } + private MimeType lookupMimeType(@Nullable final String mimeType, final XmldbURI fileName) { + final MimeTable mimeTable = MimeTable.getInstance(); + if (mimeType == null) { + return Optional.ofNullable(mimeTable.getContentTypeFor(fileName)).orElse(MimeType.BINARY_TYPE); + } + return Optional.ofNullable(mimeTable.getContentType(mimeType)).orElse(MimeType.BINARY_TYPE); + } + /** * Parse a file previously uploaded with upload. * @@ -1492,7 +1506,7 @@ private boolean parseLocal(final String localFile, final XmldbURI docUri, final // parse the source try (final FileInputSource source = sourceSupplier.get()) { - final MimeType mime = Optional.ofNullable(MimeTable.getInstance().getContentType(mimeType)).orElse(MimeType.BINARY_TYPE); + final MimeType mime = lookupMimeType(mimeType, docUri.lastSegment()); broker.storeDocument(transaction, docUri.lastSegment(), source, mime, created, modified, null, null, null, collection); diff --git a/exist-core/src/test/java/org/exist/xmlrpc/MimeTypeTest.java b/exist-core/src/test/java/org/exist/xmlrpc/MimeTypeTest.java new file mode 100644 index 00000000000..53379338168 --- /dev/null +++ b/exist-core/src/test/java/org/exist/xmlrpc/MimeTypeTest.java @@ -0,0 +1,101 @@ +/* + * 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.xmlrpc; + +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.junit.Assert.assertEquals; + +import org.exist.TestUtils; +import org.exist.test.ExistWebServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.xml.sax.SAXException; +import org.xmldb.api.DatabaseManager; +import org.xmldb.api.base.Collection; +import org.xmldb.api.base.Database; +import org.xmldb.api.base.Resource; +import org.xmldb.api.base.ResourceType; +import org.xmldb.api.base.XMLDBException; +import org.xmldb.api.modules.CollectionManagementService; +import org.xmldb.api.modules.XMLResource; + +public class MimeTypeTest { + + @ClassRule + public final static ExistWebServer existWebServer = new ExistWebServer(true, false, true, true); + + private final static String COLLECTION_NAME = "rpctest"; + private static final String DOCUMENT_NAME = "myxmldoc"; + private final static String XML_CONTENT = """ + + + """; + + private static String getBaseUri() { + return "xmldb:exist://localhost:" + existWebServer.getPort() + "/xmlrpc"; + } + + @Test + public void testXMLMimeType() throws XMLDBException { + // store an XML document without an .xml extension + try(Collection collection = DatabaseManager.getCollection(getBaseUri() + "/db/" + COLLECTION_NAME, TestUtils.ADMIN_DB_USER, TestUtils.ADMIN_DB_PWD)){ + final Class xmlResourceType = XMLResource.class; + final XMLResource resource = (XMLResource)collection.createResource(DOCUMENT_NAME, xmlResourceType); + resource.setContent(XML_CONTENT); + collection.storeResource(resource); + assertEquals(ResourceType.XML_RESOURCE, resource.getResourceType()); + } + + // retrieve the document and verify its resource type + try(Collection collection = DatabaseManager.getCollection(getBaseUri() + "/db/" + COLLECTION_NAME, TestUtils.ADMIN_DB_USER, TestUtils.ADMIN_DB_PWD)){ + Resource resource = collection.getResource(DOCUMENT_NAME); + assertEquals(ResourceType.XML_RESOURCE, resource.getResourceType()); + } + } + + @BeforeClass + public static void startServer() throws ClassNotFoundException, IllegalAccessException, InstantiationException, XMLDBException, SAXException { + // initialize XML:DB driver + Class cl = Class.forName("org.exist.xmldb.DatabaseImpl"); + Database database = (Database) cl.newInstance(); + DatabaseManager.registerDatabase(database); + + Collection root = DatabaseManager.getCollection(getBaseUri() + "/db", TestUtils.ADMIN_DB_USER, TestUtils.ADMIN_DB_PWD); + + CollectionManagementService mgmt = root.getService(CollectionManagementService.class); + assertThatNoException().isThrownBy(() -> mgmt.createCollection(COLLECTION_NAME)); + } + + @AfterClass + public static void stopServer() throws XMLDBException { + Collection root = DatabaseManager.getCollection(getBaseUri() + "/db", TestUtils.ADMIN_DB_USER, TestUtils.ADMIN_DB_PWD); + CollectionManagementService mgmt = + root.getService(CollectionManagementService.class); + mgmt.removeCollection(COLLECTION_NAME); + + Collection config = DatabaseManager.getCollection(getBaseUri() + "/db/system/config/db", "admin", ""); + mgmt = config.getService(CollectionManagementService.class); + mgmt.removeCollection(COLLECTION_NAME); + } +}