| /* |
| * 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.lang.reflect.Constructor; |
| import java.lang.reflect.InvocationTargetException; |
| import java.net.*; |
| import java.rmi.RMISecurityManager; |
| import java.util.*; |
| import javax.naming.Context; |
| import javax.naming.NamingException; |
| import org.apache.oodt.commons.io.Log; |
| import org.apache.oodt.commons.util.*; |
| import org.apache.xmlrpc.XmlRpcClientLite; |
| import org.apache.xmlrpc.XmlRpcServer; |
| import org.w3c.dom.*; |
| import org.xml.sax.*; |
| import java.rmi.Remote; |
| import java.rmi.server.RemoteStub; |
| import java.rmi.server.UnicastRemoteObject; |
| import java.rmi.server.RemoteObject; |
| import java.rmi.server.RemoteRef; |
| import java.io.ObjectOutputStream; |
| import java.io.ByteArrayOutputStream; |
| import org.apache.oodt.commons.io.Base64EncodingOutputStream; |
| |
| /** Server execution program. |
| * |
| * This is an executable class that starts a JPL EDA server. |
| * |
| * @author Kelly |
| */ |
| public class ExecServer { |
| /** Start a server. |
| * |
| * The command-line should have two arguments: |
| * |
| * <ol> |
| * <li>The fully-qualified class name of the server to execute.</li> |
| * <li>The object name of the server to register with the naming service.</li> |
| * </ol> |
| * |
| * @param argv The command-line arguments |
| */ |
| public static void main(String[] argv) { |
| if (argv.length < 2) { |
| System.err.println("Usage: class-name-of-server object-name"); |
| System.exit(1); |
| } |
| |
| String className = argv[0]; |
| String name = argv[1]; |
| |
| // Enable support of our special URLs, like stdin: |
| System.setProperty("java.protocol.handler.pkgs", "org.apache.oodt.commons.net.protocol"); |
| |
| try { |
| // Get the configuration. |
| configuration = Configuration.getConfiguration(); |
| configuration.mergeProperties(System.getProperties()); |
| |
| // Set up the logger. |
| LogInit.init(System.getProperties(), name); |
| |
| // Run initializers |
| try { |
| runInitializers(); |
| } catch (EDAException ex) { |
| ex.printStackTrace(); |
| System.exit(1); |
| } |
| |
| // Create it. |
| final ExecServer server = new ExecServer(name, className); |
| |
| // Print it. |
| if (Boolean.getBoolean(PRINT_IOR_PROPERTY)) { |
| if (server.getServant() instanceof RemoteObject) { |
| RemoteObject remoteObject = (RemoteObject) server.getServant(); |
| RemoteStub remoteStub = (RemoteStub) RemoteObject.toStub(remoteObject); |
| RemoteRef ref = remoteStub.getRef(); |
| System.out.print("RMI:"); |
| System.out.flush(); |
| ObjectOutputStream objOut |
| = new ObjectOutputStream(new Base64EncodingOutputStream(System.out)); |
| objOut.writeObject(ref); |
| objOut.flush(); |
| System.out.println(); |
| } else { |
| org.omg.PortableServer.Servant servant=(org.omg.PortableServer.Servant)server.getServant(); |
| org.omg.CORBA.ORB orb = servant._orb(); |
| System.out.println(orb.object_to_string(servant._this_object(orb))); |
| } |
| System.out.flush(); |
| } |
| |
| // Bind it. |
| if (!Boolean.getBoolean(DISABLE_BINDING)) { |
| binder = new Binder(name, server); |
| binder.start(); |
| } |
| |
| // Prepare for the inevitable |
| Runtime.getRuntime().addShutdownHook(new Thread() { |
| public void run() { |
| server.shutdown0(); |
| } |
| }); |
| |
| // We're done here. |
| for (;;) try { |
| Thread.currentThread().join(); |
| } catch (InterruptedException ignore) {} |
| } catch (IOException ex) { |
| System.err.println("I/O error during initialization: " + ex.getMessage()); |
| ex.printStackTrace(); |
| } catch (SAXParseException ex) { |
| System.err.println("Error in the configuration file at line " + ex.getLineNumber() + ", column " |
| + ex.getColumnNumber() + ": " + ex.getMessage()); |
| } catch (SAXException ex) { |
| System.err.println("Error " + ex.getClass().getName() + " while attempting to parse the configuration" |
| + " file: " + ex.getMessage()); |
| } catch (javax.naming.NamingException ex) { |
| System.err.println("Naming/directory error: " + ex.getClass().getName() + ": " + ex.getMessage()); |
| } catch (java.lang.reflect.InvocationTargetException ex) { |
| Throwable target = ex.getTargetException(); |
| System.err.println("Constructor for \"" + className + "\" threw " + target.getClass().getName() + ": " |
| + ex.getMessage()); |
| target.printStackTrace(); |
| } catch (RuntimeException ex) { |
| throw ex; |
| } catch (Exception ex) { |
| System.err.println("Exception " + ex.getClass().getName() + " initializing server \"" + name |
| + "\" with class \"" + className + "\": " + ex.getMessage()); |
| ex.printStackTrace(); |
| } |
| System.exit(1); |
| } |
| |
| protected ExecServer(String name) { |
| this.name = name; |
| } |
| |
| /** Create a new executable server. |
| * |
| * @param name Name of the server |
| * @param className Name of class that implements the server. |
| * @throws ClassNotFoundException If the class for <var>className</var> can't be found. |
| * @throws NoSuchMethodException If the constructor for <var>className</var> taking a single <code>ExecServer</code> |
| * can't be found. |
| * @throws InstantiationException If the class for <var>className</var> is abstract or is an interface. |
| * @throws IllegalAccessException If the class for <var>className</var> isn't public. |
| * @throws InvocationTargetException If an exception occurs in the constructor for <var>className</var>. |
| * @throws DOMException If the server's status document can't be created. |
| * @throws UnknownHostException If the local host name can't be determined. |
| */ |
| public ExecServer(String name, String className) throws ClassNotFoundException, NoSuchMethodException, |
| InstantiationException, IllegalAccessException, InvocationTargetException, DOMException, UnknownHostException { |
| this.name = name; |
| |
| // Find the class and the required constructor. |
| Class clazz = Class.forName(className); |
| Constructor ctor = clazz.getConstructor(new Class[]{ExecServer.class}); |
| |
| // Invoke the constructor to create the servant. |
| servant = ctor.newInstance(new Object[]{this}); |
| Date startDate = new Date(); |
| |
| // Create the XML-RPC interface to this server. |
| xmlrpcServer = new XmlRpcServer(); |
| xmlrpcServer.addHandler("server", this); |
| |
| // Create the server status document. |
| DocumentType docType = XML.getDOMImplementation().createDocumentType("server", STATUS_FPI, STATUS_URL); |
| statusDocument = XML.getDOMImplementation().createDocument(/*namespaceURI*/null, "server", docType); |
| Element serverElement = statusDocument.getDocumentElement(); |
| XML.add(serverElement, "name", name); |
| XML.add(serverElement, "class", className); |
| XML.add(serverElement, "state", "up"); |
| Element startElement = statusDocument.createElement("start"); |
| serverElement.appendChild(startElement); |
| Element userElement = statusDocument.createElement("user"); |
| startElement.appendChild(userElement); |
| XML.add(userElement, "name", System.getProperty("user.name", "UNKNOWN")); |
| XML.add(userElement, "cwd", System.getProperty("user.dir", "UNKNOWN")); |
| XML.add(userElement, "home", System.getProperty("user.home", "UNKNOWN")); |
| Element dateElement = statusDocument.createElement("date"); |
| startElement.appendChild(dateElement); |
| dateElement.setAttribute("ms", String.valueOf(startDate.getTime())); |
| dateElement.appendChild(statusDocument.createTextNode(startDate.toString())); |
| XML.add(startElement, "config", System.getProperty("org.apache.oodt.commons.Configuration.url", "UNKNOWN")); |
| Element hostElement = statusDocument.createElement("host"); |
| serverElement.appendChild(hostElement); |
| XML.add(hostElement, "name", InetAddress.getLocalHost().getHostName()); |
| Element osElement = statusDocument.createElement("os"); |
| hostElement.appendChild(osElement); |
| XML.add(osElement, "name", System.getProperty("os.name", "UNKNOWN")); |
| XML.add(osElement, "version", System.getProperty("os.version", "UNKNOWN")); |
| XML.add(osElement, "arch", System.getProperty("os.arch", "UNKNOWN")); |
| Element vmElement = statusDocument.createElement("vm"); |
| serverElement.appendChild(vmElement); |
| XML.add(vmElement, "name", System.getProperty("java.vm.name", "UNKNOWN")); |
| XML.add(vmElement, "version", System.getProperty("java.version", "UNKNOWN")); |
| XML.add(vmElement, "classpath", System.getProperty("java.class.path", "UNKNOWN")); |
| XML.add(vmElement, "extdirs", System.getProperty("java.ext.dirs", "UNKNOWN")); |
| logElement = statusDocument.createElement("log"); |
| serverElement.appendChild(logElement); |
| } |
| |
| /** Get my name. |
| * |
| * @return The name under which I'm registered in teh naming context. |
| */ |
| public String getName() { |
| return name; |
| } |
| |
| /** Return the servant for this executable server. |
| * |
| * @return The servant. |
| */ |
| public Object getServant() { |
| return servant; |
| } |
| |
| /** Control this server. |
| * |
| * @param command Command to send to the server. |
| * @return Results of <var>command</var>. |
| */ |
| public byte[] control(byte[] command) { |
| return xmlrpcServer.execute(new ByteArrayInputStream(command)); |
| } |
| |
| /** Return the server's class name. |
| * |
| * @return Its class name. |
| */ |
| public String getServerClassName() { |
| return className; |
| } |
| |
| /** Return status of this server. |
| * |
| * @return Its status. |
| */ |
| public String getServerStatus() { |
| // Update the status document with the current log. |
| for (Iterator i = LogInit.MEMORY_LOGGER.getMessages().iterator(); i.hasNext();) { |
| String message = (String) i.next(); |
| Element messageElement = statusDocument.createElement("message"); |
| messageElement.setAttribute("xml:space", "preserve"); |
| messageElement.appendChild(statusDocument.createTextNode(message)); |
| logElement.appendChild(messageElement); |
| } |
| |
| // Serialize the document. |
| String rc = XML.serialize(statusDocument); |
| |
| // Remove all the log messages from the document. |
| NodeList children = logElement.getChildNodes(); |
| for (int i = 0; i < children.getLength(); ++i) |
| logElement.removeChild(children.item(i)); |
| |
| // Return the serialized form, which included the log messages. |
| System.err.println(rc); |
| return rc; |
| } |
| |
| /** Set a system property. |
| * |
| * This uses the property manager to notify property change listeners. |
| * |
| * @param key Property's name. |
| * @param value New value. |
| * @return Zero (return required for XML-RPC access). |
| */ |
| public int setSystemProperty(String key, String value) { |
| System.err.println("Setting system property \"" + key + "\" to \"" + value + "\""); |
| PropertyMgr.setProperty(key, value); |
| return 0; |
| } |
| |
| /** |
| * Call the server manager on the local system. |
| * |
| * @param port What port on the local system the server manager is listening. |
| * @param user User name to use for authentication. |
| * @param password Authenticator for <var>user</var>. |
| * @param method What method in the server manager to call. |
| * @param params Parameters to pass to the method named by <var>method</var>. |
| * @return The return value from the method named by <var>method</var>. |
| * @throws Exception If any error occurs. |
| */ |
| public Object callLocalServerManager(int port, String user, String password, String method, List params) throws Exception { |
| XmlRpcClientLite local = new XmlRpcClientLite("localhost", port); |
| local.setBasicAuthentication(user, password); |
| return local.execute(method, new Vector(params)); |
| } |
| |
| /** Shut down and exit. |
| * |
| * @return Zero. |
| */ |
| public int shutdown() { |
| // Log it. |
| System.err.println("Received shutdown command"); |
| |
| // Make sure we actually exit sometime soon. |
| new Thread() { |
| public void run() { |
| try { |
| Thread.sleep(15000); |
| } catch (InterruptedException ignore) {} |
| System.exit(1); |
| } |
| }.start(); |
| |
| // Clean up. |
| shutdown0(); |
| |
| // And exit. |
| System.err.println("Calling System.exit with status code 0"); |
| System.exit(0); |
| return 0; |
| } |
| |
| private void shutdown0() { |
| // Unbind. |
| if (!Boolean.getBoolean(DISABLE_BINDING)) try { |
| binder.stopBinding(); |
| Context objectContext = configuration.getObjectContext(); |
| objectContext.unbind(getName()); |
| objectContext.close(); |
| } catch (NamingException ignore) {} |
| |
| // Kill the ORB. YEAH! KILL IT, KILL IT, KIIIIIIIIIIIIIIL IIIIIIIIT!!!!!!!1 |
| try { |
| if (servant instanceof org.omg.PortableServer.Servant) { |
| org.omg.PortableServer.Servant s = (org.omg.PortableServer.Servant) servant; |
| org.omg.CORBA.ORB orb = s._orb(); |
| orb.shutdown(false/*=>terminate without waiting for reqs to complete*/); |
| } |
| } catch (Throwable ignore) {} |
| } |
| |
| /** |
| * Binding thread. |
| */ |
| private static class Binder extends Thread { |
| public Binder(String name, ExecServer server) { |
| super("Binder for " + name); |
| setDaemon(true); |
| this.name = name; |
| this.server = server; |
| keepBinding = true; |
| } |
| public void run() { |
| while (shouldKeepBinding()) try { |
| Context objectContext = configuration.getObjectContext(); |
| objectContext.rebind(name, server.getServant()); |
| objectContext.close(); |
| } catch (Throwable ex) { |
| System.err.println("Exception binding at " + new Date() + "; will keep trying..."); |
| ex.printStackTrace(); |
| } finally { |
| try { |
| Thread.sleep(REBIND_PERIOD); |
| } catch (InterruptedException ignore) {} |
| } |
| } |
| public synchronized void stopBinding() { |
| keepBinding = false; |
| } |
| private synchronized boolean shouldKeepBinding() { |
| return keepBinding; |
| } |
| private boolean keepBinding; |
| private String name; |
| private ExecServer server; |
| } |
| |
| /** |
| * Run all initializers. |
| * |
| * This instantiates and calls the {@link Initializer#initialize} method of each |
| * initializer specified by class name in a comma separated list of classes in the |
| * system properties. The property name is <code>org.apache.oodt.commons.initializers</code>, or |
| * if not defined, <code>org.apache.oodt.commons.ExecServer.initializers</code>, or if not |
| * defined, <code>initializers</code>. And if that one's not defined, then none |
| * are run. |
| * |
| * @throws EDAException if an error occurs. |
| */ |
| public static void runInitializers() throws EDAException { |
| String initList = System.getProperty("org.apache.oodt.commons.initializers", |
| System.getProperty("org.apache.oodt.commons.ExecServer.initializers", System.getProperty("initializers", ""))); |
| for (Iterator i = org.apache.oodt.commons.util.Utility.parseCommaList(initList); i.hasNext();) { |
| String iname = (String) i.next(); |
| try { |
| Class initClass = Class.forName(iname); |
| Initializer init = (Initializer) initClass.newInstance(); |
| init.initialize(); |
| } catch (ClassNotFoundException ex) { |
| System.err.println("Initializer \"" + iname + "\" not found; aborting"); |
| throw new EDAException(ex); |
| } catch (InstantiationException ex) { |
| System.err.println("Initializer \"" + iname + "\" is abstract; aborting"); |
| throw new EDAException(ex); |
| } catch (IllegalAccessException ex) { |
| System.err.println("Initializer \"" + iname + "\" isn't public; aborting"); |
| throw new EDAException(ex); |
| } catch (EDAException ex) { |
| System.err.println("Initializer \"" + iname + "\" failed: " + ex.getMessage()); |
| throw new EDAException(ex); |
| } |
| } |
| } |
| |
| /** The configuration. */ |
| private static Configuration configuration; |
| |
| /** Object key name. */ |
| protected String name; |
| |
| /** The servant. */ |
| private Object servant; |
| |
| /** The server class name. */ |
| private String className; |
| |
| /** Current binder, if any. */ |
| private static Binder binder; |
| |
| /** Server's status document. */ |
| private Document statusDocument; |
| |
| /** The <log> element within the status document. */ |
| private Element logElement; |
| |
| /** The XML-RPC interface to this server. */ |
| private XmlRpcServer xmlrpcServer; |
| |
| /** Status DTD formal public identifier. */ |
| public static final String STATUS_FPI = "-//JPL//DTD EDA Server Status 1.0"; |
| |
| /** Status DTD system identifier. */ |
| public static final String STATUS_URL = "http://oodt.jpl.nasa.gov/edm-commons/xml/serverStatus.dtd"; |
| |
| /** Name of the property that prints the server's IOR or RMI handle. */ |
| public static final String PRINT_IOR_PROPERTY = "org.apache.oodt.commons.ExecServer.printIOR"; |
| |
| /** Name of the property that prevents binding of this object with the naming service. */ |
| public static final String DISABLE_BINDING = "org.apache.oodt.commons.ExecServer.disableBinding"; |
| |
| /** How long to wait before bind attempts, in ms. */ |
| private static final long REBIND_PERIOD = Long.getLong("org.apache.oodt.commons.ExecServer.rebindPeriod", 30*60*1000).longValue(); |
| } |