jp.aegif.alfresco.online_webdav.LockMethod.java Source code

Java tutorial

Introduction

Here is the source code for jp.aegif.alfresco.online_webdav.LockMethod.java

Source

/*
 * Copyright (C) 2005-2013 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco 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 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco 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 Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */
package jp.aegif.alfresco.online_webdav;

import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import javax.servlet.http.HttpServletResponse;

import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.repo.webdav.LockInfo;
import org.alfresco.repo.webdav.LockInfoImpl;
import org.alfresco.repo.webdav.WebDAV;
import org.alfresco.repo.webdav.WebDAVServerException;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileFolderUtil;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.rule.RuleService;
import org.alfresco.service.cmr.rule.RuleType;
import org.alfresco.service.namespace.QName;
import org.dom4j.io.XMLWriter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

/**
 * Implements the WebDAV LOCK method
 * 
 * @author gavinc
 */
public class LockMethod extends WebDAVMethod {
    public static final String EMPTY_NS = "";

    private static Timer timer = new Timer(true);

    protected int m_timeoutDuration = WebDAV.TIMEOUT_24_HOURS;

    protected LockInfo lockInfo = new LockInfoImpl();

    protected boolean createExclusive;

    protected String lockToken = null;

    /**
     * Default constructor
     */
    public LockMethod() {
    }

