Descripción de la Exec Nativa del Wrapper

Descripción

La función WrapperManager.exec() es una potente alternativa a Java-Runtime.exec() o Java-ProcessBuilder. Diseñada históricamente para ser más eficiente en el uso de memoria que las primeras implementaciones de Java, que dependían en gran medida del método fork() estándar, WrapperManager.exec() ofrece múltiples configuraciones de inicio y permite ajustar con precisión el mecanismo a nivel de sistema para lanzar procesos secundarios.

Una característica clave es su capacidad para vincular o desvincular procesos secundarios del proceso padre sin problemas. Si el proceso principal de Java finaliza (ya sea de forma normal o inesperada), el Wrapper limpia automáticamente cualquier proceso vinculado que siga en ejecución.

Para mejor consistencia, se ha implementado la funcción WrapperManager.exec() lo más as parecido posible a la funcción Runtime.exec() en Java.

Ejecución

Inicio

Esta sección le mostrará enseguida como ejecutar un comando. (Más detalles serán explicados paso a paso por la página)

WrapperProcess p = WrapperManager.exec("ls -lisa");

La línea anterior ejecutará un comando ls con los parámetros -l, -i, -s, y -a, y asignará un objecto WrapperProcess representando el proceso.

Si no se ha pasado la ruta completa del archivo binario (o script), se verificará el directorio de trabajo actual, así como los directorios PATH de los sistemas, y se ejecutará la primera opción encontrada. También se permite el direccionamiento relativo a partir del directorio de trabajo actual.

NOTA

El comando exec acepta el comando tanto como una Cadena Única (String) o como una Matriz de Cadenas (StringArray). En el caso de un StringArray, cada parámetro es ubicado en un solo campo de matriz.

I/O (Entrada/Salida) del Proceso

El proceso hijo creado se ejecuta en segundo plano en el sistema operativo y normalmente no es visible, siempre que no sea una aplicación de GUI (Interfaz gráfica de usuario). Para comunicarse con el proceso, la clase WrapperProcess provee 3 metodos para acceder a la entrada (Input) del processo, así como a la salida (Output) y los mensages de error (ErrorMessages) del proceso.

Se recomienda envolver el StreamObject devuelto con un BufferedReader. Para abrir el lector (Reader), por favor, proceda como se describe.

BufferedReader br = new BufferedReader( new InputStreamReader(p.getWrapperProcessInputStream()));

Esto establece el Lector del InputStream en el cual el hijo escribe su salida. Para leer datos de ErrorStream o escribir datos en el Hijo, proceda de manera similar a este ejemplo.

Una vez que el Reader/Writer se ha establecido, podemos leer/escribir de la siguiente forma:

String line;
while ((line = br.readLine()) != null)
{
    System.out.println(line);
}
br.close();

NOTA

Recuerde que al seguir este ejemplo, todos los datos se leerán hasta que el proceso hijo cierre la transmisión por su lado.

Configuración del Proceso Hijo

Al ejecutar un comando, WrapperManager también brinda la posibilidad de configurar el proceso hijo. Esto se hace pasando un objecto WrapperProcessConfig a la funcción exec(). En la siguiente sección se explicarán las Configuraciones que puede realizar WrapperProcessConfiguration.

Proceso Desvinculado

El método setDetached() le permite especificar si el subproceso se ejecutará "desvinculado" del Wrapper. Si se marca el proceso como "desvinculado", el proceso no necesitará terminar quando el Wrapper esté terminando. Si no está marcado, el Wrapper realizará un seguimiento del proceso secundario e intentará finalizar el proceso si el proceso padre debe finalizar.

NOTA

Por defecto, los procesos no se iniciarán desvinculados de sus procesos padres.

Tipo de Inicio

El método setStartType() especifica un tipo de inicio de cómo el sistema operativo iniciará el subproceso.

ADVERTENCIA

Esta propiedad no es válida en Windows.

  • FORK_EXEC:

    Este es el método más comun de iniciar un proceso hijo en Linux/UNIX. Sin embargo, en algunos sistemas operativos (especialmente Solaris), esta llamada hará que la memoria del padre sea duplicada para el hijo inicialmente. En z/OS, este tipo de inicio no es compatible desde la versión inicial. En sistemas HP-UX, el Wrapper automáticamente usará vfork() en vez de fork().

  • VFORK_EXEC:

    La funcción vfork() solamente es diferente de fork() cuando el proceso hijo puede compartir código y datos con el proceso padre. Esto acelera significantemente la actividad de clonación con el consiguiente riesgo para la integridad del proceso padre si se usa vfork() equivocadamente. En algunos sistemas, vfork es igual a fork. En macOS, este tipo de inicio ya no es compatible a partir de la versión 3.7.0 y se resuelve como DYNAMIC.

  • POSIX_SPAWN:

    El proceso se generará y no causará ninguna duplicación de memoria POSIX_SPAWN API. Esto solo está disponible en Linux, Solaris (10+), AIX, z/OS, MacOS, y FreeBSD 8+ (desde la versión 3.5.46).

  • DYNAMIC:

    El tipo de inicio óptimo se selecciona automáticamente según el sistema operativo en el que se ejecuta el Wrapper. A partir de la versión 3.7.0 del Wrapper, DYNAMIC se resuelve a POSIX_SPAWN por defecto. Sin embargo, si se configura un directorio de trabajo personalizado en una plataforma donde WrapperProcessConfig.isWorkingDirectorySupported() devuelve FALSE, recurre automáticamente a FORK_EXEC para garantizar que el proceso hijo se ejecute de forma segura dentro del directorio solicitado.

