viernes, 4 de mayo de 2012

Analizar y procesar los argumentos de línea de comandos en Java

(Parsing and processing command line arguments in Java)


“19 Jan 2038 at 3:14:07 AM”
(End of the word according to Unix–2^32 seconds after January 1, 1970)

Los límites tecnológicos son muy provisionales. Las cosas que aparentemente durarán mucho al final son más efímeras de lo que parece. El año pasado se agotaron oficialmente las direcciones IPv4 y me preguntaba, a propósito de la "cita" de hoy si, en el año 2038, aún existirán procesadores de 32 bits. El tema de este articulo, sin embargo, es una paradoja al inexorable paso del tiempo: seguimos necesitando realizar aplicaciones de consola que puedan invocarse a través de la línea de comandos.

En java, podemos procesar una línea de comandos sencilla a partir del argumento del método main. Pero, si necesitamos procesar una línea de comandos más compleja, podemos enfrentarnos a un arduo trabajo. Nuestra aplicación puede necesitar características de procesado más complejas como:
  • Argumentos con opciones
  • Análisis sintáctico (tipos de dato, restricciones, etc)
  • Valores por defecto en caso de omisión
  • Control de opciones obligatorias o requeridas
  • Generación de opción de ayuda
  • etc...

Por ejemplo, supongamos las siguientes opciones:

Option (* = required)                        Description
---------------------                        -----------
-?, -h                                       show help
-c <integer: count="">                       (default: 1)                           
--classpath, --cp <file: path1:="" path2:...="">                                                                   
* -d <mm dd="" yy="">                        some date
--output-file [File: file]
-q [Double: quantity]
-v, --chatty, --talkative                    be more verbose

Procesar las opciones mostradas anteriormente por nuestra cuenta sería reinventar la rueda. Para ayudarnos en esta tarea existen múltiples librerías que nos permiten realizar un procesado de opciones de argumentos muy complejo en unas pocas líneas de código, de las que expondré una recopilación al final. En este artículo comentaré la que he utilizado: JOpt Simple.


JOpt Simple
Es la librería usada en OpenJDK. JOpt Simple permite realizar análisis de argumentos compatibles con la sintaxis POSIX getopt() y getopt_long(), manteniendo toda la simplicidad posible. Como otras librerías, tiene opciones muy útiles:
  • generación automática del texto (formateado) de la opción de ayuda
  • control de parámetros obligatorios en argumentos
  • control de tipos de parámetros
  • control de opciones de argumentos
  • valores por defecto
  • etc...

A continuación, un ejemplo de uso:

 private static final String IMPORTED = "imported";
 private static final String A_H = "h";
 private static final String A_P = "p";
 private static final String A_U = "u";
 private static final String A_URL = "url";
 private static final String A_E = "e";
 private static final String A_I = "i";
 private static final String A_Q = "q";
 private static final String A_V = "v";
 private static final String A_GC = "gc";
 private static final String A_DR = "dry-run";

 public static void main(String[] args) {
  OptionParser parser = new OptionParser() {
            {
             acceptsAll( asList( "h", "?", "help" ), "Muestra la ayuda" );
             acceptsAll( asList( A_V, "verbose" ), "Muestra información detallada" );
                accepts( A_URL, "Url de conexión al repositorio" ).withRequiredArg().ofType( String.class ).defaultsTo( "//localhost:1099/jackrabbit.repository" );
                acceptsAll( asList( A_U, "user"), "Usuario" ).withOptionalArg().ofType( String.class ).defaultsTo( "gesif" );
                acceptsAll( asList( A_P, "password"), "Contraseña" ).withRequiredArg().ofType( String.class ).defaultsTo( "gesif" );
                acceptsAll( asList( A_E, "export"), "Exportación de consulta a directorio (requiere -q)" ).withRequiredArg().ofType( String.class ).defaultsTo( "." ).describedAs( "directorio" );
                acceptsAll( asList( A_I, "import"), "Importación de ficheros del directorio" ).withRequiredArg().ofType( String.class ).describedAs( "Path completo de ficheros a importar (se permiten comodines)" );
                acceptsAll( asList( A_Q, "query"), "Consulta a realizar" ).withRequiredArg().ofType( String.class ).describedAs("consulta XPATH");
                accepts( A_GC , "Ejecuta la limpieza de borrado (requiere -u y -p)" ).withRequiredArg().ofType( String.class ).describedAs("repository home");
                accepts( A_DR , "Ejecución de importación de prueba (no importa)");
            }
        };

        OptionSet options = parser.parse( args );
        Date time = new Date();

  try {
         if ( args.length < 1 || options.has( A_H ) ) {
             parser.printHelpOn( System.out );
             System.out.println("Ejemplos:\n" +
               " RTool -q \"//*[@gst:file and @g:stepId = 13872238]\" -e /home/szarza/tmp3\n" +
               " RTool -q \"//*[@gst:file and @g:stepId=20754674]\" -e . --url //192.168.20.12:1099/jackrabbit.repository\n" +
               " RTool --url //192.168.20.12:1099/jackrabbit.repository --import ./fc15099e-d586-4046-afd1-8fc9c03bdf17.0002.pdf");
             System.exit(0);
         }
                ....

Como se ve en este ejemplo, con Jopt Simple se puede realizar todo el control de opciones y argumentos de una forma rápida y sencilla. Sin embargo no es la única, hay realmente muchas:
Usé JOpt Simple por ser una librería muy versátil y muy sencilla de usar. Sin embargo, tengo que reconocer que, en mi búsqueda de librerías, JewelCli y JCommander me dejaron impresionado por su elegancia y limpieza. JOpt Simple es una librería al estilo clásico, donde la definición de sintaxis es programática. JewelCli y JCommander son librerías con un nuevo enfoque actualizado, que usan un estilo declarativo para la definición de sintaxis, usando anotaciones. Estas últimas librerías me parecieron más adecuadas para un proyecto mayor, con más opciones y/o que fuese a tener un mantenimiento mayor.

En todo caso, creo que en una próxima ocasión probaré JewelCli. Y tu, ¿has probado alguna? ¿qué opinión tienes?

P.D.: Por cierto, feliz Stars Wars Day a todos!! ... y  #maythe4thbewithyou.

Referencias y más información:
Related Posts Plugin for WordPress, Blogger...
cookieassistant.com