Lombok, una pequeña maravilla

Hace tiempo descubrí lombok  Empecé a encontrármelo en muchos códigos de ejemplo que iba mirando por ahí. Primero eché pestes ¿Por qué tengo que andar bajándome librerías extrañas para ejecutar pequeños códigos de ejemplo tontos?

Sin embargo, veía que se usaba bastante en muchos sitios y yo no la conocía, así que me puse a mirar qué demonios hacía … Y me encantó, descubrí por qué la gente la usa tanto.

En nuestro código java siempre tenemos que andar poniendo métodos set y métodos get, son las buenas costumbres. También constructores con parámetros para rellenar esos atributos de las clases. El famoso método toString() que saca esos atributos por pantalla y que muchas veces nos ayuda en depuración. Los métodos hashcode y equals que siempre van de la mano. Y un largo etcétera.

Los IDE dan soporte para esto. Siempre tienen opciones para generar todo este código automáticamente. Sin embargo, eso tiene una pega. Aparte de la «molestia» de andar por los menús del IDE para encontrar la opción, el código suele quedar muy extenso. Imagina una simple clase con cuatro o cinco atributos que simplemente es una estructura de datos. Hay que meterle cuatro métodos set, cuatro métodos get, el toString, el hashcode y equals de aquellos atributos que consideremos relevantes para decidir si dos estructuras de datos son iguales, constructores. Lo que era una clase de algo más de cuatro líneas puede llegar a tener .. ¿30 líneas? ¿40 líneas? de código generado por el IDE y cuya lectura no nos aporta nada. Solo «complica» la lectura de la clase.

Y eso es lo que evita lombok. Tiene muchas opciones, pero por ejemplo, si a la clase anterior le ponemos la anotación @Data, esa clase automáticamente al compilar tiene todos los métodos que hemos dicho. El código fuente queda solo con sus algo más de cuatro líneas de código, pero tenemos todos estos métodos disponibles.

El código adicional se mete en el .class al compilar. No estará en el .java. Y de aquí las pequeñas pegas de lombok, pero que, en mi opinión, creo que se pueden llevar bien:

  • Como es un proceso de compilado adicional, se suele hacer con lo que se llaman annotation processor. Son procesos que durante el compilado revisan las anotaciones de las clases para hacer «cosas» adicionales. Así que requiere que habilitemos esto en maven/gradle o nuestro IDE favorito.
  • Al no estar el código en los fuentes, el IDE no lo encuentra, por lo que nos dará error si intentamos usar por ejemplo un método set generado por lombok. Afortunadamente los IDE contemplan a lombok. No he mirado el caso de eclipse, pero en idea existe un plugin de lombok y tiene las opciones de habilitar el annotation processor.

Si quieres, aquí tienes más detalles y ejemplos de lombok.

 

 

Jugando con Mockito

A veces me da por hacer test de Junit, sobre todo con código nuevo o cuando tengo que tocar código existente del que no me fio mucho o no tengo controlado.

Y en esta me he tropezado y me ha encantado Mockito.

En el proyecto en el que ando metido se usa mucho Spring Framework y hay muchas clases ya hechas que son instanciadas por Spring y que reciben por inyección otras clases también instanciadas por Spring. Spring ofrece un entorno de test para poder instanciar esta clase con todas sus inyecciones, pero el asunto se complica si estas inyecciones tienen a su vez otras y los test se hacen lentos si por cada test hay que levantar un entorno de Spring.

Y aqui es donde me ha gustado Mockito. Sin entorno Spring, levanto mi clase bajo test y todo lo que spring le inyecta, lo hago con Mocks de mockito. Es bastante inmediato y rápido.

Luego ya queda en los test ir diciéndolo a Mockito qué debe devolver cuando llaman a sus métodos con determinados parámetros (eso se puede liar) y capturar las llamadas a otros métodos para ver si se les ha llamado con los parámetros correctos.

Así que he escrito un ejemplo sencillo con mockito.

Por cierto, mi idea inicial era aprovechar y escribir el ejemplo con Junit5, pero he desistido. Poniendo las dependencias de JUnit5 que se me ocurrieron, no conseguí que al ejecutar los test desde Intellij IDEA detectara que había test. Ejecutando con gradle ni idea si ha detectado o no test, porque no he visto nada relativo a test.

Supongo que es cuestión de leer un poco y funcionará, pero no me ha gustado lo no intuitivo que es. En documentación no dice cuales son las dependencias básicas a poner para que funcione y yo poniendo las que  me ha parecido no lo he conseguido rápidamente.

Maven: Parent, Module y BOM (Bill of Materials)

