org.alfresco.repo.webdav.WebDAVServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.repo.webdav.WebDAVServlet.java

Source

/*
 * #%L
 * Alfresco Remote API
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * 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/>.
 * #L%
 */
package org.alfresco.repo.webdav;

import java.io.IOException;
import java.util.Hashtable;
import java.util.List;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.UserTransaction;

import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.security.authentication.AuthenticationContext;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.activities.ActivityPoster;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.FileFilterMode;
import org.alfresco.util.FileFilterMode.Client;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

/**
 * Servlet that accepts WebDAV requests for the hub. The request is served by the hub's content
 * repository framework and the response sent back using the WebDAV protocol.
 * 
 * @author gavinc
 */
public class WebDAVServlet extends HttpServlet {
    private static final long serialVersionUID = 6900069445027527165L;

    // Logging
    private static Log logger = LogFactory.getLog("org.alfresco.webdav.protocol");

    // Constants
    public static final String WEBDAV_PREFIX = "webdav";
    //private static final String INTERNAL_SERVER_ERROR = "Internal Server Error: ";

    // Init parameter names
    private static final String BEAN_INIT_PARAMS = "webdav.initParams";

    // Service registry, used by methods to find services to process requests
    private ServiceRegistry serviceRegistry;

    private TransactionService transactionService;
    private static TenantService tenantService;
    private static NodeService nodeService;
    private static SearchService searchService;
    private static NamespaceService namespaceService;

    // WebDAV method handlers
    protected Hashtable<String, Class<? extends WebDAVMethod>> m_davMethods;

    // note: cache is tenant-aware (if using TransctionalCache impl)

    private static SimpleCache<String, NodeRef> singletonCache; // eg. for webdavRootNodeRef
    private static final String KEY_WEBDAV_ROOT_NODEREF = "key.webdavRoot.noderef";

    private static String rootPath;

    private static NodeRef defaultRootNode; // for default domain

    // WebDAV helper class
    private WebDAVHelper m_davHelper;
    private WebDAVActivityPoster activityPoster;

    private WebDAVInitParameters initParams;

    /**
     * @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest,
     *      javax.servlet.http.HttpServletResponse)
     */
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        if (!initParams.getEnabled()) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        long startTime = 0;
        if (logger.isTraceEnabled()) {
            startTime = System.currentTimeMillis();
        }

        FileFilterMode.setClient(Client.webdav);

