Package org.apache.xindice.server

Source Code of org.apache.xindice.server.XindiceServlet

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $Id: XindiceServlet.java 595817 2007-11-16 20:49:03Z vgritsenko $
*/

package org.apache.xindice.server;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xindice.core.Collection;
import org.apache.xindice.core.DBException;
import org.apache.xindice.core.Database;
import org.apache.xindice.server.rpc.RPCMessageInterface;
import org.apache.xindice.util.Configuration;
import org.apache.xindice.webadmin.Location;
import org.apache.xindice.webadmin.WebAdminManager;
import org.apache.xindice.webadmin.util.MimeTable;
import org.apache.xindice.webadmin.viewer.HtmlCollectionViewer;
import org.apache.xindice.webadmin.viewer.HtmlDatabaseViewer;
import org.apache.xindice.webadmin.viewer.HtmlResourceViewer;
import org.apache.xindice.webadmin.webdav.DAVRequest;
import org.apache.xindice.webadmin.webdav.DAVResponse;
import org.apache.xindice.webadmin.webdav.WebdavStatus;
import org.apache.xindice.webadmin.webdav.components.DAVComponent;
import org.apache.xindice.xml.dom.DOMParser;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.XmlRpcHandler;
import org.apache.xmlrpc.server.XmlRpcHandlerMapping;
import org.apache.xmlrpc.server.XmlRpcServerConfigImpl;
import org.apache.xmlrpc.webserver.XmlRpcServletServer;

import org.w3c.dom.Document;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
* A <code>HttpServlet</code> that enables XML-RPC access to a Xindice
* database instance.
*
* @author <a href="mailto:kstaken@xmldatabases.org">Kimbro Staken</a>
* @author <a href="mailto:vladimir@apache.org">Vladimir R. Bossicard</a>
* @author <a href="mailto:gianugo@apache.org">Gianugo Rabellino</a>
* @author <a href="mailto:vgritsenko@apache.org">Vadim Gritsenko</a>
* @version $Revision: 595817 $, $Date: 2007-11-16 15:49:03 -0500 (Fri, 16 Nov 2007) $
*/
public class XindiceServlet extends HttpServlet {

    private static final Log log = LogFactory.getLog(XindiceServlet.class);

    private static final String WEBADMIN_CONFIGURATION = "webadmin.configuration";
    private static final String MIMETABLE_CONFIGURATION = "mimetable.configuration";

    protected XmlRpcServletServer server;
    protected WebAdminManager webAdmin;