NOTA

Por defecto, los procesos se iniciarán usando el tipo de inicio DYNAMIC.

Directorio de Trabajo

El método setWorkingDirectory() especifica el directorio de trabajo de los subprocesos, o "NULL" si el subproceso debe heredar el directorio de trabajo del proceso actual.

Antes de la versión 3.7.0 del Wrapper, no era posible establecer el directorio de trabajo directamente al usar POSIX_SPAWN. Una solución sugerida era incluir el comando que se deseaba ejecutar en un script y realizar un cambio de directorio antes de la ejecución del comando.

#!/bin/sh

chdir $1
shift
$*

A partir de la versión 3.7.0 del Wrapper, el método setWorkingDirectory() es compatible con POSIX en la mayoría de las plataformas Unix modernas, incluyendo Linux (glibc 2.29 o posterior), macOS (10.15+), FreeBSD (13.1+) y Solaris (11.3+). AIX aún no admite esta funcionalidad; sin embargo, la implementación es compatible con versiones futuras y la compatibilidad será automática si IBM la incorpora en una futura actualización del sistema operativo. Es posible utilizar el método isWorkingDirectorySupported() en tiempo de ejecución para verificar la compatibilidad de la plataforma antes de intentar establecer un directorio de trabajo.

NOTA

Si no se configura esta propiedad, el subproceso heredará el directorio de trabajo de su proceso padre.

wrapper.child.allowCWDOnSpawn (obsoleta a partir de 3.7.0)

La propiedad wrapper.child.allowCWDOnSpawn controla si el Wrapper intentará cambiar el directorio de trabajo para los tipos de inicio POSIX_SPAWN y DYNAMIC.

ADVERTENCIA

Esta propiedad se introdujo inicialmente como experimental y se ha declarado obsoleta en la versión 3.7.0 debido a que no es segura para aplicaciones multihilo, ya que cambia temporalmente el directorio de trabajo global de la JVM principal y puede generar condiciones de carrera graves. Esto puede provocar que los hilos concurrentes resuelvan incorrectamente las rutas de archivo relativas y puede dañar el código nativo (JNI) que intenta crear procesos.

En la mayoría de las plataformas Unix modernas, esta propiedad no es necesaria y se prefiere el método alternativo setWorkingDirectory() (si se utiliza la versión 3.7.0 o superior del Wrapper). Para verificar si la plataforma actual admite la configuración del directorio de trabajo y evitar errores en tiempo de ejecución, utilice el método isWorkingDirectorySupported().

Por defecto, esta propiedad se establece en FALSE.

wrapper.child.allowCWDOnSpawn=TRUE

Configurar Entorno

El método setEnvironment() especifica en entorno del subproceso creado.

Este campo es una matriz de cadenas. Cada elemento tiene configuraciones de variables de entorno en formato "nombre=valor" (name=value).

NOTA

Si no se configura esta propiedad, el subproceso heredará el entorno de su proceso padre.

Especificar Tiempo de Espera Flexible para Terminar un Proceso Hijo

Desde la versión Wrapper 3.5.5, también es posible especificar un tiempo de espera flexible para cada proceso individualmente. El Wrapper dará al proceso hijo la oportunidad de terminar por sí mismo de forma limpia, mientras que por ejemplo el método java.lang.Process.destroy() siempre causará un cierre forzado, no dando al proceso la oportunidad de cerrarse por sí mismo. Antes de la versión Wrapper 3.5.5, el tiempo de espera era siempre de 5 segundos, y para mantener la coherencia de la funcionalidad, el valor por defecto es 5 segundos.

El tiempo de espera se puede especificar mientras se crea el proceso hijo con la clase WrapperProcessConfig.

WrapperProcessConfig wpConfig = new WrapperProcessConfig().setSoftShutdownTimeout(10);

La configuración en el ejemplo anterior le indicará al Wrapper que espere hasta 10 segundos por cualquier proceso hijo que fue creado con esa configuración. Si los procesos hijo no terminan después de 10 segundos, el Wrapper los cerrará a la fuerza.

Se acceptan los valores suguientes para tiempo de espera:

  • >0:

    El número de segundos que el Wrapper espera para que el proceso hijo termine correctamente antes de forzar la terminación.

  • 0:

    El Wrapper forzará instantáneamente la terminación del proceso hijo.

  • -1:

    El Wrapper nunca forzará la terminación del proceso hijo, sino que esperará indefinidamente a que el proceso hijo termine por sí mismo.

Crear un Proceso Hijo para el Usuario Activo

En Windows, WrapperProcessConfig.setCreateForActiveUser(boolean) se puede usar para especificar si los procesos hijo deben iniciarse en la sesión activa actual en lugar de en la sesión donde se ejecuta el servicio (una sesión en la que el usuario tiene el privilegio SE_TCB_NAME con el sistema operativo). En plataformas diferentes de Windows, o cuando se lanzan en modo Consola, la configuración se ignorará silenciosamente.

El valor predeterminado es FALSE.