        try {
            // Create the appropriate WebDAV method for the request and execute it
            final WebDAVMethod method = createMethod(request, response);

            if (method == null) {
                if (logger.isErrorEnabled())
                    logger.error("WebDAV method not implemented - " + request.getMethod());

                // Return an error status

                response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
                return;
            } else if (method.getRootNodeRef() == null) {
                if (logger.isDebugEnabled()) {
                    logger.debug("No root node for request [" + request.getMethod() + " " + request.getRequestURI()
                            + "]");
                }

                // Return an error status
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                return;
            }

            // Execute the WebDAV request, which must take care of its own transaction
            method.execute();
        } catch (Throwable e) {
            ExceptionHandler exHandler = new ExceptionHandler(e, request, response);
            exHandler.handle();
        } finally {
            if (logger.isTraceEnabled()) {
                logger.trace(request.getMethod() + " took " + (System.currentTimeMillis() - startTime)
                        + "ms to execute [" + request.getRequestURI() + "]");
            }

            FileFilterMode.clearClient();
        }
    }

    /**
     * Create a WebDAV method handler
     * 
     * @param request HttpServletRequest
     * @param response HttpServletResponse
     * @return WebDAVMethod
     */
    protected WebDAVMethod createMethod(HttpServletRequest request, HttpServletResponse response) {
        // Get the type of the current request

        String strHttpMethod = request.getMethod();

        if (logger.isDebugEnabled())
            logger.debug("WebDAV request " + strHttpMethod + " on path " + request.getRequestURI());

        Class<? extends WebDAVMethod> methodClass = m_davMethods.get(strHttpMethod);
        WebDAVMethod method = null;

        if (methodClass != null) {
            try {
                // Create the handler method    
                method = methodClass.newInstance();
                method.setDetails(request, response, m_davHelper, getRootNodeRef());

                // A very few WebDAV methods produce activity posts.
                if (method instanceof ActivityPostProducer) {
                    ActivityPostProducer activityPostProducer = (ActivityPostProducer) method;
                    activityPostProducer.setActivityPoster(activityPoster);
                }
            } catch (Exception ex) {
                // Debug

                if (logger.isDebugEnabled())
                    logger.debug(ex);
            }
        }

        // Return the WebDAV method handler, or null if not supported

        return method;
    }

    private static NodeRef getRootNodeRef() {
        NodeRef rootNodeRef = singletonCache.get(KEY_WEBDAV_ROOT_NODEREF);

        if (rootNodeRef == null) {
            rootNodeRef = tenantService.getRootNode(nodeService, searchService, namespaceService, rootPath,
                    defaultRootNode);
            singletonCache.put(KEY_WEBDAV_ROOT_NODEREF, rootNodeRef);
        }

        return rootNodeRef;
    }

    /**
     * Initialize the servlet
     * 
     * @param config ServletConfig
     * @exception ServletException
     */
    @SuppressWarnings("unchecked")
    public void init(ServletConfig config) throws ServletException {
        super.init(config);

        // Get service registry        
        WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());

        // If no context has been initialised, exit silently so config changes can be made
        if (context == null) {
            return;
        }

        // Get global configuration properties
        WebApplicationContext wc = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
        initParams = (WebDAVInitParameters) wc.getBean(BEAN_INIT_PARAMS);

        // Render this servlet permanently unavailable if its enablement property is not set
        if (!initParams.getEnabled()) {
            logger.info("Marking servlet WebDAV as unavailable!");
            return;
        }

        // Get root paths

        String storeValue = initParams.getStoreName();

        rootPath = initParams.getRootPath();

        // Get beans

        serviceRegistry = (ServiceRegistry) context.getBean(ServiceRegistry.SERVICE_REGISTRY);

        transactionService = serviceRegistry.getTransactionService();
        tenantService = (TenantService) context.getBean("tenantService");

        nodeService = (NodeService) context.getBean("NodeService");
        searchService = (SearchService) context.getBean("SearchService");
        namespaceService = (NamespaceService) context.getBean("NamespaceService");
        ActivityPoster poster = (ActivityPoster) context.getBean("activitiesPoster");
        singletonCache = (SimpleCache<String, NodeRef>) context.getBean("immutableSingletonCache");

        // Collaborator used by WebDAV methods to create activity posts.
        activityPoster = new ActivityPosterImpl("WebDAV", poster);

        // Create the WebDAV helper
        m_davHelper = (WebDAVHelper) context.getBean("webDAVHelper");

        // Initialize the root node
        initializeRootNode(storeValue, rootPath, context, nodeService, searchService, namespaceService,
                tenantService, transactionService);

        // Create the WebDAV methods table

        m_davMethods = new Hashtable<String, Class<? extends WebDAVMethod>>();

        m_davMethods.put(WebDAV.METHOD_PROPFIND, PropFindMethod.class);
        m_davMethods.put(WebDAV.METHOD_PROPPATCH, PropPatchMethod.class);
        m_davMethods.put(WebDAV.METHOD_COPY, CopyMethod.class);
        m_davMethods.put(WebDAV.METHOD_DELETE, DeleteMethod.class);
        m_davMethods.put(WebDAV.METHOD_GET, GetMethod.class);
        m_davMethods.put(WebDAV.METHOD_HEAD, HeadMethod.class);
        m_davMethods.put(WebDAV.METHOD_LOCK, LockMethod.class);
        m_davMethods.put(WebDAV.METHOD_MKCOL, MkcolMethod.class);
        m_davMethods.put(WebDAV.METHOD_MOVE, MoveMethod.class);
        m_davMethods.put(WebDAV.METHOD_OPTIONS, OptionsMethod.class);
        m_davMethods.put(WebDAV.METHOD_POST, PostMethod.class);
        m_davMethods.put(WebDAV.METHOD_PUT, PutMethod.class);
        m_davMethods.put(WebDAV.METHOD_UNLOCK, UnlockMethod.class);
    }

    protected WebDAVHelper getDAVHelper() {
        return m_davHelper;
    }

    /**
     * @param storeValue String
     * @param rootPath String
     * @param context WebApplicationContext
     * @param nodeService NodeService
     * @param searchService SearchService
     * @param namespaceService NamespaceService
     * @param tenantService TenantService
     * @param m_transactionService TransactionService
     */
    private void initializeRootNode(String storeValue, String rootPath, WebApplicationContext context,
            NodeService nodeService, SearchService searchService, NamespaceService namespaceService,
            TenantService tenantService, TransactionService m_transactionService) {

        // Use the system user as the authenticated context for the filesystem initialization

        AuthenticationContext authComponent = (AuthenticationContext) context.getBean("authenticationContext");
        authComponent.setSystemUserAsCurrentUser();

        // Wrap the initialization in a transaction

        UserTransaction tx = m_transactionService.getUserTransaction(true);

        try {
            // Start the transaction

            if (tx != null)
                tx.begin();

            StoreRef storeRef = new StoreRef(storeValue);

            if (nodeService.exists(storeRef) == false) {
                throw new RuntimeException("No store for path: " + storeRef);
            }

            NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef);

            List<NodeRef> nodeRefs = searchService.selectNodes(storeRootNodeRef, rootPath, null, namespaceService,
                    false);

            if (nodeRefs.size() > 1) {
                throw new RuntimeException("Multiple possible children for : \n" + "   path: " + rootPath + "\n"
                        + "   results: " + nodeRefs);
            } else if (nodeRefs.size() == 0) {
                throw new RuntimeException("Node is not found for : \n" + "   root path: " + rootPath);
            }

            defaultRootNode = nodeRefs.get(0);

            // Commit the transaction
            if (tx != null)
                tx.commit();
        } catch (Exception ex) {
            logger.error(ex);
        } finally {
            // Clear the current system user

            authComponent.clearCurrentSecurityContext();
        }
    }

    /**
     * 
     * @return root node for WebDAV
     */
    public static NodeRef getWebdavRootNode() {
        return getRootNodeRef();
    }

    /**
     * Bean to hold injected initialization parameters.
     * 
     * @author Derek Hulley
     * @since V3.5 Team
     */
    public static class WebDAVInitParameters {
        private boolean enabled = false;
        private String storeName;
        private String rootPath;
        private String urlPathPrefix;

        public boolean getEnabled() {
            return enabled;
        }

        public void setEnabled(boolean enabled) {
            this.enabled = enabled;
        }

        /**
         * @return              Returns the name of the store
         * @throws ServletException if the store name was not set
         */
        public String getStoreName() throws ServletException {
            if (!PropertyCheck.isValidPropertyString(storeName)) {
                throw new ServletException("WebDAV missing 'storeName' value.");
            }
            return storeName;
        }

        public void setStoreName(String storeName) {
            this.storeName = storeName;
        }

        /**
         * @return              Returns the WebDAV root path within the store
         * @throws ServletException if the root path was not set
         */
        public String getRootPath() throws ServletException {
            if (!PropertyCheck.isValidPropertyString(rootPath)) {
                throw new ServletException("WebDAV missing 'rootPath' value.");
            }
            return rootPath;
        }

        public void setRootPath(String rootPath) {
            this.rootPath = rootPath;
        }

        /**
         * Get the path prefix that generated URLs should exhibit, e.g.
         * <pre>
         *   http://server.name&lt;prefix&gt;/path/to/file.txt
         * </pre>
         * In the default set up this would be of the form /context-path/servlet-name e.g. /alfresco/webdav:
         * <pre>
         *   http://server.name/alfresco/webdav/path/to/file.txt
         * </pre>
         * however if using URL rewriting rules or a reverse proxy in front of the webdav server
         * you may choose to use, for example / for shorter URLs.
         * <pre>
         *   http://server.name/path/to/file.txt
         * </pre>
         * <p>
         * Leaving this property blank will cause the prefix used to be /context-path/servlet-name
         * 
         * @return the urlPathPrefix
         */
        public String getUrlPathPrefix() {
            return urlPathPrefix;
        }

        /**
         * See {@link #getUrlPathPrefix()}
         * 
         * @param urlPathPrefix String
         */
        public void setUrlPathPrefix(String urlPathPrefix) {
            this.urlPathPrefix = urlPathPrefix;
        }
    }
}