java.lang.OutOfMemoryError: Java heap space
En Java, podemos tener este error por diversos motivos. Una mala programación o una operación muy pesada pueden agotar la memoria RAM reservada a la máquina virtual y provocar que se lance esta excepción.
Si estamos en el segundo caso, una operación muy pesada, podemos solucionarlo ampliando la memoria de la máquina virtual Java. En la invocación de la máquina virtual añadiremos los siguientes parámetros:
-Xms<tamaño minimo>
-Xmx<tamaño maximo>
El tamaño puede expresarse en bytes, siendo la unidad por defecto, en kilobytes añadiendo la letra 'k' o 'K' detrás del valor numérico, megabytes añadiendo la letra 'm' o 'M' detrás del valor numérico, o gigabytes añadiendo la letra 'g' o 'G' detrás del valor numérico. El valor que utilicemos debe ser múltiplo de dos.
Algunos ejemplos:
-Xms6291456, -Xms6144k, -Xms1500M, -Xmx83886080, -Xmx81920k, -Xmx1500M
¿Qué sucede con System.out y System.err en un Servlet?
Depende del servidor de aplicaciones.
Por lo general (en JServ o Weblogic o Tomcat) System.out va 'del lado del cliente' y se ve en el navegador, mientras que System.err va 'del lado del servidor' y es visible en los logs de error y/o en la consola.
public void doGet(HttpServletRequest req, HttpServletResponse res) {
// Dejar que el cliente espere texto plano
resp.setContentType("text/plain");// Esta línea se mostrará en el explorador
System.out.println("Hola explorador!");// Esta línea se mostrará en el log de errores o similar
System.err.println("Hola log de errores!");
}
Este comportamiento no siempre se cumple para todos los servidores de aplicaciones, por lo que no es conveniente usarlos. En vez de esto usaremos lo siguiente.
Para enviar texto al cliente (explorador web):
public void doGet(HttpServletRequest req, HttpServletResponse res) {
// Obtener el PrintWriter correcto
PrintWriter pw = resp.getWriter();// Esta línea se mostrará en el explorador
w.println("Hola explorador!");
}
Para enviar texto al log de errores del servidor:
public void doGet(HttpServletRequest req, HttpServletResponse res) {
// Obtener ServletContext
ServletContext sc = getServletContext();// Esta linea se mostrará en los logs del servidor de aplicaciones
sc.log("Hola log!");
}
Comprobar el sistema operativo y el navegador del cliente desde un JSP/Servlet
En ocasiones puede que necesitemos averiguar el sistema operativo y el navegador que está usando el usuario que accede a nuestra página JSP/Servlet. Razones por las que necesitemos descubrirlo pueden ser para seleccionar una hoja de estilo distinta para cada explorador, invitar al usuario a cambiarse a Linux, recordarle que Internet Explorer es una mierda y que usandolo perjudica al desarrollo de la web, recomentarle Firefox, no dejarle ver la página hasta que se actualice si usa Internet Explorer 6 o anterior, o cualquier otra imposición parcial y subjetiva que queramos.
Para comprobar ambos datos necesitamos leer de la cabecera de la petición el atributo User-Agent de la siguiente manera.
String userAgent = request.getHeader( "User-Agent" );
Esto nos devuelve la cadena de texto que la aplicación que usamos para navegar por la web propaga en la cabecera. Algunos ejemplos.
Firefox 3 bajo Windows: Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.9) Gecko/2008052906 Firefox/3.0
IE 7 bajo Windows: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 1.1.4322; .NET CLR 3.0.04506.648)
Ahora solo nos quedaría averiguar a partir de la cadena ambos datos. Una manera simple de detectar los principales exploradores y sistemas operativos sin tener en cuenta versiones.
String SO="Desconocido", browser="Desconocido";
//Comprobamos el sistema operativo
if(userAgent.contains("Windows"))
{
SO="Windows";
}
if(userAgent.contains("Linux"))
{
SO="Linux";
}
if(userAgent.contains("Mac"))
{
SO="Mac OS";
}//Comprobamos el explorador web
if(userAgent.contains("MSIE"))
{
browser="Internet Explorer";
}
if(userAgent.contains("Opera"))
{
browser="Opera";
}
if(userAgent.contains("Firefox"))
{
browser="Firefox";
}
if(userAgent.contains("Safari"))
{
browser="Safari";
}
Para una comprobación mas precisa puedes consultar esta base de datos de cadenas User-Agent.
Problemas de cotejamiento con Java, iText y JSP (Actualización)
Atendiendo a la documentación veamos las opciones que hay para cambiar la codificación de una String. La primera opción sería usar este constructor, como hacíamos en el ejemplo publicado anteriormente.
String(byte[] bytes, String charsetName)
Constructs a new String by decoding the specified array of bytes using the specified charset.
Según dice: "Construye una nueva String decodificando el array de bytes usando el juego de caracteres especificado". Usando esto decodificamos el array, que está ya codificado en un juego de caracteres, usando la codificación de otro juego de caracteres que escojamos. Esto es válido para los caracteres que ocupan posiciones compartidas en ambos juegos de caracteres, no para aquellos que no están incluidos en ambos que se siguen viendo mal.
Por otro lado tenemos el siguiente método.
byte[]
getBytes(String charsetName)
Encodes this String into a sequence of bytes using the named charset, storing the result into a new byte array.
"Codifica este String en una secuencia de bytes usando el juego de caracteres nombrado y guardando el resultado en un nuevo array de bytes". Es decir, este método si traduce entre juegos de caracteres (codifica los bytes según la codificación del nuevo juego de caracteres), consiguiendo con esto que todos los caracteres se vean correctamente.
Para recodificar correctamente una String a UTF-8 usaremos entonces la siguiente línea.
String recodificada = new String(vieja.getBytes("UTF-8"));
Resolviendo los problemas de cotejamiento entre Java y JSP
Hace tiempo hablaba de ciertos errores que se producían al recuperar cadenas de texto de la base de datos con los caracteres especiales. La solución que aportaba en esa ocasión ha demostrado con el tiempo no ser la mas útil, la mas sencilla y ser solo una solución parcial que no funcionará en algunos casos. Para comunicar navegador y cliente de manera correcta cuando trabajamos con JSP/Servlets con una codificación UTF-8 hay que obrar del siguiente modo.
El navegador efectúa un GET sobre el servidor.
En un petición de tipo GET, los parámetros se encuentran en la URL (query string). Para que se ejecute correctamente, los parámetros hay que codificarlos correctamente al construir la url del GET y luego que el servidor los decodifique correctamente antes de pasárselos al correspondiente Servlet.
- La codificación de los parámetros en la url se debe realizar al construir el html de cada página. Para ello se pueden utilizar el método encode de la clase java.net.URLEncoder al que se le suministra como parámetro el juego de caracteres (UTF-8)
- La decodificación de los parámetros de la URL depende del servidor y requiere que este esté correctamente configurado
El navegador efectúa un POST sobre el servidor.
La codificación de los parámetros se realiza en el navegador. Para que el navegador sepa en qué juego de caracteres tiene que codificar los parámetros del POST que envía, el html tiene que tener la siguiente cabecera, que se tiene que incluir en el jsp de incluya las cabeceras
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
Al recibir los parámetros de un POST, el JSP/servlet que se encarga de recibir la petición tiene que fijar el juego de caracteres en el que llega la petición. El juego de caracteres se fija usando el método setCharacterEncoding del objeto de tipo HTTPRequest, a través de la variable implícita de este tipo request.
request.setCharacterEncoding("UTF-8");