Mar 16

google-gson para manejar json desde java

jsongoogle-gson es una pequeña librería java que nos permite convertir una clase java en un String con formato JSON y viceversa. Es útil si hacemos una aplicación web con java en el lado del servidor y usamos el formato JSON para enviar y recibir datos al navegador.

Un pequeño ejemplo, hagamos un par de clases java sencillitas, dos bean sin necesidad de métodos set y get, pero con un constructor sin parámetros. Una de ellas tiene como atributo, entre otros, a la otra clase.

class Dato {
   private double numero;
   private int array[];
   private String texto;
   private LinkedList<Boolean> lista;
   private DatoInterno dato;

   public String toString() {
      return texto + " " + numero + " " + Arrays.toString(array) + " "
        + Arrays.toString(lista.toArray()) + " " + dato;
   }
}

class DatoInterno {
   private float valor;

   public String toString() {
      return "" + valor;
   }
}
 

Le hemos puesto unos métodos toString() para ver por pantalla fácilmente su contenido

Una vez instalada la librería en nuestro proyecto (es un solo jar), podemos hacer un código como este

Gson gson = new Gson();

Dato dato = gson.fromJson("{numero:1.4,texto:dos,array:[1,2,3,4],lista:[true,false],dato:{valor:11.22}}",
        Dato.class);
System.out.println(dato.toString());

 

que da de resultado algo como esto

dos 1.4 [1, 2, 3, 4] [true, false] 11.22

es decir, la salida de nuestro System.out.println() en el que hemos obtenido una clase dato rellena con los datos del texto json. Por otro lado, también tenemos la posibilidad inversa. Si una vez relleno el dato llamamos a

System.out.println(gson.toJson(dato));

obtendremos la siguiente salida

{"numero":1.4,"array":[1,2,3,4],"texto":"dos","lista":[true,false],"dato":{"valor":11.22}}

es decir, el texto JSON correspondiente a lo que tenemos dentro del dato, pero perfectamente formateado y no la "guarrería" que había puesto yo inicialmente en el código.

En fin, sencilla de usar y bastante potente.

 

Jan 17

Constantes en los beans y JSTL

A veces, en una aplicación web con JSTL nos vendría bien tener una constante definida en uno de los beans de java, estilo

public class UnaClase {
   public static final String UNA_CONSTANTE = "algo";
   …
}

y luego poder llamarla desde JSTL con algo como

<jsp:useBean id="bean" class="com.chuidiang.UnaClase"></jsp:useBean>

<c:out value="${bean.UNA_CONSTANTE}" />

Desgraciadamente, eso no funciona. JSTL presupone que UNA_CONSTANTE es un atributo del bean y va a convertir ese ${bean.UNA_CONSTANTE} en una llamada al método bean.getUNA_CONSTANTE() que no existe.

La solución es fácil, basta con crear un método getUNA_CONSTANTE() en el bean que devuelva el valor de la constante. Unicamente tenemos que tener en cuenta que ese método no puede ser static, puesto que si no JSTL tampoco lo llamará (por debajo, probablemente usa la introspección de java y no busca métodos estáticos). El bean quedaría así

public class UnaClase {
   public static final String UNA_CONSTANTE = "algo";

   public String getUNA_CONSTANTE () {
      return UnaClase.UNA_CONSTANTE;
   }

}

que sí funciona como esperamos.

Jan 14

El WebappClassLoader y ContextClassLoader en Tomcat/Liferay

En el desarrollo nos hemos encontrado con un problema curioso. Estamos desarrollando un portlet con Liferay y necesitamos desde él hacer llamadas AJAX que actualicen parte del portlet sin refrescar la página. Se nos ha ocurrido, en la parte del servidor, hacer un jsp que sería el que reciba estas llamadas desde la parte de javascript, digamos un funcion.jsp. En esa funcion.jsp usamos clases de nuestra parte del servidor del portlet, en concreto, usamos los jar que hay en el WEB-INF/lib de ese portlet.

