miércoles, 14 de enero de 2009

Mis problemas con un CLOB en JDBC y Ajax

Mi problema de esta semana ha sido con los CLOBs, no concretamente con la clase java.sql.Clob sino añadiendo el soporte de Clob a mi framework.

El primer problema
El primer problema lo he tenido con la base de datos.Para actualizar una columna con un CLOB he usado el método del interfaz java.sql.PreparedStatement:

setCharacterStream(int parameterIndex,Reader reader)

Pero al usarlo desde Tomcat 6 da el siguiente error:

java.lang.AbstractMethodError: org.apache.tomcat.dbcp.dbcp.DelegatingPreparedStatement.setCharacterStream (ILjava/io/Reader;)V

Al mirar un poco me doy cuenta que el método que estoy usando se ha añadido a Java 6. Eso no debería ser mayor problema ya que el driver que uso soporta JDBC 4.0 y además en una aplicación de escritorio si que funciona el código.

Por lo que he visto del error parece que Tomcat además de encapsular el interfaz Connection para el tema del pool de conexiones, también tiene su propia implementación del interfaz java.sql.PreparedStatement y parece que no tiene el método de Java 6 implementado aunque el driver JDBC si que lo soporte.

¿Solución?
Usar el método que estaba desde Java 1.2 al que hay que pasarle un parámetro más que es un entero con los caracteres a leer:

setCharacterStream(int parameterIndex,Reader reader,int length)

Y ahora ya funciona sin problemas.

El segundo problema
El segundo problema que he tenido ha sido con AJAX, concretamente al enviar el XML con el CLOB desde el servidor al cliente.En principio no pensaba que habría ningún problema , simplemente era enviar un String más largo dentro del XML (el CLOB) y ponerlo en un TextArea.

Pués bien, tras probarlo me doy cuanta que en alguna parte se trunca el texto. Con firebug veo el XML que mando desde el servidor y veo que está todo.Pero al llegar al TextArea faltan datos. Después de depurar un poco y ver el tamaño del texto en el TextArea veo que solo tiene 4096 caracteres.
!!Sorpresa!!!
Esto suena a limitación del firefox en el tamaño máximo del contenido de un tag XML.

<prueba>Aqui NO que se pueden poner más de 4096 bytes porque sino FF los corta</prueba>

¿Solución?
Por suerte si pones el texto usando CDATA si que funciona. Uf!!!!!.

<prueba><!--[CDATA[Aqui si que se pueden poner más de 4096 bytes.]]</prueba>


He llegado a hacer pruebas con 50.000 caracteres y no he tenido problemas.

sábado, 15 de noviembre de 2008

Tratamiento de errores en XMLHttpRequest

Tras la primera entrada de prueba en mi blog hoy a tratar un problema que hay usando FF 3.0 al retornar errores HTTP desde Java con sendError.

En el navegador usando JavaScript hacemos una llamada AJAX al servidor mediante la clase XMLHttpRequest y el método send.
Puede ocurrir que se produzca una excepción en el tratamiento del código Java del servidor. A mi me gusta en ese caso retornar un error HTTP en vez de un XML con un mensaje de error. Desde Java es tan sencillo como:


response.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR , "Ha habido un error");


Luego desde JavaScript muestro un mensaje del estilo:


if(xmlHttpRequest.status != 200) {
alert(xmlHttpRequest.status + "=" + xmlHttpRequest.statusText);
}


Hasta aquí nada que no sepamos ya todos.

El problema está en si ponéis caracteres por encima del 127, como acentos o la ñ, no se ve bien el mensaje.

Probad ahora en FF 3 con ésto:


response.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR , "El problema está en el acento");


y veréis una pantalla como la siguiente:



Como se puede apreciar ha salido un churro de texto.
Si probáis lo mismo en IE 6.0 si que se ve perfectamente:



Tras googlear un poco y no encontrar nada, me puse a hacer pruebas a ver si sonaba la flauta y al final he encontrado una solución medio buena.

Para que funcione en FF 3.0 uso el siguiente truco:


String msg=new String("El problema está en el acento".getBytes("UTF-8"));
response.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR , msg);


Es decir lo codifico yo a mano a UTF-8.
Ahora ya se muestra correctamente en FF 3.0:



El problema ahora es que en IE 6.0 ya no se ve bien:



Como apreciamos viendo los carácteres que aparecen es el típico error de cuando algo está en UTF-8 y nosotros pensamos que no. Para solucionar esto no tenemos mas que decodificar ese texto en UTF-8.Para ello podemos por ejemplo utilizar la clase Utf8 que se encuentra en http://www.webtoolkit.info/javascript-utf8.html quedando el código de la siguiente forma:


alert(xmlHttpRequest.status + "=" + Utf8.decode(xmlHttpRequest.statusText));


Y ya vemos el texto bien:



Sin embargo , ahora lo hemos vuelto a estropear en FF 3.0, así que la única solución que he encontrado es ver desde JavaScript si estamos en FF o en IE y usar el decode o no:


var msg;
if (isIE()) {
msg=Utf8.decode(xmlHttpRequest.statusText);
alert(xmlHttpRequest.status + "=" + msg);
} else {
msg=xmlHttpRequest.statusText;
alert(xmlHttpRequest.status + "=" + msg);
}


El problema ahora lo tenemos en saber si es IE o FF. Mirando un poco en google hay páginas con snippets que nos lo indican. Pero con cada versión nueva de navegador pueden dejar de funcionar, además, tampoco sabremos si en versiones posteriores de FF o IE cambiará la forma en la que funciona XMLHttpRequest.statusText, por lo que la solución que he encontrado no es muy robusta. :-(

Otra forma se programarlo es decidir desde el servidor si estamos en FF o IE y realizar el getBytes o no.


String msg="El problema está en el acento";
String realMsg;
if (isIE()==false) {
realMsg=new String(msg.getBytes("UTF-8"));
} else {
realMsg=msg;
}

response.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR , realMsg);


La ventaja de este otro método es que te evitas el hacer el Utf8.decode desde JavaScript y por lo tanto tener que añadir la clase Utf8.

Para acabar y como dije al principio todo esto se puede evitar si retornamos un XML de error en vez de retornar un error HTTP, pero eso ya depende de cada uno y de lo que necesite.

lunes, 1 de septiembre de 2008

Diseño de librerías

Hoy empiezo mi primer blog.
La entrada siguiente Sending email in Java: There's more than one way habla sobre como enviar un eMail en Java. La forma "oficial" es usando JavaMail. Lo que me ha parecido interesante de esta entrada en ver como "critican" (en el buen sentido) el diseño de JavaMail.
Lo que dicen es que JavaMail fué pensado para grandes sitios y que para usa simple aplicación que necesita enviara eMails queda un poco grande.
A mi no me parece muy enrevesado, he visto librerías mucho peores, pero si que es interesante ver como alguien da una pequeña orientación del "porqué" del diseño una librería.
Al ver el API de una librería muchas veces me hago la pregunta, ¿Por qué hacen las cosas tan difíciles? Y siempre es de agradecer una respuesta.
Al final comentan la librería Apache Commons Mail que sobre JavaMail crear un envoltorio para hacerlo más fácil.

Por último a ver si me compro el siguiente libro Practical API Design: Confessions of a Java™ Framework Architect y aprendo un poco más del tema.