Package com.sun.jini.tool.envcheck

Source Code of com.sun.jini.tool.envcheck.EnvCheck$GetDescriptors

/*
* 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 com.sun.jini.tool.envcheck;

import com.sun.jini.resource.Service;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.io.PrintStream;

import java.lang.reflect.Modifier;

import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

import java.rmi.RMISecurityManager;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Properties;
import java.util.ResourceBundle;

import net.jini.config.Configuration;
import net.jini.config.ConfigurationException;
import net.jini.config.ConfigurationProvider;
import net.jini.config.EmptyConfiguration;
import net.jini.config.NoSuchEntryException;

import com.sun.jini.start.ServiceDescriptor;
import com.sun.jini.start.NonActivatableServiceDescriptor;
import com.sun.jini.start.SharedActivatableServiceDescriptor;
import com.sun.jini.start.SharedActivationGroupDescriptor;

import com.sun.jini.tool.envcheck.Reporter.Message;

/**
* Tool used to perform validity checks on the run-time environment of a client
* or service. The output of this tool is a report; command-line options
* control the verbosity and severity level at which report entries are
* generated. A simple plugin architecture is implemented; a set of plugins
* implementing a variety of checks is bundled with the tool, and support is
* provided to allow additional plugins to be supplied by the user.
* <p>
* The following items are discussed below:
* <ul>
* <li><a href="#running">Running the Tool</a>
* <li><a href="#processing">Processing Options</a>
* <li><a href="#examples">Examples</a>
* <li><a href="#plugins">Bundled Plugins</a>
* </ul>
*
* <a name="running"></a>
* <h3>Running the Tool</h3>
*
* This tool primarily validates the system properties and configuration
* files used when starting the target client or service. This is accomplished
* by providing the unmodified command line for launching the component as
* arguments to the tool. Thus, for a service designed to be run by the
* service starter having the hypothetical original command line:
* <blockquote><pre>
* java -Djava.security.policy=mypolicy
*      -jar /jini/lib/start.jar mystart.config
* </pre></blockquote>
* the simplest invocation of the tool would be:
* <blockquote><pre>
* java -jar /jini/lib/envcheck.jar
*      java -Djava.security.policy=mypolicy
*           -jar /jini/lib/start.jar mystart.config
* </pre></blockquote>
* Note that the entire command line, including the <code>java</code> command,
* is supplied as arguments to the tool. The <code>java</code> command used to
* run the tool may be different than the <code>java</code> command invoked by
* the command line under test. The first token in the command line being
* analyzed must not begin with a '-' and must end with the string "java".
*
* <a name="processing"></a>
* <h3>Processing Options</h3>
* <p>
* <dl>
* <dt><b><code>-traces</code></b>
* <dd>The implementation of a validity check may detect success or failure by
*     handling an expected exception. By default, an error message will be
*     generated in these cases, but the stack trace will be inhibited. This
*     option is a hint that stack traces are desired. It is the responsibility
*     of the individual plugin implementation to honor this option.
* </dd>
* <p>
* <dt><b><code>-explain</code></b>
* <dd>By default, the output of a validity check will be a short message with
*     enough detail to allow a knowledgeable user to interpret it; however, it
*     may not be understandable to a novice user. The <code>-explain</code>
*     option is a hint that may result in the generation of additional output
*     describing the purpose and context of the check. An explanation is output
*     the first time its associated message is output, and is not repeated. It
*     is the responsibility of the individual plugin implementation to honor
*     this option.
* </dd>
* <p>
* <dt><b><code>-level</code> <var>info|warning|error</var></b>
* <dd>The tool supports three severity levels for message generation.
* <p>
*     <dl>
*     <dt><var>info</var>
*     <dd>'success' messages or other non-failure oriented configuration data
*     <dt><var>warning</var>
*     <dd>a condition or value has been detected that is 'legal', but which
*         could result in unintended behavior. An example is the use of
*         <code>localhost</code> in a codebase annotation.
*     <dt><var>error</var>
*     <dd>a condition has been detected that is likely due to an error
*         on the command line or in a configuration file. An example is
*         assigning the value of a non-existant file name to the
*         <code>java.util.logging.config.file</code> system property.
*     </dl>
* <p>
*     This option is used to set the level at which message records are
*     generated. The default value is <var>warning</var>.
* </dd>
* <p>
* <dt><b><code>-plugin</code> <var>file</var></b>

* <dd>Identifies a JAR file containing user supplied plugins that will be run
*     after the standard plugins are run. All of the necessary support classes
*     and resources required to support the plugins must be included in this
*     file. The file must also include a resource named
*     <code>META-INF/services/com.sun.jini.tool.envcheck.Plugin</code>, which
*     contains the class names of the plugins to run listed one per line.
*     Every class listed must implement the
*     <code>com.sun.jini.tool.envcheck.Plugin</code> interface. This option
*     may be supplied zero or more times.
* </dd>
* <p>
* <dt><b><code>-security</code></b>
* <dd>A plugin specific option that is recognized by one of the bundled
*     plugins.  Specifying this option will activate a number of JAAS and JSSE
*     checks. User supplied plugins wishing to recognize this option must
*     implement the <code>isPlugOption</code> method of the
*     <code>Plugin</code> interface.
* </dd>
* </dl>
* <p>
* <a name="examples"></a>
* <h3>Examples</h3>

* The following example will analyze a command line used to start a user
* service. The command being analyzed defines a classpath and two system
* properties, and names a class containing a <code>main</code> method:

* <p>
* <blockquote><pre>
* java -jar /jini/lib/envcheck.jar -level info -explain -traces
*      /usr/bin/java -cp mylib/myservice.jar:/jini/lib/jsk-platform.jar
*                    -Djava.security.policy=mypolicy
*                    -Djava.server.rmi.codebase=http://myhost/myservice-dl.jar
*                    myservice.MyServiceImpl
* </blockquote></pre>
* In this case, the tool is limited to performing validity checks on the
* classpath, policy, and codebase values identified by the system properties
* and options provided on the service command line. The <code>-level</code>,
* <code>-explain</code>, and <code>-traces</code> options supplied will result
* in the most verbose output possible from the tool.
* <p>
* The following example will analyze a command line used to start reggie using
* the service starter. The command being analyzed uses a policy and service
* starter configuration located in the working directory:
* <p>
* <blockquote><pre>
* java -jar /jini/lib/envcheck.jar -level error
*      /usr/bin/java -Djava.security.policy=mystarterpolicy
*                    -jar /jini/lib/start.jar reggie.config
* </blockquote></pre>
* The tool can perform many more checks in this case because the
* bundled plugins include built-in knowledge about the service starter
* and its public configuration entries. The tool options used will minimize
* the output produced by the tool.
* <p>
* <a name="plugins"></a>
* <h3>Bundled Plugins</h3>
* A set of plugins are loaded automatically by the tool to perform some
* basic analysis.
* <p>
* If the command line being analyzed invokes the service starter, the tool will
* create a <code>Configuration</code> from the arguments of the command line
* being analyzed. Failure to create the <code>Configuration</code> will result
* in termination of the tool, otherwise the
* <code>com.sun.jini.start.ServiceDescriptor</code>s provided by the
* <code>Configuration</code> are examined. The following checks are done for
* each <code>ServiceDescriptor</code>:
* <ul>
<li>Verify that the <code>getPolicy</code> method returns a reference to a
*      policy file that is valid and accessible
<li>Check whether that policy grants <code>AllPermissions</code> to all
*      protection domains
* </ul>
* The following checks are done for each
* <code>NonActivatableServiceDescriptor</code> and each
* <code>SharedActivatableServiceDescriptor</code>
* <ul>
<li>Verify that calling <code>getServerConfigArgs</code> does not return
*      <code>null</code> or an empty array
<li>Verify that a <code>Configuration</code> can be constructed from those
*      args
<li>Verify that any entry in that <code>Configuration</code> named
*      <code>initialLookupGroups</code> does not have a value of
*      <code>ALL_GROUPS</code>
<li>Verify that the export codebase is defined. For each component in the
*      export codebase:
*   <ul>
*     <li>Verify that the URL is not malformed
*     <li>If it is an HTTPMD URL, verify that the necessary protocol handler
*         is installed
*     <li>Check that domain names are fully qualified
*     <li>Warn if an md5 HTTPMD URL is being used (md5 has a security hole)
*     <li>Verify that the host name in the URL can be resolved
*     <li>Verify that the host name does not resolve to a loopback address
*     <li>Verify that it's possible to open a connection using the URL
*   </ul>
*   <li>If the <code>-security</code> option was specified:
*     <ul>
*        <li>Verify that <code>javax.net.ssl.trustStore</code> is defined and
*            that the trust store it references is accessible
*        <li>Check whether <code>com.sun.jini.discovery.x500.trustStore</code>
*            is defined, and if so that the trust store it references is
*            accessible
*        <li>Check whether <code>javax.net.ssl.keyStore</code> is defined, and
*            if so that the key store it references is accessible
*        <li>Verify that a login configuration is defined and that it is
*            accessible and syntactically correct
*      </ul>
* </ul>
* The following checks are done for each
* <code>SharedActivatableServiceDescriptor</code>:
* <ul>
*   <li>Verify that any entry in that <code>Configuration</code> named
*       <code>persistenceDirectory</code> refers to either an empty directory
*       or a non-existant directory
* </ul>
* The following checks are done for each
* <code>SharedActivationGroupDescriptor</code>:
* <ul>
*   <li>Verify that the activation system is running
*   <li>Verify that the virtual machine (VM) created by that command is at
*       least version 1.4
*   <li>Verify that <code>jsk-policy.jar</code> is loaded from the extensions
*       directory
*    
*   <li>Verify that <code>jsk-platform.jar</code> is in the classpath
*
*
*   <li>If <code>java.util.logging.config.file</code> is defined in the
*       properties returned by calling <code>getServerProperties</code> then
*       verify that the file it references is accessible
* </ul>
*  A subset of these checks are performed on the command line being analyzed:
<ul>
*    <li>Codebase/URL checks based on the value of
*        <code>java.rmi.server.codebase</code>
*    <li>Policy file checks based on the value of
*        <code>java.security.policy</code>
*    <li>Check for <code>jsk-policy</code> being loaded by the extension
*        class loader
*    <li>Check for <code>jsk-platform</code> in the classpath
*    <li>Security checks if <code>-security</code> was specified
*    <li>The logging config file check
* </ul>
* In all cases, check that the local host name does not resolve to the
* loopback address.
*/

