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><multiserver
+ * xmlns="http://oodt.jpl.nasa.gov/edm-commons/xml/multiserver"
+ * id="My Multi Server">
+ * <server
+ * class="org.apache.oodt.commons.product.rmi.ProductServiceImpl"
+ * id="urn:eda:rmi:BioServer"
+ * bind="rebind" />
+ * <server
+ * class="org.apache.oodt.commons.product.rmi.ProductServiceImpl"
+ * id="urn:eda:rmi:SpaceServer"
+ * bind="1800000" />
+ * <server
+ * class="org.apache.oodt.commons.profile.rmi.ProfileServiceImpl"
+ * id="urn:eda:rmi:Resource"
+ * bind="bind" />
+ * <properties>
+ * <property name="urn:eda:rmi:BioServer.handlers">
+ * edrn.MedHandler,edrn.SpecimenHandler
+ * </property>
+ * <property name="urn:eda:rmi:SpaceServer.handlers">
+ * pds.PlanetoidHandler
+ * </property>
+ * <property name="org.apache.oodt.commons.profile.Handlers">
+ * com.sun.ResourceHandler
+ * </property>
+ * </properties>
+ * </multiserver></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><server></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><property></code> elements under the
+ * <code><properties></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;
+}