You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@oodt.apache.org by ke...@apache.org on 2010/07/15 01:02:32 UTC

svn commit: r964248 [2/2] - /incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/

Added: incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/MultiServer.java
URL: http://svn.apache.org/viewvc/incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/MultiServer.java?rev=964248&view=auto
==============================================================================
--- incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/MultiServer.java (added)
+++ incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/MultiServer.java Wed Jul 14 23:02:32 2010
@@ -0,0 +1,576 @@
+/*
+ * 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.IOException;
+import java.io.StringReader;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.rmi.registry.Registry;
+import java.rmi.server.RemoteObject;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Timer;
+import java.util.TimerTask;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.spi.NamingManager;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import org.apache.oodt.commons.util.LogInit;
+import org.apache.oodt.commons.util.XML;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * The MultiServer runs multiple server objects in a single JVM.  Instead of running a
+ * separate product server, profile server, query server, and so forth, in their own JVMs,
+ * which are extremely heavy-weight operating system processes, we can put them into one
+ * JVM which reduces the memory footprint on a single computer enormously.
+ *
+ * <h3>Specifying the Configuration</h3>
+ *
+ * The MultiServer configuration is an XML document.  Here's
+ * a sample:
+ * <pre>&lt;multiserver
+ *   xmlns="http://oodt.jpl.nasa.gov/edm-commons/xml/multiserver"
+ *   id="My Multi Server"&gt;
+ *   &lt;server
+ *     class="org.apache.oodt.commons.product.rmi.ProductServiceImpl"
+ *     id="urn:eda:rmi:BioServer"
+ *     bind="rebind" /&gt;
+ *   &lt;server
+ *     class="org.apache.oodt.commons.product.rmi.ProductServiceImpl"
+ *     id="urn:eda:rmi:SpaceServer"
+ *     bind="1800000" /&gt;
+ *   &lt;server
+ *     class="org.apache.oodt.commons.profile.rmi.ProfileServiceImpl"
+ *     id="urn:eda:rmi:Resource"
+ *     bind="bind" /&gt;
+ *   &lt;properties&gt;
+ *     &lt;property name="urn:eda:rmi:BioServer.handlers"&gt;
+ *       edrn.MedHandler,edrn.SpecimenHandler
+ *     &lt;/property&gt;
+ *     &lt;property name="urn:eda:rmi:SpaceServer.handlers"&gt;
+ *       pds.PlanetoidHandler
+ *     &lt;/property&gt;
+ *     &lt;property name="org.apache.oodt.commons.profile.Handlers"&gt;
+ *       com.sun.ResourceHandler
+ *     &lt;/property&gt;
+ *   &lt;/properties&gt;
+ * &lt;/multiserver&gt;</pre>
+ *
+ * <p>This would start three servers (two products, one profile) with the various property
+ * settings indicated.  The MultiServer expects the property
+ * <code>org.apache.oodt.commons.MultiServer.config</code> to identify the URL of the configuration.  You
+ * can shorten that to <code>MultiServer.config</code> or <code>multiserver.config</code>
+ * or even just <code>config</code>, in that order.  If none of those properties are
+ * specified then the MultiServer will expect the URL to be the first (and only) command
+ * line argument.
+ *
+ * <p>The <code>id</code> attribute on the <code>multiserver</code> element tells the name
+ * of the whole application; it's used to prefix log messages.
+ *
+ * <h3>Server Specification</h3>
+ *
+ * <p>Each <code>&lt;server&gt;</code> entry names a server to start.  The
+ * <code>class</code> attribute is the name of the RMI-compatible Java class that the
+ * server will run.  (Note that currently only RMI servers are supported.)  The
+ * <code>id</code> attribute tells the name the server should use to register with the
+ * naming context.  And the <code>bind</code> attribute tells how the registration should
+ * proceed.  It can take on the following values:
+ *
+ * <ul>
+ *   <li><code>true</code> meaning the object will attempt a bind.  If the ID is already
+ *   bound in the context, the MultiServer fails.</li>
+ *
+ *   <li><code>false</code> meaning the object won't be bound.</li>
+ *
+ *   <li><code>rebind</code> meaning the object will rebind its ID in the context,
+ *   overwriting any previous binding.
+ *
+ *   <li><var>number</var> meaining the object will rebind its ID once at starup, and
+ *   every <var>number</var> milliseconds thereafter.  This is, in OODT's experience, the
+ *   most useful option as it helps an entire dpeloyed network of servers self-heal after
+ *   a naming context restart.
+ * </ul>
+ *
+ * <h3>Propery Specification</h3>
+ *
+ * <p>For convenience, System Properties may be specified in the configirutaion as well.
+ * However, any properties already defined (such as using the <code>-D</code> command-line
+ * argument) get priority and their values won't be overridden.  To specify properties,
+ * list any number of <code>&lt;property&gt;</code> elements under the
+ * <code>&lt;properties&gt;</code> element with a <code>name</code> attribute naming the
+ * System Property key and the text of the element naming its value.  Note that the text
+ * will be unwrapped.
+ *
+ * @author Kelly
+ * @version $Revision: 1.3 $
+ */
+public class MultiServer {
+	/**
+	 * Start the multi server.
+	 *
+	 * @param argv Command-line arguments.
+	 * @throws Throwable if an error occurs.
+	 */
+	public static void main(String[] argv) throws Throwable {
+		String config = System.getProperty("org.apache.oodt.commons.MultiServer.config", System.getProperty("MultiServer.config",
+			System.getProperty("multiserver.config", System.getProperty("config"))));
+		if (config == null) {
+			if (argv.length != 1)
+				throw new IllegalStateException("No org.apache.oodt.commons.MultiServer.config property or config URL argument");
+			else
+				config = argv[0];
+		}
+
+		StringReader reader = new StringReader(CONFIG);
+		Configuration.configuration = new Configuration(new InputSource(reader));
+		reader.close();
+		parseConfig(new InputSource(config));
+
+		Hashtable t = new Hashtable();
+		t.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;
+		}
+		t.put("rmiregistries", registryList);
+		context = NamingManager.getInitialContext(t);
+		ExecServer.runInitializers();
+		try {
+			LogInit.init(System.getProperties(), getAppName());
+			if (servers.isEmpty()) throw new IllegalStateException("No servers defined in config");
+
+			Runtime.getRuntime().addShutdownHook(new Thread() {
+				public void run() {
+					shutdown();
+				}
+			});
+			startup();
+		} catch (Throwable ex) {
+			ex.printStackTrace();
+			try {
+				shutdown();
+			} catch (Throwable ignore) {}
+			System.exit(1);
+		}
+		for (;;) try {
+			Thread.currentThread().join();
+		} catch (InterruptedException ignore) {}
+	}
+
+	/**
+	 * Parse the MultiServer configuration.
+	 *
+	 * @param is Source of the configuration.
+	 * @throws ParserConfigurationException If we can't create a parser.
+	 * @throws SAXException If there's a parse error.
+	 * @throws IOException If there's a problem reading the configuration.
+	 * @throws ClassNotFoundException If we can't find a server class.
+	 * @throws NoSuchMethodException If the server class doesn't have the right constructor.
+	 * @throws InstantiationException If we can create a server object.
+	 * @throws IllegalAccessException If the server constructor isn't accessible.
+	 * @throws InvocationTargetException If the server constructor throws an exception.
+	 */
+	static void parseConfig(InputSource is) throws ParserConfigurationException, SAXException, IOException,
+		ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException,
+		InvocationTargetException {
+		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+		factory.setIgnoringComments(true);
+		factory.setIgnoringElementContentWhitespace(true);
+		factory.setNamespaceAware(true);
+		factory.setValidating(false);
+		DocumentBuilder builder = factory.newDocumentBuilder();
+		Document doc = builder.parse(is);
+		Element root = doc.getDocumentElement();
+		appName = root.getAttribute("id");
+		if (appName == null) throw new SAXException("id attribute missing from multiserver element");
+
+		// Set properties
+		NodeList children = root.getChildNodes();
+		for (int i = 0; i < children.getLength(); ++i) {
+			Node node = (Node) children.item(i);
+			if ("properties".equals(node.getNodeName())) {
+				NodeList props = ((Element) node).getElementsByTagName("property");
+				for (int j = 0; j < props.getLength(); ++j) {
+					Element property = (Element) props.item(j);
+					String name = property.getAttribute("name");
+					if (!System.getProperties().containsKey(name)) {
+						String value = XML.unwrappedText(property);
+						System.setProperty(name, value);
+					}
+				}
+			}
+		}		
+
+		// Create servers
+		NodeList serverNodes = root.getElementsByTagName("server");
+		servers = new HashMap();
+		for (int i = 0; i < serverNodes.getLength(); ++i) {
+			Element serverElem = (Element) serverNodes.item(i);
+			String name = serverElem.getAttribute("id");
+			if (name == null) throw new SAXException("id attribute missing from server element");
+			String className = serverElem.getAttribute("class");
+			if (className == null) throw new SAXException("class attribute missing from server element");
+			String bindKind = serverElem.getAttribute("bind");
+			if (bindKind == null) throw new SAXException("bind attribute missing from server element");
+			Server server;
+			if ("true".equals(bindKind))
+				server = new BindingServer(name, className);
+			else if ("false".equals(bindKind))
+				server = new NonbindingServer(name, className);
+			else if ("rebind".equals(bindKind))
+				server = new RebindingServer(name, className);
+			else try {
+				long period = Long.parseLong(bindKind);
+				server = new AutobindingServer(name, className, period);
+			} catch (NumberFormatException ex) {
+				throw new SAXException("Expected true, false, rebind, or auto for bind attribute but got `"
+					+ bindKind + "'");
+			}
+			servers.put(name, server);
+		}
+
+	}
+
+	/**
+	 * Start each server.
+	 *
+	 * @throws NamingException if an error occurs.
+	 */
+	static void startup() throws NamingException {
+		for (Iterator i = servers.values().iterator(); i.hasNext();) {
+			Server s = (Server) i.next();
+			s.start();
+		}
+	}
+
+	/**
+	 * Stop each server.
+	 */
+	static void shutdown() {
+		for (Iterator i = servers.values().iterator(); i.hasNext();) try {
+			Server s = (Server) i.next();
+			s.stop();
+		} catch (NamingException ignore) {}
+		TIMER.cancel();
+	}
+
+	/**
+	 * Get the name of the application.
+	 *
+	 * @return a {@link String} value.
+	 */
+	static String getAppName() {
+		return appName;
+	}
+
+	/**
+	 * Get the servers.  Keys are {@String} names and values are {@link #Server}s.
+	 *
+	 * @return a {@link Map} of the defined servers.
+	 */
+	static Map getServers() {
+		return servers;
+	}
+
+	/**
+	 * A server.
+	 */
+	static abstract class Server extends ExecServer {
+		/**
+		 * Creates a new {@link Server} instance.
+		 *
+		 * @param name ID under which to register.
+		 * @param className Class of server to instantiate.
+		 * @throws ClassNotFoundException If we can't find the server class.
+		 * @throws NoSuchMethodException If the server class doesn't have the right constructor.
+		 * @throws InstantiationException If we can create the server object.
+		 * @throws IllegalAccessException If the server constructor isn't accessible.
+		 * @throws InvocationTargetException If the server constructor throws an exception.
+		 */
+		protected Server(String name, String className) throws ClassNotFoundException, NoSuchMethodException,
+			InstantiationException, IllegalAccessException, InvocationTargetException {
+			super(name);
+			this.className = className;
+			Class clazz = Class.forName(className);
+			Constructor ctor = clazz.getConstructor(new Class[]{ ExecServer.class });
+			servant = (RemoteObject) ctor.newInstance(new Object[]{ this });
+		}
+
+		/**
+		 * Get the name of the server class to instantiate.
+		 *
+		 * @return a {@link String} value.
+		 */
+		public String getClassName() {
+			return className;
+		}
+
+		/**
+		 * Get the type of binding this server will perform.  Possible values are
+		 * {@link #BINDING}, {@link #NONBINDING}, {@link #REBINDING}, or {@link
+		 * #AUTO}.
+		 *
+		 * @return An integer identifiying the binding behavior.
+		 */
+		public abstract int getBindingBehavior();
+
+		/**
+		 * Start this server.
+		 *
+		 * @throws NamingException if an error occurs during the binding.
+		 */
+		public abstract void start() throws NamingException;
+
+		/**
+		 * Stop this server.
+		 *
+		 * @throws NamingException if an error occurs during unbinding.
+		 */
+		public abstract void stop() throws NamingException;
+
+		/** Name of server class. */
+		protected String className;
+
+		/** Server object. */
+		protected RemoteObject servant;
+	}
+
+	/** Inidcates server will try a bind. */
+	public static final int BINDING = 1;
+
+	/** Indicates server won't be bound. */
+	public static final int NONBINDING = 2;
+
+	/** Indicates server will try a rebind. */
+	public static final int REBINDING = 3;
+
+	/** Indicates server will try periodic rebinding. */
+	public static final int AUTO = 4;
+
+	/** Timer to schedule periodic rebinind. */
+	private static final Timer TIMER = new Timer(/*isDaemon*/true);
+
+	/** Name of this application. */
+	private static String appName;
+
+	/** Known servers.  Keys are {@String} names and values are {@link #Server}s. */
+	private static Map servers;
+
+	/** The naming context. */
+	private static Context context;
+
+	/** The edarc.xml file to satisfy the MultiServer's servers. */
+	private static final String CONFIG = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE configuration PUBLIC"
+		+ " \"-//JPL//DTD EDA Configuration 1.0//EN\" \"http://enterprise.jpl.nasa.gov/dtd/configuration.dtd\">\n"
+		+ "<configuration><webServer><host>localhost</host><port>80</port></webServer>"
+		+ "<nameServer><iiop><host>localhost</host><port>10000</port></iiop></nameServer></configuration>";
+
+	/**
+	 * A server that tries a single bind.
+	 */
+	static class BindingServer extends Server {
+		/**
+		 * Creates a new {@link BindingServer} instance.
+		 *
+		 * @param name ID under which to register.
+		 * @param className Class of server to instantiate.
+		 * @throws ClassNotFoundException If we can't find the server class.
+		 * @throws NoSuchMethodException If the server class doesn't have the right constructor.
+		 * @throws InstantiationException If we can create the server object.
+		 * @throws IllegalAccessException If the server constructor isn't accessible.
+		 * @throws InvocationTargetException If the server constructor throws an exception.
+		 */
+		BindingServer(String name, String className) throws ClassNotFoundException, NoSuchMethodException,
+			InstantiationException, IllegalAccessException, InvocationTargetException {
+			super(name, className);
+		}
+
+		public int getBindingBehavior() {
+			return BINDING;
+		}
+
+		/**
+		 * Start by binding.
+		 *
+		 * @throws NamingException if an error occurs.
+		 */
+		public void start() throws NamingException {
+			context.bind(name, servant);
+		}
+
+		/**
+		 * Stop by unbinding.
+		 *
+		 * @throws NamingException if an error occurs.
+		 */
+		public void stop() throws NamingException {
+			context.unbind(name);
+		}
+	}
+
+	/**
+	 * A (named, but anonymous) server that isn't bound to a naming context.
+	 */
+	static class NonbindingServer extends Server {
+		/**
+		 * Creates a new {@link NonbindingServer} instance.
+		 *
+		 * @param name ID under which to register.
+		 * @param className Class of server to instantiate.
+		 * @throws ClassNotFoundException If we can't find the server class.
+		 * @throws NoSuchMethodException If the server class doesn't have the right constructor.
+		 * @throws InstantiationException If we can create the server object.
+		 * @throws IllegalAccessException If the server constructor isn't accessible.
+		 * @throws InvocationTargetException If the server constructor throws an exception.
+		 */
+		NonbindingServer(String name, String className) throws ClassNotFoundException, NoSuchMethodException,
+			InstantiationException, IllegalAccessException, InvocationTargetException {
+			super(name, className);
+		}
+
+		public int getBindingBehavior() {
+			return NONBINDING;
+		}
+
+		/**
+		 * Start by taking no action.
+		 */
+		public void start() {}
+
+		/**
+		 * Stop by taking no action.
+		 */
+		public void stop() {}
+	}
+
+	/**
+	 * A server that rebinds its ID in the naming context.
+	 */
+	static class RebindingServer extends BindingServer {
+		/**
+		 * Creates a new {@link RebindingServer} instance.
+		 *
+		 * @param name ID under which to register.
+		 * @param className Class of server to instantiate.
+		 * @throws ClassNotFoundException If we can't find the server class.
+		 * @throws NoSuchMethodException If the server class doesn't have the right constructor.
+		 * @throws InstantiationException If we can create the server object.
+		 * @throws IllegalAccessException If the server constructor isn't accessible.
+		 * @throws InvocationTargetException If the server constructor throws an exception.
+		 */
+		RebindingServer(String name, String className) throws ClassNotFoundException, NoSuchMethodException,
+			InstantiationException, IllegalAccessException, InvocationTargetException {
+			super(name, className);
+		}
+
+		public int getBindingBehavior() {
+			return REBINDING;
+		}
+
+		/**
+		 * Start by rebinding in naming context.
+		 *
+		 * @throws NamingException if an error occurs.
+		 */
+		public void start() throws NamingException {
+			context.rebind(name, servant);
+		}
+	}
+
+	/**
+	 * A server that periodically rebinds its ID in the naming context.
+	 */
+	static class AutobindingServer extends Server {
+		/**
+		 * Creates a new {@link AutobindingServer} instance.
+		 *
+		 * @param name ID under which to register.
+		 * @param className Class of server to instantiate.
+		 * @throws ClassNotFoundException If we can't find the server class.
+		 * @throws NoSuchMethodException If the server class doesn't have the right constructor.
+		 * @throws InstantiationException If we can create the server object.
+		 * @throws IllegalAccessException If the server constructor isn't accessible.
+		 * @throws InvocationTargetException If the server constructor throws an exception.
+		 */
+		AutobindingServer(String name, String className, long period) throws ClassNotFoundException, NoSuchMethodException,
+			InstantiationException, IllegalAccessException, InvocationTargetException {
+			super(name, className);
+			this.period = period;
+		}
+
+		public int getBindingBehavior() {
+			return AUTO;
+		}
+
+		/**
+		 * Start by scheduling the timer task that rebinds this server.
+		 */
+		public void start() {
+			TIMER.schedule(binder = new Binder(), /*delay*/0L, /*period*/period);
+		}
+
+		/**
+		 * Stop by canceling the timer task and unbinding from the server.
+		 *
+		 * @throws NamingException if an error occurs.
+		 */
+		public void stop() throws NamingException {
+			if (binder != null) binder.cancel();
+			context.unbind(name);
+		}
+
+		/**
+		 * Get how often this server process will be rebound.
+		 *
+		 * @return Period in milliseconds.
+		 */
+		public long getPeriod() {
+			return period;
+		}
+
+		/** How often to rebind in milliseconds. */
+		private long period;
+
+		/** Timer task that rebinds. */
+		private Binder binder;
+
+		/**
+		 * Timer task that rebinds this server to the naming context.
+		 */
+		private class Binder extends TimerTask {
+			public void run() {
+				try {
+					context.rebind(name, servant);
+				} catch (NamingException ex) {}
+			}
+		}
+	}
+}

