Package Torello.Java

Class OSCommands

  • All Implemented Interfaces:
    java.lang.Cloneable
    Direct Known Subclasses:
    GSUTIL, MSDOS, Shell

    public abstract class OSCommands
    extends java.lang.Object
    implements java.lang.Cloneable
    Root Ancestor / Parent Class for all Operating-System Execution Classes - including: MSDOS, Shell, GSUTIL etc... This class serves as the root or parent class of all classes in this package which have been designed to start Operating System Calls. This class is generally a wrapper around several basic but important classes from the java.lang Package. These are listed below, and the functionality and features they provided are greatly expanded by this class OSCommands, and all the descendant clases that extend it.

    • class java.lang.Runtime
    • class java.lang.Process
    • class java.lang.ProcessBuilder
    • class java.lang.ProcessBuilder.Redirect


    Do note that one of the motivating forces for writing these classes is merely to serve as explanation and example of using Java's class java.lang.Process. Quite a bit of work was done writing the reader and monitor threads to ensure that the output of executing a UNIX command is collected and returned properly. This class generates a simple instance of OSResponse whenever a command is invoked. This returned class contains several fields that have all the information produced by executing an O/S command.

    Wildcard Issue:
    On most UNIX platforms, whenever a command is issued to the O/S (by typing it, for example, inside a terminal or terminal-window) there is an execution-stage before the process is spawned where file-name and / or directory-name wildcards (usually: '*' and '?') are expanded.

    When a UNIX command receives a parameter list, if the file and/or directories specified by the user contains wild-cards, those wild-cards shall have already been eliminated and replaced by the O/S when the that command receives its actual parameter list. The arguments that the terminal-command actually "sees" are the actual files and/or directories from which the wild-card expression expanded. A wild-card expression (like a "reg-ex expression") is replaced by the complete list of files before the command ever begins (usually!)

    Because of this situation, if a user would like, for instance, to copy or move a group of files - and use Java to do it, then a native Java A.P.I. such as the package java.nio.* (or the class class FileNode API) is really the only way to ensure that wild-card expressions actually work. It is usually easiest to request an array (or a java.util.Stream) of files using the FileNode.flatten(...) routines, and then pass that list to the UNIX command.

    WORA:
    In its inception, Java made the "Write Once, Run Anywhere" (WORA) pledge. The methods in this class are not in any way "Java-ized" versions of the Operating System Calls. Rather, they are just wrappers around java.lang.Process allowing a user to BOTH make Operating System calls easily AND to efficiently retrieve the generated text-output produced by the calls.

    The bulk-work of the classes in this JAR Library were written and tested in a UNIX environment. While everything else in the Java HTML JAR Library is, indeed, O/S Independent (adhering to 'WORA'), this class and all classes which extend OSCommands are not! In fact, they are highly O/S dependent. The methods in these classes are actually just a set of hooks into native Operating System calls.

    This does mean that any invocation of these methods, when running in some other Operating System Environment, will just throw exceptions.


    • Constructor Summary

      Constructors 
      Modifier Constructor
        OSCommands()
        OSCommands​(Appendable outputAppendable)
        OSCommands​(Appendable standardOutput, Appendable errorOutput)
        OSCommands​(Appendable outputAppendable, Appendable commandStrAppendable, Appendable standardOutput, Appendable errorOutput)
      protected OSCommands​(OSCommands other)
    • Method Summary

       
      Run-Method: used by Shell, GSUTIL and MSDOS
      Modifier and Type Method
      OSResponse printAndRun​(String[] command)
       
      Methods: interface java.lang.Cloneable
      Modifier and Type Method
      abstract OSCommands clone()
       
      Methods: class java.lang.Object
      Modifier and Type Method
      String toString()
      • Methods inherited from class java.lang.Object

        equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
    • Field Detail

      • outputAppendable

        🡇     🗕  🗗  🗖
        public java.lang.Appendable outputAppendable
        This is an instance of java.lang.Appendable that will accept log text generated from this class' printAndRun method. Since this class has the ability to send text to a terminal (like System.out, for instance); this field has been created to make it easy for the output from Operating System commands to printed to the screen, or just about anywhere (using this Appendable).

        This global 'log' is automatically used for printing by the methods in this class, and it may be configured using any Object that implements the java.lang.Appendable interface.

        public, non-final:
        This field may be changed / modified at any time prior to the execution of a command. This field was intentionally declared public to allow a user to configure this class output-collection procedures as needed.

        When an External / Operating-System command is executed, this field's reference-value is copied from this class to an internal, Thread-Safe, data-mirror class.

        This expects an implementation of Java's java.lang.Appendable interface which allows for a wide range of options when logging intermediate messages.
        Class or Interface InstanceUse & Purpose
        'System.out' Sends text to the standard-out terminal
        Torello.Java.StorageWriter Sends text to System.out, and saves it, internally.
        FileWriter, PrintWriter, StringWriter General purpose java text-output classes
        FileOutputStream, PrintStream More general-purpose java text-output classes

        Checked IOException:
        The Appendable interface requires that the Checked-Exception IOException be caught when using its append(...) methods.
      • commandStrAppendable

        🡅  🡇     🗕  🗗  🗖
        public java.lang.Appendable commandStrAppendable
        If a user wishes to print just the command being issues as text to some Java appendable, then a reference should be assigned to this public static field. This will guarantee that any time any command from this class is issued, the text of that command will be printed to this output java.lang.Appendable

        As should be noted, by defualt, this feild has been set to null. When this field is null, it will be ignored completely.

        Use of this Field:
        Occasionally, as projects grow in size, the logging features of a project become ever more important. In such cases, one choice / "avenue" for output printing & logging can be to allow printing all output to a single output Appendable and simultaneously printing just the command text to a second java.lang.Appendable.

        This java.lang.Appendable field is solely used for printing the command text itself. It operates comletely independent from the other fields: appendable and cmdOrCmdAndOutput.

        public, non-final:
        This field may be changed / modified at any time prior to the execution of a command. This field was intentionally declared public to allow a user to configure this class output-collection procedures as needed.

        When an External / Operating-System command is executed, this field's reference-value is copied from this class to an internal, Thread-Safe, data-mirror class.
      • standardOutput

        🡅  🡇     🗕  🗗  🗖
        public java.lang.Appendable standardOutput
        When this field is non-null, it shall receive all text that the underlying process sends to Standard Output. When this field is null, it is ignored.

        The default constructor for this class assigns 'null' to this field. Bear in mind that even when this Appendable is null, the underlying process being executed will continue to have its Standard Output text saved to an internal, private, buffer. This buffer's contents will stil be available inside the returned instance field: OSResponse.standardOutput

        The motivating force for the decision to provide another Appendable for logging Standard Output was so that a user may receive this pipe's text output in real time, as the process executes, without having to wait for the process to run to completion.

        There are numerous networked file-system operations (for instance) that may require several minutes to finish, and setting this Appendable allows a user to receive error notices as they happen, rather than waiting for the operation to complete.

        public, non-final:
        This field may be changed / modified at any time prior to the execution of a command. This field was intentionally declared public to allow a user to configure this class output-collection procedures as needed.

        When an External / Operating-System command is executed, this field's reference-value is copied from this class to an internal, Thread-Safe, data-mirror class.
      • errorOutput

        🡅  🡇     🗕  🗗  🗖
        public java.lang.Appendable errorOutput
        When this field is non-null, it shall receive all text that the underlying process sends to Error Output. When this field is null, it is ignored.

        The default constructor for this class assigns 'null' to this field. Bear in mind that even when this Appendable is null, the underlying process being executed will continue to have its Error Output text saved to an internal, private, buffer. This buffer's contents will stil be available inside the returned instance field: OSResponse.errorOutput

        The motivating force for the decision to provide another Appendable for logging Error Output was so that a user may receive this pipe's text output in real time, as the process executes, without having to wait for the process to run to completion.

        There are numerous networked file-system operations (for instance) that may require several minutes to finish, and setting this Appendable allows a user to receive error notices as they happen, rather than waiting for the operation to complete.

        public, non-final:
        This field may be changed / modified at any time prior to the execution of a command. This field was intentionally declared public to allow a user to configure this class output-collection procedures as needed.

        When an External / Operating-System command is executed, this field's reference-value is copied from this class to an internal, Thread-Safe, data-mirror class.
    • Constructor Detail

      • OSCommands

        🡅  🡇     🗕  🗗  🗖
        public OSCommands()
        This constructor assigns default values to all of the output logging Appendable's.

        Note that all of them are assigned 'null' - meaing their output will not be logged - except for the primary output Appendable field. this.outputAppendable. This field is assigned System.out, meaning that all text output by a process will be sent to the terminal window.
      • OSCommands

        🡅  🡇     🗕  🗗  🗖
        public OSCommands​(java.lang.Appendable outputAppendable)
        This constructor allows a user to assign an instance Appendable to: appendable.

        The first collects all text-output generated by the processes and prints it using the Appendable's printing methods. This includes text generated for BOTH Standard-Output AND Error-Output. This text is collected in real time as it is output by the underlying process.

        Assigning null:
        Any of this class Appendable's may be assigned null. When an Appendable is assigned null, it is simply ignored. NullPointerException will not throw, and any output text that would be appended, isn't appended.
        Parameters:
        outputAppendable - This parameter's reference is assigned directly to the OSCommands field: appendable. To clearly understand the use of this field, please review the documentation linked for that field. To summarize, the java.lang.Appendable that is synonymously named appendable simply collects all output text generated by a processes as it executes, and sends it to the reference's append(...) method. This text will include that sent to BOTH Standard-Output AND text sent to Error-Output. The text that is printed is transmitted in the order it is received from the reader-threads that read the underlying process' text-pipes.

        By default this parameter is assigned System.out. When this default value is used, it means that as the process executes, any and all text it prints is sent to the UNIX or MS-DOS Operating System Terminal Window.

        This field may be assigned null, and when so it is just ignored. Assigning null to appendable. will not produce a NullPointerException.
        See Also:
        outputAppendable
        Code:
        Exact Constructor Body:
         this.outputAppendable = outputAppendable;
        
      • OSCommands

        🡅  🡇     🗕  🗗  🗖
        public OSCommands​(java.lang.Appendable outputAppendable,
                          java.lang.Appendable commandStrAppendable,
                          java.lang.Appendable standardOutput,
                          java.lang.Appendable errorOutput)
        Constructor that allows for assigning all four text-collecting Appendable's. Please review the documentation for each of these fields to understand better how these Appendable's may be used. There are links in the See-Also section, below.

        Assigning null:
        Any of this class Appendable's may be assigned null. When an Appendable is assigned null, it is simply ignored. NullPointerException will not throw, and any output text that would be appended, isn't appended.
        Parameters:
        outputAppendable - This parameter's reference is assigned directly to the OSCommands field: appendable. To clearly understand the use of this field, please review the documentation linked for that field. To summarize, the java.lang.Appendable that is synonymously named appendable simply collects all output text generated by a processes as it executes, and sends it to the reference's append(...) method. This text will include that sent to BOTH Standard-Output AND text sent to Error-Output. The text that is printed is transmitted in the order it is received from the reader-threads that read the underlying process' text-pipes.

        By default this parameter is assigned System.out. When this default value is used, it means that as the process executes, any and all text it prints is sent to the UNIX or MS-DOS Operating System Terminal Window.

        This field may be assigned null, and when so it is just ignored. Assigning null to appendable. will not produce a NullPointerException.
        commandStrAppendable - This java.lang.Appendable allows for yet another output mechanism when executing operating system commands. The sole purpose of this Appendable is to print the process command, itself, as a Java String - exactly as it was issued to the operating system.

        This allows a type of "Monitor Log" to store all commands being issued to the Operating-System, without having to include the full-text output that those process calls have produced. The value passed to this parameter is assigned to the OSCommands field named printCmdAppendable.

        This parameter may be null, and if it is, no such output will be generated.
        standardOutput - This Appendable is assigned to the internal OSCommands field: standardOutput. This Appendabe, as the name hopefully implies, allows a user to collect any / all text sent by the process to Standard-Output.

        Please review the documentation for that field to better understand it's use. Also, keep in mind that null may be passed to this parameter, and if / when it is the corresponding Appendable field will simply be ignored during the process execution.
        errorOutput - This Appendable is assigned to the internal OSCommands field: errorOutput. This Appendabe, as the name hopefully implies, allows a user to collect any / all text sent by the process to Error-Output.

        Please review the documentation for that field to better understand it's use. Also, keep in mind that null may be passed to this parameter, and if / when it is the corresponding Appendable field will simply be ignored during the process execution.
        See Also:
        outputAppendable, commandStrAppendable, standardOutput, errorOutput
        Code:
        Exact Constructor Body:
         this.outputAppendable       = outputAppendable;
         this.commandStrAppendable   = commandStrAppendable;
         this.standardOutput         = standardOutput;
         this.errorOutput            = errorOutput;
        
      • OSCommands

        🡅  🡇     🗕  🗗  🗖
        public OSCommands​(java.lang.Appendable standardOutput,
                          java.lang.Appendable errorOutput)
        This constructor assigns values to these internal Appendable fields: standardOutput and errorOutput

        Assigning null:
        Any of this class Appendable's may be assigned null. When an Appendable is assigned null, it is simply ignored. NullPointerException will not throw, and any output text that would be appended, isn't appended.
        Parameters:
        standardOutput - This Appendable is assigned to the internal OSCommands field: standardOutput. This Appendabe, as the name hopefully implies, allows a user to collect any / all text sent by the process to Standard-Output.

        Please review the documentation for that field to better understand it's use. Also, keep in mind that null may be passed to this parameter, and if / when it is the corresponding Appendable field will simply be ignored during the process execution.
        errorOutput - This Appendable is assigned to the internal OSCommands field: errorOutput. This Appendabe, as the name hopefully implies, allows a user to collect any / all text sent by the process to Error-Output.

        Please review the documentation for that field to better understand it's use. Also, keep in mind that null may be passed to this parameter, and if / when it is the corresponding Appendable field will simply be ignored during the process execution.
        Code:
        Exact Constructor Body:
         this.outputAppendable   = null;
         this.standardOutput     = standardOutput;
         this.errorOutput        = errorOutput;
        
      • OSCommands

        🡅  🡇     🗕  🗗  🗖
        protected OSCommands​(OSCommands other)
        Clone-Constructor. This is used by sub-classes to allow for a clone() method.
        Parameters:
        other - This is an instance that is passed by a 'clone' method. It is always just 'this'.
        Code:
        Exact Constructor Body:
         this.outputAppendable       = other.outputAppendable;
         this.commandStrAppendable   = other.commandStrAppendable;
         this.standardOutput         = other.standardOutput;
         this.errorOutput            = other.errorOutput;
         this.osExtras               = other.osExtras;
        
    • Method Detail

      • printAndRun

        🡅  🡇     🗕  🗗  🗖
        public OSResponse printAndRun​(java.lang.String[] command)
                               throws java.io.IOException
        Executes a command by spawning an operating-system process.

        Sub-Class Note:
        If you are intending to write an extension of this class for executing Operating System Commands, this method here should be used as the launch-pad for invoking those commands.

        Please review the classes Shell or MSDOS to see how to employ a Shell-Script Class to extend this class ('OSCommands'), and specifically how to invoke this method to run those scripts.
        Parameters:
        command - A String[]-Array Shell / UNIX / MSDOS Command. This parameter (and this method) are usually created/invoked by the classes Shell, GSUTIL, MSDOS etc...
        Returns:
        Returns an instance of OSResponse. This response-Object (data-record) class holds four public, final fields:
        Process response-code
        Text sent to Standard-Output (as a java.lang.String)
        Text sent to Error-Output (also as a String)
        The original Command-String

        This OSResponse instance may be discarded without any effect on Process Execution. It is only provided as a convenience in case more information is required about the results of the O/S command invocation.

        Process Status: Upon completion of this method, the Operating-System java.lang.Process is guaranteed to have run to completion (or to have failed and been interrupted). If the process was interrupted, the response-code will indicate this using the value INTERRUPTED
        Throws:
        java.io.IOException
        Code:
        Exact Method Body:
         final ThreadSafeSnapshot    config = new ThreadSafeSnapshot();
         final boolean               hasOSE = (config.osExtras != null);
        
         this.osExtras = null;
        
        
         // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
         // Do as much error / exception checking as is possible... (It isn't much)
         // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        
         for (int i=0; i < command.length; i++)
        
             if (command[i] == null) throw new NullPointerException(
                 "The " + i + StringParse.ordinalIndicator(i) + " array element in the 'command' " +
                 "(O/S Command) array-parameter that was passed to this method was 'null'.  " +
                 "Java's method Runtime.getRuntime().exec(command) will not accept a 'command' " +
                 "array that contains any nulls."
             );
        
         if (hasOSE && config.osExtras.currentWorkingDirectory != null)
        
             if (! config.osExtras.currentWorkingDirectory.isDirectory())
        
                 throw new IllegalArgumentException(
                     "The file-system has stated that the reference passed to parameter " +
                     "'currentWorkingDirectory' was not a valid directory on the file-system: " +
                     '[' + config.osExtras.currentWorkingDirectory.getPath() + ']'
                 );
        
        
         // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
         // Run the "Command String Appendable" output-logger thingy
         // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        
         final StringBuilder sb = new StringBuilder();
         for (int i=0; i < command.length; i++) sb.append(command[i] + " ");
         sb.append('\n');
        
         // This is needed again at the end of this method
         final String commandStr = sb.toString();
        
         if (config.commandStrAppendable != null) config.commandStrAppendable.append(commandStr);
        
        
         // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
         // Now execute the command!
         // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        
         // Used to build the String's for this class standardOutput and errorOutput fields!
         final StringBuilder standardOutputSB    = new StringBuilder();
         final StringBuilder errorOutputSB       = new StringBuilder();
        
         try
         {
             // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
             // First, build a java.lang.Process object-instance
             // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        
             // This is the java.lang.Process class instance.  It is built with a simple constructor
             // unless the user has passed all of the OSExtras stuff.  If 'hasOSE' is true, then a
             // java.lang.ProcessBuilder object is needed to actually build the Process
             //
             // The end of this (kind of long) if-then-else statement has a simply assignment
             // ==> pro = Runtime.getRuntime().exec(command);
        
             Process pro;
        
             if (hasOSE)
             {
                 // Save some typing, that's all!
                 final OSExtras x = config.osExtras;
        
                 // This will create a process with specified modifications
                 ProcessBuilder b = new ProcessBuilder(command);
            
                 // NOTE: These "extra" configurations aren't so useful.  HOWEVER, this is the only
                 //       reason this method is provided.  Normally, you would use the other method
                 //       with the exact same name (Which leaves out all of these re-directs).  If you
                 //       have opted to use this method, instead of the one that leaves these things
                 //       out, the redirects are set here!  All they do is ask the OS Process to do that
                 //       "Operating System Thing" were input and/or output are retrieved/sent to a file
            
                 if (x.currentWorkingDirectory != null)  b.directory(x.currentWorkingDirectory);
                 if (x.mergeStdErrorAndStdOut)           b.redirectErrorStream(true);
                 if (x.outputRedirect != null)           b.redirectOutput(x.outputRedirect);
                 if (x.inputRedirect != null)            b.redirectInput(x.inputRedirect);
                 if (x.errorRedirect != null)            b.redirectError(x.errorRedirect);
            
                 if (x.environmentVariableUpdater != null)
                     x.environmentVariableUpdater.accept(b.environment());
        
                 pro = b.start();
             }
        
             // Otherwise just call this version - if there are no "OSExtras"
             else pro = Runtime.getRuntime().exec(command);
        
        
             // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
             // Now just collect output with the (Java-HTML internal) "ISPT" Printer-Reader Threads
             // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        
             // These 3 lines create a "Completion Monitor" instance, and then register the
             // "Reader Threads" for reading from Standard-Out and Standard-Error from the Process.
             // These create two "Daemon Threads" that read process-output and error-output using
             // Java Thread's.  These will not hang **UNLESS** one of the InputStream read method's
             // hang/lock.
        
             Completed completed = new Completed();
        
             // Note that the constructor's below start the printer/reader/monitors - there is no
             // need to actually keep a reference/pointer to these classes once they are
             // constructed.  Passing 'completed' to these constructors is enough!
             //
             // ALSO: It is completely immaterial whether/if any of the three appendable's passed to
             //       the TriAppendable-Constructor are actually null.  Note that only the one whose
             //       name ends with "SB" is guaranteed not to be null.  The other two
             //       (user-provided) Appendable's can easily be null!
        
             new ISPT(
                 pro.getInputStream(),
                 new TriAppendable(
                     config.outputAppendable,
                     standardOutputSB, // OSResponse uses this for it's standardOutput field
                     config.standardOuput
                 ),
                 completed,
                 "Thread for Reading from Standard Output"
             );
        
             new ISPT(
                 pro.getErrorStream(),
                 new TriAppendable(
                     config.outputAppendable,
                     errorOutputSB,  // OSResonse uses this for it's errorOutput field
                     config.errorOutput
                 ),
                 completed,
                 "Thread for Reading from Error Output"
             );
        
             // NOTE: The process, once it is instantiated, is already running.  There is no
             // need to "start" the process, the call to Runtime.exec (above does that).  Here
             // we can just wait for the reader threads to receive the EOF messages, which  is
             // how they terminate.
        
             completed.waitForCompletionOfAllThreads();
        
             // It is unlikely this would cause the current thread to wait, because the previous
             // line will wait until both Standard-Out and Error-Out have received EOF...
             // Perhaps the process COULD delay the return response-code here.  The reality is that
             // method 'waitFor' is the PREFERRED way to retrieve the exit-value from this process...
             // although method 'Process.exitValue()' would also probably work.
        
             int response = pro.waitFor();
        
        
             // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
             // Return the 'OSResponse' result (or throw an exception, if the process threw one)
             // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        
             // This prints a friendly little message at the end stating what the response-code was
             if (config.outputAppendable != null) config.outputAppendable.append
                 ("Command exit with return value " + response + '\n');
        
             completed.ifExceptionThrowException();
        
             // Note that a '0' response-code usually means 'succesfully terminated'
             return new OSResponse
                 (commandStr, response, standardOutputSB.toString(), errorOutputSB.toString());
         }
        
         catch (InterruptedException e)
         {
             return new OSResponse(
                 commandStr, OSResponse.INTERRUPTED, standardOutputSB.toString(),
                 errorOutputSB.toString()
             );
         }
        
      • toString

        🡅  🡇     🗕  🗗  🗖
        public java.lang.String toString()
        Generates a String this class. The returned String merely encodes the class-names of the non-null Appendable's.
        Overrides:
        toString in class java.lang.Object
        Returns:
        A simple representation of this class, as a java.lang.String
        Code:
        Exact Method Body:
         // private static String toStrApp(Appendable a)
         // { return (a == null) ? "null\n" : (a.getClass().getName() + '\n'); }
        
         return
             "outputAppendable:     " + toStrApp(this.outputAppendable) +
             "commandStrAppendable: " + toStrApp(this.commandStrAppendable) +
             "standardOutput:       " + toStrApp(this.standardOutput) +
             "errorOutput:          " + toStrApp(this.errorOutput);
        
      • clone

        🡅     🗕  🗗  🗖
        public abstract OSCommands clone()
        Creates a clone of 'this' instance. Though unlikely of much use, this could conceivably have some function if similar, but non-identical, output mechanisms were being used.
        Overrides:
        clone in class java.lang.Object
        Returns:
        An exact copy of 'this' instance - one in which all output Appendable's have had their references copied into the new instance.