Aunque últimamente ando con gradle y en gradle he resuelto el tema que comento aquí, quería ver si podía hacer algo similar con maven.

¿Cual es el tema?

Imagina que tienes un «producto» SW en tu empresa, algo como el control de una flotilla de camiones, que vendes a distintos clientes que tienen camiones y necesidad de controlar su flotilla. ¿Y por qué «producto» entre comillas?. Aunque tu producto está guay, cada cliente siempre quiere algunas funcionalidades adicionales que tu producto no tiene, o modificación de las que ya existen y la política de tu empresa es hacerle caso al cliente.

Así que tienes tu proyecto organizado en un montón de proyectos maven (o gradle):

  • Uno o más proyectos maven con el «core» de tu producto donde está lo que sabes que siempre va a ir a cualquier cliente.
  • Varios proyectos maven con «plugins» con funcionalidad adicional que puedes añadir o no a tu core según el cliente los pida o no.
  • Varios proyectos maven con librerías tuyas propias de utilidades, relativas a o no a tu producto. Por ejemplo, si tu producto lee de alguna forma los GPS de los camiones para saber por dónde andan, puedes tener varias librerías para leer distintos modelos de GPS.

Ahora imagina que tienes que trabajar en tu producto para hacerle modificaciones al cliente «Fulanito Logistic» que quiere determinados plugins, alguno más que tienes que desarrollar, te ha pedido además algo que te interesa meter en el core porque lo ves útil para siempre, etc.

Y encima, tienes otro proyecto en paralelo para «Menganito Logistic» que quiere otros plugins distintos y también quiere ciertas modificaciones, pero que mira tú que cosas, no son las mismas que quiere «Fulanito Logistic».

¿Cómo montas esto en tu IDE favorito? ¿Y tu compañero que está trabajando para la otra compañía?

Pues bien, inicialmente, montábamos todo (core, todos los plugins y todas las librerías), pero eso hace que el proyecto crezca cada vez más, se haga más inmanejable y los compilados sean cada vez más costosos. Además, empiezan a proliferar en el código como como «if es para fulanito ….», o algo más elaborado porque tenemos bastante friki como anotaciones en las clases estilo @Menganito.

Así que al final, montamos algo un poco más complejo, pero que permite que nuestro IDE y nuestros jenkins cojan solo lo que es necesario en cada caso.

En algún sitio de la estructura de git, metemos un pom.xml parent con las cosas que son comunes en nuestro producto. Por ejemplo, dependencyManagment con las versiones de spring-framework, junit, slf4j/logback, etc. También  properties que necesitemos, etc, etc.

En la misma estructura de git, tenemos un directorio plugins y debajo de el un proyecto maven para cada plugin. Justo debajo de plugins, tenemos un pom.xml con module para cada plugin, algo así

plugins/pom.xml
plugins/plugin1/pom.xml
plugins/plugin2/pom.xml

Cada plugin tiene como padre al plugins/pom.xml y el plugins/pom.xml tiene a su vez como padre al parent común que tenemos por ahí.

plugins/pom..xml tiene como module a todos los plugins y al core de nuestra aplicación. Este pom.xml es el que montamos en el IDE si estamos trabajando en el producto en general y no para un cliente concreto. Además, es el que metemos en jenkins para asegurarnos que los plugins y el core siempre son compatibles.

Por otro lado, para trabajar para un cliente concreto, creamos en el mismo git un directorio proyectos con dos subdirectorios, menganito y fulanito. Todos con su pom.xml de forma similar al de plugins

proyectos/pom.xml
proyectos/menganito/pom.xml
proyectos/fulanito/pom.xml

proyectos/pom.xml tiene como padre al padre común que habíamos hecho por ahí y menganito/pom.xml y fulanito/pom.xml tienen como padre a proyectos/pom.xml

A su vez, menganito y fulanito pom.xml tienen «module».que con path relativo, incluye los plugins necesarios para ese cliente concreto, así como el core. Tambien, debajo de menganito y fulanito se crean subproyectos con código que es específico para ese cliente y que no tenemos previsto reutilizar más.

El que trabaje para menganito, monta en su IDE el menganito/pom.xml y así solo tiene el core, los plugins específicos y el código específico para menganito, ganando tiempo de compilado, menos complejidad de clases/proyectos en el IDE, etc. También esto facilita el hacer el zip/instalado específico, vía maven, para este cliente concreto. Y un jenkins que solo compila lo de ese cliente.

En parent y modules con maven tienes un ejemplo y detalles de como montar todo esto.

Maven, Gradle, …


Siempre he usado maven, pero hace unos años me cayó un proyecto ya empezado, con mucho código y que estaba en gradle, así que llevo ya unos añitos trabajando también con gradle. Aquí mis impresiones.