Pues bien, el problema que se nos ha presentado es que aunque todo parece ir bien tanto en el funcion.jsp como en las clases que se van llamando, hay un sitio concreto en el que hay un problema. Esas clases crean un cliente de web service con Apache CXF para conseguir datos en otros servidores y en la creación de ese cliente nos salta el problema, una excepción fea

ClassCastException: com.sun.xml.ws.client.sei.SEIStub cannot be cast to org.apache.cxf.frontend.ClientProxy [java] at org.apache.cxf.frontend.ClientProxy.

A poco que se hurge con google, encontramos que la versión 6 de java viene con un proveedor por defecto para web services. Java proporciona mecanismos para cambiar ese proveedor por defecto por el que queramos, en concreto y entre otras opciones, basta con poner en el classpath el fichero META-INF/services/javax.xml.ws.spi con una única línea de texto que sería la clase proveedor de web service que queramos uar,  en mi caso, la de apache cxf. La excepción surge si cuando java busca el proveedor no lo encuentra, poniendo entonces el suyo por defecto, que no es compatible con Apache CXF.

Normalmente no debemos preocuparnos de esto, el jar cxf-rt-frontend-jaxws.jar de apache cxf contiene ese fichero debidamente configurado, por lo que si este jar está en el classpath, no debería haber problemas.

¿Por qué entonces surge el problema cuando llamo al funcion.jsp si este jar está en el WEB-INF/lib?. Pues tras varios días de pruebas, google y más pruebas, encontramos que nuestras clases usan por defecto el WebappClassLoader que les proporciona tomcat, por lo que todos los jar en WEB-INF/lib de nuestro portlet están disponibles, pero java, cuando busca el proveedor de servicios, usa Thread.currentThread().getContextClassLoader() que NO le devuelve el WebappClassLoader, sino el de la propia aplicación de Tomcat, por lo que las clases de nuestra aplicación no están disponibles.

Si experimentamos y hurgamos un poco más, vemos que Tomcat, cuando va a llamar a algo de nuestra aplicación web, nos cambia ese ContextClassLoader haciendo algo como

Thread.currentThread.setContextClassLoader(elWebappContextClassLoaderCorrespondiente);

y luego, cuando nuestra aplicación termina de hacer lo que tenga que hacer, Tomcat restaura el ContextClassLoader por el de defecto.

El problema que tenemos entonces es que cuando se llama a nuestra funcion.jsp, no se está cambiando ese ClassLoader. Ahora, sabiendo el problema, la solución debería ser fácil.

La primera y más inmediata es que nosotros mismos, antes de crear el cliente web service, cambiamos ese ContextClassLoader y luego lo restauramos, algo similar a lo que hace Tomcat. Esta opción no me gusta porque no me gusta jugar con los ClassLoader sin saber a ciencia cierta qué hago, y sobre todo porque Tomcat debería cambiarnos el ClassLoader y no lo hace, lo que implica que algo tenemos mal configurado.

Como estamos con liferay y llamamos a funcion.jsp con una URL directa, no a través del portlet liferay, imagino que ahí vienen los problemas. Así que toca "investigar" un poco a ver cómo conseguir que liferay/tomcat consideren funcion.jsp como parte del webapp o portlet y le cambien el classloader al Thread dichoso.

 

Nov 26

Abrir conexiones java contra un servidor https

Me ha tocado abrir conexiones desde código java contra  un servidor https que requiere certificado de cliente. Me he puesto a ello y he aprendido algunas cosas.

Cuando hacemos un cliente java contra un servidor https, java verifica que el certificado que presenta ese servidor es válido. Por ello, si no los tenemos, necesitamos obtener dichos certificados y meterlos en un almacén de certificados que luego le diremos a java que es el almacén de certificados en los que confiamos. Una forma de obtener el certificado es visitar la página con el navegador y pinchar el el candadito que sale junto a la URL. Ahí podemos ver los datos del certificado y guardarlo en un fichero.