public class EnvCheck {

    /** the list of plugins instances to run */
    private ArrayList pluginList = new ArrayList();

    /** flag controlling the display of stack traces in the output */
    private boolean printStackTraces = false;

    /** the command line arguments of the command being analyzed */
    private String[] args;

    /** the <code>ServiceDescriptor</code>s obtained from the starter config */
    private ServiceDescriptor[] descriptors = new  ServiceDescriptor[0];

    /** the localization resource bundle */
    private static ResourceBundle bundle;

    /** the java command on the command line being checked */
    private String javaCmd;

    /** the options on the command line being checked (never null) */
    String[] options = new String[0];

    /** the properties on the command line being checked (never null) */
    Properties properties = new Properties();

    /** the classpath on the command line being checked */
    String classpath = null;

    /** the main class on the command line being checked */
    String mainClass = null;

    /** the executable JAR file on the command line being checked */
    String jarToRun = null;

    /** the list of plugins supplied via the -plugin option */
    static ArrayList pluginJarList = new ArrayList();

    /** the class loader for loading plugins */
    ClassLoader pluginLoader = EnvCheck.class.getClassLoader();

    /** the classpath of the tool, including the plugins (updated in run) */
    static String combinedClasspath = System.getProperty("java.class.path");

    /**
     * The entry point for the tool. The localization resource bundle is
     * located, the plugins are loaded, and the checks are performed. The system
     * property <code>java.protocol.handler.pkgs</code> for the tool VM is set
     * to <code>net.jini.url</code> to ensure that the tool can manipulate
     * HTTPMD URLs.
     *
     * @param args the command line arguments
     */
    public static void main(String[] args) {
  System.setProperty("java.protocol.handler.pkgs", "net.jini.url");
  bundle = Util.getResourceBundle(EnvCheck.class);
  if (args.length == 0) {
      usage();
      System.exit(1);
  }
        findPlugins(args);
  new EnvCheck().run(args);
    }

