org.xwiki.platform.patchservice.web.PatchServiceServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.xwiki.platform.patchservice.web.PatchServiceServlet.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This 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 software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 *
 */
package org.xwiki.platform.patchservice.web;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSInput;
import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSParser;
import org.xwiki.container.Container;
import org.xwiki.container.servlet.ServletContainerException;
import org.xwiki.container.servlet.ServletContainerInitializer;
import org.xwiki.context.Execution;
import org.xwiki.platform.patchservice.api.Patch;
import org.xwiki.platform.patchservice.api.PatchId;
import org.xwiki.platform.patchservice.impl.PatchIdImpl;
import org.xwiki.platform.patchservice.impl.PatchImpl;
import org.xwiki.platform.patchservice.plugin.PatchservicePlugin;

import com.xpn.xwiki.XWiki;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.web.Utils;
import com.xpn.xwiki.web.XWikiRequest;
import com.xpn.xwiki.web.XWikiResponse;
import com.xpn.xwiki.web.XWikiServletContext;
import com.xpn.xwiki.web.XWikiServletRequest;
import com.xpn.xwiki.web.XWikiServletResponse;

/**
 * @version $Id$
 */
public class PatchServiceServlet extends HttpServlet implements Servlet {
    /** Logging helper object. */
    private static final Log LOG = LogFactory.getLog(PatchServiceServlet.class);

    protected List<String> allVerbs;

    protected List<String> getVerbs;

    protected List<String> putVerbs;

    protected List<String> postVerbs;