Con la herramienta keytool que viene con java podemos meter esos certificados en un fichero único, de nombre genérico "KeyStore". De hecho, el fichero por defecto que crea/usa esta herramienta keytool suele ser $HOME/.keystore. La herramienta keytool nos pedirá una clave cuando cree el fichero para protegerlo y nos pedirá la misma clave cada vez que queramos ver, añadir o borrar certificados de ese fichero. El formato de este fichero se conoce como "JKS" y es propio de java.

El certificado de cliente habitualmente nos lo darán como un fichero, protegido por una clave y su formato suele ser "PKCS12", este es un formato estándar entendido por la mayoría de las aplicaciones no java. Su extensión suele ser .p12

Pues bien, en java tenemos dos opciones para indicar cual es nuestro KeyStore y cual es nuestro certificado de cliente. Una es por medio de propiedades de sistema. Podemos arrancar nuestro código java con propiedades de este estilo

java -Djavax.net.ssl.trustStore=c:/usuario/.keystore ….

y necesitamos hasta 6 de estas propiedades: fichero, clave del fichero y formato del fichero, para el KeyStore y para el certificado del cliente.

Y la otra opción es codificarlo en java directamente, usando clases como SSLContext, TrustStoreFactory, etc, etc. Algo más complejo, pero que nos dará más versatilidad si queremos abrir varias conexiones con juegos de certificados distintos.

Cómo no, todo esto con más detalle en la chuwiki.

 

Sep 20

jax-ws, metro y cxf

No hace mucho que empecé a trabajar con web services. Buscando, buscando, vi como opciones axis y jax-ws. Al final, por sencillez, me decidí por jax-ws … pero hoy he descubierto un pequeño detalle que no sabía.

jax-ws no es más que una especificación y hay varias posibles implementaciones. Las más conocidas son metro y cxf. Yo he estado usando metro, confundiéndolo con jax-ws. Ahora que he descubierto que hay dos, he investigado un poco en internet a ver ventajas de una y otra. No he hecho pruebas ni una búsqueda exhaustiva, simplemente comento aquí algunas cosas que he visto.

Por un lado, metro obliga a poner las anotaciones sobre clases, mientras que cxf admite que se pongan en interfaces. No es algo demasiado importante, pero siempre es más elegante tener definido el web service sobre una interfaz.

Metro sólo soporta SOAP, mientras que cxf soporta también otros protocolos. De hecho, una misma interfaz puede llevar simultáneamente anotaciones de SOAP y de REST, de forma que la clase que lo implementa no necesita saber qué tipo de web service habrá detrás.Por su parte, Metro no soporta REST. La implementación de esta gente para REST está en un proyecto separado, Jersey.

Tanto Metro como CXF tienen plugin de maven para generar el WSDL a partir de la clase java o para generar las clases java a partir del WSDL.

Una cosa que he leído pero no he acabado de entender es que parece ser que hay diferencias a la hora de tratar/generar código en los parámetros de un WebMethod si estos parámetros tienen cierta complejidad, como ser colecciones. Intentaré echarle un ojo con más calma a ver.

Jun 21

JPcap vs JNetPcap

No hace mucho comentaba que me había puesto a partir paquetes IP a bajo nivel, usando JPcap. Haciéndolo encontré un pequeño bug en la librería, el campo offset de la cabecera IP no se rellenaba correctamente, por lo que tuve que hacerme mi pequeño arreglo. Una vez hecho, todos felices y contentos.

Pero me ha salido un nuevo problema. Cuando el sistema operativo recibe los fragmentos IP de un paquete grande, tiene un pequeño timeout en el que espera recibir todos esos paquetes (creo que de 1 minuto aproximadamente). Si en 1 minuto no recibe todos los fragmentos, desecha el paquete y no lo entrega a las aplicaciones. En windows ese timeout se controla ¿cómo no?, con un valor del registro, en concreto IpReassemblyTimeout. Pero mira tú que 1 minuto me resulta escaso (tenemos un canal de comunicación muy lento, casi como un antiguo RS-232 de 9600 baudios y encima compartido) y no todas las versiones de windows hacen caso de ese valor de registro (casualmente la que nosotros usamos no lo hace).

