Package org.exist.webdav

Source Code of org.exist.webdav.ExistDocument

/*
*  eXist Open Source Native XML Database
*  Copyright (C) 2010 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.webdav;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URISyntaxException;

import org.exist.EXistException;
import org.exist.collections.Collection;
import org.exist.collections.triggers.TriggerException;
import org.exist.dom.BinaryDocument;
import org.exist.dom.DocumentImpl;
import org.exist.dom.LockToken;
import org.exist.security.Account;
import org.exist.security.Permission;
import org.exist.security.PermissionDeniedException;
import org.exist.storage.BrokerPool;
import org.exist.storage.DBBroker;
import org.exist.storage.lock.Lock;
import org.exist.storage.serializers.Serializer;
import org.exist.storage.txn.TransactionManager;
import org.exist.storage.txn.Txn;
import org.exist.util.LockException;
import org.exist.util.VirtualTempFile;
import org.exist.webdav.exceptions.DocumentAlreadyLockedException;
import org.exist.webdav.exceptions.DocumentNotLockedException;
import org.exist.xmldb.XmldbURI;

import org.xml.sax.SAXException;

/**
* Class for accessing the Collection class of the exist-db native API.
*
* @author Dannes Wessels (dizzzz_at_exist-db.org)
*/
public class ExistDocument extends ExistResource {

    /**
     *  Constructor.
     *
     * @param uri   URI of document
     * @param pool  Reference to brokerpool
     */
    public ExistDocument(XmldbURI uri, BrokerPool pool) {

        if (LOG.isTraceEnabled()) {
            LOG.trace(String.format("New document object for %s", uri));
        }

        brokerPool = pool;
        this.xmldbUri = uri;
    }

    /**
     * Initialize Collection, authenticate() is required first
     */
    @Override
    public void initMetadata() {

        if (subject == null) {
            LOG.error("User not initialized yet");
            return;
        }

        // check if initialization is required
        if (isInitialized) {
            LOG.debug("Already initialized");
            return;
        }

        DBBroker broker = null;
        DocumentImpl document = null;
        try {
            broker = brokerPool.get(subject);

            // If it is not a collection, check if it is a document
            document = broker.getXMLResource(xmldbUri, Lock.READ_LOCK);

            if (document.getResourceType() == DocumentImpl.XML_FILE) {
                isXmlDocument = true;
            }

            // Get meta data        
            creationTime = document.getMetadata().getCreated();
            lastModified = document.getMetadata().getLastModified();
            mimeType = document.getMetadata().getMimeType();

            // Retrieve perssions
            permissions = document.getPermissions();
            readAllowed = permissions.validate(subject, Permission.READ);
            writeAllowed = permissions.validate(subject, Permission.WRITE);
            executeAllowed = permissions.validate(subject, Permission.EXECUTE);


            ownerUser = permissions.getOwner().getUsername();
            ownerGroup = permissions.getGroup().getName();

            // Get (estimated) file size
            contentLength = document.getContentLength();

        } catch (EXistException | PermissionDeniedException e) {
            LOG.error(e);

        } finally {

            // Cleanup resources
            if (document != null) {
                document.getUpdateLock().release(Lock.READ_LOCK);
            }

            if(broker != null) {
                brokerPool.release(broker);
            }

            isInitialized = true;
        }
    }
    private String mimeType;

    public String getMimeType() {
        return mimeType;
    }
   
    private long contentLength = 0;

    public long getContentLength() {
        return contentLength;
    }
   
    private boolean isXmlDocument = false;

    public boolean isXmlDocument() {
        return isXmlDocument;
    }