    /**
     * Output the usage message.
     */
    private static void usage(){
  System.err.println(Util.getString("envcheck.usage", bundle));
    }

    /**
     * Helper to print a localized string
     *
     * @param key the resource key
     */
    private String getString(String key) {
  return Util.getString(key, bundle);
    }

    /**
     * Helper to print a localized string
     *
     * @param key the resource key
     * @param val the value parameter expected by the message
     */
    private String getString(String key, String val) {
  return Util.getString(key, bundle, val);
    }

    /**
     * Search the command line for user supplied plugin definitions
     * and place them in the internal plugin list.
     *
     * @param cmdline the original command line args
     */
    private static void findPlugins(String[] cmdLine) {
  int index = 0;
  String arg;
  while ((arg = cmdLine[index++]).startsWith("-")) {
      if (arg.equals("-plugin")) {
    String pluginName = cmdLine[index++];
    if (!(new File(pluginName).exists())) {
        System.err.println(Util.getString("envcheck.noplugin",
               bundle,
               pluginName));
        System.exit(1);
    }
    combinedClasspath += File.pathSeparator + pluginName;
    try {
        pluginJarList.add(new URL("file:" + pluginName));
    } catch (MalformedURLException e) { // should never happen
        e.printStackTrace();
        System.exit(1);
    }
      } else if (arg.equals("-level")) {
    index++;
      }
  }
    }

    /**
     * Parse the command line, identifying options for the tool and parsing
     * the VM, system properties, options, JAR/main class and arguments
     * for the command line being tested. The <code>-plugin</code> option
     * is ignored by this parser since they must have been processed
     * earlier.
     *
     * @param cmdLine the original command line arguments
     */
    private void parseArgs(String[] cmdLine) {
  int index = 0;
  try {
      while (true) {
    String arg = cmdLine[index++];
    if (arg.equals("-traces")) {
        printStackTraces = true;
        Reporter.setPrintTraces(true);
    } else if (arg.equals("-explain")) {
        Reporter.setExplanation(true);
    } else if (arg.equals("-level")) {
        String val = cmdLine[index++];
        if (val.equals("info")) {
      Reporter.setLevel(Reporter.INFO);
        } else if (val.equals("warning")) {
      Reporter.setLevel(Reporter.WARNING);
        } else if (val.equals("error")) {
      Reporter.setLevel(Reporter.ERROR);
        } else {
      System.err.println(getString("envcheck.badlevel"));
      usage();
      System.exit(1);
        }
          } else if (arg.equals("-plugin")) {
        index++; // already processed the plugin
    } else if (isPluginOption(arg)) { //no additional work to do
    } else if (arg.startsWith("-")) {
        System.err.println(getString("envcheck.illegalopt", arg));
        usage();
        System.exit(1);
    } else if (arg.endsWith("java")) {
        javaCmd = arg;
        break;
    } else {
        System.err.println(getString("envcheck.nojavacmd"));
        usage();
        System.exit(1);
    }
      }
  } catch (ArrayIndexOutOfBoundsException e) {
      System.err.println(getString("envcheck.nocmdargs"));
      usage();
      System.exit(1);
  }
  try {
      ArrayList optList = new ArrayList();
      while (true) {
    String opt = cmdLine[index++];
    if (!opt.startsWith("-")) {
        mainClass = opt;
        break;
    } else if (opt.startsWith("-D")) {
        int valueIndex = opt.indexOf("=");
        String key = opt.substring(2, valueIndex);
        String value = opt.substring(valueIndex + 1);
        properties.put(key, value);
    } else if (opt.equals("-cp") || opt.equals("-classpath")) {
        classpath = cmdLine[index++];
    } else if (opt.equals("-jar")) {
        jarToRun = cmdLine[index++];
        break;
    } else {
        optList.add(opt);
    }
      }
      options =
    (String[])  optList.toArray(new String[optList.size()]);
  } catch (ArrayIndexOutOfBoundsException e) {
      System.err.println(getString("noexecutable"));
      usage();
      System.exit(1);
  }
  ArrayList argList = new ArrayList();
  while (index < cmdLine.length) {
      argList.add(cmdLine[index++]);
  }
  args = (String[]) argList.toArray(new String[argList.size()]);
    }
     
