View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.oodt.commons;
19  
20  import java.io.*;
21  import java.net.MalformedURLException;
22  import java.net.URL;
23  import java.net.UnknownHostException;
24  import java.util.*;
25  import org.apache.oodt.commons.util.*;
26  import org.w3c.dom.*;
27  import org.xml.sax.*;
28  import javax.naming.Context;
29  import javax.naming.InitialContext;
30  import javax.naming.NamingException;
31  import javax.naming.NoInitialContextException;
32  import java.rmi.registry.Registry;
33  import java.util.StringTokenizer;
34  
35  /** EDA Configuration.
36   *
37   * An object of this class represents the configuration information for the EDA software.
38   *
39   * @author Kelly
40   */
41  public class Configuration {
42  	/** The singleton configuration. */
43  	static Configuration configuration = null;
44  
45  	/** Name of property that specifies the direcotries that contains XML entities. */
46  	public static final String ENTITY_DIRS_PROP = "entity.dirs";
47  
48  	/** Name of the default config file. */
49  	public static final String DEFAULT_CONFIG_FILE = ".edarc.xml";
50  
51  	/** Alternate config file. */
52  	public static final String ALT_CONFIG_FILE = ".oodtrc.xml";
53  
54  	/** Library-location config file. */
55  	public static final File LIB_CONFIG_FILE = new File(System.getProperty("java.home", "/") + File.separator + "lib"
56  		+ File.separator + "edarc.xml");
57  
58  	/** Non-JRE library location of config file. */
59  	public static final File ALT_LIB_CONFIG_FILE = new File(System.getProperty("java.home", "/") + File.separator + ".."
60  		+ File.separator + "lib" + File.separator + "edarc.xml");
61  
62  	 /** Get the singleton configuration.
63  	  *
64  	  * This method returns the singleton configuration object, or creates it if it
65  	  * doesn't yet exist.  To create it, it reads the configuration file specified by
66  	  * the system property <code>org.apache.oodt.commons.Configuration.url</code> or the file in the
67  	  * user's home directory called .edarc.xml if the system property isn't
68  	  * specified.  It parses the file and returns a <code>Configuration</code> object
69  	  * initialized with the data specified therein.
70  	  *
71  	  * @throws IOException If reading the configuration file fails.
72  	  * @throws SAXException If parsing the configuration file fails.
73  	  * @throws MalformedURLException If the URL specification is invalid.
74  	  * @return An initialized configuration object.
75  	  */
76  	 public static Configuration getConfiguration() throws IOException, SAXException, MalformedURLException {
77  		 // Got one?  Use it.
78  		 if (configuration != null) return configuration;
79  
80  		 URL url;
81  
82  		 // First preference: URL via the org.apache.oodt.commons.Configuration.url prop.
83  		 String urlString = System.getProperty("org.apache.oodt.commons.Configuration.url");
84  		 if (urlString != null) {
85  			 url = new URL(urlString);
86  		 } else {
87  			 File file = null;
88  
89  			 // Second preference: file via the org.apache.oodt.commons.Configuration.file prop.
90  			 String filename = System.getProperty("org.apache.oodt.commons.Configuration.file");
91  			 if (filename != null) {
92  				 file = new File(filename);
93  				 if (!file.exists()) throw new IOException("File " + file + " not found");
94  			 } else {
95  				 List candidates = new ArrayList();
96  
97  				 // Third preference: ~/.edarc.xml
98  				 File homedir = new File(System.getProperty("user.home", "/"));
99  				 File homedirfile = new File(homedir, DEFAULT_CONFIG_FILE);
100 				 candidates.add(homedirfile);
101 
102 				 // Fourth preference: ~/.oodtrc.xml
103 				 File alt = new File(homedir, ALT_CONFIG_FILE);
104 				 candidates.add(alt);
105 
106 				 // Fifth and sixth preferences: $EDA_HOME/conf/edarc.xml and $EDA_HOME/etc/edarc.xml
107 				 String edaHome = System.getProperty("eda.home");
108 				 if (edaHome != null) {
109 					 File edaHomeDir = new File(edaHome);
110 					 candidates.add(new File(new File(edaHomeDir, "conf"), "edarc.xml"));
111 					 candidates.add(new File(new File(edaHomeDir, "etc"), "edarc.xml"));
112 				 }
113 
114 				 // Seventh preference: JAVA_HOME/lib/edarc.xml
115 				 candidates.add(LIB_CONFIG_FILE);
116 
117 				 // Final preference: JAVA_HOME/../lib/edarc.xml (to get out of JRE)
118 				 candidates.add(ALT_LIB_CONFIG_FILE);
119 
120 				 // Now find one.
121 				 boolean found = false;
122 				 for (Iterator i = candidates.iterator(); i.hasNext();) {
123 					 file = (File) i.next();
124 					 if (file.exists()) {
125 						 found = true;
126 						 break;
127 					 }
128 				 }
129 				 if (found && file == alt)
130 					 System.err.println("WARNING: Using older config file " + alt + "; rename to "
131 						 + homedirfile + " as soon as possible.");
132 				 if (!found) {
133 					 return getEmptyConfiguration();
134 				 }
135 			 }
136 			 url = file.toURL();
137 		 }
138 
139 		 return getConfiguration(url);
140 
141 	 }
142 
143 	/** Get the singleton configuration.
144 	 *
145 	 * This method returns the singleton configuration object from a 
146 	 * specified file url.  It parses the file and returns a <code>Configuration</code> object
147 	 * initialized with the data specified therein.  Added by Chris Mattmann 12/05/03.
148 	 *
149 	 * @throws IOException If an I/O error occurs.
150 	 * @return An initialized configuration object.
151 	 */
152 	public static Configuration getConfiguration(URL configFileUrl) throws SAXException, IOException {
153 		synchronized (Configuration.class) {
154 			if (configuration == null)
155 				configuration = new Configuration(configFileUrl);
156 		}
157 		return configuration;
158 	}
159 
160 	private static Configuration getEmptyConfiguration() {
161 		synchronized (Configuration.class) {
162 			if (configuration == null)
163 				configuration = new Configuration();
164 		}
165 		return configuration;
166 	}
167 
168 
169 	/** Get the singleton configuration without exception.
170 	 *
171 	 * This method is identical to {@link #getConfiguration} but traps all checked
172 	 * exceptions.  If the configuration can't be read, it returns null.
173 	 *
174 	 * @return An initialized configuration object, or null if an error occurred.
175 	 */
176 	public static Configuration getConfigurationWithoutException() {
177 		// Got one?  Use it.  Do this out of a try block for performance.
178 		if (configuration != null) return configuration;
179 
180 		// Try to get it.
181 		try {
182 			return getConfiguration();
183 		} catch (RuntimeException ex) {
184 			throw ex;
185 		} catch (Exception ex) {
186 			System.err.println("Exception " + ex.getClass().getName() + " while getting configuration: "
187 				+ ex.getMessage());
188 			ex.printStackTrace();
189 			return null;
190 		}
191 	}
192 
193 	Configuration() {
194 		serverMgrPort = 7577;
195 		nameServerStateFrequency = 6000000;
196 		nameServerObjectKey = "StandardNS%20POA";
197 		nameServerPort = "10000";
198 		nameServerHost = "localhost";
199 		nameServerVersion = "1.0";
200 		nameServerUsingRIRProtocol = false;
201 		webServerDocumentDirectory = new File(System.getProperty("user.home", "/") + "tomcat/webapps/ROOT");
202 		webPort = "8080";
203 		webHost = "localhost";
204 		System.setProperty(WEB_PROTOCOL_PROPERTY, "http");
205 		initializeContext();
206 	}
207 
208 	/** Construct a configuration.
209 	 *
210 	 * @param url The location of the configuration.
211 	 * @throws IOException If reading the configuration file fails.
212 	 * @throws SAXParseException If parsing the configuration file fails.
213 	 */
214 	Configuration(URL url) throws IOException, SAXException {
215 		this(new InputSource(url.toString()));
216 	}
217 
218 	Configuration(InputSource inputSource) throws IOException, SAXException {
219 		String systemID = inputSource.getSystemId();
220 		if (systemID == null) inputSource.setSystemId("file:/unknown");
221 
222 		// Get the document
223 		DOMParser parser = XML.createDOMParser();
224 		parser.setEntityResolver(new ConfigurationEntityResolver());
225 		parser.setErrorHandler(new ErrorHandler() {
226 			public void error(SAXParseException ex) throws SAXException {
227 				throw ex;
228 			}
229 			public void warning(SAXParseException ex) {
230 				System.err.println("Warning: " + ex.getMessage());
231 			}
232 			public void fatalError(SAXParseException ex) throws SAXException {
233 				System.err.println("Fatal parse error: " + ex.getMessage());
234 				throw ex;
235 			}
236 		});
237 		parser.parse(inputSource);
238 		Document document = parser.getDocument();
239 		XML.removeComments(document);
240 		document.normalize();
241 		
242 		// See if this really is a <configuration> document.
243 		if (!document.getDocumentElement().getNodeName().equals("configuration"))
244 			throw new SAXException("Configuration " + inputSource.getSystemId() + " is not a <configuration> document");
245 
246 		NodeList list = document.getDocumentElement().getChildNodes();
247 		for (int eachChild = 0; eachChild < list.getLength(); ++eachChild) {
248 			Node childNode = list.item(eachChild);
249 			if (childNode.getNodeName().equals("webServer")) {
250 				NodeList children = childNode.getChildNodes();
251 				for (int i = 0; i < children.getLength(); ++i) {
252 					Node node = children.item(i);
253 					if ("host".equals(node.getNodeName()))
254 						webHost = XML.unwrappedText(node);
255 					else if ("port".equals(node.getNodeName()))
256 						webPort = XML.unwrappedText(node);
257 					else if ("dir".equals(node.getNodeName()))
258 						webServerDocumentDirectory = new File(XML.unwrappedText(node));
259 				}					
260 				properties.setProperty("org.apache.oodt.commons.Configuration.webServer.baseURL", getWebServerBaseURL());
261 				if (webServerDocumentDirectory == null)
262 					webServerDocumentDirectory = new File(System.getProperty("user.home", "/")
263 						+ "/dev/htdocs");
264 			} else if (childNode.getNodeName().equals("nameServer")) {
265 				Element nameServerNode = (Element) childNode;
266 				String nameServerStateFrequencyString = nameServerNode.getAttribute("stateFrequency");
267 				if (nameServerStateFrequencyString == null || nameServerStateFrequencyString.length() == 0)
268 					nameServerStateFrequency = 0;
269 				else try {
270 					nameServerStateFrequency = Integer.parseInt(nameServerStateFrequencyString);
271 				} catch (NumberFormatException ex) {
272 					throw new SAXException("Illegal nun-numeric value \"" + nameServerStateFrequencyString
273 						+ "\" for stateFrequency attribute");
274 				}
275 				if (childNode.getFirstChild().getNodeName().equals("rir")) {
276 					nameServerUsingRIRProtocol = true;
277 					NodeList children = childNode.getFirstChild().getChildNodes();
278 					nameServerObjectKey = children.getLength() == 1? XML.unwrappedText(children.item(0)):null;
279 				} else {
280 					nameServerUsingRIRProtocol = false;
281 					nameServerVersion = null;
282 					nameServerPort = null;
283 					// Must be same as CORBAMgr.NS_OBJECT_KEY:
284 					nameServerObjectKey = "StandardNS/NameServer%2DPOA/_root";
285 					NodeList children = childNode.getFirstChild().getChildNodes();
286 					for (int i = 0; i < children.getLength(); ++i) {
287 						Node node = children.item(i);
288 						if (node.getNodeName().equals("version"))
289 							nameServerVersion = XML.unwrappedText(node);
290 						else if (node.getNodeName().equals("host"))
291 							nameServerHost = XML.unwrappedText(node);
292 						else if (node.getNodeName().equals("port"))
293 							nameServerPort = XML.unwrappedText(node);
294 						else if (node.getNodeName().equals("objectKey"))
295 							nameServerObjectKey = XML.unwrappedText(node);
296 					}
297 				}
298 			} else if (childNode.getNodeName().equals("xml")) {
299 				NodeList children = childNode.getChildNodes();
300 				for (int i = 0; i < children.getLength(); ++i) {
301 					Node xmlNode = children.item(i);
302 					if ("entityRef".equals(xmlNode.getNodeName())) {
303 						NodeList dirNodes = xmlNode.getChildNodes();
304 						StringBuffer refDirs = new StringBuffer(System.getProperty(ENTITY_DIRS_PROP, ""));
305 						for (int j = 0; j < dirNodes.getLength(); ++j)
306 							refDirs.append(',').append(XML.unwrappedText(dirNodes.item(j)));
307 						if (refDirs.length() > 0)
308 							System.setProperty(ENTITY_DIRS_PROP, refDirs.charAt(0) == ','?
309 								refDirs.substring(1) : refDirs.toString());
310 					}
311 				}
312 			} else if ("serverMgr".equals(childNode.getNodeName())) {
313 				serverMgrPort = Integer.parseInt(XML.unwrappedText(childNode.getFirstChild()));
314 			} else if (childNode.getNodeName().equals("properties")) {
315 				loadProperties(childNode, properties);
316 			} else if (childNode.getNodeName().equals("programs")) {
317 				NodeList children = childNode.getChildNodes();
318 				for (int i = 0; i < children.getLength(); ++i) {
319 					// They're all of type execServer---for now.
320 					ExecServerConfig esc = new ExecServerConfig(children.item(i));
321 					esc.getProperties().setProperty("org.apache.oodt.commons.Configuration.url", inputSource.getSystemId());
322 					execServers.add(esc);
323 				}
324 			}
325 		}
326 
327 		initializeContext();
328 	}
329 
330 	private void initializeContext() {
331 		contextEnvironment.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.oodt.commons.object.jndi.ObjectCtxFactory");
332 		String registryList = System.getProperty("org.apache.oodt.commons.rmiregistries", System.getProperty("rmiregistries"));
333 		if (registryList == null) {
334 			String host = System.getProperty("rmiregistry.host", "localhost");
335 			int port = Integer.getInteger("rmiregistry.port", Registry.REGISTRY_PORT).intValue();
336 			registryList = "rmi://" + host + ":" + port;
337 		}
338 		contextEnvironment.put("rmiregistries", registryList);
339 	}
340 
341 	/** Serialize this configuration into a serialized XML document.
342 	 *
343 	 * @return Serialized XML version of this configuration.
344 	 * @throws DOMException If an error occurs constructing the XML structure.
345 	 */
346 	public String toXML() throws DOMException {
347 		Document doc = createDocument("configuration");
348 		doc.replaceChild(toXML(doc), doc.getDocumentElement());
349 		return XML.serialize(doc);
350 	}
351 
352 	/** 
353 	 *
354 	 * @param document The document to which the XML structure will belong.
355 	 * @return The root node representing this configuration.
356 	 * @throws DOMException If an error occurs constructing the XML structure.
357 	 */
358 	public Node toXML(Document document) throws DOMException {
359 		// <configuration>
360 		Element configurationNode = document.createElement("configuration");
361 
362 		// <webServer>
363 		Element webServerNode = document.createElement("webServer");
364 		configurationNode.appendChild(webServerNode);
365 
366 		// <webServer>
367 		//   <host>...</host><port>...</port><dir>...</dir>
368 		XML.add(webServerNode, "host", webHost);
369 		XML.add(webServerNode, "port", webPort);
370 		XML.add(webServerNode, "dir", webServerDocumentDirectory.toString());
371 
372 		// <nameServer>
373 		Element nameServerNode = document.createElement("nameServer");
374 		nameServerNode.setAttribute("stateFrequency", String.valueOf(nameServerStateFrequency));
375 		configurationNode.appendChild(nameServerNode);
376 
377 		// <nameServer>
378 		//   <rir> or <iiop>
379 		if (nameServerUsingRIRProtocol) {
380 			Element rirNode = document.createElement("rir");
381 			nameServerNode.appendChild(rirNode);
382 			if (nameServerObjectKey != null)
383 				XML.add(rirNode, "objectKey", nameServerObjectKey);
384 		} else {
385 			Element iiopNode = document.createElement("iiop");
386 			nameServerNode.appendChild(iiopNode);
387 			if (nameServerVersion != null)
388 				XML.add(iiopNode, "version", nameServerVersion);
389 			XML.add(iiopNode, "host", nameServerHost);
390 			if (nameServerPort != null)
391 				XML.add(iiopNode, "port", nameServerPort);
392 			if (nameServerObjectKey != null)
393 				XML.add(iiopNode, "objectKey", nameServerObjectKey);
394 		}
395 
396 		// <xml><entityRef><dir>...
397 		if (!getEntityRefDirs().isEmpty()) {
398 			Element xmlNode = document.createElement("xml");
399 			configurationNode.appendChild(xmlNode);
400 			Element entityRefNode = document.createElement("entityRef");
401 			xmlNode.appendChild(entityRefNode);
402 			XML.add(entityRefNode, "dir", getEntityRefDirs());
403 		}
404 
405 		// <serverMgr><port>...
406 		if (getServerMgrPort() != 0) {
407 			Element serverMgrNode = document.createElement("serverMgr");
408 			configurationNode.appendChild(serverMgrNode);
409 			XML.add(serverMgrNode, "port", String.valueOf(getServerMgrPort()));
410 		}
411 
412 		// Global <properties>...</properties>
413 		if (properties.size() > 0)
414 			dumpProperties(properties, configurationNode);
415 
416 		// <programs>...
417 		if (execServers.size() > 0) {
418 			Element programsNode = document.createElement("programs");
419 			configurationNode.appendChild(programsNode);
420 
421 			for (Iterator i = execServers.iterator(); i.hasNext();) {
422 				ExecServerConfig esc = (ExecServerConfig) i.next();
423 				Element execServerNode = document.createElement("execServer");
424 				programsNode.appendChild(execServerNode);
425 				XML.add(execServerNode, "class", esc.getClassName());
426 				XML.add(execServerNode, "objectKey", esc.getObjectKey());
427 				XML.add(execServerNode, "host", esc.getPreferredHost().toString());
428 				if (esc.getProperties().size() > 0)
429 					dumpProperties(esc.getProperties(), execServerNode);
430 			}
431 		}
432 
433 		return configurationNode;
434 	}
435 
436 	/** Merge the properties in the configuration into the given properties.
437 	 *
438 	 * Properties that already exist in the <var>targetProps</var> won't be
439 	 * overwritten.
440 	 *
441 	 * @param targetProps The target properties.
442 	 */
443 	public void mergeProperties(Properties targetProps) {
444 		for (Iterator i = properties.entrySet().iterator(); i.hasNext();) {
445 			Map.Entry entry = (Map.Entry) i.next();
446 			if (!targetProps.containsKey(entry.getKey()))
447 				targetProps.put(entry.getKey(), entry.getValue());
448 		}
449 	}
450 
451 	/** Get the exec-server configurations.
452 	 *
453 	 * @return A collection of exec server configurations, each of class {@link ExecServerConfig}.
454 	 */
455 	public Collection getExecServerConfigs() {
456 		return execServers;
457 	}
458 
459         /** Get the exec-server configurations.
460          *
461          * @param clazz The class of exec servers that will be returned.
462          * @return A collection of exec server configurations, each of class {@link ExecServerConfig}.
463          */
464         public Collection getExecServerConfigs(Class clazz) {
465                 String className = clazz.getName();
466                 Collection execServerConfigs = new ArrayList();
467                 for (Iterator i = execServers.iterator(); i.hasNext();) {
468                         ExecServerConfig exec = (ExecServerConfig) i.next();
469                         if (className.equals(exec.getClassName()))
470                                 execServerConfigs.add(exec);
471                 }
472                 return execServerConfigs;
473         }
474 
475         /** Get an exec-server configuration.
476          *
477          * @param objectKey The object key of the Exec Server to retrieve.
478          * @return An {@link ExecServerConfig} or null if object key not found.
479          */
480         public ExecServerConfig getExecServerConfig(String objectKey) {
481                 ExecServerConfig execServerConfig = null;
482                 for (Iterator i = execServers.iterator(); i.hasNext() && execServerConfig == null;) {
483                         ExecServerConfig exec = (ExecServerConfig) i.next();
484                         if (objectKey.equals(exec.getObjectKey()))
485                                 execServerConfig = exec;
486                 }
487                 return execServerConfig;
488         }
489 
490 	/** Get the web server base URL.
491 	 *
492 	 * @return The base web server URL.
493 	 */
494 	public String getWebServerBaseURL() {
495 		String proto = System.getProperty(WEB_PROTOCOL_PROPERTY);
496 		if (proto == null) {
497 			if ("443".equals(webPort)) proto = "https";
498 			else proto = "http";
499 		}
500 		return proto + "://" + webHost + ":" + webPort;
501 	}
502 
503 	/** Get the web server document directory.
504 	 *
505 	 * @return The document directory.
506 	 */
507 	public File getWebServerDocumentDirectory() {
508 		return webServerDocumentDirectory;
509 	}
510 
511 	/** Get the name server URL.
512 	 *
513 	 * @return The name server URL.
514 	 */
515 	public String getNameServerURL() {
516 		return getWebServerBaseURL() + "/ns.ior";
517 	}
518 
519 	/** Get the name server port, if any.
520 	 *
521 	 * @return The port.
522 	 */
523 	public String getNameServerPort() {
524 		return nameServerPort;
525 	}
526 
527 	/** Get the frequency with which the name server saves its state.
528 	 *
529 	 * @return The state-save frequency in milliseconds; <= 0 means never save state.
530 	 */
531 	public int getNameServerStateFrequency() {
532 		return nameServerStateFrequency;
533 	}
534 
535 	/** Get the object context.
536 	 *
537 	 * @return The object context based on this configuration.
538 	 * @throws NamingException If the context can't be created.
539 	 */
540 	public Context getObjectContext() throws NamingException {
541 		Context c = null;
542 		final String className = (String) contextEnvironment.get(javax.naming.Context.INITIAL_CONTEXT_FACTORY);
543 		if (className == null)
544 			c = new InitialContext(contextEnvironment);
545 		else try {
546 			// Work around iPlanet bug.  JNDI uses the thread's context class
547 			// loader to load the initial context factory class.  For some
548 			// reason, that loader fails to find things in iPlanet's
549 			// classpath, such as the EDA initial context factory.  Here, we
550 			// cut a new thread and explicitly set its context class loader to
551 			// the application class loader.  When JNDI looks up the initial
552 			// context factory, the thread's context class loader is the app
553 			// class loader, which succeeds.
554 			Class clazz = Class.forName(className);
555 			final ClassLoader loader = clazz.getClassLoader();
556 			InitialContextThread thread = new InitialContextThread(loader);
557 			thread.start();
558 			try {
559 				thread.join();
560 			} catch (InterruptedException ex) {
561 				throw new NoInitialContextException("Initial context thread interrupted: " + ex.getMessage());
562 			}
563 			c = thread.getContext();
564 			if (c == null)
565 				throw thread.getException();
566 		} catch (ClassNotFoundException ex) {
567 			throw new NoInitialContextException("Class " + className + " not found");
568 		}
569 		return c;
570 	}
571 
572 	/** Get the entity reference directories.
573 	 *
574 	 * @return A list of {@link java.lang.String}s naming directories for entity references.
575 	 */
576 	public List getEntityRefDirs() {
577 		List dirs = new ArrayList();
578 		for (StringTokenizer t = new StringTokenizer(System.getProperty(ENTITY_DIRS_PROP, ""), ",;|"); t.hasMoreTokens();)
579 			dirs.add(t.nextToken());
580 		return dirs;
581 	}
582 
583 	/** Get the port number on which the server manager is listening.
584 	 *
585 	 * @return The port number, or 0 if there is no server manager.
586 	 */
587 	public int getServerMgrPort() {
588 		return serverMgrPort;
589 	}
590 
591 	/** Load the properties described in an XML properties element into the
592 	 * given properties object.
593 	 *
594 	 * @param propertiesNode The XML node which is a <code>&lt;properties&gt;</code> element.
595 	 * @param props The properties object to load with properties from <var>propertiesNode</var>.
596 	 */
597 	static void loadProperties(Node propertiesNode, Properties props) {
598 		NodeList children = propertiesNode.getChildNodes();
599 		for (int i = 0; i < children.getLength(); i += 2) {
600 			String key = XML.unwrappedText(children.item(i));
601 			String value = XML.unwrappedText(children.item(i+1));
602 			props.setProperty(key, value);
603 		}
604 	}
605 
606 	/** Dump the properties from the given properties object in XML form, appending
607 	 * them to the given node under a &lt;properties&gt; element.
608 	 *
609 	 * @param props The properties to dump in XML form.
610 	 * @param node The node to which to append the &lt;properties&gt; element.
611 	 * @throws DOMException If a DOM error occurs.
612 	 */
613 	static void dumpProperties(Properties props, Node node) {
614 		Element propertiesElement = node.getOwnerDocument().createElement("properties");
615 		node.appendChild(propertiesElement);
616 		for (Iterator i = props.entrySet().iterator(); i.hasNext();) {
617 			Map.Entry entry = (Map.Entry) i.next();
618 			XML.add(propertiesElement, "key", (String) entry.getKey());
619 			XML.add(propertiesElement, "value", (String) entry.getValue());
620 		}
621 	}
622 
623 	/** Create a new XML document with the configuration DTD.
624 	 *
625 	 * @param name Name to give to the document element.
626 	 * @returns An XML DOM document with the doctype and the root document empty element in place.
627 	 * @throws DOMException If we can't create the document.
628 	 */
629 	static Document createDocument(String documentElementName) throws DOMException {
630 		DocumentType docType = XML.getDOMImplementation().createDocumentType(documentElementName, DTD_FPI, DTD_URL);
631 		Document doc = XML.getDOMImplementation().createDocument(/*namespaceURI*/null, documentElementName, docType);
632 		return doc;
633 	}
634 
635 	/** The formal public identifier (FPI) of the configuration document type definition (DTD). */
636 	public static final String DTD_FPI = "-//JPL//DTD EDA Configuration 1.0//EN";
637 	
638 	/** The old formal public identifier (FPI) of the configuration document type definition (DTD). */
639 	public static final String DTD_OLD_FPI = "-//JPL//DTD OODT Configuration 1.0//EN";
640 
641 	/** The system identifier of the configuration document type definition (DTD). */
642 	public static final String DTD_URL = "http://oodt.jpl.nasa.gov/edm-commons/Configuration.dtd";
643 
644 	/** Name of the system property that names the web protocol to use. */
645 	public static final String WEB_PROTOCOL_PROPERTY = "org.apache.oodt.commons.Configuration.webProtocol";
646 
647 	/** Global properties. */
648 	private Properties properties = new Properties();
649 
650 	/** Object context environment. */
651 	Hashtable contextEnvironment = new Hashtable();
652 
653 	/** Exec-servers. */
654 	private List execServers = new ArrayList();
655 
656 	/** Web server host. */
657 	private String webHost;
658 
659 	/** Web server port. */
660 	private String webPort;
661 
662 	/** Web server doc dir. */
663 	private File webServerDocumentDirectory;
664 
665 	/** Name server using rir protocol.
666 	 *
667 	 * If false, then it's using iiop.
668 	 */
669 	private boolean nameServerUsingRIRProtocol;
670 
671 	/** Name server version. */
672 	private String nameServerVersion;
673 
674 	/** Name server host. */
675 	private String nameServerHost;
676 
677 	/** Name server port. */
678 	private String nameServerPort;
679 
680 	/** Name server object key. */
681 	private String nameServerObjectKey;
682 
683 	/** How often the name server saves its state. */
684 	private int nameServerStateFrequency;
685 
686 	/** On what port the server manager will listen. */
687 	private int serverMgrPort;
688 
689 	/** Thread to set a context class loader and get a JNDI initial context. */
690 	private class InitialContextThread extends Thread {
691 		/** Ctor
692 		 *
693 		 * @param loader What class loader to use as thread's context class loader.
694 		 */
695 		public InitialContextThread(ClassLoader loader) {
696 			setContextClassLoader(loader);
697 		}
698 
699 		public void run() {
700 			try {
701 				context = new InitialContext(contextEnvironment);
702 			} catch (NamingException ex) {
703 				exception = ex;
704 			} catch (Throwable t) {
705 				System.err.println("Unexpected throwable " + t.getClass().getName() + " getting initial context: "
706 					+ t.getMessage());
707 				t.printStackTrace();
708 			}
709 		}
710 
711 		/** Get the context.
712 		 *
713 		 * <strong>Warning!</strong> Do not call this method until the thread terminates.
714 		 *
715 		 * @return The context, or null if the context could not be retrieved.
716 		 */
717 		public Context getContext() {
718 			return context;
719 		}
720 
721 		/** Get any exception.
722 		 *
723 		 * @return Any exception that occurred while retrieving the context.
724 		 */
725 		public NamingException getException() {
726 			return exception;
727 		}
728 
729 		/** JNDI context. */
730 		private Context context;
731 
732 		/** Any exception. */
733 		private NamingException exception;
734 	}
735 }