    /**
     * Stream document to framework.
     */
    public void stream(OutputStream os) throws IOException, PermissionDeniedException {

        if (LOG.isDebugEnabled()) {
            LOG.debug("Stream started");
        }

        long startTime = System.currentTimeMillis();

        DBBroker broker = null;
        DocumentImpl document = null;
        try {
            broker = brokerPool.get(subject);

            // If it is not a collection, check if it is a document
            document = broker.getXMLResource(xmldbUri, Lock.READ_LOCK);

            if (document.getResourceType() == DocumentImpl.XML_FILE) {
                // Stream XML document
                Serializer serializer = broker.getSerializer();
                serializer.reset();
                try {
                    // Set serialization options
                    serializer.setProperties(configuration);

                    // Serialize document
                    try (Writer w = new OutputStreamWriter(os, "UTF-8")) {
                        serializer.serialize(document, w);
                        w.flush();
                    }

                    // don;t flush
                    if (!(os instanceof VirtualTempFile)) {
                        os.flush();
                    }

                } catch (SAXException e) {
                    LOG.error(e);
                    throw new IOException(String.format("Error while serializing XML document: %s", e.getMessage()), e);
                }

            } else {
                // Stream NON-XML document
                broker.readBinaryResource((BinaryDocument) document, os);
                os.flush();
            }

        } catch (EXistException e) {
            LOG.error(e);
            throw new IOException(e.getMessage());

        } catch (PermissionDeniedException e) {
            LOG.error(e);
            throw e;

        } finally {

            if (document != null) {
                document.getUpdateLock().release(Lock.READ_LOCK);
            }

            brokerPool.release(broker);

            if (LOG.isDebugEnabled()) {
                LOG.debug(String.format("Stream stopped, duration %s msec.", System.currentTimeMillis() - startTime));
            }
        }

    }