Así que me toca reconstruir también los fragmentos con JPcap. Y me pongo a ello…. y encuentro un nuevo bug. Hay un flag en la cabecera IP que indica si ese fragmento es el último o hay más. Pues bien, JPcap no rellena bien en recepción ese flag ni los otros dos flags que le acompañan. Poniendo el arreglo del enlace ya se arregla el tema de los flags. Pero me sale un nuevo problema … no recibo el último fragmento … hasta que  llega un mensaje. Es como si JPcap se quedara el último paquete recibido y no lo entregara a la aplicación hasta que reciba uno nuevo. No sé si es eso, pero el caso es que si sólo envio un gran paquete UDP fragmentado, no recibo el último fragmento y no puedo recomponer el paquete UDP.

Así que me pongo a buscar alternativas a JPcap y me encuentro con JNetPcap. Con esta librería sólo he probado la reconstrucción de paquetes, pero me ha funcionado bien a la primera (o casi). La librería es un pelín más compleja de usar que JPcap, pero viene bastante mejor documentada en su página web, con más ejemplos y lo más importante, parece que funciona mejor.

De momento no voy a migrar el envío de fragmentos, pero creo que sí voy a reconstruirlos con esta librería, afortunadamente, ambas son compatibles y una misma aplicación puede usarlas simultáneamente.

May 20

Y finalmente, haciendo un servidor de chat (III)

Al final no conseguí resolver totalmente los problemas de conexiones de mi cliente de chat. En ocasiones, sin saber muy bien el motivo, al entrar en la página web la conexión no se establecía. Y al dejar la página, google chrome capturaba bien el evento onUnload y desconectaba correctamente, pero internet explorer hacía cosas raras. Aparentemente capturaba el evento y mandaba la desconexión… pero el servidor openfire mantenía la conexión durante un minuto más. En ese tiempo, si intentaba reconectarme con el mismo navegador pero otro usuario, tenía el problema asegurado. Leí en algún sitio que internet explorer abre conexiones permanentes de justo 1 minuto de time out. Suficiente coincidencia como para pensar que mis problemas pueden ir por ahí.

Así que decidí dejar de pelearme con ello (casi una semana) y hacerme yo mismo mi servidor de Chat (sencillo y a medida) usando GWT. De paso paso aprendería a hacer la parte del servidor desde GWT y reaprovecharía el cliente de Chat que ya tenía en GWT, quitando las conexiones con jaxmpp2.

Dicho y hecho, en cuestión de una mañana tenía mi propio chat (sencillo y a medida) funcionando. Me queda pendiente revisar el tema del polling. Ahora mismo mi cliente pregunta una vez por segundo al servidor si hay mensajes nuevos que mostrar. Este mecanismo ya no se usa en los chats porque no es eficiente. En mi caso puede valer porque es un chat limitado a 10 ó 15 usuarios máximo.

Actualmente, en los chats se utiliza algo que se llama BOSH. Básicamente consiste en que el cliente pide datos al servidor y el servidor le deja bloqueada la llamada hasta que haya datos disponibles. Si pasado un tiempo (unos minutos), no hay datos disponibles, salta un timeout que el mismo cliente captura para volver a hacer la petición y quedarse colgado otra vez en espera de datos. Esto no deja de ser un polling, pero con mucho menos tráfico, ya que no es como en mi caso cada segundo, sino que de alguna forma es cada vez que salta el timeout (minutos) o cuando realmente hay datos.

También obliga a abrir dos conexiones. El cliente abre la que hemos comentado para pedir novedades, y abre una segunda cada vez que quiera enviar algo al servidor. En el caso del chat, sería para enviar un texto que ha escrito el usuario o la desconexión. El servidor no devolverá nada en estas peticiones, pero sí lo hará a través de la otra conexión, la de consulta.

