Shell.java
上传用户:quxuerui
上传日期:2018-01-08
资源大小:41811k
文件大小:11k
源码类别:

网格计算

开发平台:

Java

  1. /**
  2.  * Licensed to the Apache Software Foundation (ASF) under one
  3.  * or more contributor license agreements.  See the NOTICE file
  4.  * distributed with this work for additional information
  5.  * regarding copyright ownership.  The ASF licenses this file
  6.  * to you under the Apache License, Version 2.0 (the
  7.  * "License"); you may not use this file except in compliance
  8.  * with the License.  You may obtain a copy of the License at
  9.  *
  10.  *     http://www.apache.org/licenses/LICENSE-2.0
  11.  *
  12.  * Unless required by applicable law or agreed to in writing, software
  13.  * distributed under the License is distributed on an "AS IS" BASIS,
  14.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15.  * See the License for the specific language governing permissions and
  16.  * limitations under the License.
  17.  */
  18. package org.apache.hadoop.util;
  19. import java.io.BufferedReader;
  20. import java.io.File;
  21. import java.io.IOException;
  22. import java.io.InputStreamReader;
  23. import java.util.Map;
  24. import org.apache.commons.logging.Log;
  25. import org.apache.commons.logging.LogFactory;
  26. import org.apache.hadoop.conf.Configuration;
  27. /** 
  28.  * A base class for running a Unix command.
  29.  * 
  30.  * <code>Shell</code> can be used to run unix commands like <code>du</code> or
  31.  * <code>df</code>. It also offers facilities to gate commands by 
  32.  * time-intervals.
  33.  */
  34. abstract public class Shell {
  35.   
  36.   public static final Log LOG = LogFactory.getLog(Shell.class);
  37.   
  38.   /** a Unix command to get the current user's name */
  39.   public final static String USER_NAME_COMMAND = "whoami";
  40.   /** a Unix command to get the current user's groups list */
  41.   public static String[] getGROUPS_COMMAND() {
  42.     return new String[]{"bash", "-c", "groups"};
  43.   }
  44.   /** a Unix command to set permission */
  45.   public static final String SET_PERMISSION_COMMAND = "chmod";
  46.   /** a Unix command to set owner */
  47.   public static final String SET_OWNER_COMMAND = "chown";
  48.   public static final String SET_GROUP_COMMAND = "chgrp";
  49.   /** Return a Unix command to get permission information. */
  50.   public static String[] getGET_PERMISSION_COMMAND() {
  51.     //force /bin/ls, except on windows.
  52.     return new String[] {(WINDOWS ? "ls" : "/bin/ls"), "-ld"};
  53.   }
  54.   /** 
  55.    * Get the Unix command for setting the maximum virtual memory available
  56.    * to a given child process. This is only relevant when we are forking a
  57.    * process from within the {@link org.apache.hadoop.mapred.Mapper} or the 
  58.    * {@link org.apache.hadoop.mapred.Reducer} implementations 
  59.    * e.g. <a href="{@docRoot}/org/apache/hadoop/mapred/pipes/package-summary.html">Hadoop Pipes</a> 
  60.    * or <a href="{@docRoot}/org/apache/hadoop/streaming/package-summary.html">Hadoop Streaming</a>.
  61.    * 
  62.    * It also checks to ensure that we are running on a *nix platform else 
  63.    * (e.g. in Cygwin/Windows) it returns <code>null</code>.
  64.    * @param conf configuration
  65.    * @return a <code>String[]</code> with the ulimit command arguments or 
  66.    *         <code>null</code> if we are running on a non *nix platform or
  67.    *         if the limit is unspecified.
  68.    */
  69.   public static String[] getUlimitMemoryCommand(Configuration conf) {
  70.     // ulimit isn't supported on Windows
  71.     if (WINDOWS) {
  72.       return null;
  73.     }
  74.     
  75.     // get the memory limit from the configuration
  76.     String ulimit = conf.get("mapred.child.ulimit");
  77.     if (ulimit == null) {
  78.       return null;
  79.     }
  80.     
  81.     // Parse it to ensure it is legal/sane
  82.     int memoryLimit = Integer.valueOf(ulimit);
  83.     return new String[] {"ulimit", "-v", String.valueOf(memoryLimit)};
  84.   }
  85.   
  86.   /** Set to true on Windows platforms */
  87.   public static final boolean WINDOWS /* borrowed from Path.WINDOWS */
  88.                 = System.getProperty("os.name").startsWith("Windows");
  89.   
  90.   private long    interval;   // refresh interval in msec
  91.   private long    lastTime;   // last time the command was performed
  92.   private Map<String, String> environment; // env for the command execution
  93.   private File dir;
  94.   private Process process; // sub process used to execute the command
  95.   private int exitCode;
  96.   
  97.   public Shell() {
  98.     this(0L);
  99.   }
  100.   
  101.   /**
  102.    * @param interval the minimum duration to wait before re-executing the 
  103.    *        command.
  104.    */
  105.   public Shell( long interval ) {
  106.     this.interval = interval;
  107.     this.lastTime = (interval<0) ? 0 : -interval;
  108.   }
  109.   
  110.   /** set the environment for the command 
  111.    * @param env Mapping of environment variables
  112.    */
  113.   protected void setEnvironment(Map<String, String> env) {
  114.     this.environment = env;
  115.   }
  116.   /** set the working directory 
  117.    * @param dir The directory where the command would be executed
  118.    */
  119.   protected void setWorkingDirectory(File dir) {
  120.     this.dir = dir;
  121.   }
  122.   /** check to see if a command needs to be executed and execute if needed */
  123.   protected void run() throws IOException {
  124.     if (lastTime + interval > System.currentTimeMillis())
  125.       return;
  126.     exitCode = 0; // reset for next run
  127.     runCommand();
  128.   }
  129.   /** Run a command */
  130.   private void runCommand() throws IOException { 
  131.     ProcessBuilder builder = new ProcessBuilder(getExecString());
  132.     boolean completed = false;
  133.     
  134.     if (environment != null) {
  135.       builder.environment().putAll(this.environment);
  136.     }
  137.     if (dir != null) {
  138.       builder.directory(this.dir);
  139.     }
  140.     
  141.     process = builder.start();
  142.     final BufferedReader errReader = 
  143.             new BufferedReader(new InputStreamReader(process
  144.                                                      .getErrorStream()));
  145.     BufferedReader inReader = 
  146.             new BufferedReader(new InputStreamReader(process
  147.                                                      .getInputStream()));
  148.     final StringBuffer errMsg = new StringBuffer();
  149.     
  150.     // read error and input streams as this would free up the buffers
  151.     // free the error stream buffer
  152.     Thread errThread = new Thread() {
  153.       @Override
  154.       public void run() {
  155.         try {
  156.           String line = errReader.readLine();
  157.           while((line != null) && !isInterrupted()) {
  158.             errMsg.append(line);
  159.             errMsg.append(System.getProperty("line.separator"));
  160.             line = errReader.readLine();
  161.           }
  162.         } catch(IOException ioe) {
  163.           LOG.warn("Error reading the error stream", ioe);
  164.         }
  165.       }
  166.     };
  167.     try {
  168.       errThread.start();
  169.     } catch (IllegalStateException ise) { }
  170.     try {
  171.       parseExecResult(inReader); // parse the output
  172.       // clear the input stream buffer
  173.       String line = inReader.readLine();
  174.       while(line != null) { 
  175.         line = inReader.readLine();
  176.       }
  177.       // wait for the process to finish and check the exit code
  178.       exitCode = process.waitFor();
  179.       try {
  180.         // make sure that the error thread exits
  181.         errThread.join();
  182.       } catch (InterruptedException ie) {
  183.         LOG.warn("Interrupted while reading the error stream", ie);
  184.       }
  185.       completed = true;
  186.       if (exitCode != 0) {
  187.         throw new ExitCodeException(exitCode, errMsg.toString());
  188.       }
  189.     } catch (InterruptedException ie) {
  190.       throw new IOException(ie.toString());
  191.     } finally {
  192.       // close the input stream
  193.       try {
  194.         inReader.close();
  195.       } catch (IOException ioe) {
  196.         LOG.warn("Error while closing the input stream", ioe);
  197.       }
  198.       if (!completed) {
  199.         errThread.interrupt();
  200.       }
  201.       try {
  202.         errReader.close();
  203.       } catch (IOException ioe) {
  204.         LOG.warn("Error while closing the error stream", ioe);
  205.       }
  206.       process.destroy();
  207.       lastTime = System.currentTimeMillis();
  208.     }
  209.   }
  210.   /** return an array containing the command name & its parameters */ 
  211.   protected abstract String[] getExecString();
  212.   
  213.   /** Parse the execution result */
  214.   protected abstract void parseExecResult(BufferedReader lines)
  215.   throws IOException;
  216.   /** get the current sub-process executing the given command 
  217.    * @return process executing the command
  218.    */
  219.   public Process getProcess() {
  220.     return process;
  221.   }
  222.   /** get the exit code 
  223.    * @return the exit code of the process
  224.    */
  225.   public int getExitCode() {
  226.     return exitCode;
  227.   }
  228.   /**
  229.    * This is an IOException with exit code added.
  230.    */
  231.   public static class ExitCodeException extends IOException {
  232.     int exitCode;
  233.     
  234.     public ExitCodeException(int exitCode, String message) {
  235.       super(message);
  236.       this.exitCode = exitCode;
  237.     }
  238.     
  239.     public int getExitCode() {
  240.       return exitCode;
  241.     }
  242.   }
  243.   
  244.   /**
  245.    * A simple shell command executor.
  246.    * 
  247.    * <code>ShellCommandExecutor</code>should be used in cases where the output 
  248.    * of the command needs no explicit parsing and where the command, working 
  249.    * directory and the environment remains unchanged. The output of the command 
  250.    * is stored as-is and is expected to be small.
  251.    */
  252.   public static class ShellCommandExecutor extends Shell {
  253.     
  254.     private String[] command;
  255.     private StringBuffer output;
  256.     
  257.     public ShellCommandExecutor(String[] execString) {
  258.       command = execString.clone();
  259.     }
  260.     public ShellCommandExecutor(String[] execString, File dir) {
  261.       this(execString);
  262.       this.setWorkingDirectory(dir);
  263.     }
  264.     public ShellCommandExecutor(String[] execString, File dir, 
  265.                                  Map<String, String> env) {
  266.       this(execString, dir);
  267.       this.setEnvironment(env);
  268.     }
  269.     
  270.     /** Execute the shell command. */
  271.     public void execute() throws IOException {
  272.       this.run();    
  273.     }
  274.     protected String[] getExecString() {
  275.       return command;
  276.     }
  277.     protected void parseExecResult(BufferedReader lines) throws IOException {
  278.       output = new StringBuffer();
  279.       char[] buf = new char[512];
  280.       int nRead;
  281.       while ( (nRead = lines.read(buf, 0, buf.length)) > 0 ) {
  282.         output.append(buf, 0, nRead);
  283.       }
  284.     }
  285.     
  286.     /** Get the output of the shell command.*/
  287.     public String getOutput() {
  288.       return (output == null) ? "" : output.toString();
  289.     }
  290.     /**
  291.      * Returns the commands of this instance.
  292.      * Arguments with spaces in are presented with quotes round; other
  293.      * arguments are presented raw
  294.      *
  295.      * @return a string representation of the object.
  296.      */
  297.     public String toString() {
  298.       StringBuilder builder = new StringBuilder();
  299.       String[] args = getExecString();
  300.       for (String s : args) {
  301.         if (s.indexOf(' ') >= 0) {
  302.           builder.append('"').append(s).append('"');
  303.         } else {
  304.           builder.append(s);
  305.         }
  306.         builder.append(' ');
  307.       }
  308.       return builder.toString();
  309.     }
  310.   }
  311.   
  312.   /** 
  313.    * Static method to execute a shell command. 
  314.    * Covers most of the simple cases without requiring the user to implement  
  315.    * the <code>Shell</code> interface.
  316.    * @param cmd shell command to execute.
  317.    * @return the output of the executed command.
  318.    */
  319.   public static String execCommand(String ... cmd) throws IOException {
  320.     return execCommand(null, cmd);
  321.   }
  322.   
  323.   /** 
  324.    * Static method to execute a shell command. 
  325.    * Covers most of the simple cases without requiring the user to implement  
  326.    * the <code>Shell</code> interface.
  327.    * @param env the map of environment key=value
  328.    * @param cmd shell command to execute.
  329.    * @return the output of the executed command.
  330.    */
  331.   public static String execCommand(Map<String,String> env, String ... cmd) 
  332.   throws IOException {
  333.     ShellCommandExecutor exec = new ShellCommandExecutor(cmd);
  334.     if (env != null) {
  335.       exec.setEnvironment(env);
  336.     }
  337.     exec.execute();
  338.     return exec.getOutput();
  339.   }
  340. }