Por un lado, al principio me gustó. Un fichero build.gradle para un proyecto sencillo se escribe desde cero sin demasiados problemas. Apply plugin java, poner repositories, dependencias y ya. Un pom.xml equivalente no se pone tan fácil desde cero, siempre es copiar y pegar de algún sitio donde ya esté hecho.

Pero con el uso y en proyectos complejos que requieren cosas, me he encontrado con bastantes pegas que hacen que gradle no me guste, más allá de usarlo en proyecto sencillos. Comento algunas de las pegas

Los classifier

En los artifactos (jar) se pone el groupId, un artifactId y una versión. A veces se pone también un classifier para poder distinguir dos artefactos que nos interesa agrupar en el mismo groupId, artifactId y version, para poder distinguirlos. Un ejemplo claro es cuando hay librerías nativas de por medio. Imagina una librería java que use gráficos que tire de librerías nativas: dll en windows, .so en linux, etc. Una práctica habitual es meter estas librerías nativas en un jar, ya que un jar no es más que empaquetado como un zip o similar y se le pueden meter todo tipo de ficheros. Luego, en ejecución, nuestro código java debe determinar el sistema operativo en el que está corriendo y cargar la librería nativa que corresponda: la .dll, la .so, etc. Es también habitual, puesto que hay distintos tipos de micros y luego arquitecturas de 32 y 64 bits, no empaquetar todas las posibles librerías nativas en un solo jar, sino meter en un jar distinto cada una de ellas, así tendremos la dlll de 32 y 64 bits, etc. Y aquí es donde entran los classifier en juego. Nuestra librería java sería por ejemplo (formato groupId:artifactId:version:classifier) com.chuidiang:librería:1.0 y cada una de las que tienen librerías nativas dentro llevarían un classifier estilo com.chuidiang:libreria:1.0:windows-32, com.chuidiang:libreria:1.0:windows-64, com.chuidiang:libreria:1.0:linux-64, etc, etc.

Pues gradle, según como montes esto, lo entiende o no. Puedes tener en tu repositorio maven todos los jar correctamente subidos, pero si tu librería tiene dependencia de todas las librerías con classifier, hay casos que gradle no las baja. Hay otros casos que sí, seguramente dependiendo de algún detalle de cómo se han organizado en el repositorio. En maven funciona siempre. Y si el repositorio y la librería no son tuyo y no puedes organizar estas dependencias, no te queda más remedio, como nos ha pasado a nosotros, que poner una a una todas las dependencias con classifier a mano. En nuestro caso, librería es de terceros, en su pom subido al repositorio maven tienen las dependencias runtime de todos las librerías con nativas dentro y gradle lo ignora, no las baja.

Repositorio local propio

Imagina dos proyectos totalmente separados, en tu PC, que no tienen que ver como para montarlos juntos en un multi-proyecto gradle o como módulos maven. Uno es una librería y el otro es un proyecto con su main. E imagina que tu proyecto usa esa librería, así que pones la dependencia correspondiente. Ahora imagina que necesitas tocar la librería, a esta le pones una versión SNAPSHOT para decir que se está modificando y para probar rápido, en tu proyecto pones dependencia de esa versión SNAPSHOT.

Con maven no hay ningún problema. Cuando tocas la librería haces un mvn install y luego desde el proyecto ya estás viendo los cambios.

Gradle, sin embargo, no tiene una forma «nativa» de hacer esto. Por mucho gradle build que hagas en la librería, no verás los cambios en tu proyecto.  Gradle build no guarda los jar que genera en su repositorio/caché de jars, por lo que el proyecto no ve los cambios.

¿Cual es la solución? Ponerle a gradle el plugin de maven, de forma que puedas hacer gradle install que subirá el jar al repositorio local de maven, y poner en tu proyecto un repositorio mavenLocal(). Bueno, es una solución, pero desde mi punto de vista le quita un punto a gradle, para hacer algo que en proyectos relativamente grandes que reutilizan librerías propias hechas aparte, tenemos que tirar de maven.

Y se me presenta además un problema adicional. Si por el motivo que sea la dependencia de la librería SNAPSHOT está en la caché de gradle, ya la hemos liado. Aunque hagas gradle install para subir una nueva versión SNAPSHOT a tu repositorio maven local, el gradle de proyecto se lía un poco, como ya la tienen en la caché, no mira si hay actualizaciones en mavenLocal. La opción –refresh-dependencies no me ha parecido funcionar, debe mirar solo en repositorios externos. Así que mi única forma de hacer esto es borrar de la caché de gradle esas versiones snapshot, cosa que según documentación no se debe hacer (tocar la caché).

