The WrapperManager.exec()
function is an alternative to Java-Runtime.exec() or
Java-ProcessBuilder
that has the disadvantage to use the fork() method,
which can become on some platforms very memory expensive to create a new process.
The problem on those platforms is
that in the Parent Process that wants to start a side process,
fork() causes to clone the memory heap of the parent for the child.
This will double the used memory for a short time.
Given a fairly large application that is close to the system's memory quota,
it might fail to just create a small application like ls,
or the memory is going to get swapped on the HD, which decreases the performance drastically.
Another issue is to ease bind or detach the child while starting from the parent.
If the Java Process terminates expectedly or unexpectedly,
the Wrapper will clean all bind processes that haven't finished yet.
For better consistency,
the WrapperManager.exec() function was implemented
as close to the Runtime.exec() function in Java as possible.
This section will start right away to show how to execute a command.
(Further details will be explained step-by-step during reading this page)
WrapperProcess p = WrapperManager.exec("ls -lisa");
The above line will execute a ls command
with the parameters -l, -i, -s, and -a, and then assign a WrapperProcess object representing the process.
If the full path of the binary (or script) file hasn't been passed,
the current working directory as well as systems' PATH directories
will be checked and the first found hit gets executed.
Relative addressing starting from the current working directory is also supported.
NOTE
The exec command accepts the command either as a single String as well as a StringArray.
In case of a StringArray, each parameter is located in a single array field.
Process I/O
The created child process runs in the background of the OS and is usually not visible,
as long as it's not a GUI application.
To communicate with the process, the WrapperProcess class
provides three methods to access the Input to the process
as well as the Output and the ErrorMessages to the process.
It is recommended to wrap the returned StreamObject with a BufferedReader.
To open the Reader, please proceed as described.
BufferedReader br = new BufferedReader( new InputStreamReader(p.getWrapperProcessInputStream()));
This establishes the Reader of the InputStream on which the child writes its output.
To read data from the ErrorStream or write data to the Child,
please proceed similar to this example.
Once the Reader/Writer has been established, we can read/write data by:
Please note that by following this example, all data will be read
until the child process closes the stream on its side.
Configuration of the Child Process
When executing a command, WrapperManager also provides the possibility to configure the child process.
This is done by passing a WrapperProcessConfig object to the exec() function.
The following section will explain the Configurations that can be made by WrapperProcessConfiguration.
The setDetached() method lets you specify whether the subprocess will be run "detached" from the Wrapper.
If the process is marked as "detached", the process doesn't need to terminate when the Wrapper is shutting down.
If not marked, the Wrapper will keep track of the child process and try to terminate the process if the parent process should terminate.
NOTE
By default, processes won't be started detached from its parent process.
Start Type
The setStartType() method specifies a Start Type of how the subprocess will be started by the OS.
WARNING
This property has no effect on Windows.
FORK_EXEC:
This is the most common way in Linux/UNIX to create a child process.
However, on some OS (esp. Solaris), this call causes to initially duplicate the memory of the parent for the child.
On z/OS, this Start Type is not supported as of the initial release.
On HP-UX systems, the Wrapper will automatically switch to using
vfork() rather than fork().
VFORK_EXEC:
The vfork() function differs from fork()
only when the child process can share code and data with the parent process.
This speeds cloning activity significantly at a risk for the integrity of the parent process
if vfork() is misused.
On some systems, vfork is the same as fork.
On z/OS, this Start Type is not supported as of the initial release.
POSIX_SPAWN:
Process will be spawned and won't cause any memory duplication
POSIX_SPAWN API.
This is only available on Linux, Solaris (10+), AIX, z/OS, MacOS, and FreeBSD 8+ (Since version 3.5.46).
DYNAMIC:
The optimal Start Type will be automatically selected according to the OS the Wrapper is running on.
Please note that this property also implies that changing the working directory will not be supported
because of not supporting changing the working directory when using POSIX_SPAWN.
There is an experimental property
wrapper.child.allowCWDOnSpawn
that could be used to achieve in changing the working directory.
NOTE
By default, processes will be launched using the Start Type DYNAMIC.
Working Directory
The setWorkingDirectory() method specifies the working directory of the subprocess, or "NULL"
if the subprocess should inherit the working directory of the current process.
Please note that it is not possible to set the working directory
directly when using POSIX_SPAWN.
A suggested workaround would be to wrap the command you wish to run in a script
and perform a change of directory prior to the execution of the command.
#!/bin/sh
chdir $1
shift
$*
NOTE
If this property is not set, the subprocess will inherit the working directory from its parent process.
wrapper.child.allowCWDOnSpawn
The wrapper.child.allowCWDOnSpawn property controls
whether the Wrapper will try to change the working directory
for the Start Types POSIX_SPAWN and DYNAMIC.
This property is experimental and could lead to problems
if the wrapped process is using Java Native Interface (JNI) Code
and would try to create a child process using POSIX_SPAWN.
By default, this property is set to FALSE.
wrapper.child.allowCWDOnSpawn=TRUE
Setting the Environment
The setEnvironment() method specifies the environment of the created subprocess.
The field is an array of strings. Each element of it has environment variable settings in format "name=value".
NOTE
If this property is not set, the subprocess will inherit the environment from its parent process.
Specifying a soft timeout for child termination
Since Wrapper version 3.5.5, it is also possible to specify a soft timeout for each
process individually. The Wrapper
will give the child process the chance to terminate by itself gracefully, whereas for
instance the java.lang.Process.destroy() method will always cause a forced shutdown,
giving the process no chance on shutting down by itself.
Before Wrapper version 3.5.5, the timeout was always 5 seconds, so in order to keep functionality consistent,
the default value of timeout is 5 seconds.
The timeout can be specified during the child process creation with the WrapperProcessConfig class.
WrapperProcessConfig wpConfig = new WrapperProcessConfig().setSoftShutdownTimeout(10);
The configuration in the above example will tell the Wrapper to wait for up to 10
seconds for any child processes that got created with that configuration. If the child
processes don't finish after 10 seconds, the Wrapper will kill them forcibly.
The following values for a timeout are possible:
>0:
The amount of seconds the Wrapper waits for the child process to
exit gracefully before forced termination.
0:
The Wrapper will instantly force the termination of the child process.
-1:
The Wrapper will never force the termination of the child process,
but wait indefinitely for the child process to finish by itself.
Creating a Child Process for the Active User
On Windows, WrapperProcessConfig.setCreateForActiveUser(boolean)
can be used to specify if the child processes should be launched
in the current active session rather than in the session where the service
is running (a session in which the user has SE_TCB_NAME
privilege with the OS). On non-Windows platforms or when launched in Console
mode, the setting will be ignored silently.