/* * 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.beans.*; import java.io.*; import java.lang.reflect.*; import java.net.*; import java.rmi.RMISecurityManager; import java.rmi.registry.LocateRegistry; import java.util.*; import javax.naming.*; import javax.xml.parsers.*; import org.xml.sax.SAXException; // log4j packages import org.apache.log4j.*; import org.apache.log4j.xml.DOMConfigurator; // locally written packages import org.mulgara.config.MulgaraConfig; import org.mulgara.config.Connector; import org.mulgara.config.PublicConnector; import org.mulgara.config.XpathFunctionResolver; import org.mulgara.query.FunctionResolverRegistry; import org.mulgara.server.SessionFactory; import org.mulgara.store.StoreException; import org.mulgara.store.xa.SimpleXAResourceException; import org.mulgara.util.Reflect; import org.mulgara.util.Rmi; import org.mulgara.util.StackTrace; import org.mulgara.util.TempDir; import static org.mulgara.server.ServerMBean.ServerState; /** * Embedded production Mulgara server. * * <p> Creates a Mulgara server instance, and a SOAP server instance to handle * <a href="http://www.w3.org/TR/SOAP">SOAP</a> requests for the Mulgara server.</p> * * @created 2001-10-04 * * @author Tom Adams * @author Simon Raboczi * @author Paula Gearon * @author Tate Jones * * @company <a href="mailto:info@PIsoftware.com">Plugged In Software</a> * @copyright ©2001-2004 <a href="http://www.pisoftware.com/">Plugged In Software Pty Ltd</a> * @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a> * @see <a href="http://developer.java.sun.com/developer/JDCTechTips/2001/tt0327.html#jndi"> * <cite>JNDI lookup in distributed systems</cite> </a> */ public class EmbeddedMulgaraServer implements SessionFactoryProvider { /** Line separator. */ protected static final String eol = System.getProperty("line.separator"); /** Default port to listen for a shutdown. */ public final static int SHUTDOWN_PORT = 6789; /** System property for the shutdown port. */ public final static String SHUTDOWN_PROP = "shutdownhook.port"; /** The request required to shutdown mulgara. */ public final static String SHUTDOWN_MSG = "shutdownmulgara"; /** The logging category to log to. */ protected static final Logger log = Logger.getLogger(EmbeddedMulgaraServer.class.getName()); /** The embedded configuration file path */ protected static String CONFIG_PATH = "conf/mulgara-x-config.xml"; /** The RMI permission security policy file path. */ protected static String RMI_SECURITY_POLICY_PATH = "conf/mulgara-rmi.policy"; /** The property for identifying the policy type. */ private static final String SYSTEM_MAIL = "mail.smtp.host"; /** The default server class to use for the Mulgara server. */ private static final String DEFAULT_SERVER_CLASS_NAME = "org.mulgara.server.rmi.RmiServer"; /** The registry context factory class. */ private static final String CONTEXT_FACTORY = "com.sun.jndi.rmi.registry.RegistryContextFactory"; /** The property for identifying the policy type. */ private static final String SECURITY_POLICY_PROP = "java.security.policy"; /** The system property to disable the HTTP service. */ private static final String DISABLE_HTTP = "mulgara.http.disable"; /** The system property to disable the RMI service. */ private static final String DISABLE_RMI = "no_rmi"; /** The Mulgara server instance. In this case, an RMIServer. */ private ServerMBean serverManagement = null; /** The web service container. */ private HttpServices webServices = null; /** The embedded Mulgara server configuration */ private MulgaraConfig mulgaraConfig = null; /** The (RMI) name of the server. */ private String rmiServerName = null; /** The path to persist server data to. */ private String persistencePath = null; /** The hostname to accept SOAP requests on. */ private String httpHostName = null; /** A flag to indicate if the server is configured to be started. */ private boolean canStart = false; /** A flag to indicate if the http server is configured to be started. */ private boolean httpEnabled; /** * Starts a Mulgara server and a WebServices (SOAP) server to handle SOAP queries to the * Mulgara server. * <p>Database files for the Mulgara server are written to the directory from * where this class was run.</p> * @param args command line arguments */ public static void main(String[] args) { // report the version and build number System.out.println("@@build.label@@"); // Set up the configuration, using command line arguments to override configured options EmbeddedMulgaraOptionParser optsParser = new EmbeddedMulgaraOptionParser(args); // load up the basic logging configuration in case we get an error before // we've loaded the real logging configuration BasicConfigurator.configure(); try { // parse the command line options to the server optsParser.parse(); } catch (EmbeddedMulgaraOptionParser.UnknownOptionException uoe) { System.err.println("ERROR: Unknown option(s): " + uoe.getOptionName()); printUsage(); System.exit(3); } catch (EmbeddedMulgaraOptionParser.IllegalOptionValueException iove) { System.err.println("ERROR: Illegal value '" + iove.getValue() + "' for option " + iove.getOption().shortForm() + "/" + iove.getOption().longForm()); printUsage(); System.exit(4); } try { // TODO: Iterate over all configured servers and start each one // Create the server instance EmbeddedMulgaraServer standAloneServer = new EmbeddedMulgaraServer(optsParser); if (standAloneServer.isStartable()) { // start the server, including all the configured services standAloneServer.startServices(); // Setup the network service for shutting down the server ShutdownService shutdownServer = new ShutdownService(); shutdownServer.start(); } } catch (ExceptionList el) { for (Throwable e: (List<Throwable>)el.getCauses()) { log.error("ExceptionList", e); e.printStackTrace(); } System.exit(2); } catch (Exception e) { log.error("Exception in main", e); e.printStackTrace(); System.exit(5); } } /** * Shutdown the Mulgara server */ public static void shutdown(String[] args) { // create a basic Configurator for the shutdown BasicConfigurator.configure(); // get the socket port int port = getShutdownHookPort(); // create a socket to the local host and port Socket clientSocket = null; try { clientSocket = new Socket(InetAddress.getByName("localhost"), port); PrintWriter toServer = new PrintWriter(new OutputStreamWriter(clientSocket.getOutputStream())); toServer.println(EmbeddedMulgaraServer.SHUTDOWN_MSG); toServer.flush(); toServer.close(); } catch (ConnectException ioCon) { System.out.println("Server is not currently running"); } catch (IOException ioEx) { log.error("Unable to establish connection to shutdown server on port " + port, ioEx); } catch (SecurityException secEx) { log.error("Unable to establish connection shutdown server due to a security exception. Check security policy", secEx); } catch (Exception ex) { log.error("Unable to establish shutdown connection to shutdown server on port " + port, ex); } finally { // attempt to close the socket try { if (clientSocket != null) clientSocket.close(); } catch (Exception ex) { /* skip */ } } } /** * Loads the embedded logging configuration (from the JAR file). * @param loggingConfig the path to the logging configuration file */ private static void loadLoggingConfig(String loggingConfig) { // get a URL from the classloader for the logging configuration URL log4jConfigURL = ClassLoader.getSystemResource(loggingConfig); // if we didn't get a URL, tell the user that something went wrong if (log4jConfigURL == null) { System.err.println("Unable to find logging configuration file in JAR " + "with " + loggingConfig + ", reverting to default configuration."); BasicConfigurator.configure(); } else { try { // configure the logging service DOMConfigurator.configure(log4jConfigURL); if (log.isDebugEnabled()) log.debug("Using logging configuration from " + log4jConfigURL); } catch (FactoryConfigurationError e) { System.err.println("Unable to configure logging service, reverting to default configuration"); BasicConfigurator.configure(); } catch (Exception e) { System.err.println("Unable to configure logging service, reverting to default configuration"); BasicConfigurator.configure(); } } } /** * Loads the embedded logging configuration from an external URL. * @param loggingConfig the URL of the logging configuration file */ private static void loadLoggingConfig(URL loggingConfig) { if (loggingConfig == null) throw new IllegalArgumentException("Null \"loggingConfig\" parameter"); try { // configure the logging service DOMConfigurator.configure(loggingConfig); if (log.isDebugEnabled()) log.debug("Using logging configuration from " + loggingConfig); } catch (FactoryConfigurationError e) { System.err.println("Unable to configure logging service, reverting to default configuration"); BasicConfigurator.configure(); } catch (Exception e) { System.err.println("Unable to configure logging service, reverting to default configuration"); BasicConfigurator.configure(); } } /** * Attempts to obtain the localhost name or defaults to the IP address of the localhost * @return the hostname this Mulgara server is bound to. */ public static String getResolvedLocalHost() { String hostname = null; try { // attempt for the localhost canonical host name hostname = InetAddress.getLocalHost().getCanonicalHostName(); } catch (UnknownHostException uhe) { try { // attempt to get the IP address for the localhost hostname = InetAddress.getByName("localhost").getHostAddress(); log.info("Obtain localhost IP address of " + hostname); } catch (UnknownHostException uhe2 ) { // default to the localhost IP hostname = "127.0.0.1"; log.info("Defaulting to 127.0.0.1 IP address"); } } return hostname; } /** * Get the shutdown hook port to allow the BootStrap to shutdown the server * from the same machine but different JVM To override the default port of * 6789 set a system property called shutdownhook.port * @return the shutdown port for this server */ private static int getShutdownHookPort() { int port = EmbeddedMulgaraServer.SHUTDOWN_PORT; // check if the default shutdown port has been overrided by a system property. String portString = System.getProperty(SHUTDOWN_PROP); if ((portString != null) && (portString.length() > 0)) { try { port = Integer.parseInt(portString); if (log.isInfoEnabled()) log.info("Override default shutdown hook port to " + port); } catch (NumberFormatException ex) { log.error("Unable to convert supplied port " + portString + " to int " + " for shutdown hook. Defaulting to port :" + port, ex); } } return port; } /////////////////////////////////////////////////////////////// // Member methods /////////////////////////////////////////////////////////////// EmbeddedMulgaraServer(EmbeddedMulgaraOptionParser options) throws IOException, ClassNotFoundException, SAXException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { // TODO: Attach ServerInfo to all databases, so it can be instantiated per server // configure the server, and set up the global ServerInfo canStart = configure(options); // start the server if we're allowed to if (canStart) { // create the params need for a new Mulgara instance File statePath = new File(new File(getPersistencePath()), getServerName()); String tripleStoreClassName = getConfig().getTripleStoreImplementation(); String hostname = ServerInfo.getBoundHostname(); int rmiPort = ServerInfo.getRMIPort(); //set the tripleStoreImplemention property System.setProperty("triple.store.implementation", tripleStoreClassName); // create a Mulgara server instance serverManagement = createServer( getServerName(), statePath, hostname, rmiPort, tripleStoreClassName, DEFAULT_SERVER_CLASS_NAME ); // install a shutdown hook for System.exit(#); if (log.isDebugEnabled()) log.debug("Registering shutdown hook"); Runtime.getRuntime().addShutdownHook(new RuntimeShutdownHook(this)); // create a web service if (httpEnabled) { // create a HTTP server instance if (log.isDebugEnabled()) log.debug("Configuring HTTP server"); try { webServices = createHttpServices(this, httpHostName, mulgaraConfig); } catch (Exception e) { String message = e.getMessage(); if (message == null) message = StackTrace.throwableToString(e); log.error("Unable to start web services due to: " + message + " [Continuing]"); if (log.isDebugEnabled()) log.debug("Web Server problem", e); } } } } /** * Starts the Mulgara server. * @throws IllegalStateException if this method is called before the servers have been created * @throws IOException if the Mulgara server cannot access its state keeping files * @throws NamingException if the Mulgara server cannot communicate with the RMI registry * @throws ExceptionList if an error ocurrs while starting up the SOAP server * @throws SimpleXAResourceException If operations on the database cannot be instigated * @throws Exception General catch all exception * @throws StoreException if the database could not be started */ public void startServices() throws IOException, NamingException, ExceptionList, SimpleXAResourceException, StoreException, Exception { if (serverManagement == null) throw new IllegalStateException("Servers must be created before they can be started"); // log that we're starting a Mulgara server if (log.isDebugEnabled()) log.debug("Starting server"); // start the Mulgara server serverManagement.init(); serverManagement.start(); // get the configured factory and URI and set the ServerInfo for this Mulgara server ServerInfo.setLocalSessionFactory(((AbstractServer)serverManagement).getSessionFactory()); // start the HTTP server if required if (webServices != null) { if (log.isDebugEnabled()) log.debug("Starting HTTP server"); webServices.start(); } } /** * Returns the flag that indicates if the server was configured to be started. * @return <code>true</code> if the server is configured to be started. */ private boolean isStartable() { return canStart; } /** * Returns a reference to the local {@link org.mulgara.server.SessionFactory} of * the underlying database. * @return a {@link org.mulgara.server.SessionFactory} from the underlying database */ public SessionFactory getSessionFactory() { SessionFactory sessionFactory = null; if (serverManagement != null) sessionFactory = ((AbstractServer)serverManagement).getSessionFactory(); return sessionFactory; } /** * Returns the Mulgara server instance. * @return the Mulgara server instance */ ServerMBean getServerMBean() { return serverManagement; } /** * Returns the embedded Mulgara server configuration. * @return the embedded Mulgara server configuration */ private MulgaraConfig getConfig() { return mulgaraConfig; } /** * Returns the (RMI) name of the server. * @return the (RMI) name of the server */ String getServerName() { return rmiServerName; } /** * Returns the path to persist server data to. * @return the path to persist server data to */ private String getPersistencePath() { return persistencePath; } /** * Configures an embedded Mulgara server. * @param parser the options parser containing the command line arguments to the server * @return true if the server is allowed to start */ private boolean configure(EmbeddedMulgaraOptionParser parser) { // flag to indicate whether we can start the server boolean startServer = true; try { // find out if the user wants help if (parser.getOptionValue(EmbeddedMulgaraOptionParser.HELP) != null) { // print the help printUsage(); // don't start the server startServer = false; } else if (parser.getOptionValue(EmbeddedMulgaraOptionParser.SHUTDOWN) != null) { // shut down the remote server shutdown(new String[0]); // don't start the server startServer = false; } else { // load the Mulgara configuration file String configURLStr = (String)parser.getOptionValue(EmbeddedMulgaraOptionParser.SERVER_CONFIG); mulgaraConfig = new MulgaraUserConfig(configURLStr); // set up any local registries used in the system configureRegistries(); // disable automatic starting of the RMI registry if (parser.getOptionValue(EmbeddedMulgaraOptionParser.NO_RMI) != null) { // disable automatic starting of the RMI Registry System.setProperty(DISABLE_RMI, DISABLE_RMI); } // disable automatic starting of the HTTP server if (parser.getOptionValue(EmbeddedMulgaraOptionParser.NO_HTTP) != null) { System.setProperty(DISABLE_HTTP, DISABLE_HTTP); } // set the hostname to bind Mulgara to String host = (String)parser.getOptionValue(EmbeddedMulgaraOptionParser.SERVER_HOST); if (host != null) { ServerInfo.setBoundHostname(host); } else { // get the hostname from configuration file String configHost = mulgaraConfig.getMulgaraHost(); // obtain the default host name if none is configured if ((configHost == null) || configHost.equals("")) configHost = getResolvedLocalHost(); // set the host name ServerInfo.setBoundHostname(configHost); } // set up the client peer port in RMI Integer rmiObjectPort = (Integer)parser.getOptionValue(EmbeddedMulgaraOptionParser.RMI_OBJECT_PORT); if (rmiObjectPort != null) Rmi.setDefaultPort(rmiObjectPort); // set the port on which the RMI registry will be created String rmiPortStr = (String)parser.getOptionValue(EmbeddedMulgaraOptionParser.RMI_PORT); int rmiPort = (rmiPortStr != null) ? Integer.parseInt(rmiPortStr) : mulgaraConfig.getRMIPort(); ServerInfo.setRMIPort(rmiPort); System.setProperty(Context.PROVIDER_URL, "rmi://" + ServerInfo.getBoundHostname() + ":" + rmiPort + "/"); // set up the default graph to use for SPARQL String defaultGraph = (String)parser.getOptionValue(EmbeddedMulgaraOptionParser.DEFAULT_GRAPH); if (defaultGraph != null) { ServerInfo.setDefaultGraphURI(new URI(defaultGraph)); } else { defaultGraph = mulgaraConfig.getDefaultGraph(); if (defaultGraph != null) ServerInfo.setDefaultGraphURI(new URI(defaultGraph)); } // set up system properties that are used by external packages configureSystemProperties(); // load an external logging configuration String loggingConfig = (String)parser.getOptionValue(EmbeddedMulgaraOptionParser.LOG_CONFIG); if (loggingConfig != null) { loadLoggingConfig(new URL(loggingConfig)); } else { loadLoggingConfig(mulgaraConfig.getExternalConfigPaths().getMulgaraLogging()); } if (System.getProperty(DISABLE_HTTP) == null && !mulgaraConfig.getJetty().getDisabled()) { httpEnabled = true; Connector httpConnector = mulgaraConfig.getJetty().getConnector(); PublicConnector httpPublicConnector = mulgaraConfig.getJetty().getPublicConnector(); String httpHost = (String)parser.getOptionValue(EmbeddedMulgaraOptionParser.HTTP_HOST); httpHostName = (httpHost != null || httpConnector == null) ? httpHost : httpConnector.getHost(); // set the port on which to accept HTTP requests String httpPort = (String)parser.getOptionValue(EmbeddedMulgaraOptionParser.PORT); if (httpPort != null) { ServerInfo.setHttpPort(Integer.parseInt(httpPort)); } else { if (httpConnector != null) ServerInfo.setHttpPort(httpConnector.getPort()); } // set the port on which to accept public HTTP requests httpPort = (String)parser.getOptionValue(EmbeddedMulgaraOptionParser.PUBLIC_PORT); if (httpPort != null) { ServerInfo.setPublicHttpPort(Integer.parseInt(httpPort)); } else { if (httpPublicConnector != null) ServerInfo.setPublicHttpPort(httpPublicConnector.getPort()); } } else { httpEnabled = false; httpHostName = ""; } // set the (RMI) name of the server, preferencing the command line String serverName = (String)parser.getOptionValue(EmbeddedMulgaraOptionParser.SERVER_NAME); rmiServerName = (serverName != null) ? serverName : mulgaraConfig.getServerName(); // set the server's persistence path persistencePath = (String)parser.getOptionValue(EmbeddedMulgaraOptionParser.PERSISTENCE_PATH); if (persistencePath == null) persistencePath = mulgaraConfig.getPersistencePath(); // if the persistence path was one we know about, substitute it if (persistencePath.equalsIgnoreCase(".")) { persistencePath = System.getProperty("user.dir"); } else if (persistencePath.equalsIgnoreCase("temp")) { persistencePath = System.getProperty("java.io.tmpdir"); } // set the smtp name of the server String smtpServer = (String)parser.getOptionValue(EmbeddedMulgaraOptionParser.SMTP_SERVER); if (smtpServer == null) smtpServer = mulgaraConfig.getSmtp(); // set the property for mail package to pickup System.setProperty(SYSTEM_MAIL, smtpServer); } } catch (MalformedURLException mue) { log.warn("Invalid URL on command line - " + mue.getMessage()); printUsage(); startServer = false; } catch (IOException ioe) { log.error(ioe.getMessage(), ioe); printUsage(); startServer = false; } catch (NumberFormatException nfe) { log.warn("Invalid port specified on command line: " + nfe.getMessage()); printUsage(); startServer = false; } catch (org.exolab.castor.xml.MarshalException me) { log.warn("Castor Marshal Exception: " + me.getMessage(), me); printUsage(); startServer = false; } catch (org.exolab.castor.xml.ValidationException ve) { log.warn("Unable to load configuration - " + ve.getMessage()); printUsage(); startServer = false; } catch (Exception e) { log.warn("Could not start embedded Mulgara server", e); startServer = false; } // return true if the server should be started, false otherwise return startServer; } /** * Creates a Mulgara server. * @param serverName the RMI binding name of the server * @param statePath the path to the directory containing server state * @param hostname the hostname to bind the Mulgara server to * @param providerClassName class name of a {@link org.mulgara.server.Session} implementation * @param serverClassName class name of a {@link org.mulgara.server.ServerMBean} * @return a Mulgara server * @throws ClassNotFoundException if <var>serverClassName</var> isn't in the classpath * @throws IOException if the <var>statePath</var> is invalid */ public ServerMBean createServer(String serverName, File statePath, String hostname, int portNumber, String providerClassName, String serverClassName) throws ClassNotFoundException, IOException { // log that we're createing a Mulgara server if (log.isDebugEnabled()) { log.debug("Creating server instance at rmi://" + hostname + "/" + serverName + " in directory " + statePath); } // Create the Mulgara server ServerMBean mbean = (ServerMBean)Beans.instantiate(getClass().getClassLoader(),serverClassName); // Set ServerMBean properties mbean.setDir(statePath); File tempDir = new File(statePath,"temp"); mbean.setTempDir(tempDir); mbean.setConfig(mulgaraConfig); if (log.isDebugEnabled()) log.debug("Set config to be: " + mulgaraConfig); // set the directory that all temporary files will be created in. if (!tempDir.mkdirs()) { tempDir = TempDir.getTempDir(); } else { TempDir.setTempDir(tempDir); } // remove any temporary files cleanUpTemporaryFiles(); mbean.setProviderClassName(providerClassName); // Check to see if the port number is not 1099 and we're using the RMI server. if ((portNumber != 1099) && (serverClassName.equals(DEFAULT_SERVER_CLASS_NAME))) { mbean.setPortNumber(portNumber); } mbean.setHostname(hostname); // Set protocol-specific properties (FIXME: hardcoded to do "name" only) try { Method setter = new PropertyDescriptor("name", mbean.getClass()).getWriteMethod(); try { setter.invoke(mbean, new Object[] {serverName}); } catch (InvocationTargetException e) { log.warn(mbean + " doesn't have a name property", e); } // Now the mbean has the hostname and server name, we can set the URI for the server info ServerInfo.setServerURI(mbean.getURI()); } catch (IllegalAccessException e) { log.warn(serverClassName + " doesn't have a public name property", e); } catch (IntrospectionException e) { log.warn(serverClassName + " doesn't have a name property", e); } // return the newly created server instance return mbean; } /** * Sets up any system properties needed by system components like JNDI and security. * @throws IOException if any files embedded within the JAR file cannot be found */ protected final void configureSystemProperties() throws IOException { boolean startedLocalRMIRegistry = false; // attempt to start a rmiregistry if (System.getProperty(DISABLE_RMI) == null) { try { // start the registry LocateRegistry.createRegistry(ServerInfo.getRMIPort()); if (log.isInfoEnabled()) log.info("RMI Registry started automatically on port " + ServerInfo.getRMIPort()); // set the flag startedLocalRMIRegistry = true; } catch (java.rmi.server.ExportException ex) { log.info("Existing RMI registry found on port " + ServerInfo.getRMIPort()); } catch (Exception ex) { log.error("Failed to start or detect RMI Registry", ex); } } // set system properties needed for RMI System.setProperty(Context.INITIAL_CONTEXT_FACTORY, CONTEXT_FACTORY); if (log.isDebugEnabled()) log.debug("No system security manager set"); // only set the security policy if a RMI registry has started if (startedLocalRMIRegistry) { if (System.getProperty(SECURITY_POLICY_PROP) == null) { if (log.isDebugEnabled()) log.debug("Started local RMI registry -> setting security policy"); URL mulgaraSecurityPolicyURL = ClassLoader.getSystemResource(RMI_SECURITY_POLICY_PATH); System.setProperty(SECURITY_POLICY_PROP, mulgaraSecurityPolicyURL.toString()); if (log.isInfoEnabled()) log.info("java.security.policy set to " + mulgaraSecurityPolicyURL.toString()); } // create a security manager System.setSecurityManager(new RMISecurityManager()); } } /** * Configure any singleton registries based on the configuration file. */ protected void configureRegistries() { FunctionResolverRegistry fnReg = FunctionResolverRegistry.getFunctionResolverRegistry(); for (XpathFunctionResolver r: mulgaraConfig.getXpathFunctionResolver()) { try { fnReg.register(r.getType()); } catch (ClassNotFoundException e) { log.error("Unable to load the XPathFunctionResolver: " + r.getType(), e); } } } /** * Prints the usage instructions for starting the server. */ public static void printUsage() { // build the usage message StringBuilder usage = new StringBuilder(); usage.append("Usage: java -jar <jarfile> "); usage.append("[-h] "); usage.append("[-n] "); usage.append("[-x] "); usage.append("[-l <url>] "); usage.append("[-c <path>] "); usage.append("[-k <hostname>] "); usage.append("[-o <hostname>] "); usage.append("[-p <port>] "); usage.append("[-r <port>] "); usage.append("[-s <servername>] "); usage.append("[-a <path>] "); usage.append("[-m <smtp>]" + eol); usage.append("" + eol); usage.append("-h, --help display this help screen" + eol); usage.append("-n, --normi disable automatic starting of the RMI registry" + eol); usage.append("-w, --nohttp disable the HTTP web service" + eol); usage.append("-x, --shutdown shutdown the local running server" + eol); usage.append("-l, --logconfig use an external logging configuration file" + eol); usage.append("-c, --serverconfig use an external server configuration file" + eol); usage.append("-k, --serverhost the hostname to bind the server to" + eol); usage.append("-o, --httphost the hostname for HTTP requests" + eol); usage.append("-p, --port the port for HTTP requests" + eol); usage.append("-g, --defaultgraph the default graph to use for SPARQL connections" + eol); usage.append("-r, --rmiport the RMI registry port" + eol); usage.append("-t, --rmiobjectport the RMI peer port for objects" + eol); usage.append("-s, --servername the (RMI) name of the server" + eol); usage.append("-a, --path the path server data will persist to, specifying " + eol + " '.' or 'temp' will use the current working directory " + eol + " or the system temporary directory respectively" + eol); usage.append("-m, --smtp the SMTP server for email notifications" + eol); usage.append(eol); usage.append("Note 1. A server can be started without any options, all options" + eol + "override default settings." + eol + eol); usage.append("Note 2. If an external configuration file is used, and other options" + eol + "are specified, the other options will take precedence over any settings" + eol + "specified in the configuration file." + eol + eol); // print the usage System.out.println(usage.toString()); } /** * Clean up any temporary files and directories. In some instances the VM does * not remove temporary files and directories. ie Windows JVM Some files maybe * left due to file locking, however they will be deleted on the next clean * up. * */ private static void cleanUpTemporaryFiles() { File tempDirectory = TempDir.getTempDir(false); // Add a filter to ensure we only delete the correct files File[] list = tempDirectory.listFiles(new TemporaryFileNameFilter()); // nothing to be removed if (list == null) return; // Remove the top level files and recursively remove all files in each directory. for (File f: list) { if (f.isDirectory()) removeDirectory(f); f.delete(); } } /** * Remove the contents of a directory. * @param directory A {@link java.io.File} representing a directory. */ private static void removeDirectory(File directory) { File[] list = directory.listFiles(); if (list == null) { log.error("Unable to remove directory: \"" + directory + "\""); return; } for (File f: list) { if (f.isDirectory()) removeDirectory(f); f.delete(); } } /** * Create an HttpServices implementation without requiring the source to be available * at compile time. Ensures that the original exception type is thrown. * @param server The server to create the services for. * @param httpHostName The name of the host for the HTTP server. * @param mulgaraConfig The configuration for the server. * @return A new HttpServices object. * @throws IOException Exception setting up with files or network. * @throws SAXException Problem reading XML configurations. * @throws ClassNotFoundException An expected class was not found. * @throws NoSuchMethodException A configured class was not built as expected. * @throws InvocationTargetException A configured class did not behave as expected. * @throws IllegalAccessException A configured class was not accessible. */ private static HttpServices createHttpServices(EmbeddedMulgaraServer server, String httpHostName, MulgaraConfig mulgaraConfig) throws IOException, SAXException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { Class<?> servicesClass = Class.forName(HttpServices.IMPL_CLASS_NAME); try { return (HttpServices)Reflect.newInstance(servicesClass, server, httpHostName, mulgaraConfig); } catch (RuntimeException e) { Throwable wrapped = e.getCause(); if (wrapped instanceof IOException) { throw (IOException)wrapped; } else if (wrapped instanceof SAXException) { throw (SAXException)wrapped; } else if (wrapped instanceof ClassNotFoundException) { throw (ClassNotFoundException)wrapped; } else if (wrapped instanceof NoSuchMethodException) { throw (NoSuchMethodException)wrapped; } else if (wrapped instanceof InvocationTargetException) { throw (InvocationTargetException)wrapped; } else if (wrapped instanceof IllegalAccessException) { throw (IllegalAccessException)wrapped; } else throw e; } } /** * Filter class for detecting temporary files created by Mulgara */ private static class TemporaryFileNameFilter implements FilenameFilter { public boolean accept(File dir, String name) { // check for files and directories with // mulgara*.jar , Jetty-*.war and JettyContext*.tmp return (((name.indexOf("mulgara") == 0) && (name.indexOf(".jar") > 0)) || ((name.indexOf("Jetty-") == 0) && (name.indexOf(".war") > 0)) || ((name.indexOf("JettyContext") == 0) && (name.indexOf(".tmp") > 0))); } } /** * A server side shutdown service to allow the BootStrap class to force a * shutdown from another JVM while on the same machine only. To override the * default port of 6789 set a system property called shutdownhook.port */ private static class ShutdownService extends Thread { private ServerSocket shutdownSocket; public ShutdownService() { // register a thread name this.setName("Server side shutdown hook"); this.setDaemon(true); } public void run() { boolean stop = false; // get the current shutdownhook port int port = EmbeddedMulgaraServer.getShutdownHookPort(); // bind to the specified socket on the local host Socket socket = null; BufferedReader input = null; try { shutdownSocket = new ServerSocket(port, 0, InetAddress.getByName("localhost")); // wait until a request to stop the server while (!stop) { // wait for a shutdown request socket = shutdownSocket.accept(); // read the response from the client input = new BufferedReader(new InputStreamReader(socket.getInputStream())); // check if the require request is correct String message = null; try { message = org.mulgara.util.io.IOUtil.readLine(input, EmbeddedMulgaraServer.SHUTDOWN_MSG.length() + 2); } finally { socket.close(); } if ((message != null) && message.equals(EmbeddedMulgaraServer.SHUTDOWN_MSG)) { stop = true; } else { if (message != null) log.error("Incorrect request to shutdown mulgara"); } } } catch (IOException ioEx) { log.error("Unable to establish shutdown socket due to an I/O exception on port " + port, ioEx); } catch (SecurityException secEx) { log.error("Unable to establish shutdown socket due to a security exception. Check security policy", secEx); } catch (Exception ex) { log.error("Unable to establish shutdown socket on port " + port, ex); } finally { if (input != null) { try { input.close(); } catch (IOException e) { log.error("Unexpected problem closing input from a socket"); } } if (socket != null) { try { socket.close(); } catch (IOException e) { log.error("Unexpected problem closing socket"); } } // attempt to close the socket try { shutdownSocket.close(); } catch (Exception ex) { log.error("Unexpected problem closing the shutdown socket", ex); } } // log that we're sutting down the servers if (log.isInfoEnabled()) { log.info("Started system exit."); } // finally // issue the shutdown if (stop) System.exit(0); } } /** * The standard shutdown hook that will get run when this server is killed normally. * This gets registered with the Runtime. */ private static class RuntimeShutdownHook extends Thread { EmbeddedMulgaraServer server; public RuntimeShutdownHook(EmbeddedMulgaraServer server) { this.server = server; // register a thread name this.setName("Standard shutdown hook"); } public void run() { // log that we're sutting down the servers if (log.isInfoEnabled()) { log.info("Shutting down server, please wait..."); } else { // regardless of the log level output this to stdout. // Note. "\n" Will give us a new line beneath a Ctrl-C System.out.println("\nShutting down server, please wait..."); } // stop RMI service ServerMBean mbean = server.getServerMBean(); if (mbean != null) { ServerState state = mbean.getState(); if (state == ServerState.STARTED) { try { mbean.stop(); } catch (Exception e) { log.error("Couldn't stop server", e); } } // close the server if (state == ServerState.STARTED || state == ServerState.STOPPED) { try { mbean.destroy(); } catch (Exception e) { log.error("Couldn't destroy server", e); } } } // shut down the SOAP server try { if (server.webServices != null) server.webServices.stop(); } catch (Exception e) { log.error("Couldn't destroy http server", e); } // log that we've shut down the servers if (log.isInfoEnabled()) { log.info("Completed shutting down server"); } else { // regardless of the log level out this to stdout. System.out.println("Completed shutting down server"); } // Clean up any temporary directories and files cleanUpTemporaryFiles(); } } }