Plugins

Maven es más veterano y tiene muchos más plugins hechos y probados que gradle. Con gradle, desde luego, los hay o te los puedes hacer, pero es complicado. Un ejemplo claro que vi hace tiempo es el trabajar con OSGI. En maven hay un plugin, que tendrá sus cosas, pero funciona correctamente. En gradle, cuando miré, había uno de aquella manera que apenas facilitaba nada.

Gradle, por supuesto, es mucho más versátil que maven, en el build.gradle prácticamente puedes hacer programación en groovy y hacer lo que quieras, pero no es fácil y precisamente esta versatilidad, creo que es también un problema. ¿Por qué?. En maven, si quieres hacer un plugin, hay unas reglas muy definidas, te haces una clase que debe heredar de AbstractMojo y hacer lo que ahí diga, con las reglas que definen esa clase abstracta. En gradle puedes hacer lo que quieras como quieras. En maven quizás te limita, en gradle tienes libertad de acción

¿Cual es el problema? Pues principalmente entender su configuración si eres simplemente un usuario del plugin o su forma de funcionar. En maven las configuraciones de los plugin se escriben siempre igual, puedes no entender qué significan algunos valores de la configuración o incluso si funcionan correctamente, pero sabes donde ponerlos. En gradle es poco menos que imposible saberlo si no tienes una buena documentación, que no siempre es así.

Aparte, en gradle, posibles efectos secundarios con otros plugins porque el desarrollador del otro plugin puede haber hecho algo que interfiera con el tuyo o el tuyo con el de él, mantenimiento del plugin según avanza gradle, porque igual lo que usa el plugin de gradle luego no está o cambia, etc, etc.

Un ejemplo concreto, hace poco me puse a mirar cómo ofuscar código desde maven/gradle. Lo intenté con Proguard.

¿Mi experiencia con proguard y maven?. Correcta. Busqué el maven-gradle-plugin en google, tuve que aprender algo de proguard para saber qué ofuscar, que no ofuscar y cómo, hice mis experimentos y funcionó correctamente sin problemas. No tuve que aprender cómo meter el plugin en maven, sólo como configurarlo siguiendo los pasos de maven para configuración de plugins.

¿Mi experiencia con gradle?. Bueno, seguramente la culpa no es de gradle, sino del plugin. La página de Proguard tiene un plugin para gradle, así que ese usé. La primera pega, la versión del plugin que dice dicha página (7.0.0) no está en los repositorios, tuve que buscar en mvnrepository la más moderna.

Bueno, eso no es importante. Siguiente problema, un mensaje de error como este «ERROR: Output jar must be specified after an input jar or it will be empty» realmente quiere decir que no encuentra el input jar. Una horita de google para darse cuenta de que habia puesto mal el path del jar que quiero ofuscar.

Y luego el tercer problema, donde ya desistí. proguard necesita de alguna forma ver las dependencias externas que tiene tu proyecto. Si tu proeycto necesita log4j, apache-commons, etc, necesita verlas para hacer bien su trabajo. En maven no tuve que hacer absolutamente nada. En gradle me da el error de que nos las encuentra y tienes que «currarte» el ponérselas accesibles. Googleando, no hay documentación a este respecto y los posts que he visto donde intentan resolver este problema hablan de hacerse un task de gradle que se baje las dependencias en un directorio y luego decirle al plugin de proguard que las busque ahí.

Bien, lo dicho, seguramente no es culpa de gradle, sino de la gente de proguard con su plugin, igual la versión antigua porque la 7.0.0 no está, igual mi versión de gradle, no lo sé. Pero con maven fue un rato tenerlo en marcha y con gradle veo que voy a tener que echar un buen rato.

Así que, por mi parte, seguiré usando maven en proyectos más o menos serios/grandes/con necesidad de plugins y dejaré gradle para mis pequeños programas de prueba o donde no prevea la utilización de las cosas que son más o menos estándar en gradle.

Puñetitas con SpringBoot y RestTemplateBuilder

Cuando en Spring Boot queremos hacer un cliente REST, podemos usar RestTemplate. Esta clase permite hacer llamadas REST al servidor.
Para evitar en todas las llamadas poner la URL completa, se puede poner la URL base de esta forma

