Apr 30

Composición sobre herencia, mi “pequeño” error.

En la programación orientada a objetos hay un principio que dice "preferir la composición sobre la herencia". El motivo tradicional para hacer esta afirmación es que la herencia es una dependencia fuerte entre clases. La clase hija depende fuertemente de la clase padre y la necesita para funcionar. Cualquier cambio en la clase padre puede tener efectos inesperados en cualquiera de las clases hijas y no tenemos, entonces, tanta libertad para cambiar el comportamiento de la clase padre.

Si además trabajamos en proyectos grandes con bastante gente implicada, la pega comentada puede convertirse en fuente grave de problemas. En un grupo grande es casi imposible saber si otra persona se le ha ocurrido heredar de nuestra clase o qué efectos secundarios puede tener si modificamos una clase que necesitamos modificar. Sí, todo esto se evita con un buen diseño, con test automáticos, con gente responsable…. cosas ideales que nunca se dan en proyectos grandes y que sólo se dan en unos pocos proyectos pequeños llevados a cabo por uno, dos o tres programadores buenos.

Otro problema añadido que no se comenta tanto, es la gran dificultad de leer el código cuando hay herencia, cuantos más niveles de herencias, peor. Para saber qué hace una clase, no podemos fijarnos sólo en esa clase, sino en sus clases padre. Y cuando queremos seguir un trozo de código que usa una clase, tampoco podemos fiarnos de esa clase usada, sino que tenemos que asegurarnos muy mucho de qué posibles clases hijas se están instanciando y están realmente siendo usadas ahí. Cuando el código es grande y hay muchos niveles de herencias, casi es imposible seguir el código y se hace imprescindible un buen debugger para seguirlo en tiempo de ejecución. Y arrancar un debugger en un proyecto grande, con varios ejecutables servidor que se comunican entre sí, ejecutables cliente distintos que se comunican con los servidores, puede ser realmente difícil.

Si no está muy justificada, siempre es mucho mejor la composición. Si nuestra clase A necesita la funcionalidad de una clase B, es mejor instanciarla que heredar de ella.

Por supuesto, para no perder la potencia de la orientación a objetos, si prevemos que es necesario, podemos hacer que la clase A use interfaces cuya implementación alguien le pasará desde fuera. De esta forma, la clase A podrá usar en un momento dado una clase B que implemente la interfaz, o una clase C que implemente la interfaz. Eso sí, salvo que esté muy justificado, siempre interfaces (nunca otras clase o clases abstractas) e implementación directa de las interfaces, sin varios niveles de herencias.

Comento todo esto porque hace poco hice una reflexión sobre el código que tenemos heredado de proyectos anteriores. En un momento dado, hace mucho, me dieron un grupo de gente nueva para desarrollar tres proyectos similares entre si, pero para clientes distintos y que debíamos hacer a la vez (plazos de entrega similares).

Yo, con toda mi buena intención, pensé en hacer los proyectos por capas. Las capas más básicas serían aquellas cosas comunes a todos los proyectos y según fuésemos subiendo de nivel, iríamos implementando las cosas específicas de cada proyecto. Y basé estas capas en herencia. El resultado es que una clase específica de un proyecto heredaba de una clase de la capa de abajo que a su vez heredaba de otra clase de la capa de más abajo y así sucesivamente (menos mal que java no permite herencia múltiple).

En esos proyectos todo iba bien y de hecho salieron bien dentro de lo que cabe. El grupo de desarrolladores y yo mismo nos conocíamos bien la estructura de herencias, qué se podía tocar, qué no se podía tocar y dónde había que tocar para incrementar la funcionalidad o cambiar la configuración. Basándose en las capas comunes, el desarrollo fue además bastante rápido.

Pero pasa el tiempo. Ese código se ha seguido usando para otros proyectos (las capas básicas), pero los desarrolladores han ido desapareciendo, cambiando de departamento o empresa, llevando sus propios proyectos … y los nuevos desarrolladores, incluso los más experimentados, se las ven y se las desean para ver dónde demonios se hace una cosa. Empiezan por la clase del proyecto y tienen que ir bajando por el árbol de herencias hasta llegar a clase padres que son realmente complejas. ¿Por qué son complejas? Porque tienen que ser lo suficientemente versátiles como para soportar a todos las clases hijas de todos los proyectos pasados y actuales. Esas clases abusan de tipos "Object", de Hashtables que no tienen definidas las posibles claves en ningún sitio, de paneles swing que no tienen nada porque se construyen sobre la marcha en tiempo de ejecución, etc, etc.

Resumiendo, un código que nos ayudó mucho en los tres primeros proyectos, pero que se fue convirtiendo en una pesadilla cuando los desarrolladores originales dejaron de estar disponibles y a los pocos que quedamos nos da "yuyu" de mantener, porque incluso a nosotros mismos nos resulta complejo con el paso del tiempo. Y eso sólo por las herencias, aparte de la posible complejidad de la algorítmica implicada.

Apr 27

