Hi,

Trying to deploy cocoon (2.1.6) webapp under an already installed jetty 
server (5.1.1), I met some problems with Xindice (1.1b4) servlet
configuration embedded in cocoon.

I had error messages saying that the configuration file of xindice
could not be loaded (the trace is listed below).

The Xindice servlet declaration in web.xml is as follow :

----
  <servlet>
    <servlet-name>Xindice</servlet-name>
    <display-name>Xindice XML-RPC Server</display-name>
    <servlet-class>org.apache.xindice.server.XindiceServlet</servlet-class>
    <init-param>
      <param-name>xindice.configuration</param-name>
      <param-value>WEB-INF/xindice.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
----

I had a look at Xindice source code and found that the
following statement in  org/apache/xindice/server/XindiceServlet.java
,loadConfiguration() method  (near line #240) :

------

  String path = servletConfig.getInitParameter(Xindice.PROP_XINDICE_CONFIGURA
TION);
  if (path != null) {
    InputStream inputStream = null;
    if (path.startsWith("/")) {
        // Absolute file path
        ...
     } else {
        // Relative (to the context) path
        log.debug("Loading configuration from context path " + path) ;
        ServletContext context = servletConfig.getServletContext();
        inputStream = context.getResourceAsStream(path);
     }
    ...
  }
-------
returned a null InputStream when the path is relative, causing the error
messages...

I changed the line 

----
inputStream = context.getResourceAsStream(path) ;
----

by

----
inputStream = new FileInputStream(context.getRealPath(path)) ;
----

And the loading of Xindice servlet configuration file now works fine.

I don't know if this modification is accurate, but it solved my problem.
I join the modified XindiceServlet.java to this mail if someone is
interested in it...

Cheers

Gilles


P.S. : Here is (part of) the trace I had in jetty logs at startup :

-----
14:46:04.770 WARN!! [main] org.mortbay.jetty.Server.main(Server.java:438) >08> 
EXCEPTION
org.mortbay.util.MultiException[org.apache.xindice.util.ConfigurationException: 
Failed to load configuration.]
        at org.mortbay.http.HttpServer.doStart(HttpServer.java:673)
        at org.mortbay.util.Container.start(Container.java:72)
        at org.mortbay.jetty.Server.main(Server.java:433)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at 
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at 
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:324)
        at org.mortbay.start.Main.invokeMain(Main.java:151)
        at org.mortbay.start.Main.start(Main.java:480)
        at org.mortbay.start.Main.main(Main.java:94)
org.apache.xindice.util.ConfigurationException: Failed to load configuration.
        at 
org.apache.xindice.server.XindiceServlet.loadConfiguration(XindiceServlet.java:273)
        at 
org.apache.xindice.server.XindiceServlet.init(XindiceServlet.java:103)
        at 
org.mortbay.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:369)
        at 
org.mortbay.jetty.servlet.ServletHolder.start(ServletHolder.java:243)
        at 
org.mortbay.jetty.servlet.ServletHandler.initializeServlets(ServletHandler.java:445)
        at 
org.mortbay.jetty.servlet.WebApplicationHandler.initializeServlets(WebApplicationHandler.java:310)
        at 
org.mortbay.jetty.servlet.WebApplicationContext.doStart(WebApplicationContext.java:512)
        at org.mortbay.util.Container.start(Container.java:72)
        at org.mortbay.http.HttpServer.doStart(HttpServer.java:695)
        at org.mortbay.util.Container.start(Container.java:72)
        at org.mortbay.jetty.Server.main(Server.java:433)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at 
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at 
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:324)
        at org.mortbay.start.Main.invokeMain(Main.java:151)
        at org.mortbay.start.Main.start(Main.java:480)
        at org.mortbay.start.Main.main(Main.java:94)
Caused by: java.lang.NullPointerException
        at 
org.apache.xindice.server.XindiceServlet.loadConfiguration(XindiceServlet.java:261)
...
-----


/*
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * Licensed 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.
 *
 * CVS $Id: XindiceServlet.java,v 1.30 2004/02/11 14:03:09 vgritsenko Exp $
 */

package org.apache.xindice.server;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xindice.core.Database;
import org.apache.xindice.server.rpc.RPCMessageInterface;
import org.apache.xindice.util.Configuration;
import org.apache.xindice.util.ConfigurationException;
import org.apache.xindice.xml.dom.DOMParser;
import org.apache.xmlrpc.XmlRpc;
import org.apache.xmlrpc.XmlRpcServer;

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;
import java.io.OutputStream;