RestTemplateBuilder builder = new RestTemplateBuilder();
RestTemplate restTemplate = builder.rootUri(«http://loquesea.com/»).build();

// y a partir de aqui, para una llamada GET por ejemplo
restTemplate.getForObject(«/entidad», ….);

En getForObject() no hace falta poner la URL completa, basta empezarla con / y el rootUri que añadiemos anteriormente se añade al principio.

Pues bien, esto funciona bien, pero cuando hice mi primera prueba, iluso de mí, se me ocurrió poner esto

RestTemplateBuilder builder = new RestTemplateBuilder();
builder.rootUri(«http://loquesea.com/»)
RestTemplate restTemplate = builder.build();

peeero … ¡¡eso no funciona!!. ¿Por qué?. Metiéndome en el código del método rootUri() me asombra ver

/**
* Set a root URL that should be applied to each request that starts with {@code ‘/’}.
* See {@link RootUriTemplateHandler} for details.
* @param rootUri the root URI or {@code null}
* @return a new builder instance
*/
public RestTemplateBuilder rootUri(String rootUri) {
return new RestTemplateBuilder(this.detectRequestFactory, rootUri,
this.messageConverters, this.requestFactorySupplier,
this.uriTemplateHandler, this.errorHandler, this.basicAuthorization,
this.restTemplateCustomizers, this.requestFactoryCustomizers,
this.interceptors);
}
}

Lo advierte el comentario, devuelve una nueva instancia, por lo que la rootUri no se guarda en la instancia actual. Así que esta llamada solo funciona si se pone todo seguido como en el primer ejemplo rootUri(..).build(), o si nos guardamos en una nueva variable lo que devuelve rootUri() para luego llamar a buld() de lo devuelto.

En fin, despista un poco.

Comunicación Consola Servidor : Hazelcast, Vert.x, REST

Logo HazelcastEn el mismo proyecto del post anterior, estaba replanteandome como hacer que la interfaz de usuario (consola, aplicación java de escritorio) se conecte con el servidor (ejecutable java que no es servidor web ni de aplicaciones). Habitualmente abrimos un socket TCP/IP y definimos la mensajería. Tenemos librerías ya preparadas para esto, es eficiente y lleva muchos años funcionando. Pero a todos nos gusta jugar. Para este proyecto, me estoy planteando usar algún otro tipo de comunicación.

En otros proyectos que he visto (uno de los cuales lo he heredado), usan servicios web REST para todo lo que sea petición/respuesta desde la consola al servidor. Cuando el servidor quiere enviar algo de motu propio a todas las consolas, se usa ActiveMQ como cola de mensajes. Como el proyecto está con Spring Framework, hay soporte para las dos cosas haciendo fácil su codificación. Este mecanismo también está funcionando sin problemas.

Pero lo dicho, quiero seguir jugando.

Lo del servicio REST … ¿podemos eliminarlo usando sólo colas de mensajes o algo parecido? ¿Y en vez de una cola de mensajes ActiveMQ podemos usar algo más sencillo y ligero?

Pues me plantee hacerlo primero todo con Hazelcast. Si pongo modelos compartidos entre consola y servidor y con el mecanismo que tiene Hazelcast de publicación/suscripción a base de topics, quizás me bastaría sólo Hazelcast para reemplazar los web services REST y a ActiveMQ.

Pues no. Por un lado, Hazelcast no ofrece un mecanismo de petición/respuesta. Si uso un topic como petición de forma que el servidor se suscribe a ese topic de petición y cuando la consola quiere algo, publica en ese topic qué quiere, el servidor no tiene forma de responder. Habría que crear un topic temporal de respuesta, de forma que la consola que ha hecho la petición se suscribe a ese topic temporal y recoge la respuesta. Esto lo ofrece ActiveMQ de forma transparente, pero con Hazelcast habría que currárselo.

Y me surgió un segundo tema. Si en base de datos tenemos guardadas una serie de cosas y la consola las quiere, lo normal es pedírselas al servidor y el servidor en respuesta las devuelve. ¿Cómo lo hace en el caso de Hazelcast? Tendría que meterlo en algún tipo de colección de Hazelcast en memoria. ¿Y qué pasa sin son muchos datos?. Pues que hay que cargarlos todos en memoria, o inventar un mecanismo de cargas parciales, con paginados, etc, etc.

Así que descarté el uso exclusivo de Hazelcast. Lo he dejado, pero principalmente porque me vale para el modelo «vivo» de datos. En la aplicación se verá un mapa con barcos moviéndose. Estos barcos moviéndose en lo que llamo modelo «vivo» de datos, los datos que actualmente se presentan en todas las consolas y que el servidor se encarga de ir actualizando para todas. Esto es una ventaja frente a lo que teníamos. Sin Hazelcast (o un equivalente), el servidor tenía que enviar todos los cambios de ese modelo «vivo» a través de eventos por socket TCP/IP o ActiveMQ y las consolas debías actualizar su copia de dicho modelo en base a esos eventos. Con Hazelcast, el servidor toca la colección compartida y la consola ve dicha colección actualizada en todo momento. Es Hazelcast el que se «come» por debajo todo el tema de eventos de cambios del modelo y la actualización en el lado de la consola. La consola puede suscribirse a cambios en esa colección compartida, hazelcast ofrece esa posibilidad, pero para hacer otras cosas que necesite, como borrar el icono del barco en el mapa o desplazarlo, pero no para actualizar esa misma colección de barcos que ya está actualizada

Bien, no me vale Hazelcast para todo. ¿Qué tal Vert.x?. Tiene un EventBus, más sencillo de manejar que ActiveMQ, contempla la petición/respuesta, usa (o puede usar) Hazelcast por debajo por lo que encaja con el Hazelcast que me he planteado usar. Así que eso me planteé. Dejo Hazelcast para el modelo vivo y usar EventBus de Vert.x para peticion/respuesta y publicación/suscripción con Topics.

Me pongo a hacer mis pruebas y … ¡¡Sorpresa!!. Vert.x no garantiza el orden en la entrega de los eventos. Si garantiza que si un publicador envía eventos, un suscriptor concreto los recibe en el orden que el publicador los ha enviado. Pero no garantiza nada más. Si un publicador publica en varios topic y cada topic tiene un suscriptor distinto, los suscriptores pueden recibir los eventos en cualquier orden, no necesariamente en el orden en que el publicador los ha enviado. Y eso me hizo polvo. Para petición/respuesta a veces se necesita que las cosas vayan en orden, que no me contesten antes a lo que he pedido después. Así que que para petición/respuesta descarté Vert.x, seguro que puedo hacer apaños para garantizar el orden, pero va en contra de la filosofía de Vert.x y me parece una fuente importante de bugs si los desarrolladores se despistan.

¿Y para publicación/suscripción con topics, sin respuesta?. Bien, tengo Hazelcast que también lo hace. No voy a meter un framework por encima sólo para eso. Lo dejo aparcado por si en un momento dado Hazelcast se me queda cojo en este asunto y Vert.x me lo solucionara, pero lo dicho, queda aparcado.

¿Cómo hago al final lo de petición/respuesta?. Pues al final he dejado los web services REST.

¿Qué creo que he ganado con todo este cambio aparte de jugar y aprender? Principalmente el tema del modelo «vivo». La colección compartida de Hazelcast entre consola y servidor me ahorra enviar eventos de cambios en ese modelo en el servidor, recogerlos en la consola y reconstruir en consola con código el mismo modelo que tiene el servidor.

En cuanto a topics, AciveMQ frente a Hazelcast, desde luego es mucho más potente y con más posibilidades ActiveMQ, pero es más complejo y no necesito todas esas posibilidades, tiro de momento por la opción más simple de Hazelcast.

 

Estructura de Plugins con OSGI / Spring Boot

osgi

spring boot

Estoy empezando una aplicación java, de escritorio, desde cero. Una de las cosas que pretendo hacer es que tenga plugins que se puedan instalar de forma adicional, no solo en el lado de la consola (javax.swing), sino también funcionalidades en el lado del servidor (java de escritorio).

Me he planteado dos opciones. Una es usar OSGI, que es el framework más estándar y conocido para hacer una aplicación modular con plugins. Pero aunque no está pensado para ello, se me ha ocurrido otra forma de hacerlo usando Spring Boot (por ir a lo más moderno).

Con OSGI, cada jar lleva un fichero de manifiesto donde indica qué paquetes java exporta a otros jar y qué paquetes java necesita de otros jar. Si el fichero de manifiesto no exporta o importa paquetes, el desarrollador no puede usar los paquetes que no han sido exportados/importados, el mismo IDE debidamente montado no le deja.

Con Spring Boot no hay esta opción de importar y exportar paquetes, pero se puede «apañar» con disciplina. Debemos montar una estructura de proyectos/subproyectos donde haya jar que sólo tengan interfaces o estructuras de datos. Los demás jar donde está el código sólo pueden depender de estos jar de interfaces/estructuras de datos. Esto se puede montar correctamente con maven/gradle. Puede quedar quizás una estructura de proyecto/subproyectos un poco engorrosa, pero si organizada y que impide al desarrollador usar en un proyecto cosas que no debe de otro (siempre que no se líe a tocar dependencias maven/gradle)

Para enganchar las distintas clases/servicios entre si de distintos jar, OSGI ofrece lo que llama Declarative Services. Cada jar registra en OSGI los servicios que ofrece (clases que implementan una interfaz java y que ofrecen alguna funcionalidad) y cada jar pide a OSGI los servicios que necesita (servicios a través de interfaces java y que necesita que alguien se las dé ya implementadas) . OSGI se encarga de «enganchar» en el arranque, automáticamente, a unos con otros en función de lo que ofrecen y necesitan.

Con Spring Boot, está el mecanismo que tiene de inyección de dependencias y anotaciones. Si una clase necesita un servicio, le basta con poner un atributo con la interfaz de ese servicio anotada con @Autowired. Si Un jar ofrece un servicio, basta que le ponga a la clase que lo ofrece la anotación @Service (o cualquiera de las alternativas, como @Component u otras). Spring Boot se encarga de ver qué servicios se ofrecen e inyectarlos en las clases que lo necesitan.

De una forma o de otra, el mecanismo de plugin sería que la aplicación principial definiera la interfaz que deben implementar los plugins y los servicios que deben ofrecer para integrarse correctamente en la aplicación. Por ejemplo, en la interfaz de usuario la interfaz que deben implementar los plugins pueden tener métodos como getActions() para añadir dichas acciones a un menú o barra de herramientas, getIcon() para poner el icono de dicho plugin en algún sitio, getPanel() para que el plugin pueda proporcionar su propio panel visual, etc. La aplicación principal pediría al framework todas las clases que ofrezcan este servicio (implementen esta interfaz) y los plugin presentes registrarían sus servicios en el framework.

¿Qué ventajas/inconvenientes tiene cada mecanismo?

OSGI es el estándar para plugins, y además permite el añadir plugins en caliente. Debidamente configurado, bastaría situar el jar con el plugin en un directorio concreto de la aplicación que está en ejecución y el plugin se cargaría automáticamente, inyectando en caliente los servicios que ofrece al que los necesite. Sin embargo, la pega de OSGI es es que es algo retorcido a la hora de desarrollar. Su objetivo es limitarnos para obligarnos a hacer el desarrollo modular y esas limitaciones a veces son un poco engorrosas.

Spring Boot por otro lado es muy sencillo de usar. Un par de anotaciones (@Autowired y @Service) y está todo apañado (por supuesto, siempre hay peguillas en el mundo real y casos extraños). Las pegas son que no permite el despliegue de plugins en caliente y que no obliga a desarrollo modular limitando visibilidad, por lo que tenemos que obligar a ese desarrollo modular a bases de subproyectos maven/gradle debidamente configurados.

Otra posible pega de Spring Boot es que en el arranque busca por todas las clases las anotaciones mencionadas para hacer el enganche. Si es un proyecto grande con muchas clases, el proceso de escaneo de todas las clases y sus anotaciones puede volverse lento. OSGI no tiene ese problema, porque cada jar, con código (o un xml si optamos por esa opción), pide o registra servicios en OSGI, no es OSGI el que se dedica a recorrer todo el código.

Y una ventaja clara de Spring Boot sobre OSGI es la cantidad de funcionalidades adicionales que de forma sencilla nos vienen de regalo. Por ejemplo, montar web services o soporte con bases de datos en Spring Boot es casi inmediato. En OSGI, aunque existe algo, es muy, muy engorroso de poner en marcha. Si conoces ambos frameworks, pero no has levantado nunca web services con ellos, en Spring Boot puedes buscar un tutorial y tener tu web service «hola mundo» en 10 minutos. Con OSGI, te puedes pasar una o dos semanas buscando los plugins que te dan soporte para crear web services y tenerlos funcionando, sin haber implementado todavía tu hola mundo. Spring Boot es seguramente más pesado, pero si quieres funcionalidad adicional es mucho más fácil.

¿Mi elección?

Para este proyecto que estoy empezando y que no tiene ningún requisito (ni visos de tenerlo) de despliegue de plugins en caliente, me he tirado por Spring Boot. Por su sencillez y porque voy a usar web services para comunicación entre consola y servidor, aunque sea java de escritorio.

¿Es nuevo esto del machine learning?

Pajarito de Weka

Me ha dado por mirar temas de machine learning. Como también he oído mucho de deep learning, lo primero he intentado ver es la diferencia entre uno y otro. Pues parece que machine learning es el término general para a partir de unos datos de entrada encontrar correlaciones y poder hacer predicciones a futuro sobre estos datos, usando determinados algoritmos. Y deep learning es un subconjunto de machine learning en el que el algoritmo a usar es el de redes neuronales.

En machine learning en general se usan cosas como árboles de decisión, redes bayesianas, clustering, etc, etc. En deep learning se usan redes neuronales.

Y una vez entendido el asunto, me he dicho … seguro que hay cosas en java hechas. Y sí, hay bastantes. La que más me han llamado la atención, por la parte de machine learning, es Weka. He estado jugando con Weka y no es muy compleja de usar, me ha parecido bien. También he visto, aunque no jugado todavía, TensorFlow, esta de redes neuronales (deep learning) y al menos la web tiene muy buena pinta y TensorFlow es de google, que siempre hace que al menos merezca la pena mirarla. De todas formas he sido «malo» y le puesto la mosca detrás de la oreja a un compañero de trabajo, que sí ha jugado con ella y me ha dado buenas referencias tras hacer sus experimentos.

Pero tras leer un poco sobre esto y jugar con weka … me he decepcionado un poco. Weka no me ha costado mucho porque he visto que tenía una cosa que se llama árboles de decisión como algoritmo para clasificar muestras y he podido hacer rápidamente mis pruebas porque sé cómo funciona un árbol de decisión. ¿Y por qué lo sé?. Porque allá por 1993 (hace ya 25 años) estuve intentando hacer el doctorado de mi carrera (no lo terminé, por eso «intentando») y ya entonces existían los árboles de decisión. También por aquella época existían las redes neuronales (ahora por lo visto llamadas deep learning).

Entonces .. . ¿hemos evolucionado algo?. Pues mi conclusión es que en conceptos/algoritmos me da la impresión de que no, posiblemente se han mejorado o se han variado, pero básicamente sigue siendo lo mismo que había al menos hace 25 años. Lo que sí ha evolucionado un montón es que ahora hay un mogollón más de datos, tenemos más potencia de CPU y tenemos más posibilidades de hacer cálculo distribuido en cluster de servidores, por lo que todos estos algoritmos tienen ahora mucha más potencia y posibilidades. De hecho, aunque no he encontrado una referencia clara, en algunos sitios si mencionan que la palabra «deep» es porque ahora se pueden poner muchas más capas en las redes neuronales.

Y lo que más ha evolucionado, en 1993 internet no era lo que es hoy y me tuve que hacer yo desde cero el código de los árboles de decisión, estudiando e implementando las fórmulas de entropía del conjunto de muestras. También me hice desde cero mis redes neuronales. Ahora hay mucha gente que ha hecho librerías que tienen todo esto implementado, como Weka o TensorFlow, y están fácilmente accesibles y documentadas en internet. Sólo nos queda por hacer la parte difícil, elegir bien los parámetros de interés de nuestras muestras y entrenar el algoritmo 🙂

Jugando con Docker

Logo Docker Descubrí y jugué con Docker hace ya bastante tiempo, pero ahora me ha dado otra vez y estoy en ello.

He hecho alguna pruebecilla con Docker y Multicast, con Docker y Postgres/Postgis … y ahora ando liado a ver cómo hacer imágenes Docker desde Maven.

Ya en su día hice una prueba y me funcionó bien, use el plugin de maven spotify / docker-maven-plugin y más o menos me gustó. En el pom.xml ponías a base de tags cómo construir la imagen y todo bien. Sin embargo, la documentación pone que ese plugin no se mantiene más y que se use en su lugar spotify / dockerfile-maven. Pues bien, he estado jugando un muy poco con él y lo que he visto no me ha gustado nada. En vez de poner cómo construir la imagen a base de tags en el pom.xml, te obliga a usar un Dockerfile. Nada malo hasta ahí. En los ejemplos de la documentación el fichero Dockerfile está en el raíz del proyecto maven y funciona bien. Estupendo. Pero a mí me gusta tener libertad y organizar las cosas a mi manera, así que pongo el Dockerfile en src/main/docker/Dockerfile. Y ahí empiezan mis problemas. Para meter dentro el jar (y sus dependencias), tengo que poner en el Dockerfile algo como

COPY ../../../target/mijar.jar /path/dentro/de/mi/imagen

Queda feo, pero podría vivir con ello. El problema es que cuando ejecutas el plugin, da un error de que el directorio target está fuera del contexto del Dockerfile. Obliga (no sé si el plugin o el Dockerfile, creo que el plugin) a que todos los ficheros que quieres copiar estén donde el Dockerfile o en subdirectorios por debajo. Eso me obliga a poner el Dockerfile en el raíz para poder coger cosas del subdirectorio target, como mi propio jar. Es su forma de funcionar, pero no me gusta. No he encontrado (tampoco me he matado buscando) una forma de solucionar esto. De momento voy a mirar el fabric8io / docker-maven-plugin, que de momento no he probado, pero solo ver el detalle de la documentación (sigue el link que aparece en esa página), ya me ha causado muy buena impresión.

Así que … ¡¡ ha probar se dicho !!