    /**
     * Return the <code>ServiceDescriptor</code>s contained in the service
     * starter configuration. If the command being analyzed does not invoke the
     * service starter, a zero-length array will be returned.
     *
     * @return the descriptors in the starter configuration or an empty array
     */
    public ServiceDescriptor[] getDescriptors() {
  return descriptors;
    }

    /**
     * Return the <code>SharedActivationGroupDescriptor</code> contained in the
     * service starter configuration. Returns <code>null</code> if there is no
     * such descriptor, or if the command being analyzed does not invoke the
     * service starter.
     *
     * @return the <code>SharedActivationGroupDescriptor</code> or
     *         <code>null</code>
     */
    public SharedActivationGroupDescriptor getGroupDescriptor() {
  for (int i = 0; i < descriptors.length; i++) {
      if (descriptors[i] instanceof SharedActivationGroupDescriptor) {
    return (SharedActivationGroupDescriptor) descriptors[i];
      }
  }
  return null;
    }

    /**
     * Perform the runtime checks. If any user plugins were supplied, construct
     * a class loader capable of loading them and modify
     * <code>combinedClasspath</code> to make the classes available to subtasks.
     * Load the plugins and parse the command line. The plugins must be
     * available to the parser so that plugin specific options can be checked
     * for. Load the service starter configuration if the service starter is
     * being invoked. Execute all of the plugin classes in the order loaded.
     *
     * @param cmdLine the original command line arguments
     */
    private void run(String[] cmdLine) {
  if (pluginJarList.size() > 0) {
      URL[] urls =
    (URL[]) pluginJarList.toArray(new URL[pluginJarList.size()]);
      pluginLoader = new URLClassLoader(urls, pluginLoader);
  }
  loadPlugins();
  parseArgs(cmdLine);
  if (jarToRun == null && classpath == null) {
      System.err.println(getString("envcheck.missingclasspath"));
      System.exit(1);
  }
  loadConfiguration();
  Iterator plugins = pluginList.iterator();
  while (plugins.hasNext()) {
      Plugin plugin = (Plugin) plugins.next();
      try {
    plugin.run(this);
      } catch (SecurityException e) { // limp on if permission check fails
    e.printStackTrace();
      }
  }
  int warningCount = Reporter.getWarningCount();
  int errorCount = Reporter.getErrorCount();
  if ((warningCount + errorCount) > 0) {
      System.out.println();
      System.out.println();
      System.out.println(getString("envcheck.summaryseparator"));
  }
  if (warningCount > 0) {
      System.out.println(getString("envcheck.warningheader")
             + warningCount);
  }
  if (errorCount > 0) {
      System.out.println(getString("envcheck.errorheader") + errorCount);
  }
     
    }

    /**
     * Check whether <code>arg</code> is a plugin specific option. The
     * <code>isPluginOption</code> method of every plugin is called in
     * case an option is shared among multiple plugins.
     *
     * @param arg the argument to check
     * @return <code>true</code> if any plugin's <code>isPluginOption</code>
     *         method returns <code>true</code>
     */
    private boolean isPluginOption(String arg) {
  boolean gotOne = false;
  Iterator plugins = pluginList.iterator();
  while (plugins.hasNext()) {
      Plugin plugin = (Plugin) plugins.next();
      if (plugin.isPluginOption(arg)) {
    gotOne = true;
      }
  }
  return gotOne;
    }
     
    /**
     * Instantiate the service starter configuration if the command line being
     * analyzed runs the service starter.  This is assumed to be true if the
     * command line specified an executable JAR file named <code>start.jar</code> and
     * if that file resides in the same directory as a file named
     * <code>jsk-platform.jar</code>. Extract and save all of the service
     * descriptors and service destructors from the configuration. If a
     * <code>ConfigurationException</code> is thrown the tool will exit. If
     * there are no <code>ServiceDescriptor</code> or
     * <code>ServiceDestructor</code> entries supplied by the configuration a
     * error is generated and a normal return is done. The instantiation of
     * the configuration is done in a child process using the VM, properties,
     * options and arguments supplied in the command line under test.
     */
    private void loadConfiguration() {
  if (jarToRun != null) {
      if (!jarToRun.endsWith("start.jar")) {
    return;
      }
      //XXX do existence check in parser
      File starterFile = new File(jarToRun);
      File jskLibDir = starterFile.getParentFile();
      File jskplatform = new File(jskLibDir, "jsk-platform.jar");
      if (!jskplatform.exists()) {
    return; // presumably a user file coincidentally named start.jar
      }
  } else {
      if (!mainClass.equals("com.sun.jini.start.ServiceStarter")) {
    return;
      }
  }
  //XXX need to check validity of javaCmd and arg list length
  Object lobj =
      launch("com.sun.jini.tool.envcheck.EnvCheck$GetDescriptors", args);
  if (lobj instanceof ServiceDescriptor[]) {
      descriptors = (ServiceDescriptor[]) lobj;
      if (descriptors.length == 0) {
    Reporter.print(new Message(Reporter.ERROR,
             getString("envcheck.emptyconfig"),
             null));
      }
  } else if (lobj instanceof ConfigurationException) {
      System.err.println(getString("envcheck.ssdescfailed"));
      ((Throwable) lobj).printStackTrace();
      System.exit(1);
  } else {
      System.err.println(getString("envcheck.subtaskex", lobj.toString()));
      ((Throwable) lobj).printStackTrace();
      System.exit(1);
  }
    }