    /*
     * (non-Javadoc)
     * 
     * @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        this.allVerbs = new ArrayList<String>();
        this.allVerbs.add("all");
        this.allVerbs.add("id");
        this.allVerbs.add("updates");
        this.allVerbs.add("delta");
        this.allVerbs.add("keys");
        this.allVerbs.add("key");
        this.allVerbs.add("patch");
        this.getVerbs = new ArrayList<String>();
        this.getVerbs.add("all");
        this.getVerbs.add("id");
        this.getVerbs.add("updates");
        this.getVerbs.add("delta");
        this.getVerbs.add("keys");
        this.putVerbs = new ArrayList<String>();
        this.putVerbs.add("patch");
        this.putVerbs.add("key");
        this.postVerbs = new ArrayList<String>();
        this.postVerbs.add("id");
        this.postVerbs.add("updates");
        this.postVerbs.add("delta");
        this.postVerbs.add("patch");
        this.postVerbs.add("key");
    }

    /**
     * {@inheritDoc}
     * 
     * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest,
     *      javax.servlet.http.HttpServletResponse)
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        handleRequest(req, resp, this.getVerbs);
    }

    /**
     * {@inheritDoc}
     * 
     * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
     *      javax.servlet.http.HttpServletResponse)
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        handleRequest(req, resp, this.postVerbs);
    }

    /**
     * {@inheritDoc}
     * 
     * @see javax.servlet.http.HttpServlet#doPut(javax.servlet.http.HttpServletRequest,
     *      javax.servlet.http.HttpServletResponse)
     */
    @Override
    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        handleRequest(req, resp, this.putVerbs);
    }

    protected void handleRequest(HttpServletRequest req, HttpServletResponse resp, List<String> acceptedCommands) {
        XWikiContext context = null;
        try {
            // Get the command, between the first and the second /
            String command = req.getPathInfo();
            if (command != null) {
                command = command.substring(1);
                if (command.indexOf('/') > 0) {
                    command = command.substring(0, command.indexOf('/'));
                }
                // Check that this is something supported
                if (!this.allVerbs.contains(command)) {
                    resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                    resp.getWriter().print("Unknown command: " + command);
                    return;
                }
                // Check that the HTTP method was correct
                if (!acceptedCommands.contains(command)) {
                    resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
                    resp.getWriter().print("Bad HTTP method for command: " + command);
                    return;
                }
                context = initializeXWikiContext(command, req, resp);

                XWiki.getXWiki(context);
                execute(command, context);
            }
        } catch (Exception ex) {
            resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            try {
                resp.getWriter().print("Unhandled server error: " + ex.getMessage());
            } catch (IOException ex1) {
                // Cannot send message, ignore.
            }
            ex.printStackTrace();
        } finally {
            if (context != null) {
                cleanupComponents();
            }
        }
    }

    protected void execute(String command, XWikiContext context) throws Exception {
        XWikiRequest request = context.getRequest();
        XWikiResponse response = context.getResponse();
        PatchservicePlugin plugin = (PatchservicePlugin) context.getWiki().getPlugin("patchservice", context);
        if (command.equals("all")) {
            processAll(request, response, plugin, context);
        } else if (command.equals("patch")) {
            processPatch(request, response, plugin, context);
        } else if (command.equals("id")) {
            processId(request, response, plugin, context);
        } else if (command.equals("updates")) {
            processUpdates(request, response, plugin, context);
        } else if (command.equals("delta")) {
            processDelta(request, response, plugin, context);
        } else if (command.equals("keys")) {
            processKeys(request, response, plugin, context);
        } else if (command.equals("key")) {
            processKey(request, response, plugin, context);
        }
    }

    protected void processAll(XWikiRequest request, XWikiResponse response, PatchservicePlugin plugin,
            XWikiContext context) throws Exception {
        Document doc = createEmptyDocument();
        doc.appendChild(doc.createElement("patches"));
        for (Patch p : plugin.getAllPatches()) {
            doc.getDocumentElement().appendChild(p.toXml(doc));
        }
        outputXml(doc, response);
    }

    protected void processUpdates(XWikiRequest request, XWikiResponse response, PatchservicePlugin plugin,
            XWikiContext context) throws Exception {
        if (StringUtils.isEmpty(request.getParameter("patch_id"))) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.getWriter().print("The request must contain a patch_id parameter.");
            return;
        }
        PatchId id = new PatchIdImpl();
        try {
            id.fromXml(parseString(request.getParameter("patch_id")).getDocumentElement());
        } catch (Exception ex) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.getWriter().print("Invalid patch_id parameter: " + ex.getMessage());
            return;
        }
        Document doc = createEmptyDocument();
        doc.appendChild(doc.createElement("patches"));
        for (Patch p : plugin.getUpdatesFrom(id)) {
            doc.getDocumentElement().appendChild(p.toXml(doc));
        }
        outputXml(doc, response);
    }

    protected void processDelta(XWikiRequest request, XWikiResponse response, PatchservicePlugin plugin,
            XWikiContext context) throws Exception {
        if (StringUtils.isEmpty(request.getParameter("patch_id_from"))) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.getWriter().print("The request must contain a patch_id_from parameter.");
            return;
        }
        if (StringUtils.isEmpty(request.getParameter("patch_id_to"))) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.getWriter().print("The request must contain a patch_id_from parameter.");
            return;
        }
        PatchId startId = new PatchIdImpl();
        try {
            startId.fromXml(parseString(request.getParameter("patch_id_from")).getDocumentElement());
        } catch (Exception ex) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.getWriter().print("Invalid patch_id_from parameter: " + ex.getMessage());
            return;
        }
        PatchId endId = new PatchIdImpl();
        try {
            endId.fromXml(parseString(request.getParameter("patch_id_to")).getDocumentElement());
        } catch (Exception ex) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.getWriter().print("Invalid patch_id_to parameter: " + ex.getMessage());
            return;
        }
        Document doc = createEmptyDocument();
        doc.appendChild(doc.createElement("patches"));
        for (Patch p : plugin.getDelta(startId, endId)) {
            doc.getDocumentElement().appendChild(p.toXml(doc));
        }
        outputXml(doc, response);
    }

    protected void processId(XWikiRequest request, XWikiResponse response, PatchservicePlugin plugin,
            XWikiContext context) throws Exception {
        if (StringUtils.isEmpty(request.getParameter("patch_id"))) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.getWriter().print("The request must contain a patch_id parameter.");
            return;
        }
        Document doc = createEmptyDocument();
        PatchId id = new PatchIdImpl();
        try {
            id.fromXml(parseString(request.getParameter("patch_id")).getDocumentElement());
        } catch (Exception ex) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.getWriter().print("Invalid patch_id parameter: " + ex.getMessage());
            return;
        }
        Patch p = plugin.getPatch(id);
        if (p == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            response.getWriter().print("No patch with the given id");
            return;
        }
        doc.appendChild(p.toXml(doc));
        outputXml(doc, response);
    }

    protected void processPatch(XWikiRequest request, XWikiResponse response, PatchservicePlugin plugin,
            XWikiContext context) throws Exception {
        if (StringUtils.isEmpty(request.getParameter("patch_id"))) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.getWriter().print("The request must contain a patch_id parameter.");
            return;
        }
        if (StringUtils.isEmpty(request.getParameter("patch"))) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.getWriter().print("The request must contain a patch parameter.");
            return;
        }
        PatchId id = new PatchIdImpl();
        try {
            id.fromXml(parseString(request.getParameter("patch_id")).getDocumentElement());
        } catch (Exception ex) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.getWriter().print("Invalid patch_id parameter: " + ex.getMessage());
            return;
        }
        Patch p = new PatchImpl();
        try {
            p.fromXml(parseString(request.getParameter("patch")).getDocumentElement());
        } catch (Exception ex) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.getWriter().print("Invalid patch parameter: " + ex.getMessage());
            return;
        }
        try {
            if (plugin.getDocumentUpdatesFrom(id).size() > 1) {
                response.setStatus(HttpServletResponse.SC_CONFLICT);
                response.getWriter().print("needs update");
                return;
            } else {
                try {
                    plugin.applyPatch(p, id, context);
                    plugin.logPatch(p);
                    response.getWriter().print("OK");
                } catch (Exception ex) {
                    response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                    response.getWriter().print("Failed to apply patch: " + ex.getMessage());
                }
            }
        } catch (IOException ex) {
            LOG.warn("Failed to send response");
        }
    }

    protected void processKeys(XWikiRequest request, XWikiResponse response, PatchservicePlugin plugin,
            XWikiContext context) throws Exception {
        response.setStatus(HttpServletResponse.SC_NOT_IMPLEMENTED);
        response.getWriter().print("Not implemented yet");
    }

    protected void processKey(XWikiRequest request, XWikiResponse response, PatchservicePlugin plugin,
            XWikiContext context) throws Exception {
        response.setStatus(HttpServletResponse.SC_NOT_IMPLEMENTED);
        response.getWriter().print("Not implemented yet");
    }

    private Document createEmptyDocument() {
        try {
            Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
            return doc;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private Document parseString(String content) {
        try {
            DOMImplementationLS lsImpl = (DOMImplementationLS) DOMImplementationRegistry.newInstance()
                    .getDOMImplementation("LS 3.0");
            LSInput input = lsImpl.createLSInput();
            input.setCharacterStream(new StringReader(content));
            LSParser p = lsImpl.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null);
            return p.parse(input);
        } catch (Exception ex) {
            return null;
        }
    }

    private void outputXml(Document doc, XWikiResponse response) {
        response.setContentType("text/xml");
        DOMImplementationLS ls = (DOMImplementationLS) doc.getImplementation();
        try {
            LSOutput o = ls.createLSOutput();
            o.setByteStream(response.getOutputStream());
            ls.createLSSerializer().write(doc, o);
        } catch (Exception e) {
        }
    }

    protected XWikiContext initializeXWikiContext(String command, HttpServletRequest req, HttpServletResponse resp)
            throws XWikiException, ServletException {
        XWikiRequest request = new XWikiServletRequest(req);
        XWikiResponse response = new XWikiServletResponse(resp);
        XWikiContext context = Utils.prepareContext(command, request, response,
                new XWikiServletContext(this.getServletContext()));

        // Initialize the Container component which is the new of transporting the Context in the new
        // component architecture.
        initializeContainerComponent(context);

        return context;
    }

    protected void initializeContainerComponent(XWikiContext context) throws ServletException {
        // Initialize the Container fields (request, response, session).
        // Note that this is a bridge between the old core and the component architecture.
        // In the new component architecture we use ThreadLocal to transport the request,
        // response and session to components which require them.
        // In the future this Servlet will be replaced by the XWikiPlexusServlet Servlet.
        ServletContainerInitializer containerInitializer = (ServletContainerInitializer) Utils
                .getComponent(ServletContainerInitializer.class);

        try {
            containerInitializer.initializeRequest(context.getRequest().getHttpServletRequest(), context);
            containerInitializer.initializeResponse(context.getResponse().getHttpServletResponse());
            containerInitializer.initializeSession(context.getRequest().getHttpServletRequest());
        } catch (ServletContainerException e) {
            throw new ServletException("Failed to initialize Request/Response or Session", e);
        }
    }

    protected void cleanupComponents() {
        Container container = (Container) Utils.getComponent(Container.class);
        Execution execution = (Execution) Utils.getComponent(Execution.class);

        // We must ensure we clean the ThreadLocal variables located in the Container and Execution
        // components as otherwise we will have a potential memory leak.
        container.removeRequest();
        container.removeResponse();
        container.removeSession();
        execution.removeContext();
    }
}