/**
 * A <code>HttpServlet</code> that enables XML-RPC access to a Xindice
 * database instance.
 *
 * @author <a href="mailto:[EMAIL PROTECTED]">Kimbro Staken</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Vladimir R. Bossicard</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Gianugo Rabellino</a>
 * @author <a href="mailto:[EMAIL PROTECTED]">Vadim Gritsenko</a>
 * @version CVS $Revision: 1.30 $, $Date: 2004/02/11 14:03:09 $
 */
public class XindiceServlet extends HttpServlet {

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

    private static final String DEFAULT_XMLRPC_DRIVER = "xerces";
    protected XmlRpcServer xmlrpcServer;

    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);
            }
        }
    }

    /**
     * Delegate GET requests to the UglyBrowser.
     */
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        UglyBrowser.doGet(request, response);
    }

    /**
     * Sends an XML query to the server and writes the output back. Currenlty
     * only XML-RPC query is supported.
     */
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        byte[] result = xmlrpcServer.execute(request.getInputStream());
        response.setContentType("text/xml");
        response.setContentLength(result.length);
        OutputStream output = response.getOutputStream();
        output.write(result);
        output.flush();
    }

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

        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 ConfigurationException("The database configuration is missing the <root-collection> element");
            } else {
                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 ConfigurationException(
                                    "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");
                }
            }

            // Setup the XML-RPC impl to support UTF-8 input via Xerces.
            XmlRpc.setEncoding("UTF8");

            /*
             * Setup the SAX parser XML-RPC impl will use.
             * The XmlRpc.setDriver() method takes either the classname or a shorthand
             * name for the SAX parser it will use.  The default (for backwards compatibility
             * if nothing else) is xerces.
             */
            String xmlrpcDriver = DEFAULT_XMLRPC_DRIVER;

            Configuration xmlRpcConfiguration = configuration.getChild("xml-rpc");
            if (xmlRpcConfiguration != null) {
                Configuration xmlRpcDriverConfiguration = xmlRpcConfiguration.getChild("driver");
                if (xmlRpcDriverConfiguration != null) {
                    // xmlrpcDriver will have non-empty value, guaranteed by providing default value
                    xmlrpcDriver = xmlRpcDriverConfiguration.getAttribute("name", DEFAULT_XMLRPC_DRIVER);
                }
            }

            try {
                XmlRpc.setDriver(xmlrpcDriver);
            } catch (Exception e) {
                throw new ConfigurationException("Failed to set driver for XmlRpc to: " + xmlrpcDriver, e);
            }

            // Create the XML-RPC server and add our handler as the default.
            this.xmlrpcServer = new XmlRpcServer();
            try {
                this.xmlrpcServer.addHandler("$default", new RPCMessageInterface());
            } catch (Exception e) {
                throw new ConfigurationException("Failed to add default handler to XmlRpc server.", e);
            }

            log.info("Xindice server successfully started");
        } 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);
        }
    }

    /**
     * Loads the Xindice configuration file. The file is searched in the following locations:
     * <ul>
     * <li>the <tt>ServletConfig.getInitParameter(Xindice.PROP_XINDICE_CONFIGURATION)</tt> variable located in the servlet
     * configuration file</li>
     * <li>use the default configuration stored in the <tt>Xindice</tt> class</li>
     * </ul>
     *
     * <br/>
     * TODO: we should probably try to load from the file system if we can't load it this way.
     */
    public Configuration loadConfiguration(ServletConfig servletConfig) {
        try {
            Document configurationDocument;

            String path = servletConfig.getInitParameter(Xindice.PROP_XINDICE_CONFIGURATION);
            if (path != null) {
                InputStream inputStream = null;
                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 = servletConfig.getServletContext();
                    // inputStream = context.getResourceAsStream(path);
                    inputStream = new FileInputStream(context.getRealPath(path));

                }

                try {
                    configurationDocument = DOMParser.toDocument(inputStream);
                } finally {
                    try {
                        inputStream.close();
                    } catch (IOException ignored) {
                    }
                }
            } else {
                log.debug("Loading the standard configuration");
                configurationDocument = DOMParser.toDocument(Xindice.DEFAULT_CONFIGURATION);
            }

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

Reply via email to