Java para tareas de «scripting»

Publicado por pico.dev el , actualizado el .
blog-stack java planeta-codigo planeta-linux programacion
Comentarios

Para programar pequeños scripts normalmente se suele emplear el intérprete de comandos en GNU/Linux bash o si es algo complejo un lenguaje interpretado como Python, Ruby o Groovy. Pero no pienses que Java no puede ser empleado para tareas de scripting, en este artículo muestro que problemas presentan los lenguajes interpretados o dinámicos, que ventajas tiene usar Java y finalmente como usarlo con la misma sencillez que un lenguaje interpretado para el nicho funcional de los scripts.

Java

Java puede emplearse para cualquier propósito desde aplicaciones web en la parte servidora, aplicaciones de escritorio como escribí en dos artículos introductorios sobre JavaFX, juegos con alta calidad gráfica incluso para dispositivos de capacidades más reducidas como IoT o embebidos y verdaderamente limitados. Para cualquier plataforma en la que haya disponible una JVM se pueden ejecutar aplicaciones programadas con el lenguaje Java. Sin embargo, hay multitud de otros lenguajes aunque solo una docena con un porcentaje de uso significativo que tratan de ocupar nichos de funcionalidades específicas. Unos pueden ser el análisis de datos como con el lenguaje R y otro la programación de pequeñas tareas, los scripts o lenguajes de scripting.

Para los scripts normalmente se han utilizado intérpretes como bash por su disponibilidad en cualquier sistema GNU/Linux o si se necesita un lenguaje más avanzado Python, Ruby o Groovy. Cualquiera de estas opciones son empleadas para realizar tareas de scripting que involucran desde generación de informes o archivos, envío de correos electrónicos hasta actualización de una base de datos relacional o nosql, cualquier cosa que queramos automatizar. Al no necesitar compilarse ni generar un artefacto extraño como los archivos .class o .jar de Java basta con escribir el script con cualquier editor de texto y ejecutarlo con el intérprete correspondiente directamente desde el código fuente. El despliegue en un entorno de pruebas o producción es sencillo, basta con subir el código fuente de los scripts a la máquina correspondiente donde se vaya a ejecutar y ejecutarlos, únicamente necesitaremos instalar la versión del intérprete adecuado y las dependencias adicionales del script si necesita alguna como en Python posiblemente en un virtualenv.

Pero al contrario de lo que piensa mucha gente Java puede ser usado perfectamente como lenguaje de scripting con igual simpleza o más que Python, Ruby o Groovy. Java es más verboso sí pero en mi opinión estas son 10 razones para seguir usando Java entre ellas el compilador, en el artículo Java for everything dan una buena extensa descripción de porque usar Java también para los scripts y donde se expone una forma de usarlo, en este artículo mostraré otra creo que mejor. El compilador de Java validará al menos que el código no tienen errores léxicos o de sintaxis que en un lenguaje interpretado son fáciles de introducir cuando hace meses que no modificas el script olvidando gran parte del código, cuando no has escrito tú el script, cuando hay prisa y presión para hacer las modificaciones por un error en producción apremiante, cuando el tamaño del código empieza a ser considerable, es modificado por varias personas o cuando no se domina el lenguaje profundamente. ¿Qué prefieres, algo más verbosidad (ahora menos con varias de las novedades introducidas en Java 8) o evitar que un script importante en producción se quede a medio ejecutar por un error de compilación y provoque alguna catástrofe? Yo lo tengo claro, que el compilador me salve el culo.

En cuanto al número de líneas necesarias para hacer un script en Java puede que se necesiten alguna más por su menor azúcar sintáctico o la falta de algún método de utilidad no disponible en la propia API del JDK ajustado a la necesidad pero si el script es pequeño ¿realmente importan unas pocas lineas más? y si el script es grande el compilador y el IDE serán de gran ayuda.

Entrando a discutir el apartado de la simplicidad de ejecución de un lenguaje interpretado, en Java se puede conseguir lo mismo con Gradle. Gradle se encargará de descargar la dependencias que necesite la aplicación y junto con el wrapper de ejecución de Gradle no necesitaremos tener nada más instalado que la máquina virtual de Java. Si el script tiene algún error de compilación el compilador lo indicará y no se ejecutará, podremos hacer que se ejecuten previamente los teses unitarios si disponemos de ellos. Con el plugin application solo podremos tener un único programa con su main pero definiendo tareas de tipo JavaExec podremos definir cuantos diferentes scripts deseemos que ejecutarán el método main de diferentes clases.

Teniendo 3 pequeños scripts de ejemplo el archivo build.gradle que necesitaríamos sería el siguiente definiendo de forma dinámica una tarea runScript y otra createStartScripts para cada uno, los scripts son lo más sencillos posible emitiendo en la consola un mensaje pero podrían hacer cualquier cosa, incluso usar Spring Boot:

Si necesitamos ejecutar los scripts cada cierto tiempo en cron podemos programar el comando que queremos ejecutar regularmente, en vez de usar el comando gradlew que comprueba si ha cambiado el código fuente y necesita compilarse de nuevo tardando un poco más en iniciarse, con una tarea de tipo CreateStartScripts podemos generar el script bash tanto para sistemas de la familia Unix como Windows que realmente lanzará los scripts Java. La tarea de Gradle createStartScripts genera todos los scripts de inicio del proyecto.

La ejecución de cada uno de los scripts con gradlew y usando los scripts lanzadores sería:

    <figcaption>Ejecución de &lt;i&gt;scripts&lt;/i&gt; en Java</figcaption>

</figure>

Para un proyecto de scripts con Java necesitaremos un archivo build.gradle y cierta estructura de directorios pero no es algo suficiente complejo como para descartar Java como lenguaje para este propósito más teniendo en cuenta sus ventajas, a parte de la JVM el proyecto de scripts será autocontenido incluso para las dependencias con lo que su despliegue en un entorno de producción será muy sencillo basta con copiar archivos (FTP, wget, …) o hacer git clone y git pull directamente del repositorio de código fuente para actualizarlo, si se usan los scripts lanzadores después de actualizar el código fuente del proyecto será necesario reconstruirlo con ./gradlew build.

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew runScript1.

Yo apoyo al software libre