GWT lleva algo "raro" parecido a eso, ya que de alguna forma el cliente pide datos pero recupera el control inmediatamente. El resultado le llega a través de un evento que provoca GWT. No he mirado con detalle el mecanismo que hay por debajo y por tanto no lo tengo claro del todo, pero lo apunto para investigar y ver si puedo hacer algo parecido al mecanismo BOSH con GWT.

Y también me gustaría repetir el ciente chat con una librería javascript reconocida (jaxmpp2 para gwt me parece un poco caótica y no sé si se usa mucho), para ver realmente el tema de conexiones y desconexiones con un servidor xmpp de verdad (openfire o ejabberd).

Apr 25

Haciendo un cliente de chat XMPP (II)

Siguiendo con el post anterior, dos días de trabajo después, creo que he conseguido solucionar todos los problemas que tenía. Debo una o más entradas en la Chuwiki contando todos los detalles ….

Las pegas, muchas y variadas. Un resumen de ellas.

Lo primero es que con la librería jaxmpp2, y supongo que con cualquier otra librería de javascript que se conecte con un servidor de chat XMPP sobre http, acaba llamando al servidor de chat con la misma URL en la que está ubicada la librería, por lo que es necesario montar en nuestro servidor apache un servidor virtual que redirija ciertas llamadas al servidor de chat. Con el servidor apache es fácil, ya que soporta en su fichero de configuración http.conf este tipo de cosas. Con un servidor de aplicaciones como Tomcat es necesario quizás montar un servlet que haga esta redirección, como JabberHTTPbind. Estas redirecciones hacen, por ejemplo, que sea muy difícil arrancar con eclipse y un servidor interno nuestra aplicación GWT.

Segundo problema, por más que probaba en google chrome, no había manera de conseguir la conexión. El problema es el de las peticiones "cross-site". Los navegadores, por seguridad (evitar el cross-site scripting) ponen restricciones a las páginas que se cargan de un lado, ejecutan scripts de otros sitios y modifican el html de la página (o algo así de retorcido). El caso es que entre que el código javascript generado por GWT está en un servidor y el Openfire es otro servidor, y encima hay una redirección de host virtual entre medias, google chrome no deja ejecutar el código javascript…. ¡¡¡¡ y ni siquiera avisa !!!!. Simplemente, no funciona y no lo ves salvo que abras el debugger de javascript que viene con el navegador. En este sentido al menos, el maligno es algo más amigable. Saca un aviso indicando el problema y dándote opción a ejecutar el código de todas formas. Afortunadamente, después de unas horas para descubrirlo, tiene fácil arreglo. Basta llamar a todos los servidores con el mismo nombre (la url del navegador, el del host virtual y el de openfire).

Y la última estupidez. Tras conseguir hacer funcionar lo básico de mi aplicación de chat, a los 30 segundos o así se desconecta ella soiita del servidor. Este problema casi me hace dejar el asunto e intentarlo de otra manera, no veía el motivo de la desconexión, sobre todo cuando aparentemente era provocado por un mensaje procedente del servidor openfire. Pero sonó la flauta. Tras una tarde de pruebas e intentar depurar el código de la librería, me doy cuenta que la primera vez que arranco openfire y mi aplicación funciona bien, es en los siguientes arranques de mi aplicación (sin rearrancar el servidor de chat openfire) cuando ocurre la desconexión a los 30 segundos. Pues bien, tirando del hilo acabo de descubrir que es por no cerrar las conexiones. Si voy arrancando la misma aplicación varias veces (o matándola y rearrancándola), las conexiones anteriores con el mismo usuario/password quedan abiertas y openfire no debe admitir dos sesiones iguales con el mismo usuario y password, por lo que las va cerrando.

Lo dicho, por fin creo tener resueltos todos los problemas (o casi, me queda ver cómo cerrar la conexión) y un mini-cliente-chat básico funcionando, para seguir desarrollando pero ya sólo los detalles. Creo que todo esto me dará para un par de artículos en la chuwiki, uno sobre los host virtuales de apache y el http-bind (nombrecito que se da a la conexión de chat xmpp sobre http) y otro sobre lo básico de la librería jaxmpp2.