    /**
     * Returns true if request has lock token in the If header
     * 
     * @return boolean
     */
    protected final boolean hasLockToken() {
        if (m_conditions != null) {
            for (Condition condition : m_conditions) {
                if (!condition.getLockTokensMatch().isEmpty()) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Return the lock timeout, in seconds.
     * 
     * @return int
     */
    protected final int getLockTimeout() {
        return m_timeoutDuration;
    }

    /**
     * Parse the request headers
     * 
     * @exception WebDAVServerException
     */
    protected void parseRequestHeaders() throws WebDAVServerException {
        // Get user Agent

        m_userAgent = m_request.getHeader(WebDAV.HEADER_USER_AGENT);

        // Get the depth

        parseDepthHeader();

        // According to the specification: "Values other than 0 or infinity MUST NOT be used with the Depth header on a LOCK method.".
        // The specification does not specify the error code for this case - so we use HttpServletResponse.SC_INTERNAL_SERVER_ERROR.
        if (m_depth != WebDAV.DEPTH_0 && m_depth != WebDAV.DEPTH_INFINITY) {
            throw new WebDAVServerException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }

        // Parse Lock tokens and ETags, if any

        parseIfHeader();

        // Get the lock timeout value

        String strTimeout = m_request.getHeader(WebDAV.HEADER_TIMEOUT);

        // If the timeout header starts with anything other than Second
        // leave the timeout as the default

        if (strTimeout != null && strTimeout.startsWith(WebDAV.SECOND)) {
            try {
                // Some clients send header as Second-180 Seconds so we need to
                // look for the space

                int idx = strTimeout.indexOf(" ");

                if (idx != -1) {
                    // Get the bit after Second- and before the space

                    strTimeout = strTimeout.substring(WebDAV.SECOND.length(), idx);
                } else {
                    // The string must be in the correct format

                    strTimeout = strTimeout.substring(WebDAV.SECOND.length());
                }
                m_timeoutDuration = Integer.parseInt(strTimeout);
            } catch (Exception e) {
                // Warn about the parse failure and leave the timeout as the
                // default

                logger.warn("Failed to parse Timeout header: " + strTimeout);
            }
        }

        // DEBUG

        if (logger.isDebugEnabled())
            logger.debug("Timeout=" + getLockTimeout() + ", depth=" + getDepth());
    }

    /**
     * Parse the request body
     * @exception WebDAVServerException
     */
    protected void parseRequestBody() throws WebDAVServerException {
        if (m_request.getContentLength() == -1) {
            return;
        }

        Document body = getRequestBodyAsDocument();
        if (body != null) {
            OUTER: for (Node currentNode = body.getDocumentElement()
                    .getFirstChild(); currentNode != null; currentNode = currentNode.getNextSibling()) {
                if (currentNode instanceof Element
                        && WebDAV.DEFAULT_NAMESPACE_URI.equals(((Element) currentNode).getNamespaceURI())
                        && WebDAV.XML_LOCK_SCOPE.equals(((Element) currentNode).getLocalName())) {
                    for (Node propertiesNode = currentNode
                            .getFirstChild(); propertiesNode != null; propertiesNode = propertiesNode
                                    .getNextSibling()) {
                        if (propertiesNode instanceof Element && WebDAV.DEFAULT_NAMESPACE_URI
                                .equals(((Element) propertiesNode).getNamespaceURI())) {
                            this.createExclusive = WebDAV.XML_EXCLUSIVE.equals(propertiesNode.getLocalName());
                            break OUTER;
                        }
                    }
                    break OUTER;
                }
            }
            if (!createExclusive) {
                // We do not support shared locks - return 412 (section 8.10.7 of RFC 2518)
                throw new WebDAVServerException(HttpServletResponse.SC_PRECONDITION_FAILED);
            }
        }
    }

    /**
     * Execute the request
     * 
     * @exception WebDAVServerException
     */
    protected void executeImpl() throws WebDAVServerException, Exception {
        RuleService ruleService = getServiceRegistry().getRuleService();
        try {
            // Temporarily disable update rules.
            ruleService.disableRuleType(RuleType.UPDATE);
            attemptLock();
        } finally {
            // Re-instate update rules.
            ruleService.enableRuleType(RuleType.UPDATE);
        }
    }

    /**
     * The main lock implementation method.
     * 
     * @throws WebDAVServerException
     * @throws Exception
     */
    protected void attemptLock() throws WebDAVServerException, Exception {
        FileFolderService fileFolderService = getFileFolderService();
        final String path = getPath();
        NodeRef rootNodeRef = getRootNodeRef();
        // Get the active user
        final String userName = getDAVHelper().getAuthenticationService().getCurrentUserName();

        if (logger.isDebugEnabled()) {
            logger.debug("Locking node: \n" + "   user: " + userName + "\n" + "   path: " + path);
        }

        FileInfo lockNodeInfo = null;

        try {
            lockNodeInfo = getDAVHelper().getFileInfoFromRequestPath(m_request);
        } catch (FileNotFoundException e) {
            throw new WebDAVServerException(HttpServletResponse.SC_NOT_FOUND);
        }

        // Check if this is a new lock or a lock refresh
        if (hasLockToken()) {
            lockInfo = checkNode(lockNodeInfo);
            // If a request body is not defined and "If" header is sent we have createExclusive as false,
            // but we need to check a previous LOCK was an exclusive. I.e. get the property for node. It
            // is already has got in a checkNode method, so we need just get a scope from lockInfo.
            // This particular case was raised as ALF-4008.
            this.createExclusive = WebDAV.XML_EXCLUSIVE.equals(this.lockInfo.getScope());
            // Refresh an existing lock
            refreshLock(lockNodeInfo, userName);
        } else {
            lockInfo = checkNode(lockNodeInfo, true, createExclusive);
            // Create a new lock
            createLock(lockNodeInfo, userName);
        }

        m_response.setHeader(WebDAV.HEADER_LOCK_TOKEN,
                "<" + WebDAV.makeLockToken(lockNodeInfo.getNodeRef(), userName) + ">");
        m_response.setHeader(WebDAV.HEADER_CONTENT_TYPE, WebDAV.XML_CONTENT_TYPE);

        // We either created a new lock or refreshed an existing lock, send back the lock details
        generateResponse(lockNodeInfo, userName);
    }

    /**
     * Create a new node
     * 
     * @param parentNodeRef the parent node.  
     * @param name the name of the node
     * @param typeQName the type to create
     * @return Returns the new node's file information
     *  
     */
    protected FileInfo createNode(NodeRef parentNodeRef, String name, QName typeQName) {
        return getFileFolderService().create(parentNodeRef, name, ContentModel.TYPE_CONTENT);
    }

    /**
     * Create a new lock
     * 
     * @param lockNode NodeRef
     * @param userName String
     * @exception WebDAVServerException
     */
    protected final void createLock(FileInfo lockNode, String userName) throws WebDAVServerException {
        // Create Lock token
        lockToken = WebDAV.makeLockToken(lockNode.getNodeRef(), userName);

        if (createExclusive) {
            // Lock the node
            lockInfo.setTimeoutSeconds(getLockTimeout());
            lockInfo.setExclusiveLockToken(lockToken);
        } else {
            throw new WebDAVServerException(HttpServletResponse.SC_PRECONDITION_FAILED);
        }

        // Store lock depth
        lockInfo.setDepth(WebDAV.getDepthName(m_depth));
        // Store lock scope (shared/exclusive)
        String scope = createExclusive ? WebDAV.XML_EXCLUSIVE : WebDAV.XML_SHARED;
        lockInfo.setScope(scope);
        // Store the owner of this lock
        lockInfo.setOwner(userName);
        // Lock the node
        getDAVLockService().lock(lockNode.getNodeRef(), lockInfo);

        if (logger.isDebugEnabled()) {
            logger.debug("Locked node " + lockNode + ": " + lockInfo);
        }
    }

    /**
     * Refresh an existing lock
     * 
     * @param lockNode NodeRef
     * @param userName String
     * @exception WebDAVServerException
     */
    protected final void refreshLock(FileInfo lockNode, String userName) throws WebDAVServerException {
        if (this.createExclusive) {
            // Update the expiry for the lock
            lockInfo.setTimeoutSeconds(getLockTimeout());
        }
    }

    /**
     * Generates the XML lock discovery response body
     */
    protected void generateResponse(FileInfo lockNodeInfo, String userName) throws Exception {
        String scope;
        String lt;
        String owner;
        Date expiry;

        if (lockToken != null) {
            // In case of lock creation take the scope from request header
            scope = this.createExclusive ? WebDAV.XML_EXCLUSIVE : WebDAV.XML_SHARED;
            // Output created lock
            lt = lockToken;
        } else {
            // In case of lock refreshing take the scope from previously stored lock
            scope = this.lockInfo.getScope();
            // Output refreshed lock
            lt = this.lockInfo.getExclusiveLockToken();
        }
        owner = lockInfo.getOwner();
        expiry = lockInfo.getExpires();

        XMLWriter xml = createXMLWriter();

        xml.startDocument();

        String nsdec = generateNamespaceDeclarations(null);
        xml.startElement(WebDAV.DAV_NS, WebDAV.XML_PROP + nsdec, WebDAV.XML_NS_PROP + nsdec,
                getDAVHelper().getNullAttributes());

        // Output the lock details
        generateLockDiscoveryXML(xml, lockNodeInfo, false, scope, WebDAV.getDepthName(m_depth), lt, owner, expiry);

        // Close off the XML
        xml.endElement(WebDAV.DAV_NS, WebDAV.XML_PROP, WebDAV.XML_NS_PROP);

        // Send remaining data
        flushXML(xml);

    }

    /**
     * Generates a list of namespace declarations for the response
     */
    protected String generateNamespaceDeclarations(HashMap<String, String> nameSpaces) {
        StringBuilder ns = new StringBuilder();

        ns.append(" ");
        ns.append(WebDAV.XML_NS);
        ns.append(":");
        ns.append(WebDAV.DAV_NS);
        ns.append("=\"");
        ns.append(WebDAV.DEFAULT_NAMESPACE_URI);
        ns.append("\"");

        // Add additional namespaces

        if (nameSpaces != null) {
            Iterator<String> namespaceList = nameSpaces.keySet().iterator();

            while (namespaceList.hasNext()) {
                String strNamespaceUri = namespaceList.next();
                String strNamespaceName = nameSpaces.get(strNamespaceUri);

                ns.append(" ").append(WebDAV.XML_NS).append(":").append(strNamespaceName).append("=\"");
                ns.append(strNamespaceUri).append("\" ");
            }
        }

        return ns.toString();
    }
}