Added: incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/Service.java
URL: http://svn.apache.org/viewvc/incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/Service.java?rev=964248&view=auto
==============================================================================
--- incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/Service.java (added)
+++ incubator/oodt/trunk/commons/src/main/java/org/apache/oodt/commons/Service.java Wed Jul 14 23:02:32 2010
@@ -0,0 +1,61 @@
+/*
+ * 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.rmi.Remote;
+import java.rmi.RemoteException;
+
+/**
+ * An enterprise service.
+ *
+ * @author Kelly
+ * @version $Revision: 1.1 $
+ */
+public interface Service extends Remote {
+	/**
+	 * Get the interface name of the service.
+	 *
+	 * Nominally, this should return the fully qualified class name of the
+	 * implementation class, not the interface name.  No idea how that "standard" got
+	 * started.  So, a good return value might be
+	 * <code>jpl.oodt.product.rmi.ProductServiceImpl</code> and <strong>not</strong>
+	 * <code>jpl.oodt.product.ProductService</code>.
+	 *
+	 * @return a <code>String</code> value.
+	 * @throws RemoteException if an error occurs.
+	 */
+	String getServerInterfaceName() throws RemoteException;
+
+	/**
+	 * Control the server.
+	 *
+	 * @param command a <code>byte[]</code> value.
+	 * @return Response.
+	 * @throws RemoteException if an error occurs.
+	 */
+	byte[] control(byte[] command) throws RemoteException;
+
+	
+	/**
+	 * Control the server asynchronously.
+	 *
+	 * @param command a <code>byte[]</code> value.
+	 * @throws RemoteException if an error occurs.
+	 */
+	void controlAsync(byte[] command) throws RemoteException;
+}