    /**
     * Load the plugin classes. All of the resources named
     * <code>META-INF/services/com.sun.jini.tool.envcheck.Plugin</code> are
     * read; each such resource is expected to contain a list of plugin class
     * names, one per line (white space is trimmed). The corresponding classes
     * are loaded and saved. Any other exceptions thrown while processing
     * the plugin JAR files will cause the tool to exit.
     */
    private void loadPlugins() {
  Package pkg = getClass().getPackage();
  String pkgName = "";
  if (pkg != null) {
      pkgName = pkg.getName();
  }
  pkgName = pkgName.replace('.', '/');
  if (pkgName.length() > 0) {
      pkgName += "/";
  }
  Iterator plugins =
      Service.providers(com.sun.jini.tool.envcheck.Plugin.class,
            pluginLoader);
  while (plugins.hasNext()) {
      pluginList.add(plugins.next());
  }
    }

    /**
     * Get the command line arguments of the command being analyzed.
     *
     * @return the args
     */
    // unused, but supplied to support user developed plugins
    public String[] getArgs() {
  return args;
    }

    /**
     * Return the flag indicating whether to output stack traces that
     * result from a check.
     */
    public boolean printStacks() {
  return printStackTraces;
    }

    /**
     * Launch a child VM using the <code>java</code> command, properties, and
     * options supplied on the command line being analyzed. If an executable JAR
     * file was specified, the classpath of the child VM consists of the JAR
     * file name augmented with the classpath of the tool and plugins. If a main
     * class was specified, the classpath of the child VM consists of the
     * <code>-cp/-classpath</code> option value of the command line being
     * analyzed augmented with the classpath of the tool and plugins.
     *
     * @param task the class name of the task to launch, which must implement
     *             the <code>SubVMTask</code> interface
     * @param args the arguments to pass to the main method of the task
     * @return the result or exception returned by the subtask supplied as a
     *         serialized object written on the subtask's
     *         <code>System.out</code> stream.
     */
    public Object launch(String task, String[] args) {
  String cp;
  if (jarToRun != null) {
      cp = jarToRun + File.pathSeparator;
  } else {
      cp = classpath + File.pathSeparator;
  }
  cp += combinedClasspath;
  String[] opts = new String[options.length + 2];
  opts[0] = "-cp";
  opts[1] = cp;
  System.arraycopy(options, 0, opts, 2, options.length);
  if (args == null) {
      args = new String[0];
  }
  String[] subvmArgs = new String[args.length + 1];
  subvmArgs[0] = task;
  System.arraycopy(args, 0, subvmArgs, 1, args.length);
  return launch(javaCmd, properties, opts, subvmArgs);
    }

    /**
     * Launch a child VM using the <code>java</code> command, properties, and
     * options supplied on the command line. If an executable JAR file was
     * specified, the classpath of the child VM consists of the JAR file name
     * augmented with the classpath of the tool and plugins. If a main class was
     * specified, the classpath of the child VM consists of the
     * <code>-cp/-classpath</code> option value of the command line being
     * analyzed augmented with the classpath of the tool and plugins.
     *
     * @param task the class name of the task to launch, which must implement
     *             the <code>SubVMTask</code> interface
     * @return the result or exception returned by the subtask supplied as a
     *         serialized object written on the subtask's
     *         <code>System.out</code> stream.
     */
    public Object launch(String task) {
  return launch(task, null);
    }

    /**
     * Return a property value that was specified on the command line being
     * analyzed. Only properties explicitly defined on the command line will
     * resolve to a value.
     *
     * @param key the name of the property
     * @return the property value, or <code>null</code> if undefined
     */
    public String getProperty(String key) {
  return properties.getProperty(key);
    }

    /**
     * Return a copy of the properties that were specified on the
     * command line being analyzed. The caller may modify the returned
     * properties object.
     *
     * @return the properties, which may be empty
     */
    public Properties getProperties() {
  return (Properties) properties.clone();
    }