    /**
     * Remove document from database.
     */
    void delete() {

        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("Deleting %s", xmldbUri));
        }

        DBBroker broker = null;
        Collection collection = null;
        DocumentImpl resource = null;

        TransactionManager txnManager = brokerPool.getTransactionManager();
        Txn txn = txnManager.beginTransaction();

        try {
            broker = brokerPool.get(subject);

            // Need to split path into collection and document name
            XmldbURI collName = xmldbUri.removeLastSegment();
            XmldbURI docName = xmldbUri.lastSegment();

            // Open collection if possible, else abort
            collection = broker.openCollection(collName, Lock.WRITE_LOCK);
            if (collection == null) {
                LOG.debug("Collection does not exist");
                txnManager.abort(txn);
                return;
            }

            // Open document if possible, else abort
            resource = collection.getDocument(broker, docName);
            if (resource == null) {
                LOG.debug(String.format("No resource found for path: %s", xmldbUri));
                txnManager.abort(txn);
                return;
            }

            if (resource.getResourceType() == DocumentImpl.BINARY_FILE) {
                collection.removeBinaryResource(txn, broker, resource.getFileURI());

            } else {
                collection.removeXMLResource(txn, broker, resource.getFileURI());
            }

            // Commit change
            txnManager.commit(txn);

            if (LOG.isDebugEnabled()) {
                LOG.debug("Document deleted sucessfully");
            }

        } catch (LockException e) {
            LOG.error("Resource is locked.", e);
            txnManager.abort(txn);

        } catch (EXistException | TriggerException | PermissionDeniedException e) {
            LOG.error(e);
            txnManager.abort(txn);

        } finally {

            // TODO: check if can be done earlier
            if (collection != null) {
                collection.release(Lock.WRITE_LOCK);
            }
            txnManager.close(txn);
            brokerPool.release(broker);

            if (LOG.isDebugEnabled()) {
                LOG.debug("Finished delete");
            }
        }
    }

    /**
     * Get lock token from database.
     */
    public LockToken getCurrentLock() {

        if (LOG.isDebugEnabled()) {
            LOG.debug("Get current lock " + xmldbUri);
        }

        DBBroker broker = null;
        DocumentImpl document = null;

        try {
            broker = brokerPool.get(subject);

            // If it is not a collection, check if it is a document
            document = broker.getXMLResource(xmldbUri, Lock.READ_LOCK);

            if (document == null) {
                LOG.debug("No resource found for path: " + xmldbUri);
                return null;
            }

            // TODO consider. A Webdav lock can be set without subject lock.
            Account lock = document.getUserLock();
            if (lock == null) {

                if (LOG.isDebugEnabled()) {
                    LOG.debug("Document " + xmldbUri + " does not contain userlock");
                }
                return null;
            }

            // Retrieve Locktoken from document metadata
            org.exist.dom.LockToken token = document.getMetadata().getLockToken();
            if (token == null) {

                if (LOG.isDebugEnabled()) {
                    LOG.debug("Document meta data does not contain a LockToken");
                }
                return null;
            }


            if (LOG.isDebugEnabled()) {
                LOG.debug("Successfully retrieved token");
            }

            return token;


        } catch (EXistException | PermissionDeniedException e) {
            LOG.error(e);
            return null;

        } finally {

            if (document != null) {
                document.getUpdateLock().release(Lock.READ_LOCK);
            }

            brokerPool.release(broker);

            if (LOG.isDebugEnabled()) {
                LOG.debug("Finished probe lock");
            }
        }
    }

    /**
     * Lock document.
     */
    public LockToken lock(LockToken inputToken) throws PermissionDeniedException,
            DocumentAlreadyLockedException, EXistException {

        if (LOG.isDebugEnabled()) {
            LOG.debug("create lock " + xmldbUri);
        }

        DBBroker broker = null;
        DocumentImpl document = null;
        TransactionManager txnManager = null;
        Txn txn = null;

        try {
            broker = brokerPool.get(subject);

            // Try to get document (add catch?)
            document = broker.getXMLResource(xmldbUri, Lock.WRITE_LOCK);

            if (document == null) {

                if (LOG.isDebugEnabled()) {
                    LOG.debug(String.format("No resource found for path: %s", xmldbUri));
                }
                //return null; // throw exception?
                throw new EXistException("No resource found.");
            }

            // Get current userlock
            Account userLock = document.getUserLock();

            // Check if Resource is already locked. @@ToDo
            if (userLock != null) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Resource was already locked, ignored.");
                }
            }

            if ( userLock != null && userLock.getName() != null
                    && !userLock.getName().equals(subject.getName())
                    && !subject.hasDbaRole() ) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug(String.format("Resource is locked by user %s.", userLock.getName()));
                }
                throw new PermissionDeniedException(userLock.getName());
            }

            // Check for request for shared lock. @@TODO
            if (inputToken.getScope() == LockToken.LOCK_SCOPE_SHARED) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Shared locks are not implemented.");
                }
                throw new EXistException("Shared locks are not implemented.");
            }

            // Update locktoken
            inputToken.setOwner(subject.getName());
            inputToken.createOpaqueLockToken();
            //inputToken.setTimeOut(inputToken.getTimeOut());
            inputToken.setTimeOut(LockToken.LOCK_TIMEOUT_INFINITE);

            // Update document
            document.getMetadata().setLockToken(inputToken);
            document.setUserLock(subject);

            // Make token persistant
            txnManager = brokerPool.getTransactionManager();
            txn = txnManager.beginTransaction();
            broker.storeMetadata(txn, document);
            txnManager.commit(txn);

            if (LOG.isDebugEnabled()) {
                LOG.debug("Successfully retrieved token");
            }
           
            return inputToken;


        } catch (EXistException | PermissionDeniedException e) {
            LOG.error(e);
            if (txnManager != null) {
                txnManager.abort(txn);
            }
            throw e;

        } catch (TriggerException e) {
            LOG.error(e);
            //dead code, remove?
            if (txnManager != null) {
                txnManager.abort(txn);
            }
            //-----------------------
            throw new EXistException(e);

    } finally {

            // TODO: check if can be done earlier
            if (document != null) {
                document.getUpdateLock().release(Lock.WRITE_LOCK);
            }

            if (txnManager != null) {
                txnManager.close(txn);
            }
           
            brokerPool.release(broker);

            if (LOG.isDebugEnabled()) {
                LOG.debug("Finished create lock");
            }
        }
    }

    /**
     * Unlock document in database.
     */
    void unlock() throws PermissionDeniedException, DocumentNotLockedException, EXistException {

        if (LOG.isDebugEnabled()) {
            LOG.debug("unlock " + xmldbUri);
        }

        DBBroker broker = null;
        DocumentImpl document = null;

        TransactionManager txnManager = brokerPool.getTransactionManager();
        Txn txn = txnManager.beginTransaction();

        try {
            broker = brokerPool.get(subject);


            // Try to get document (add catch?)
            document = broker.getXMLResource(xmldbUri, Lock.WRITE_LOCK);

            if (document == null) {
                final String msg = String.format("No resource found for path: %s", xmldbUri);
                LOG.debug(msg);
                throw new EXistException(msg);
            }

            // Get current userlock
            Account lock = document.getUserLock();

            // Check if Resource is already locked.
            if (lock == null) {
                LOG.debug(String.format("Resource %s is not locked.", xmldbUri));
                throw new DocumentNotLockedException("" + xmldbUri);
            }

            // Check if Resource is from subject
            if (!lock.getName().equals(subject.getName()) && !subject.hasDbaRole() ) {
                LOG.debug(String.format("Resource lock is from user %s", lock.getName()));
                throw new PermissionDeniedException(lock.getName());
            }

            // Update document
            document.setUserLock(null);
            document.getMetadata().setLockToken(null);

            // Make it persistant
            broker.storeMetadata(txn, document);
            txnManager.commit(txn);

        } catch (EXistException | PermissionDeniedException e) {
            txnManager.abort(txn);
            LOG.error(e);
            throw e;

        } catch (TriggerException e) {
            txnManager.abort(txn);
            LOG.error(e);
            throw new EXistException(e);

    } finally {

            if (document != null) {
                document.getUpdateLock().release(Lock.WRITE_LOCK);
            }
            txnManager.close(txn);
            brokerPool.release(broker);

            if (LOG.isDebugEnabled()) {
                LOG.debug("Finished create lock");
            }
        }
    }

    /**
     * Copy document or collection in database.
     */
    void resourceCopyMove(XmldbURI destCollectionUri, String newName, Mode mode) throws EXistException {

        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("%s %s to %s named %s", mode, xmldbUri, destCollectionUri, newName));
        }

        XmldbURI newNameUri = null;
        try {
            newNameUri = XmldbURI.xmldbUriFor(newName);
        } catch (URISyntaxException ex) {
            LOG.error(ex);
            throw new EXistException(ex.getMessage());
        }

        DBBroker broker = null;
        Collection srcCollection = null;
        DocumentImpl srcDocument = null;

        Collection destCollection = null;


        TransactionManager txnManager = brokerPool.getTransactionManager();
        Txn txn = txnManager.beginTransaction();

        try {
            broker = brokerPool.get(subject);

            // Need to split path into collection and document name
            XmldbURI srcCollectionUri = xmldbUri.removeLastSegment();
            XmldbURI srdDocumentUri = xmldbUri.lastSegment();

            // Open collection if possible, else abort
            srcCollection = broker.openCollection(srcCollectionUri, Lock.WRITE_LOCK);
            if (srcCollection == null) {
                txnManager.abort(txn);
                return; // TODO throw
            }

            // Open document if possible, else abort
            srcDocument = srcCollection.getDocument(broker, srdDocumentUri);
            if (srcDocument == null) {
                LOG.debug(String.format("No resource found for path: %s", xmldbUri));
                txnManager.abort(txn);
                return;
            }

            // Open collection if possible, else abort
            destCollection = broker.openCollection(destCollectionUri, Lock.WRITE_LOCK);
            if (destCollection == null) {
                LOG.debug(String.format("Destination collection %s does not exist.", xmldbUri));
                txnManager.abort(txn);
                return;
            }


            // Perform actial move/copy
            if (mode == Mode.COPY) {
                broker.copyResource(txn, srcDocument, destCollection, newNameUri);

            } else {
                broker.moveResource(txn, srcDocument, destCollection, newNameUri);
            }


            // Commit change
            txnManager.commit(txn);

            if (LOG.isDebugEnabled()) {
                LOG.debug(String.format("Document %sd sucessfully", mode));
            }

        } catch (LockException e) {
            LOG.error("Resource is locked.", e);
            txnManager.abort(txn);
            throw new EXistException(e.getMessage());

        } catch (EXistException e) {
            LOG.error(e);
            txnManager.abort(txn);
            throw e;

        } catch (IOException | PermissionDeniedException | TriggerException e) {
            LOG.error(e);
            txnManager.abort(txn);
            throw new EXistException(e.getMessage());

        } finally {

            // TODO: check if can be done earlier
            if (destCollection != null) {
                destCollection.release(Lock.WRITE_LOCK);
            }

            if (srcCollection != null) {
                srcCollection.release(Lock.WRITE_LOCK);
            }

            txnManager.close(txn);
            brokerPool.release(broker);

            if (LOG.isDebugEnabled()) {
                LOG.debug("Finished " + mode);
            }
        }
    }

    public LockToken refreshLock(String token) throws PermissionDeniedException,
            DocumentAlreadyLockedException, EXistException, DocumentNotLockedException {

        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("refresh lock %s  lock=%s", xmldbUri, token));
        }

        DBBroker broker = null;
        DocumentImpl document = null;

        if (token == null) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("token is null");
            }
            throw new EXistException("token is null");
        }

        // Prepare transaction
        TransactionManager txnManager = null;
        Txn txn = null;

        try {
            broker = brokerPool.get(subject);

            // Try to get document (add catch?)
            document = broker.getXMLResource(xmldbUri, Lock.WRITE_LOCK);

            if (document == null) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug(String.format("No resource found for path: %s", xmldbUri));
                }
                //return null; // throw exception?
                throw new EXistException("No resource found.");
            }

            // Get current userlock
            Account userLock = document.getUserLock();

            // Check if Resource is already locked.
            if (userLock == null) {
                final String msg = "Resource was not locked.";
                if (LOG.isDebugEnabled()) {
                    LOG.debug(msg);
                }
                throw new DocumentNotLockedException(msg);
            }

            if (userLock.getName() != null && !userLock.getName().equals(subject.getName())
                    && !subject.hasDbaRole()) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug(String.format("Resource is locked by %s", userLock.getName()));
                }
                throw new PermissionDeniedException(userLock.getName());
            }

            LockToken lockToken = document.getMetadata().getLockToken();

            if (!token.equals(lockToken.getOpaqueLockToken())) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Token does not match");
                }
                throw new PermissionDeniedException(String.format("Token %s does not match %s", token, lockToken.getOpaqueLockToken()));
            }

            lockToken.setTimeOut(LockToken.LOCK_TIMEOUT_INFINITE);

            // Make token persistant
            txnManager = brokerPool.getTransactionManager();
            txn = txnManager.beginTransaction();
            broker.storeXMLResource(txn, document);
            txnManager.commit(txn);

            if (LOG.isDebugEnabled()) {
                LOG.debug("Successfully retrieved token");
            }
            return lockToken;


        } catch (EXistException | PermissionDeniedException e) {
            LOG.error(e);
            if (txnManager != null) {
                txnManager.abort(txn);
            }
            throw e;

        } finally {

            // TODO: check if can be done earlier
            if (document != null) {
                document.getUpdateLock().release(Lock.WRITE_LOCK);
            }
           
            if (txnManager != null) {
                txnManager.close(txn);
            }
           
            if(broker != null) {
                brokerPool.release(broker);
            }

            if (LOG.isDebugEnabled()) {
                LOG.debug("Finished create lock");
            }
        }
    }
}
TOP

Related Classes of org.exist.webdav.ExistDocument

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.