    /**
     * Initializes database
     */
    public void init(ServletConfig servletConfig) throws ServletException {
        super.init(servletConfig);

        Configuration configuration = loadConfiguration(servletConfig);

        //
        // The configuration is wrapped in a <xindice> element so we need to get the "root-collection" configuration.
        //
        try {
            Configuration[] rootConfigurations = configuration.getChildren("root-collection");
            if (rootConfigurations.length == 0) {
                throw new ServletException("The database configuration is missing the <root-collection> element");
            }

            for (int i = 0; i < rootConfigurations.length; i++) {
                Configuration rootConfiguration = rootConfigurations[i];
                String name = rootConfiguration.getAttribute(Database.NAME);

                //
                // We need to ensure that the database points to a place where it makes
                // sense. If the path in the system.xml file is an absolute path, then
                // honor it. If it's not, we first check for the system property "xindice.db.home"
                // and if the lookup is successful we use it as the database root parent. If
                // the property is not set, we use /WEB-INF relative to the servlet context, unless
                // the war has not been unpacked. In this case, we throw an exception and
                // ask the user to specify the location of database root
                //
                String dbRoot = rootConfiguration.getAttribute(Database.DBROOT, Database.DBROOT_DEFAULT);

                //
                // If there is no absolute path, we have to perform some checks.
                //
                if (!new File(dbRoot).isAbsolute()) {

                    // Stupid hack but spec compliant:
                    // If getRealPath() returns null the war archive has not been unpacked.
                    String realPath = servletConfig.getServletContext().getRealPath("/WEB-INF");

                    // Let's see if the property was specified.
                    String home = System.getProperty(Xindice.PROP_XINDICE_DB_HOME);
                    if (log.isDebugEnabled()) {
                        log.debug(Xindice.PROP_XINDICE_DB_HOME + " is set to " + home);
                    }

                    if (home != null) {
                        dbRoot = new File(home + File.separator + dbRoot).getCanonicalPath();
                    } else if (realPath != null) {
                        dbRoot = new File(realPath + File.separator + dbRoot).getCanonicalPath();
                        log.warn("The database '" + name + "' root directory has been set to " + dbRoot +
                                 ". Keep in mind that if a war upgrade will take place the database will be lost.");
                    } else {
                        throw new ServletException(
                                "The database '" + name + "' configuration points to a relative path, "
                                + "but there was no " + Xindice.PROP_XINDICE_DB_HOME + " property set. "
                                + "Furthermore, the war was not unpacked by the application server "
                                + "so Xindice was unable to find a database location "
                                + "Please check /WEB-INF/system.xml and set an absolute path "
                                + "as the \"dbroot\" attribute of \"root-collection\" "
                                + "or specify a suitable " + Xindice.PROP_XINDICE_DB_HOME + " system property.");
                    }

                    rootConfiguration.setAttribute(Database.DBROOT, dbRoot);
                }

                //
                // We need to use this method to be consistent between deployments (embed, standalone, etc)
                // and let the Database object maintain the set of Databases.
                //
                Database.getDatabase(rootConfiguration);
                log.info("Database '" + name + "' successfully opened");
            }

        } catch (Exception e) {
            log.fatal("Failed to initialize database, throwing ServletException", e);
            // Make sure to close database if it was opened already.
            destroy();
            throw new ServletException("Error while handling the configuration", e);
        }

        // Initialize XML-RPC
        Configuration xmlRpcConfiguration = configuration.getChild("xml-rpc");
        if (xmlRpcConfiguration != null) {
            XmlRpcServerConfigImpl cfg = new XmlRpcServerConfigImpl();
            // Setup the XML-RPC impl to support UTF-8 encoding
            cfg.setEncoding("utf-8");
            cfg.setKeepAliveEnabled(true);

            // Create the XML-RPC server and add our handler as the default.
            this.server = new XmlRpcServletServer();
            this.server.setConfig(cfg);
            this.server.setHandlerMapping(new XmlRpcHandlerMapping() {
                public XmlRpcHandler getHandler(String s) throws XmlRpcException {
                    return RPCMessageInterface.getHandler();
                }
            });

            log.info("XML-RPC interface initialized.");
        }

        // Initialize WebAdmin
        try {
            String path = servletConfig.getInitParameter(WEBADMIN_CONFIGURATION);
            Document doc = loadFile(path, servletConfig);
            webAdmin = new WebAdminManager(doc.getDocumentElement());
        } catch(Exception e) {
            throw new ServletException("Could not process WebAdmin configuration", e);
        }

        try {
            String path = servletConfig.getInitParameter(MIMETABLE_CONFIGURATION);
            Document doc = loadFile(path, servletConfig);
            MimeTable.addMimeConfig(doc);
        } catch(Exception e) {
            log.error("Could not process Mime Table configuration", e);
        }

        log.info("Xindice server successfully started");
    }

    /**
     * Loads the Xindice configuration file. The file is searched in the following locations:
     * <ul>
     * <li>the <i>System.getProperty(Xindice.PROP_XINDICE_CONFIGURATION)</i> system property.
     * <li>the <i>ServletConfig.getInitParameter(Xindice.PROP_XINDICE_CONFIGURATION)</i> servlet
     * configuration parameter in web.xml file.</li>
     * <li>default configuration stored in the <tt>Xindice</tt> class</li>
     * </ul>
     *
     * @param servletConfig servlet configuration
     * @return Xindice configuration
     * @throws ServletException if unable to load configuration
     */
    public Configuration loadConfiguration(ServletConfig servletConfig) throws ServletException {
        try {
            InputStream in = null;
            String path = System.getProperty(Xindice.PROP_XINDICE_CONFIGURATION);
            if (path != null && path.length() > 0) {
                // Configuration file specified by system property
                log.info("Loading configuration from file path " + path + " (system property)");
                in = new FileInputStream(path);
            } else {
                path = servletConfig.getInitParameter(Xindice.PROP_XINDICE_CONFIGURATION);
                if (path != null && path.length() > 0) {
                    if (path.startsWith("/")) {
                        // Absolute file path
                        log.info("Loading configuration from file path " + path);
                        in = new FileInputStream(path);
                    } else {
                        // Relative (to the context) path
                        log.info("Loading configuration from context path " + path);
                        ServletContext context = servletConfig.getServletContext();
                        in = context.getResourceAsStream("/" + path);
                    }
                }
            }

            Document doc;
            if (in != null) {
                try {
                    doc = DOMParser.toDocument(in);
                } finally {
                    in.close();
                }
            } else {
                log.warn("Loading the standard configuration");
                doc = DOMParser.toDocument(Xindice.DEFAULT_CONFIGURATION);
            }

            return new Configuration(doc, false);
        } catch (Exception e) {
            throw new ServletException("Failed to load configuration.", e);
        }
    }