    /**
     * Launch a subtask VM using the <code>java</code> command given by
     * <code>javaCmd</code>.  If <code>javaCmd</code> is <code>null</code>, the
     * command to run is derived from the value of the <code>java.home</code>
     * property of the tool VM.  The first value in <code>args</code> must name
     * a class that implements the <code>SubVMTask</code> interface. The
     * <code>props</code> and <code>opts</code> arrays must contain fully
     * formatted command line values for properties and options
     * (i.e. "-Dfoo=bar" or "-opt"). <code>opts</code> must include a
     * <code>-cp</code> or <code>-classpath</code> option and its value must
     * completely specify the classpath required to run the subtask.
     *
     * @param javaCmd the <code>java</code> command to execute, or
     *                <code>null</code> to create another instance of the
     *                tool VM
     * @param props   properties to define, which may be <code>null</code> or
     *                empty
     * @param opts    options to define, which must include a classpath
     *                definition
     * @param args    arguments to pass to the child VM
     * @return        the result or exception returned by the subtask supplied
     *                as a serialized object written on the subtask's
     *                <code>System.out</code> stream.
     *
     * @throws IllegalArgumentException if <code>opts</code> does not
     *         include a classpath definition, or if <code>args[0]</code>
     *         does not contain the name of a class that implements
     *         <code>SubVMTask</code>
     */
    public Object launch(String javaCmd,
       Properties props,
       String[] opts,
       String[] args)
    {
  if (args == null || args.length == 0) {
      throw new IllegalArgumentException("No class name in args[0]");
  }
  // make sure subtask is valid to avoid obscure failures at exec time
  String taskName = args[0];
  try {
      Class taskClass = Class.forName(taskName, true, pluginLoader);
      if (!SubVMTask.class.isAssignableFrom(taskClass)) {
    throw new IllegalArgumentException(taskName
               + " does not implement "
               + "SubVMTask");
      }
      // make sure inner classes are static
      if (taskClass.getDeclaringClass() != null) {
    if ((taskClass.getModifiers() & Modifier.STATIC) == 0) {
        throw new IllegalArgumentException(taskName
               + " must be a static class");
    }
      }
  } catch (ClassNotFoundException e) {
      throw new IllegalArgumentException("Class not found: " + taskName);
  }
  if (javaCmd == null) {
      javaCmd = System.getProperty("java.home");
      javaCmd += File.separator + "bin" + File.separator + "java";
  }
  ArrayList cmdList = new ArrayList();
  cmdList.add(javaCmd);
  boolean gotClasspath = false;
  if (opts != null) {
      for (int i = 0; i < opts.length; i++) {
    if (opts[i].equals("-cp") || opts[i].equals("-classpath"))
        if (i + 1 == opts.length) {
      throw new IllegalArgumentException("classpath option "
                 + "does not have "
                 + "a value");
        }
    gotClasspath = true;
    cmdList.add(opts[i]);
      }
  }
  if (!gotClasspath) {
      throw new IllegalArgumentException("no classpath defined");
  }
  if (props != null) {
      Enumeration en = props.propertyNames();
      while (en.hasMoreElements()) {
    String key = (String) en.nextElement();
    String value = props.getProperty(key);
    cmdList.add("-D" + key + "=" + value);
      }
  }
  cmdList.add("com.sun.jini.tool.envcheck.SubVM");
  for (int i = 0; i < args.length; i++) {
      cmdList.add(args[i]);
  }
  try {
      String[] argList =
    (String[]) cmdList.toArray(new String[cmdList.size()]);
//        for (int i = 0; i < argList.length; i++) {
//      System.out.print(argList[i] + " ");
//        }
//        System.out.println();
      Process p = Runtime.getRuntime().exec(argList);
      Pipe pipe = new Pipe("errorPipe",
         p.getErrorStream(),
         System.err,
         "Child VM: ");
      ObjectInputStream s = new ObjectInputStream(p.getInputStream());
      Object o = s.readObject();
      pipe.waitTillEmpty(5000);
      return o;
  } catch (Exception e) {
      return e;
  }
    }

    /**
     * Launch a subtask using the environment defined by the given service
     * descriptors. Calling this method is equivalent to calling
     * <code>launch(d, g, taskName, null)</code>.
     *
     * @param d the services descriptor, which may be <code>null</code>
     * @param gd the group descriptor, which may be <code>null</code
     * @param taskName the name of the subtask to run
     * @return the result or exception returned by the subtask supplied as a
     *         serialized object written on the subtask's
     *         <code>System.out</code> stream.
     */
    public Object launch(NonActivatableServiceDescriptor d,
       SharedActivationGroupDescriptor gd,
       String taskName)
    {
  return launch(d, gd, taskName, null);
    }

