domingo, 6 de octubre de 2019

Procesando ficheros en JAVA



Con los últimos artículos que estoy escribiendo estoy sentando las bases de dos proyectos en JAVA.

El primero es más pequeño y se basa en una aplicación de consola, y de eso vamos a hablar hoy.

Voy a ir creando una pequeña aplicación de consola que lee todas las líneas de un fichero de texto.

Es bastante simple, pero lo que quiero hacer es crear un proyecto con todas las necesidades que puede tener una aplicación de consola:
  • Librerías externas
  • Sistema de log
  • Fichero externo de configuración
  • Multidioma
  • Generación de un ejecutable (jar) para su posterior uso
Todo el código y los pasos están en el siguiente repositorio: https://github.com/tecnificados/lector

El entorno de trabajo con el que estoy trabajando es el siguiente:
  • Sistema operativo: Windows 10
  • Openjdk version "12.0.2" 2019-07-16
  • IDE: Eclipse 2019-06 (4.12.0)
El primer paso es crear con Maven un proyecto "quickstart" dentro de Eclipse (File -> New -> Maven Project) :



Con esto ya tendremos nuestra estructura lista. Y éste va ser el segundo commit del proyecto: c04808759483432e185fae554a215e682b981af8

Por defecto Maven nos deja el compilador de JAVA con la versión 5.



Ya que estamos utilizando el Openjdk 12, vamos a subirla a esta versión añadiendo estas líneas en el fichero pom.xml:


<maven.compiler.target>1.12</maven.compiler.target>
<maven.compiler.source>1.12</maven.compiler.source>

Podéis ver el detalle del cambio en este commit: 6c483c4abc3f46c0aea1b5679605ac3db27d251a

Si hacemos un "Maven Update", nos debe aparecer la versión correcta en el proyecto.

Ahora vamos a buscar una librería que nos ayude a leer ficheros. Mi favorita es "commons-io" del proyecto Apache. La incluimos en el fichero pom.xml para que sus objetos estén disponibles. Otro commit: 5fa357852024488ab5a43080ee51c446ea7a3c1c

Ahora vamos a hacer una pequeña prueba, vamos crear un pequeño archivo "fichero.txt" que va a tener 10 líneas. Y vamos a escribir unas líneas de código para contar las líneas del fichero utilizando el objeto FileUtils de "commons-io".

El código que escribimos es el siguiente:


List<String> readedLines = new ArrayList<String>();
try {
     readedLines = FileUtils.readLines(new File("fichero.txt"),"utf-8");
     System.out.println("Lineas: "+readedLines.size());
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

Tenemos que meter un try/catch por si el método "readLines" lanza una excepción.

Si ejecutamos este código, nos debería aparecer esta salida:

Lineas: 10


Hago un commit con todos estos cambios: 1287bbeaaf2c21021ce5432b4fd094ea0426554d

Hasta ahora no hemos hecho nada con el contenido del fichero, pero tenemos una lista en la variable readedLines donde está el contenido de cada línea.

Vamos a añadir más información al fichero de prueba, y vamos hacer que el programa nos escriba en pantalla cada una de ellas.

Sé que es muy simple, pero me sirve para demostrar que estamos leyendo el fichero.

Añadiendo estas líneas podremos ver el contenido de cada línea leída:


for (String actual:readedLines)
{
     System.out.println("\t"+actual);
}

Las podéis ver en el commit: d65b491461ba90ed8a1cdef121a1b043985fc733

El funcionamiento básico ya no va a evolucionar más.

Ahora vamos a mejorar el programa para que tenga unas características más profesionales.

Sistema de log

Voy a añadir la librería "logback", antes era más de log4j, pero como muchos sabéis ha quedado sin mantenimiento.

Añadimos la librería al fichero "pom.xml", y en la aplicación nos creamos una variable estática con esta línea:


private static final Logger log = LoggerFactory.getLogger(App.class);

Ya tenemos el log a nuestra disposición.

A partir de ahora todo lo que sale por pantalla, en lugar de usar "system.out" lo hago con el sistema de log que acabamos de añadir.

En la excepción en lugar de usar "log.info" utilizo "log.error".

Todos los cambios del sistema de log están disponibles en este commit: 94d2d8ee44347ede47e67cef3a92ccedd48e9583

Fichero de configuración externo

Ahora mismo se está leyendo el fichero "fichero.txt" y el nombre del fichero está a nivel de código, una práctica que no es muy recomendable.

Vamos a crear un fichero "conf.properties" donde se va a configurar este parámetro.

A continuación voy a crear un método "configuration()" que será el que realice la lógica de leer el fichero de configuración y asignar su valor.

En este commit están todos estos cambios: e33d60eea78b23eca3e07229e856a5fd5d09a6c7

Multidioma

En aplicaciones de consola, es raro que nos soliciten multidioma, pero todo es posible.

Aquí eclipse nos echa un cable, con su asistente en botón derecho -> source -> externalize strings.

Este asistente nos crea una clase "Message" para realizar la sustitución dinámica de las cadenas que seleccionemos.

Antes de lanzar el asistente he generado unas cuantas constantes, para evitar problemas.

He creado tres ficheros con traducciones:
  • messages.properties: es la configuración por defecto (inglés)
  • messages_en_EN.properties: inglés de Inglaterra
  • messages_es_ES.properties: español de España

Voy a dejar fijado este último con la siguiente línea:


Locale.setDefault(new Locale("es_ES"));


Si fijáramos otro como "fr_FR", al no existir, cargaría el contenido del messages.properties.

El un con todos los cambios que acabamos de generar es: a6033bf9c31cbacae035ae69432b85919714c88b

Antes de continuar voy a refactorizar un poco, no me gusta mucho la estructura que me ha generado el asistente.

Voy a generar una clase Constant con todas las constantes, y un paquete Util con las clases y los ficheros nuevos.



Todos los cambios los tenemos en este commit: 80aa109d65ddfadda75e20228e84ce585afb2ed6

Ejecutable Jar Completo

Y para terminar vamos a decirle a Maven que cuando genere un empaquetado lo haga con todas las librerías que utiliza, y especificarle cuál es la clase principal.

Todo esto lo conseguimos gracias al plugin Shade de Maven, sólo hay que retocar unas cuantas líneas en el pom.xml, y añadir un par de plugins. Podéis ver en detalle estos cambios aquí: 86369ce3d028b405eed691a556a5e7fcf90148ef

Después de esto si lanzamos un mvn package tendremos un jar con todas las librerías que necesita, y está listo para ser invocado con el comando: "java -jar lector.jar"

Espero que os sea de utilidad, ha sido una entrada muy larga, pero he conseguido hacer todo lo que quería en sólo 11 commits.