/*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is the Kowari Metadata Store.
*
* The Initial Developer of the Original Code is Plugged In Software Pty
* Ltd (http://www.pisoftware.com, mailto:info@pisoftware.com). Portions
* created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002
* Plugged In Software Pty Ltd. All Rights Reserved.
*
* Contributor(s): N/A.
*
* [NOTE: The text of this Exhibit A may differ slightly from the text
* of the notices in the Source Code files of the Original Code. You
* should use the text of this Exhibit A rather than the text found in the
* Original Code Source Code for Your Modifications.]
*
*/
package org.mulgara.server;
// java 2 standard packages
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.UnknownHostException;
// servlet packages
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// thirdparty packages
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
// locall written packages
import org.mulgara.server.SessionFactory;
import org.mulgara.server.SessionFactoryException;
import org.mulgara.server.SessionFactoryFactory;
import org.mulgara.util.Closable;
/**
* Servlet startup wrapper for a Mulgara server. <p>
*
* You can get a handle on the database created by this servlet by calling: </p>
* <pre>
* Database database = ServletMulgaraServer.getDatabase();
* </pre>
* <p>
*
* Note that this servlet cannot furnish requests. Any (HTTP) requests will
* result in an exception being thrown indicating as much. It is only intended
* to start a Mulgara instance that can be retrieved locally if needed (ie. not
* over RMI) for speed.
* </p>
* <p>
* Note. This servlet should eventually start a "normal" Mulgara server that will
* accept requests as per normal. At the moment, queries can only be sent via
* direct method calls.
* </p>
*
* @created 2002-03-04
* @author Tom Adams
* @company <A href="mailto:info@PIsoftware.com">Plugged In Software</A>
* @copyright ©2002 <a href="http://www.pisoftware.com/">Plugged In Software Pty Ltd</a>
* @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
*/
public class ServletMulgaraServer extends HttpServlet {
/** Used for serializing - an unlikely occurance. */
private static final long serialVersionUID = -5680050587689401924L;
/** The logging category to log to */
private final static Logger log = Logger.getLogger(ServletMulgaraServer.class.getName());
/** the key to retreive the Mulgara server name */
private final static String MULGARA_CONFIG_SERVERNAME = "mulgara.config.servername";
/** the key to retrieve the database persistence path */
private final static String MULGARA_CONFIG_PERSISTENCE_PATH = "mulgara.config.persistencepath";
/** the key to retreive the Mulgara host name */
private final static String MULGARA_HOSTNAME = "mulgara.hostname";
/** the key to retreive the Mulgara LDAP security file */
private final static String MULGARA_LOG4J_CONFIG = "mulgara.log4j.config";
/** the key to retreive the lucene index directory */
private final static String LUCENE_INDEX_DIR = "lucene.index.dir";
/** the database of this server */
private static SessionFactory database = null;
public ServletMulgaraServer() {
}
/**
* Sets the database of this server.
* @param database the database of this server, refered to as a SessionFactory.
*/
public static void setDatabase(SessionFactory database) {
ServletMulgaraServer.database = database;
}
/**
* Gets the sessionFactory/database of this server.
* @return The database value as a SessionFactory
*/
public static SessionFactory getDatabase() {
return ServletMulgaraServer.database;
}
/**
* Creates a new Mulgara database, and stores it in the servlet context.
* @throws ServletException if the Mulgara database cannot be created for some reason
*/
public void init() throws ServletException {
BasicConfigurator.configure();
try {
// get the loction of the logging configuration file
String log4jConfigPath = getRealPath(ServletMulgaraServer.MULGARA_LOG4J_CONFIG);
//this.getResourceURL(ServletMulgaraServer.MULGARA_LOG4J_CONFIG);
if (log4jConfigPath == null) throw new IOException("Unable to retrieve log4j configuration file");
// load the logging configuration
loadLoggingConfig((new File(log4jConfigPath)).toURI().toURL());
} catch (Exception e) {
// log the error
System.err.println("Could not initialize logging configuration!");
e.printStackTrace(System.err);
// wrap it in a servlet exception
throw new ServletException(e);
}
// log what we're doing
if (log.isInfoEnabled()) log.info("Initialising Mulgara server servlet");
// log that we've created a new server
if (log.isDebugEnabled()) log.debug("Created servlet-wrapped Mulgara server");
// if it we don't have one already, create a new database
if (ServletMulgaraServer.getDatabase() == null) {
try {
ServletMulgaraServer.setDatabase(createDatabase());
} catch (Throwable t) {
log.info("No local database available. Using RMI.");
}
}
}
/**
* Closes the database.
*/
public synchronized void destroy() {
if (getDatabase() != null) {
// log that we're stopping the database
if (log.isInfoEnabled()) log.info("Stopping Mulgara server");
try {
((Closable)getDatabase()).close();
} catch (Exception e) {
log.error("Unable to close database cleanly", e);
}
setDatabase(null);
}
}
/**
* Closes the database at the last moment.
* @throws Throwable General catch-all for closing problems.
*/
protected void finalize() throws Throwable {
try {
destroy();
} finally {
super.finalize();
}
}
/**
* As this servlet cannot handle requests (its only job is to start create a
* database), this method always throws an exception.
* @param req The request.
* @param res The response to the client.
* @throws ServletException always thrown.
*/
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException {
throw new ServletException("Mulgara server servlet does not handle requests");
}
/**
* Returns the URI of the server based on the server name.
* @param serverName the name of the server
* @return the URI of the server
* @throws URISyntaxException if the URI cannot be created
*/
private URI getServerURI(String serverName) throws URISyntaxException {
String hostname = getServletContext().getInitParameter(MULGARA_HOSTNAME);
// attempt to determine the hostname if not supplied by servlet
if (hostname == null || hostname.trim().length() == 0) {
hostname = "localhost";
try {
hostname = InetAddress.getLocalHost().getHostName();
log.info("Obtained " + hostname + " automatically for server");
} catch (UnknownHostException e) {
log.warn("Problem getting host name -> using localhost");
}
}
// return the server URI
return new URI("rmi", hostname, "/" + serverName, null);
}
/**
* Unaliases the persistence path specified in the configuration file. <p>
*
* The configuration file allows the constants <code>temp</code> and <code>.</code>
* to be used instead of a real path, to enable easier cross platform
* deployment. These are then mapped to the system temp directory and the
* user's current directory respectively by this method. </p>
*
* @param aliasedPath the persistence path as it appears in the configuration file
* @return the unaliased persistence path
*/
private String getPersistencePath(String aliasedPath) {
// initialis the path to what was specified in the config
String persistencePath = aliasedPath;
// unalias the path (if we need to)
if ((aliasedPath == null) || aliasedPath.equals("") || aliasedPath.equalsIgnoreCase(".")) {
// use the current directory
persistencePath = System.getProperty("user.dir");
} else if (aliasedPath.equalsIgnoreCase("temp")) {
persistencePath = System.getProperty("java.io.tmpdir");
}
// return the persistence path
return persistencePath;
}
/**
* Returns the real location of a file path specified by the <code>parameter</code>
* in the servlet context.
* @param param the init param in the servlet context to find the real location of
* @return the real location of the given param
*/
private String getRealPath(String param) {
// get the path specified in the servlet config
String filePath = this.getServletContext().getInitParameter(param);
// try to find the real path
if (filePath != null) {
// log that we've found the config file
if (log.isDebugEnabled()) log.debug("Found file location " + filePath + " for param " + param);
// get the real path
filePath = this.getServletContext().getRealPath(filePath);
}
// return the path we found
return filePath;
}
/**
* Creates a new database.
* @return The created database.
* @throws ServletException if the database could not be created.
*/
private synchronized SessionFactory createDatabase() throws ServletException {
try {
// configure the system properties
if (log.isDebugEnabled()) log.debug("Configuring system properties");
configureSystemProperties();
// get params we'll need to create the server
String tmpConfiguredPath = getServletContext().getInitParameter(MULGARA_CONFIG_PERSISTENCE_PATH);
String persistencePath = getPersistencePath(tmpConfiguredPath);
String serverName = getServletContext().getInitParameter(MULGARA_CONFIG_SERVERNAME);
// throw an error if anything is null
if (serverName == null) throw new ServletException("Server name not in deployment descriptor");
// get the server's URI and the database state path
URI serverURI = getServerURI(serverName);
File statePath = new File(new File(persistencePath), serverName);
// create the state path if needed
if (!statePath.exists()) {
if (!statePath.mkdirs()) throw new IOException("Could not create server directory: " + statePath);
}
// log that we're creating a Mulgara server
if (log.isInfoEnabled()) log.info("Starting Mulgara server at " + serverURI + " in directory " + statePath);
// return the database
SessionFactoryFactory factory = new SessionFactoryFactory();
SessionFactory sessionFactory = factory.newSessionFactory(serverURI, statePath);
return sessionFactory;
} catch (SessionFactoryException sfe) {
// log the error
log.error("Error creating database", sfe);
// wrap it in a servlet exception
throw new ServletException(sfe);
} catch (UnknownHostException uhe) {
// log the error
log.error("Error creating database", uhe);
// wrap it in a servlet exception
throw new ServletException(uhe);
} catch (IOException ioe) {
// log the error
log.error("Error creating database", ioe);
// wrap it in a servlet exception
throw new ServletException(ioe);
} catch (URISyntaxException use) {
// log the error
log.error("Error creating database", use);
// wrap it in a servlet exception
throw new ServletException(use);
}
}
/**
* Sets up any system properties needed by components.
* @throws IOException if any files embedded within the JAR file cannot be found
*/
private void configureSystemProperties() throws IOException {
// set the system properties needed
System.setProperty("org.mulgara.xml.ResourceDocumentBuilderFactory",
"org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
// set the lucene index
String luceneIndex = this.getServletContext().getInitParameter(ServletMulgaraServer.LUCENE_INDEX_DIR);
if (luceneIndex != null) {
System.setProperty(ServletMulgaraServer.LUCENE_INDEX_DIR, luceneIndex);
}
}
/**
* Loads the embedded logging configuration from an external URL.
* @param loggingConfig the URL of the logging configuration file
*/
private void loadLoggingConfig(URL loggingConfig) {
// validate the loggingConfig parameter
if (loggingConfig == null) throw new IllegalArgumentException("Null \"loggingConfig\" parameter");
// configure the logging service
PropertyConfigurator.configure(loggingConfig);
log.info("Using logging configuration from " + loggingConfig);
}
}