    /**
     * Launch a subtask using the environment defined by the given service
     * descriptors.
     * <p>
     * If <code>d</code> and <code>gd</code> are both <code>null</code>, then
     * calling this method is equivalent to calling
     * <code>launch(taskName, args)</code>.
     * <p>

     * If <code>d</code> is <code>null</code> and <code>gd</code> is
     * non-<code>null</code> then the properties are taken from
     * <code>gd.getServerProperties()</code> and the
     * <code>java.security.policy</code> property is added or replaced with the
     * value of <code>gd.getPolicy()</code>.  The options are taken from
     * <code>gd.getServerOptions()</code>, but any <code>-cp/-classpath</code>
     * option is discarded; a <code>-cp</code> option is added that is the value
     * of <code>gd.getClasspath()</code> augmented with the classpath of the
     * tool and plugins.  If <code>gd.getServerCommand()</code> is
     * non-<code>null</code>, its value is used to invoke the child VM;
     * otherwise the <code>java</code> command of the command line being
     * analyzed is used. The arguments passed to the child VM consist of an
     * array whose first element is <code>taskName</code> and whose remaining
     * elements are taken from <code>args</code>.

     * <p>

     * If <code>d</code> is not <code>null</code>, but <code>gd</code> is
     * <code>null</code>, then if <code>d</code> is an instance of
     * <code>SharedActivatableServiceDescriptor</code> an
     * <code>IllegalArgumentException</code> is thrown. Otherwise the properties
     * and options are taken from the command line being analyzed. The
     * <code>java.security.policy</code> property is added or replaced using the
     * value of <code>d.getPolicy()</code>.  The <code>-cp/-classpath</code>
     * option is replaced with the value of <code>d.getImportCodebase()</code>
     * augmented with the classpath of the tool and plugins.  The arguments
     * passed to the child VM consist of <code>taskName</code> followed by
     * <code>args</code> if <code>args</code> is non-<code>null</code>, or
     * followed by <code>d.getServerConfigArgs()</code> otherwise.  The VM is
     * invoked using the <code>java</code> command of the command line being
     * analyzed.

     * <p>

     * if <code>d</code> and <code>gd</code> are both non-<code>null</code> then
     * if <code>d</code> is an instance of
     * <code>SharedActivatableServiceDescriptor</code> then the properties,
     * options, and <code>java</code> command are taken from
     * <code>gd.getServerProperties()</code>,
     * <code>gd.getServerOptions()</code>, and
     * <code>gd.getServerCommand()</code>; however, if the value of
     * <code>gd.getServerCommand()</code> is <code>null</code>, the
     * <code>java</code> command is taken from the command line being
     * analysed. If <code>d</code> is not an instance of
     * <code>SharedActivatableServiceDescriptor</code> then the properties,
     * options, and <code>java</code> command are taken from the command line
     * being analyzed.  In all cases the <code>java.security.policy</code>
     * property is added or replaced using the value of
     * <code>d.getPolicy()</code>.  The <code>-cp/-classpath</code> option is
     * added or replaced with the value of <code>d.getImportCodebase()</code>
     * augmented with the value of the classpath of the tool and plugins.  The
     * arguments passed to the child VM consist of <code>taskName</code>
     * followed by <code>args</code> if <code>args</code> is
     * non-<code>null</code>, or followed by
     * <code>d.getServerConfigArgs()</code> otherwise.

     * <p>
     *
     * @param d the service descriptor, which may be <code>null</code>
     * @param gd the group descriptor, which may be <code>null</code
     * @param taskName the name of the subtask to run
     * @param args the arguments to pass to the child VM, which may be
     *             <code>null</code>
     * @return the result or exception returned by the subtask supplied as a
     *         serialized object written on the subtask's
     *         <code>System.out</code> stream.
     */
    public Object launch(NonActivatableServiceDescriptor d,
       SharedActivationGroupDescriptor gd,
       String taskName,
       String[] args)
    {
  if (d == null && gd == null) {
      return launch(taskName, args);
  }
  //build taskArgs array
  if (args == null) {
      if (d != null) {
    args = d.getServerConfigArgs();
    if (args == null || args.length == 0) {
        return new IllegalArgumentException("No configuration args "
              + "in descriptor");
    }
      } else {
    args = new String[0];
      }
  }
  String[] taskArgs = new String[args.length + 1];
  taskArgs[0] = taskName;
  System.arraycopy(args, 0, taskArgs, 1, args.length);
 
  if (d == null && gd != null) {
      Properties props = gd.getServerProperties();
      props.put("java.security.policy", gd.getPolicy());
      ArrayList l = new ArrayList();
      String[] opts = gd.getServerOptions();
      if (opts != null) {
    for (int i = 0; i < opts.length; i++ ) {
        if (opts[i].equals("-cp")) {
      i++; // bump past value
        } else {
      l.add(opts[i]);
        }
    }
      }
      l.add("-cp");
      l.add(gd.getClasspath() + File.pathSeparator + combinedClasspath);
      opts = (String[]) l.toArray(new String[l.size()]);
      String cmd = gd.getServerCommand();
      if (cmd == null) {
    cmd = javaCmd;
      }
      return launch(cmd, props, opts, taskArgs);
  } else if (d != null && gd == null) {
      if (d instanceof SharedActivatableServiceDescriptor) {
    throw new IllegalArgumentException("no group for service");
      }
      Properties props = getProperties();
      props.put("java.security.policy", d.getPolicy());
      ArrayList l = new ArrayList();
      for (int i = 0; i < options.length; i++ ) {
    if (options[i].equals("-cp")
        || options[i].equals("-classpath")) {
        i++; // bump past value
    } else {
        l.add(options[i]);
    }
      }
      l.add("-cp");
      l.add(d.getImportCodebase()
        + File.pathSeparator
        + combinedClasspath);
      String[] opts = (String[]) l.toArray(new String[l.size()]);
      return launch(javaCmd, props, opts, taskArgs);
  } else if (d != null && gd != null) {
      Properties props = getProperties();
      String[] opts = options;
      String vm = null;
      if (d instanceof SharedActivatableServiceDescriptor) {
    props = gd.getServerProperties();
    if (props == null) {
        props = new Properties();
    }
    opts = gd.getServerOptions();
    vm = gd.getServerCommand();
      }
      if (vm == null) {
    vm = javaCmd;
      }
      props.put("java.security.policy", d.getPolicy());
      ArrayList l = new ArrayList();
      if (opts != null) {
    for (int i = 0; i < opts.length; i++ ) {
        if (opts[i].equals("-cp")
         || opts[i].equals("-classpath")) {
      i++; // bump past value
        } else {
      l.add(opts[i]);
        }
    }
      }
      l.add("-cp");
      l.add(d.getImportCodebase()
      + File.pathSeparator
      + combinedClasspath);
      opts = (String[]) l.toArray(new String[l.size()]);
      return launch(vm, props, opts, taskArgs);
  } else {
      throw new IllegalStateException("Should never get here");
  }
    }

    /**
     * Return the <code>java</code> command for the command line being analyzed.
     *
     * @return the <code>java</code> command
     */
    public String getJavaCmd() {
  return javaCmd;
    }