Apr 22

Haciendo un cliente de chat XMPP

Por cosas del trabajo me he puesto a hacer un cliente web de chat sencillito. Básicamente necesito una sala única en la que los usuarios se conecten nada más entrar en la aplicación web y puedan chatear en común.

La primera aproximación fue instalar un servidor de chat XMPP (Openfire) y un cliente web, con la intención de "tunear" el cliente para que hiciera un auto-login en la sala en cuestión. Los clientes que probé fueron Sparkweb y jwchat. El primero está en flash, por lo que se me hace un poco imposible de tunear (no tengo ni idea de flash ni herramientas de desarrollo adecuadas). El segundo es javascript, pero tras un par de días trasteando con su código, decidí dejarlo y ver lo que me costaría hacer yo mi propio cliente a medida.

Puestos a codificar, la primera prueba en java, ya que es el lenguaje que mejor conozco. Un Applet no me convence (los usuarios tienen que tener la máquina virtual instalada), pero me puede servir para aprender un poco la mecánica de XMPP y los chats.

Buscando una librería java que me permita hacer un Applet cliente, me encuentro con smack. Aunque tuve algún problemilla inicial con la conexión, la librería me funcionó bien y está bastante bien documentada. En una tarde conseguí hacerme un Applet rápido con lo que yo quería. Por supuesto, código guarro para que funcionara y sin rematar los detalles, ya que mi objetivo era aprender y no terminar la aplicación.

Después fuí al siguiente paso, conseguir hacer el cliente de Chat, pero sin Applet. Hay bastantes librerías javascript para hacer este tipo de cliente: xmpp4js, jsjac, strophe, …), pero la programación en javascript se me hace un poco cuesta arriba. Así que dándole vueltas al asunto y buscando en google, me encuentro con xmpp4gwt. Parecía la solución a todos mis problemas. GWT me permite programar en java el cliente y luego, la magia de GWT, convertiría mi código java en javascript, lo que me evita programar directamente en javascript y evita que los usuarios tengan que tener instalada la máquina virtual. Además, me permitiría aprender un poco de GWT, del que sólo he oído hablar. Dicho y hecho, manos a la obra.

Pero no todo el monte es orégano. No he visto librería más diabólica que este xmpp4gwt.

  1. Esa página mantiene montones de proyectos asociados, servidor de chat, cliente de chat, librerías de chat, etc, etc, etc. Esos proyectos van creando variantes, cambiando de nombre e incluso de página, repositorio y de todo. Me costó casi una tarde entera encontrar dónde estaba la dichosa librería xmpp4gwt como librería. Resulta que ahora se llama jaxmpp2, sólo vienen los fuentes que debes bajarte de subversion y el único enlace que he encontrado al repositorio está en su redmine, aquí https://projects.tigase.org/projects/jaxmpp2/repository El enlace al repositorio en la página principal pide usuario y password, por lo que imagino que será otro repositorio.
  2. No hay documentación en absoluto de nada de nada, ni siquiera viene comentado el código. Lo más parecido a algo que te diga por donde empezar, está aquí http://www.tigase.org/content/jaxmpp2 (documentación abundante donde las haya). El resto de documentación, como he leído por ahí, es coger el proyecto emite (un cliente chat gwt que usa xmpp4gwt) y ver cómo lo usa.

Total, que tres días después de pelearme con encontrar la librería, montarlo todo en eclipse, descubrir cómo manejarme con GWT y mucho ensayo, prueba y error, tengo la cosa encaminada aunque todavía no he conseguido ni siquiera conectarme  a Openfire. Mi error actual creo que es que pensaba que podía conectarme directamente con esta librería al puerto 7070 de Openfire, que es el que usa para clientes http. Pero veo que parece ser necesario hacer el montaje en serio, es decir, un servidor Apache con una redirección del puerto http al 7070 de Openfire y el cliente web instalado en el servidor apache. Creo que a esto lo llaman http-binding y será mi entretenimiento durante el día de hoy.

