blob: 47da91ec57e06b72fbfecf61bda7f1b4cd25b637 [file] [log] [blame]
/*
* 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.
*/
package org.apache.oodt.commons;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.*;
import org.apache.oodt.commons.util.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.NoInitialContextException;
import java.rmi.registry.Registry;
import java.util.StringTokenizer;
/** EDA Configuration.
*
* An object of this class represents the configuration information for the EDA software.
*
* @author Kelly
*/
public class Configuration {
/** The singleton configuration. */
static Configuration configuration = null;
/** Name of property that specifies the direcotries that contains XML entities. */
public static final String ENTITY_DIRS_PROP = "entity.dirs";
/** Name of the default config file. */
public static final String DEFAULT_CONFIG_FILE = ".edarc.xml";
/** Alternate config file. */
public static final String ALT_CONFIG_FILE = ".oodtrc.xml";
/** Library-location config file. */
public static final File LIB_CONFIG_FILE = new File(System.getProperty("java.home", "/") + File.separator + "lib"
+ File.separator + "edarc.xml");
/** Non-JRE library location of config file. */
public static final File ALT_LIB_CONFIG_FILE = new File(System.getProperty("java.home", "/") + File.separator + ".."
+ File.separator + "lib" + File.separator + "edarc.xml");
/** Get the singleton configuration.
*
* This method returns the singleton configuration object, or creates it if it
* doesn't yet exist. To create it, it reads the configuration file specified by
* the system property <code>org.apache.oodt.commons.Configuration.url</code> or the file in the
* user's home directory called .edarc.xml if the system property isn't
* specified. It parses the file and returns a <code>Configuration</code> object
* initialized with the data specified therein.
*
* @throws IOException If reading the configuration file fails.
* @throws SAXException If parsing the configuration file fails.
* @throws MalformedURLException If the URL specification is invalid.
* @return An initialized configuration object.
*/
public static Configuration getConfiguration() throws IOException, SAXException, MalformedURLException {
// Got one? Use it.
if (configuration != null) return configuration;
URL url;
// First preference: URL via the org.apache.oodt.commons.Configuration.url prop.
String urlString = System.getProperty("org.apache.oodt.commons.Configuration.url");
if (urlString != null) {
url = new URL(urlString);
} else {
File file = null;
// Second preference: file via the org.apache.oodt.commons.Configuration.file prop.
String filename = System.getProperty("org.apache.oodt.commons.Configuration.file");
if (filename != null) {
file = new File(filename);
if (!file.exists()) throw new IOException("File " + file + " not found");
} else {
List candidates = new ArrayList();
// Third preference: ~/.edarc.xml
File homedir = new File(System.getProperty("user.home", "/"));
File homedirfile = new File(homedir, DEFAULT_CONFIG_FILE);
candidates.add(homedirfile);
// Fourth preference: ~/.oodtrc.xml
File alt = new File(homedir, ALT_CONFIG_FILE);
candidates.add(alt);
// Fifth and sixth preferences: $EDA_HOME/conf/edarc.xml and $EDA_HOME/etc/edarc.xml
String edaHome = System.getProperty("eda.home");
if (edaHome != null) {
File edaHomeDir = new File(edaHome);
candidates.add(new File(new File(edaHomeDir, "conf"), "edarc.xml"));
candidates.add(new File(new File(edaHomeDir, "etc"), "edarc.xml"));
}
// Seventh preference: JAVA_HOME/lib/edarc.xml
candidates.add(LIB_CONFIG_FILE);
// Final preference: JAVA_HOME/../lib/edarc.xml (to get out of JRE)
candidates.add(ALT_LIB_CONFIG_FILE);
// Now find one.
boolean found = false;
for (Iterator i = candidates.iterator(); i.hasNext();) {
file = (File) i.next();
if (file.exists()) {
found = true;
break;
}
}
if (found && file == alt)
System.err.println("WARNING: Using older config file " + alt + "; rename to "
+ homedirfile + " as soon as possible.");
if (!found) {
return getEmptyConfiguration();
}
}
url = file.toURL();
}
return getConfiguration(url);
}
/** Get the singleton configuration.
*
* This method returns the singleton configuration object from a
* specified file url. It parses the file and returns a <code>Configuration</code> object
* initialized with the data specified therein. Added by Chris Mattmann 12/05/03.
*
* @throws IOException If an I/O error occurs.
* @return An initialized configuration object.
*/
public static Configuration getConfiguration(URL configFileUrl) throws SAXException, IOException {
synchronized (Configuration.class) {
if (configuration == null)
configuration = new Configuration(configFileUrl);
}
return configuration;
}
private static Configuration getEmptyConfiguration() {
synchronized (Configuration.class) {
if (configuration == null)
configuration = new Configuration();
}
return configuration;
}
/** Get the singleton configuration without exception.
*
* This method is identical to {@link #getConfiguration} but traps all checked
* exceptions. If the configuration can't be read, it returns null.
*
* @return An initialized configuration object, or null if an error occurred.
*/
public static Configuration getConfigurationWithoutException() {
// Got one? Use it. Do this out of a try block for performance.
if (configuration != null) return configuration;
// Try to get it.
try {
return getConfiguration();
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
System.err.println("Exception " + ex.getClass().getName() + " while getting configuration: "
+ ex.getMessage());
ex.printStackTrace();
return null;
}
}
Configuration() {
serverMgrPort = 7577;
nameServerStateFrequency = 6000000;
nameServerObjectKey = "StandardNS%20POA";
nameServerPort = "10000";
nameServerHost = "localhost";
nameServerVersion = "1.0";
nameServerUsingRIRProtocol = false;
webServerDocumentDirectory = new File(System.getProperty("user.home", "/") + "tomcat/webapps/ROOT");
webPort = "8080";
webHost = "localhost";
System.setProperty(WEB_PROTOCOL_PROPERTY, "http");
initializeContext();
}
/** Construct a configuration.
*
* @param url The location of the configuration.
* @throws IOException If reading the configuration file fails.
* @throws SAXParseException If parsing the configuration file fails.
*/
Configuration(URL url) throws IOException, SAXException {
this(new InputSource(url.toString()));
}
Configuration(InputSource inputSource) throws IOException, SAXException {
String systemID = inputSource.getSystemId();
if (systemID == null) inputSource.setSystemId("file:/unknown");
// Get the document
DOMParser parser = XML.createDOMParser();
parser.setEntityResolver(new ConfigurationEntityResolver());
parser.setErrorHandler(new ErrorHandler() {
public void error(SAXParseException ex) throws SAXException {
throw ex;
}
public void warning(SAXParseException ex) {
System.err.println("Warning: " + ex.getMessage());
}
public void fatalError(SAXParseException ex) throws SAXException {
System.err.println("Fatal parse error: " + ex.getMessage());
throw ex;
}
});
parser.parse(inputSource);
Document document = parser.getDocument();
XML.removeComments(document);
document.normalize();
// See if this really is a <configuration> document.
if (!document.getDocumentElement().getNodeName().equals("configuration"))
throw new SAXException("Configuration " + inputSource.getSystemId() + " is not a <configuration> document");
NodeList list = document.getDocumentElement().getChildNodes();
for (int eachChild = 0; eachChild < list.getLength(); ++eachChild) {
Node childNode = list.item(eachChild);
if (childNode.getNodeName().equals("webServer")) {
NodeList children = childNode.getChildNodes();
for (int i = 0; i < children.getLength(); ++i) {
Node node = children.item(i);
if ("host".equals(node.getNodeName()))
webHost = XML.unwrappedText(node);
else if ("port".equals(node.getNodeName()))
webPort = XML.unwrappedText(node);
else if ("dir".equals(node.getNodeName()))
webServerDocumentDirectory = new File(XML.unwrappedText(node));
}
properties.setProperty("org.apache.oodt.commons.Configuration.webServer.baseURL", getWebServerBaseURL());
if (webServerDocumentDirectory == null)
webServerDocumentDirectory = new File(System.getProperty("user.home", "/")
+ "/dev/htdocs");
} else if (childNode.getNodeName().equals("nameServer")) {
Element nameServerNode = (Element) childNode;
String nameServerStateFrequencyString = nameServerNode.getAttribute("stateFrequency");
if (nameServerStateFrequencyString == null || nameServerStateFrequencyString.length() == 0)
nameServerStateFrequency = 0;
else try {
nameServerStateFrequency = Integer.parseInt(nameServerStateFrequencyString);
} catch (NumberFormatException ex) {
throw new SAXException("Illegal nun-numeric value \"" + nameServerStateFrequencyString
+ "\" for stateFrequency attribute");
}
if (childNode.getFirstChild().getNodeName().equals("rir")) {
nameServerUsingRIRProtocol = true;
NodeList children = childNode.getFirstChild().getChildNodes();
nameServerObjectKey = children.getLength() == 1? XML.unwrappedText(children.item(0)):null;
} else {
nameServerUsingRIRProtocol = false;
nameServerVersion = null;
nameServerPort = null;
// Must be same as CORBAMgr.NS_OBJECT_KEY:
nameServerObjectKey = "StandardNS/NameServer%2DPOA/_root";
NodeList children = childNode.getFirstChild().getChildNodes();
for (int i = 0; i < children.getLength(); ++i) {
Node node = children.item(i);
if (node.getNodeName().equals("version"))
nameServerVersion = XML.unwrappedText(node);
else if (node.getNodeName().equals("host"))
nameServerHost = XML.unwrappedText(node);
else if (node.getNodeName().equals("port"))
nameServerPort = XML.unwrappedText(node);
else if (node.getNodeName().equals("objectKey"))
nameServerObjectKey = XML.unwrappedText(node);
}
}
} else if (childNode.getNodeName().equals("xml")) {
NodeList children = childNode.getChildNodes();
for (int i = 0; i < children.getLength(); ++i) {
Node xmlNode = children.item(i);
if ("entityRef".equals(xmlNode.getNodeName())) {
NodeList dirNodes = xmlNode.getChildNodes();
StringBuffer refDirs = new StringBuffer(System.getProperty(ENTITY_DIRS_PROP, ""));
for (int j = 0; j < dirNodes.getLength(); ++j)
refDirs.append(',').append(XML.unwrappedText(dirNodes.item(j)));
if (refDirs.length() > 0)
System.setProperty(ENTITY_DIRS_PROP, refDirs.charAt(0) == ','?
refDirs.substring(1) : refDirs.toString());
}
}
} else if ("serverMgr".equals(childNode.getNodeName())) {
serverMgrPort = Integer.parseInt(XML.unwrappedText(childNode.getFirstChild()));
} else if (childNode.getNodeName().equals("properties")) {
loadProperties(childNode, properties);
} else if (childNode.getNodeName().equals("programs")) {
NodeList children = childNode.getChildNodes();
for (int i = 0; i < children.getLength(); ++i) {
// They're all of type execServer---for now.
ExecServerConfig esc = new ExecServerConfig(children.item(i));
esc.getProperties().setProperty("org.apache.oodt.commons.Configuration.url", inputSource.getSystemId());
execServers.add(esc);
}
}
}
initializeContext();
}
private void initializeContext() {
contextEnvironment.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.oodt.commons.object.jndi.ObjectCtxFactory");
String registryList = System.getProperty("org.apache.oodt.commons.rmiregistries", System.getProperty("rmiregistries"));
if (registryList == null) {
String host = System.getProperty("rmiregistry.host", "localhost");
int port = Integer.getInteger("rmiregistry.port", Registry.REGISTRY_PORT).intValue();
registryList = "rmi://" + host + ":" + port;
}
contextEnvironment.put("rmiregistries", registryList);
}
/** Serialize this configuration into a serialized XML document.
*
* @return Serialized XML version of this configuration.
* @throws DOMException If an error occurs constructing the XML structure.
*/
public String toXML() throws DOMException {
Document doc = createDocument("configuration");
doc.replaceChild(toXML(doc), doc.getDocumentElement());
return XML.serialize(doc);
}
/**
*
* @param document The document to which the XML structure will belong.
* @return The root node representing this configuration.
* @throws DOMException If an error occurs constructing the XML structure.
*/
public Node toXML(Document document) throws DOMException {
// <configuration>
Element configurationNode = document.createElement("configuration");
// <webServer>
Element webServerNode = document.createElement("webServer");
configurationNode.appendChild(webServerNode);
// <webServer>
// <host>...</host><port>...</port><dir>...</dir>
XML.add(webServerNode, "host", webHost);
XML.add(webServerNode, "port", webPort);
XML.add(webServerNode, "dir", webServerDocumentDirectory.toString());
// <nameServer>
Element nameServerNode = document.createElement("nameServer");
nameServerNode.setAttribute("stateFrequency", String.valueOf(nameServerStateFrequency));
configurationNode.appendChild(nameServerNode);
// <nameServer>
// <rir> or <iiop>
if (nameServerUsingRIRProtocol) {
Element rirNode = document.createElement("rir");
nameServerNode.appendChild(rirNode);
if (nameServerObjectKey != null)
XML.add(rirNode, "objectKey", nameServerObjectKey);
} else {
Element iiopNode = document.createElement("iiop");
nameServerNode.appendChild(iiopNode);
if (nameServerVersion != null)
XML.add(iiopNode, "version", nameServerVersion);
XML.add(iiopNode, "host", nameServerHost);
if (nameServerPort != null)
XML.add(iiopNode, "port", nameServerPort);
if (nameServerObjectKey != null)
XML.add(iiopNode, "objectKey", nameServerObjectKey);
}
// <xml><entityRef><dir>...
if (!getEntityRefDirs().isEmpty()) {
Element xmlNode = document.createElement("xml");
configurationNode.appendChild(xmlNode);
Element entityRefNode = document.createElement("entityRef");
xmlNode.appendChild(entityRefNode);
XML.add(entityRefNode, "dir", getEntityRefDirs());
}
// <serverMgr><port>...
if (getServerMgrPort() != 0) {
Element serverMgrNode = document.createElement("serverMgr");
configurationNode.appendChild(serverMgrNode);
XML.add(serverMgrNode, "port", String.valueOf(getServerMgrPort()));
}
// Global <properties>...</properties>
if (properties.size() > 0)
dumpProperties(properties, configurationNode);
// <programs>...
if (execServers.size() > 0) {
Element programsNode = document.createElement("programs");
configurationNode.appendChild(programsNode);
for (Iterator i = execServers.iterator(); i.hasNext();) {
ExecServerConfig esc = (ExecServerConfig) i.next();
Element execServerNode = document.createElement("execServer");
programsNode.appendChild(execServerNode);
XML.add(execServerNode, "class", esc.getClassName());
XML.add(execServerNode, "objectKey", esc.getObjectKey());
XML.add(execServerNode, "host", esc.getPreferredHost().toString());
if (esc.getProperties().size() > 0)
dumpProperties(esc.getProperties(), execServerNode);
}
}
return configurationNode;
}
/** Merge the properties in the configuration into the given properties.
*
* Properties that already exist in the <var>targetProps</var> won't be
* overwritten.
*
* @param targetProps The target properties.
*/
public void mergeProperties(Properties targetProps) {
for (Iterator i = properties.entrySet().iterator(); i.hasNext();) {
Map.Entry entry = (Map.Entry) i.next();
if (!targetProps.containsKey(entry.getKey()))
targetProps.put(entry.getKey(), entry.getValue());
}
}
/** Get the exec-server configurations.
*
* @return A collection of exec server configurations, each of class {@link ExecServerConfig}.
*/
public Collection getExecServerConfigs() {
return execServers;
}
/** Get the exec-server configurations.
*
* @param clazz The class of exec servers that will be returned.
* @return A collection of exec server configurations, each of class {@link ExecServerConfig}.
*/
public Collection getExecServerConfigs(Class clazz) {
String className = clazz.getName();
Collection execServerConfigs = new ArrayList();
for (Iterator i = execServers.iterator(); i.hasNext();) {
ExecServerConfig exec = (ExecServerConfig) i.next();
if (className.equals(exec.getClassName()))
execServerConfigs.add(exec);
}
return execServerConfigs;
}
/** Get an exec-server configuration.
*
* @param objectKey The object key of the Exec Server to retrieve.
* @return An {@link ExecServerConfig} or null if object key not found.
*/
public ExecServerConfig getExecServerConfig(String objectKey) {
ExecServerConfig execServerConfig = null;
for (Iterator i = execServers.iterator(); i.hasNext() && execServerConfig == null;) {
ExecServerConfig exec = (ExecServerConfig) i.next();
if (objectKey.equals(exec.getObjectKey()))
execServerConfig = exec;
}
return execServerConfig;
}
/** Get the web server base URL.
*
* @return The base web server URL.
*/
public String getWebServerBaseURL() {
String proto = System.getProperty(WEB_PROTOCOL_PROPERTY);
if (proto == null) {
if ("443".equals(webPort)) proto = "https";
else proto = "http";
}
return proto + "://" + webHost + ":" + webPort;
}
/** Get the web server document directory.
*
* @return The document directory.
*/
public File getWebServerDocumentDirectory() {
return webServerDocumentDirectory;
}
/** Get the name server URL.
*
* @return The name server URL.
*/
public String getNameServerURL() {
return getWebServerBaseURL() + "/ns.ior";
}
/** Get the name server port, if any.
*
* @return The port.
*/
public String getNameServerPort() {
return nameServerPort;
}
/** Get the frequency with which the name server saves its state.
*
* @return The state-save frequency in milliseconds; <= 0 means never save state.
*/
public int getNameServerStateFrequency() {
return nameServerStateFrequency;
}
/** Get the object context.
*
* @return The object context based on this configuration.
* @throws NamingException If the context can't be created.
*/
public Context getObjectContext() throws NamingException {
Context c = null;
final String className = (String) contextEnvironment.get(javax.naming.Context.INITIAL_CONTEXT_FACTORY);
if (className == null)
c = new InitialContext(contextEnvironment);
else try {
// Work around iPlanet bug. JNDI uses the thread's context class
// loader to load the initial context factory class. For some
// reason, that loader fails to find things in iPlanet's
// classpath, such as the EDA initial context factory. Here, we
// cut a new thread and explicitly set its context class loader to
// the application class loader. When JNDI looks up the initial
// context factory, the thread's context class loader is the app
// class loader, which succeeds.
Class clazz = Class.forName(className);
final ClassLoader loader = clazz.getClassLoader();
InitialContextThread thread = new InitialContextThread(loader);
thread.start();
try {
thread.join();
} catch (InterruptedException ex) {
throw new NoInitialContextException("Initial context thread interrupted: " + ex.getMessage());
}
c = thread.getContext();
if (c == null)
throw thread.getException();
} catch (ClassNotFoundException ex) {
throw new NoInitialContextException("Class " + className + " not found");
}
return c;
}
/** Get the entity reference directories.
*
* @return A list of {@link java.lang.String}s naming directories for entity references.
*/
public List getEntityRefDirs() {
List dirs = new ArrayList();
for (StringTokenizer t = new StringTokenizer(System.getProperty(ENTITY_DIRS_PROP, ""), ",;|"); t.hasMoreTokens();)
dirs.add(t.nextToken());
return dirs;
}
/** Get the port number on which the server manager is listening.
*
* @return The port number, or 0 if there is no server manager.
*/
public int getServerMgrPort() {
return serverMgrPort;
}
/** Load the properties described in an XML properties element into the
* given properties object.
*
* @param propertiesNode The XML node which is a <code>&lt;properties&gt;</code> element.
* @param props The properties object to load with properties from <var>propertiesNode</var>.
*/
static void loadProperties(Node propertiesNode, Properties props) {
NodeList children = propertiesNode.getChildNodes();
for (int i = 0; i < children.getLength(); i += 2) {
String key = XML.unwrappedText(children.item(i));
String value = XML.unwrappedText(children.item(i+1));
props.setProperty(key, value);
}
}
/** Dump the properties from the given properties object in XML form, appending
* them to the given node under a &lt;properties&gt; element.
*
* @param props The properties to dump in XML form.
* @param node The node to which to append the &lt;properties&gt; element.
* @throws DOMException If a DOM error occurs.
*/
static void dumpProperties(Properties props, Node node) {
Element propertiesElement = node.getOwnerDocument().createElement("properties");
node.appendChild(propertiesElement);
for (Iterator i = props.entrySet().iterator(); i.hasNext();) {
Map.Entry entry = (Map.Entry) i.next();
XML.add(propertiesElement, "key", (String) entry.getKey());
XML.add(propertiesElement, "value", (String) entry.getValue());
}
}
/** Create a new XML document with the configuration DTD.
*
* @param name Name to give to the document element.
* @returns An XML DOM document with the doctype and the root document empty element in place.
* @throws DOMException If we can't create the document.
*/
static Document createDocument(String documentElementName) throws DOMException {
DocumentType docType = XML.getDOMImplementation().createDocumentType(documentElementName, DTD_FPI, DTD_URL);
Document doc = XML.getDOMImplementation().createDocument(/*namespaceURI*/null, documentElementName, docType);
return doc;
}
/** The formal public identifier (FPI) of the configuration document type definition (DTD). */
public static final String DTD_FPI = "-//JPL//DTD EDA Configuration 1.0//EN";
/** The old formal public identifier (FPI) of the configuration document type definition (DTD). */
public static final String DTD_OLD_FPI = "-//JPL//DTD OODT Configuration 1.0//EN";
/** The system identifier of the configuration document type definition (DTD). */
public static final String DTD_URL = "http://oodt.jpl.nasa.gov/edm-commons/Configuration.dtd";
/** Name of the system property that names the web protocol to use. */
public static final String WEB_PROTOCOL_PROPERTY = "org.apache.oodt.commons.Configuration.webProtocol";
/** Global properties. */
private Properties properties = new Properties();
/** Object context environment. */
Hashtable contextEnvironment = new Hashtable();
/** Exec-servers. */
private List execServers = new ArrayList();
/** Web server host. */
private String webHost;
/** Web server port. */
private String webPort;
/** Web server doc dir. */
private File webServerDocumentDirectory;
/** Name server using rir protocol.
*
* If false, then it's using iiop.
*/
private boolean nameServerUsingRIRProtocol;
/** Name server version. */
private String nameServerVersion;
/** Name server host. */
private String nameServerHost;
/** Name server port. */
private String nameServerPort;
/** Name server object key. */
private String nameServerObjectKey;
/** How often the name server saves its state. */
private int nameServerStateFrequency;
/** On what port the server manager will listen. */
private int serverMgrPort;
/** Thread to set a context class loader and get a JNDI initial context. */
private class InitialContextThread extends Thread {
/** Ctor
*
* @param loader What class loader to use as thread's context class loader.
*/
public InitialContextThread(ClassLoader loader) {
setContextClassLoader(loader);
}
public void run() {
try {
context = new InitialContext(contextEnvironment);
} catch (NamingException ex) {
exception = ex;
} catch (Throwable t) {
System.err.println("Unexpected throwable " + t.getClass().getName() + " getting initial context: "
+ t.getMessage());
t.printStackTrace();
}
}
/** Get the context.
*
* <strong>Warning!</strong> Do not call this method until the thread terminates.
*
* @return The context, or null if the context could not be retrieved.
*/
public Context getContext() {
return context;
}
/** Get any exception.
*
* @return Any exception that occurred while retrieving the context.
*/
public NamingException getException() {
return exception;
}
/** JNDI context. */
private Context context;
/** Any exception. */
private NamingException exception;
}
}