    /**
     * Check for the existence of a file identified by a property
     * supplied on the command line being analyzed.
     *
     * @param prop the name of the property
     * @param desc a brief description of the file
     * @return the property value, or <code>null</code> if undefined
     */
    public String checkFile(String prop, String desc) {
  String name = getProperty(prop);
  if (name == null) {
      return getString("util.undef", desc);
  }
  return Util.checkFileName(name, desc);
    }

    /**
     * Return the name of the executable JAR file supplied on the
     * command line being analyzed.
     *
     * @return the JAR file name, or <code>null</code> if the command line
     *         did not specify one.
     */
    public String getJarToRun() {
        return jarToRun;
    }

    /**
     * Get the classpath provided by the command line being analyzed.  If
     * <code>getJarToRun()</code> returns a non-<code>null</code> value then
     * its value is returned. Otherwise the value supplied by the command
     * line <code>-cp/-classpath</code> option is returned.
     *
     * @return the classpath supplied on the command line being analyzed.
     */
    public String getClasspath() {
  if (jarToRun != null) {
      return jarToRun;
  }
  return classpath;
    }

    /**
     * A subtask which returns the service descriptors and service
     * destructors supplied by the service starter configuration constructed
     * from <code>args</code>.
     */
     static class GetDescriptors implements SubVMTask {

  public Object run(String[] args) {
      Configuration starterConfig = null;
      try {
    starterConfig = ConfigurationProvider.getInstance(args);
      } catch (ConfigurationException e) {
    return e;
      }
      try {
    ServiceDescriptor[] d1 =  (ServiceDescriptor[])
        starterConfig.getEntry("com.sun.jini.start",
             "serviceDescriptors",
             ServiceDescriptor[].class,
             new ServiceDescriptor[0]);
    ServiceDescriptor[] d2 =  (ServiceDescriptor[])
        starterConfig.getEntry("com.sun.jini.start",
             "serviceDestructors",
             ServiceDescriptor[].class,
             new ServiceDescriptor[0]);
    ServiceDescriptor[] descriptors =
        new ServiceDescriptor[d1.length + d2.length];
    System.arraycopy(d1, 0, descriptors, 0, d1.length);
    System.arraycopy(d2, 0, descriptors, d1.length, d2.length);
    return descriptors;
      } catch (ConfigurationException e) {
    return e;
      }
  }
    }

    /**
     * An I/O redirection pipe. A daemon thread copies data from an input
     * stream to an output stream. An optional annotation may be provided
     * which will prefix each line of the copied data with a label which
     * can be used to identify the source.
     */
    private class Pipe implements Runnable {

  /** the line separator character */
  private final static byte SEPARATOR = (byte) '\n';

  /** output line buffer */
  private ByteArrayOutputStream bufOut = new ByteArrayOutputStream();

  /** the input stream */
  private InputStream in;

  /** the output PrintStream */
  private PrintStream stream;

  /** the output stream annotation */
  private String annotation;

  /** the thread to process the data */
  private Thread outThread;
 
  /**
   * Create a new Pipe object and start the thread to handle the data.
   *
   * @param name the name to assign to the thread
   * @param in input stream from which pipe input flows
   * @param stream the stream to which output will be sent
   * @param a the annotation for prepending text to logged lines
   */
  Pipe(String name,
        InputStream in,
        PrintStream stream,
        String a)
  {
      this.in = in;
      this.stream = stream;
      this.annotation = a;
      outThread = new Thread(this, name);
      outThread.setDaemon(true);
      outThread.start();
  }

  /**
   * Wait until the run method terminates due to reading EOF on input
   *
   * @param timeout max time to wait for the thread to terminate
   */
  void waitTillEmpty(int timeout) {
      try {
    outThread.join(timeout);
      } catch (InterruptedException ignore) {
      }
  }

  /**
   * Read and write data until EOF is detected. Flush any remaining data to
   * the output steam and return, terminating the thread.
   */
  public void run() {
      byte[] buf = new byte[256];
      int count;
      try {
    /* read bytes till there are no more. */
    while ((count = in.read(buf)) != -1) {
        write(buf, count);
    }

    /*  If annotating, flush internal buffer... may not have ended on a
     *  line separator, we also need a last annotation if
     *  something was left.
     */
    String lastInBuffer = bufOut.toString();
    bufOut.reset();
    if (lastInBuffer.length() > 0) {
        if (annotation != null) {
      stream.print(annotation);
        }
        stream.println(lastInBuffer);
    }
      } catch (IOException e) {
      }
  }
 
  /**
   * Write each byte in the give byte array.
   *
   * @param b the array of input bytes
   * @param len the number data bytes in the array
   */
  private void write(byte b[], int len) throws IOException {
      if (len < 0) {
    throw new ArrayIndexOutOfBoundsException(len);
      }
      for (int i = 0; i < len; i++) {
    write(b[i]);
      }
  }

  /**
   * If not annotated, write the byte to the stream immediately. Otherwise,
   * write a byte of data to the internal buffer. If we have matched a line
   * separator, then the currently buffered line is sent to the output writer
   * with a prepended annotation string.
   */
  private void write(byte b) throws IOException {

      bufOut.write(b);
     
      // write buffered line if line separator detected
      if (b == SEPARATOR) {
    String s = bufOut.toString();
    bufOut.reset();
    if (annotation != null) {
        stream.print(annotation);
    }
    stream.print(s);
      }
  }
    }
}
TOP

Related Classes of com.sun.jini.tool.envcheck.EnvCheck$GetDescriptors

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.