/* * eXist Open Source Native XML Database * Copyright (C) 2012 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 * * $Id$ */ package org.exist.xquery.functions.xmldb; import java.net.URISyntaxException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.exist.dom.persistent.DocumentImpl; import org.exist.dom.QName; import org.exist.security.PermissionDeniedException; import org.exist.storage.BrokerPool; import org.exist.storage.DBBroker; import org.exist.storage.lock.Lock.LockMode; import org.exist.storage.txn.Txn; import org.exist.util.MimeTable; import org.exist.util.MimeType; import org.exist.xmldb.XmldbURI; 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.AnyURIValue; import org.exist.xquery.value.FunctionParameterSequenceType; import org.exist.xquery.value.Sequence; import org.exist.xquery.value.SequenceType; import org.exist.xquery.value.Type; /** * @author Dannes Wessels (dannes@exist-db.org) */ public class XMLDBSetMimeType extends BasicFunction { protected static final Logger logger = LogManager.getLogger(XMLDBSetMimeType.class); public final static FunctionSignature signature = new FunctionSignature( new QName("set-mime-type", XMLDBModule.NAMESPACE_URI, XMLDBModule.PREFIX), "Set the MIME type of the resource $resource-uri." + XMLDBModule.ANY_URI, new SequenceType[] { new FunctionParameterSequenceType("resource-uri", Type.ANY_URI, Cardinality.EXACTLY_ONE, "The resource URI"), new FunctionParameterSequenceType("mime-type", Type.STRING, Cardinality.ZERO_OR_ONE, "The new mime-type, use empty sequence to set default value.") }, new SequenceType(Type.EMPTY, Cardinality.EMPTY) ); public XMLDBSetMimeType(XQueryContext context, FunctionSignature signature) { super(context, signature); } @Override public Sequence eval(Sequence args[], Sequence contextSequence) throws XPathException { // Get handle to Mime-type info final MimeTable mimeTable = MimeTable.getInstance(); // Get first parameter final String pathParameter = new AnyURIValue(args[0].itemAt(0).getStringValue()).toString(); if (pathParameter.matches("^[a-z]+://.*")) { throw new XPathException("Can not set mime-type for resources outside the database."); } XmldbURI pathUri = null; try { pathUri = XmldbURI.xmldbUriFor(pathParameter); } catch (final URISyntaxException ex) { logger.debug(ex.getMessage()); throw new XPathException("Invalid path '" + pathParameter + "'"); } // Verify mime-type input MimeType newMimeType = null; if (args[1].isEmpty()) { // No input, use default mimetype newMimeType = mimeTable.getContentTypeFor(pathParameter); if (newMimeType == null) { throw new XPathException("Unable to determine mimetype for '" + pathParameter + "'"); } } else { // Mimetype is provided, check if valid newMimeType = mimeTable.getContentType(args[1].getStringValue()); if (newMimeType == null) { throw new XPathException("mime-type '" + args[1].getStringValue() + "' is not supported."); } } // Get mime-type of resource MimeType currentMimeType = getMimeTypeStoredResource(pathUri); if (currentMimeType == null) { // stored resource has no mime-type (unexpected situation) // fall back to document name logger.debug("Resource '" + pathUri + "' has no mime-type, retrieve from document name."); currentMimeType = mimeTable.getContentTypeFor(pathUri); // if extension based lookup still fails if (currentMimeType == null) { throw new XPathException("Unable to determine mime-type from path '" + pathUri + "'."); } } // Check if mimeType are equivalent // in some cases value null is set, then allow to set to new value (repair action) if (newMimeType.isXMLType() != currentMimeType.isXMLType() ) { throw new XPathException("New mime-type must be a " + currentMimeType.getXMLDBType() + " mime-type"); } // At this moment it is possible to update the mimetype final DBBroker broker = context.getBroker(); final BrokerPool brokerPool = broker.getBrokerPool(); DocumentImpl doc = null; try(final Txn txn = brokerPool.getTransactionManager().beginTransaction()) { // relative collection Path: add the current base URI pathUri = context.getBaseURI().toXmldbURI().resolveCollectionPath(pathUri); // try to open the document and acquire a lock doc = (DocumentImpl) broker.getXMLResource(pathUri, LockMode.WRITE_LOCK); if (doc == null) { // no document selected, abort txn.abort(); } else { // set new mime-type doc.getMetadata().setMimeType(newMimeType.getName()); // store meta data into database broker.storeMetadata(txn, doc); // commit changes txn.commit(); } } catch (final Exception e) { logger.error(e.getMessage()); throw new XPathException(this, e); } finally { //release all locks if (doc != null) { doc.getUpdateLock().release(LockMode.WRITE_LOCK); } } return Sequence.EMPTY_SEQUENCE; } /** * Determine mimetype of currently stored resource. Copied from * get-mime-type. */ private MimeType getMimeTypeStoredResource(XmldbURI pathUri) throws XPathException { MimeType returnValue = null; DocumentImpl doc = null; try { // relative collection Path: add the current base URI pathUri = context.getBaseURI().toXmldbURI().resolveCollectionPath(pathUri); } catch (final XPathException ex) { logger.debug("Unable to convert path " + pathUri); return returnValue; } try { // try to open the document and acquire a lock doc = (DocumentImpl) context.getBroker().getXMLResource(pathUri, LockMode.READ_LOCK); if (doc == null) { throw new XPathException("Resource '" + pathUri + "' does not exist."); } else { final String mimetype = ((DocumentImpl) doc).getMetadata().getMimeType(); returnValue = MimeTable.getInstance().getContentType(mimetype); } } catch (final PermissionDeniedException ex) { logger.debug(ex.getMessage()); } finally { if (doc != null) { doc.getUpdateLock().release(LockMode.READ_LOCK); } } return returnValue; } }