    private Document loadFile(String path, ServletConfig config) throws Exception {
        InputStream inputStream = null;

        try {
            if (path.startsWith("/")) { // Absolute file path
                log.debug("Loading configuration from filesystem path " + path);
                inputStream = new FileInputStream(path);
            } else {
                // Relative (to the context) path
                log.debug("Loading configuration from context path " + path);
                ServletContext context = config.getServletContext();
                inputStream = context.getResourceAsStream("/" + path);
            }

            Document configuration = null;
            if (inputStream != null) {
                configuration = DOMParser.toDocument(inputStream);
            }

            return configuration;
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException ignored) {
                // ignore
            }
        }
    }

    public void destroy() {
        // When the servlet engine goes down we need to close the database instance.
        // By the time destroy() is called, no more client requests can come in,
        // so no need to worry about multithreading.
        String[] databases = Database.listDatabases();
        for (int i = 0; i < databases.length; i++) {
            String name = databases[i];
            try {
                Database.getDatabase(name).close();
                log.info("Database '" + name + "' successfully closed");
            } catch (Exception e) {
                log.error("Error closing database '" + name + "'", e);
            }
        }
    }

    /**
     * Process request. Determine type of a request and delegate for processing
     * either to XML-RPC server, WebDAV, or WebAdmin.
     *
     * @param request a HttpServletRequest instance
     * @param response a HttpServletResponse instance
     * @exception IOException if an IO error occurs
     * @exception ServletException if a servlet error occurs
     */
    public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        // get requested information
        String path = request.getServletPath();
        String method = request.getMethod();

        // xmlrpc requests do not have path (always '/') and are always POST
        if ("/".equals(path) && method.equalsIgnoreCase("POST")) {
            if (server != null) {
                server.execute(request, response);
            } else {
                response.sendError(HttpServletResponse.SC_NOT_FOUND);
            }
            return;
        }

        // empty path - redirect to initial page
        if (path.length() == 0) {
            String redirect = request.getContextPath() + request.getServletPath() + "/?viewer=default";
            response.sendRedirect(redirect);
            return;
        }

        // get request target
        Location target;
        try {
            target = new Location(path);
        } catch (DBException e) {
            log.error("Unable to process request '" + path + "'", e);
            throw new ServletException(e);
        }

        // get viewer parameter (missing if DAV request)
        String viewer = request.getParameter("viewer");

        // WebDAV requests do not have viewer parameter, nor can not GET a collection
        if (viewer == null && !(path.endsWith("/") && method.equalsIgnoreCase("GET"))) {
            DAVComponent m = webAdmin.getMethod(method);
            if (m == null) {
                // method is not supported
                if (log.isInfoEnabled()) {
                    log.info("Method " + method + " is not supported.");
                }
                response.setStatus(WebdavStatus.SC_NOT_IMPLEMENTED);
                return;
            }

            m.execute(new DAVRequest(request), new DAVResponse(response), target);
            return;
        }

        // HTML requests are all the rest
        Collection col = target.getCollection();
        String resource = target.getName();
        if (col == null) {
            // redirect if path is not '/'
            if (!path.equals("/")) {
                String redirect = request.getContextPath() + request.getServletPath() + "/?viewer=" + viewer;
                response.sendRedirect(redirect);
                return;
            }

            HtmlDatabaseViewer v = webAdmin.getDatabaseViewer(viewer);
            if (v == null) {
                response.setStatus(WebdavStatus.SC_NOT_IMPLEMENTED);
                return;
            }

            v.execute(request, response);
        } else if (resource == null) {
            // redirect if path does not end with '/'
            if (!path.endsWith("/")) {
                String redirect = request.getContextPath() + request.getServletPath() + path + "/?viewer=" + viewer;
                response.sendRedirect(redirect);
                return;
            }

            HtmlCollectionViewer v = webAdmin.getCollectionViewer(viewer);
            if (v == null) {
                response.setStatus(WebdavStatus.SC_NOT_IMPLEMENTED);
                return;
            }

            v.execute(request, response, col);
        } else {
            HtmlResourceViewer v = webAdmin.getResourceViewer(viewer);
            if (v == null) {
                response.setStatus(WebdavStatus.SC_NOT_IMPLEMENTED);
                return;
            }

            v.execute(request, response, col, resource);
        }
    }

}
TOP

Related Classes of org.apache.xindice.server.XindiceServlet

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.