Tu navegador dice mucho de tí…

 Me ha gustado esta entrada http://www.victorcuervo.com/2011/01/19/tu-navegador-dice-mucho-de-ti/ Según eso, escribiendo cada letra el navegador te completa con la URL que sueles usar que comience por esa letra. Ahí van las mías (las que me salto es porque no sale nada):

A : alcala.chuidiang.org

B : blog.chuidiang.com

C : cinetube.es

E : eltiempo.es

F : facebook.com

G : gettertools.com

H : http://webmail.indra.es

I : imageshack.us

J : javadocx.com

L : localhost:8080

M : micro-blog.chuidiang.org

O : openxmldeveloper.org

P : peliculasyonkis.com

T : travian.net

W : webmail.indra.es

X : xmpp.org

Y : youtube.com

Me gustaría saber a cuántos no les sale facebook en la f, o youtube en la Y…..

Por cierto, en cuanto navegas un poco los resultados cambian…. y la falta de http:// en mis URL revela que uso chrome y el que no haya puesto los enlaces con enlace "pinchable", que soy muy vago ;-)

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.

 

Apr 07

Quien mucho abarca…

La verdad es que últimamente estoy la mar de entretenido en el curro, aprendiendo muchas cosas. Pero hoy me ha dado por pensarlo y la verdad es que es un poco surrealista el tema, no me extraña que la empresas en general vayan mal y el software sale como sale. Mucho trabajo y poca gente agobiada en modo "multitarea".

El martes estuve en otro edificio de la empresa con un compañero de otro departamento programando con flex air. No tengo ni idea del tema, salvo un hola mundo que hice hace tiempo, así que yo programaba y él me iba guiando. La idea era que yo viera la facilidad de uso y rapidez de desarrollo para ver si lo usábamos o no en un proyecto.

El miércoles en Aranjuez (mi lugar de trabajo es Torrejón de Ardoz, en la otra punta de la provinicia de Madrid), en otro centro de la empresa. Allí estuve fraccionando paquetes IP a bajo nivel, con jpcap.

Y hoy jueves, en mi sitio habitual de trabajo, peleándome para enchufar una aplicación java con OpenLdap para autentificar un usuario/password. Por un lado jugando con JLDAP para el tema de la conexión y mira tú por donde, la password está encriptada con SSHA y he tenido que pelearme también para ver como encripto la password en claro usando ese algoritmo para poder verificar si es correcta o no.

Desde luego es entretenido tocar muchos temas tan variados, pero si lo pensamos fríamente, un cero para la empresa. No se puede tener a una persona (yo en este caso), saltando de un lado a otro, de un tema a otro que no tiene nada que ver. Al final, los cambios de contexto tan bruscos se pagan en falta de eficiencia y en mi caso, no me da para aprender nada con un mínimo nivel de detalle.

Apr 02

Fragmentando paquetes con JPcap

 JPcap es una librería que nos permite desde java enviar paquetes IP hechos a nuestra medida, podemos decidir todos los campos de las cabeceras IP y demás.

Usándola para enviar paquetes UDP por multicast, me encontré con una limitación. Sólo permite enviar paquetes de un máximo de 1472 bytes de datos, por lo que me he visto obligado a fragmentar el envío de datos en varios paquetes IP. Como el protocolo IP contempla el fragmentado de paquetes y como estoy usando jpcap que permite el control a bajo nivel de estos paquetes, decidí hacerlo a ese nivel.

El fragmentado IP es bastante fácil, son cuatro campos mal contados los que hay que rellenar y no demasiado complejo. Me pongo a ello, me hago mi pequeño programa para enviar arrays de bytes grandes en varios paquetes. Me hago mi pequeño programa de prueba para recibir, con un socket normalito de java, ya que se supone que la reconstrucción de paquetes se hace sola en recepción. Y todo en apariencia correcto. Mi log de enviar me dice que he enviado muchos paquetitos pequeños y el programa de prueba en recepción recibe uno solo.

Pero cual es mi sorpresa cuando reviso los datos recibidos y veo que no se parecen en nada a los enviados. Me abro un sniffer (wireshark), analizo los paquetes que envío … y sorpresa, todos tiene el campo offset a 0. Ese campo indica qué posición ocupan el grupo de bytes que se envían dentro del mensaje grande, es decir, si envío 10000 bytes en grupos de 1000, los offset son 0, 1000, 2000,…. Pues bien, todos son cero. En recepción, los bytes se machacan unos a otros y sólo se reciben los del último paquete.

Reviso mi código con debugger y el offset lo estoy rellenando correctamente. Me bajo los fuentes de jpcap (están en java) y me encuentro una sorpresa. En apariencia un error tonto. El método recibe el parámetro offset y en vez de hacer

this.offset = (short)offset;

hace

offset = (short)offset;

es decir, nada de nada. Bueno, parece fácil de arreglar. Hago mi arreglo, luego no es tan evidente por culpa del ordenamiento de bytes (tengo que darle la vuelta a los dos bytes del short para que funcione en windows) y listo, todo funcionando correctamente.

Cuando descubrí el fallo lo puse en el grupo de discusión de jpcap. Pero parece que me lo han borrado, así que la solución me la quedo para mí.

El código con todo esto y los detalles de cómo enviar paquetes, en la chuwiki.