Apr 16

Jugando con javadocx

Javadocx es una librería java que permite generar documentos docx

 La gente de javadocx se puso en contacto conmigo a través de twitter. Querían que publicara una nota de prensa que anunciaba el lanzamiento de la versión 2.0. Así que me decidí primero a probar un poco la librería e incluso hacer algún pequeño tutorial en la Chuwiki si se terciaba.

La librería se baja en forma de fuentes java que necesitamos compilar con ant. Así que me descargué también ant, ya que hace tiempo que reemplacé su uso por maven y no lo tenía instalado. El compilado fue bien a la primera, salvo por unos pequeños warnings relativos a algo de unos caracteres extraños de un idioma extraño. No sé si el warning es importante, pero lo comento más abajo.

Miro la documentación en la página web de javadocx para ver cómo empezar. La documentación me remite a los ejemplos java que vienen con la librería, así que me voy a mirarlos. La verdad es que los ejemplos son muy sencillos y al ejecutarlos se genera el docx sin problemas. Casi nada más verlos y ejecutarlos, decidí que no merecía la pena hacer un tutorial en la Chuwiki. El tutorial, de hacerlo, sería prácticamente copiar y pegar esos ejemplos.

Me llevé una pequeña sorpresa al abrir el docx generado con el word. Nada más abrirlo me salen un par de errores, pero aceptando la ventana de error, el documento se abre correctamente. Comentando el problema con la gente de javadocx, me comentan que es un caso raro que pasa con algunas versiones concretas de sistema operativo/máquina virtual java y se debe a caracteres chinos y japoneses que no reconoce correctamente. Qué casualidad, justo los warnings que me dio al compilar. La verdad es que tengo un Windows Vista muy machacado (lleva casi cuatro años instalado) y yo soy un "culo inquieto", por lo que estoy continuamente instalando y desinstalando programas, librerías, herramientas y todo lo que se me ocurre.

Lo otro que he echado de menos es una documentación con las opciones que se pueden poner en los distintos métodos. Me explico. El siguiente ejemplo que viene con la librería genera un docx con un párrafo

CreateDocx docx = new CreateDocx("docx");
String text = "Lorem ipsum dolor sit amet, consectetur adipisicing elit"
+ " sed do eiusmod tempor incididunt ut labore et dolore magna "
+ "aliqua. Ut enim ad minim veniam, quis nostrud exercitation "
+ "ullamco laboris nisi ut aliquip ex ea commodo consequat. "
+ "Duis aute irure dolor in reprehenderit in voluptate velit "
+ "esse cillum dolore eu fugiat nulla pariatur. Excepteur sint "
+ "occaecat cupidatat non proident, sunt in culpa qui officia "
+ "deserunt mollit anim id est laborum.";
HashMap paramsText = new HashMap();
paramsText.put("b", "single");
paramsText.put("font", "Arial");

docx.addText(text, paramsText);

docx.createDocx("example_text");
 

 Como se puede ver, al addText() se le pasa un paramsText que es un conjunto de parejas clave-valor. Pues bien, no he encontrado en la documentación un listado de qué claves son posibles ni sus valores posibles. Buscándolo, me puse a mirar dentro del código de la librería y he visto que prácticamente esas claves-valor son las que se escriben directamente en el docx. Eso hace pensar que esas claves-valor son propias del formato docx y no de la librería.

Me pongo a investigar el formato docx y veo que un docx no es más que un fichero zip que podemos desempaquetar con winzip, winrar o cualquiera de esas herramientas. Una vez desempaquetado, tenemos un montón de ficheros xml y efectivamente, esas parejas clave-valor forman más o menos parte de los xml.

Resumiendo, una librería muy sencilla de usar y a tener en cuenta si queremos generar desde java este tipo de documentos. La pequeña pega es que necesitaremos conocer o tener una referencia al formato docx para saber exactamente cómo hacer algo que no aparezca tal cual en los ejemplos que vienen.