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.