lunes, 24 de octubre de 2011

Log con campos CLOB en Log4j

Recientemente he estado probando diferentes configuraciones del log4j, una de las cosas interesantes era guardar el log en base de datos. usando log4j no representa ningún problema, ya que podemos hacer uso del appender  JDBCAppender para inserciones sencillas.

El problema surge cuando queremos almacenar la traza de un error, y su tamaño por lo general tiene un tamaño considerable (muy superior a los 4000 caracteres que te permite un varchar2 de Oracle, por ejemplo). La solución obvia (en Oracle) es usar un CLOB (Character Large Object), pero actualmente Log4j no permite el uso de CLOB.

Buscando un poco por la red, encontremos algunas soluciones alternativas como la de Danko Mannhaupt, muy recomendada, la cuál permite el uso de CLOB si usamos "prepared statements". En mi caso, no planteamos usar esta solución, ya que implicaba ciertos cambios en el proyecto así como añadir librerías de terceros, etc. Además la estructura de la tabla en BBDD planteada no se ajustaba a lo que requeríamos.

En tal caso, optamos por hacerlo nosotros mismos. Tan solo es necesario heredar de la clase  JDBCAppender (la de Log4j),  y sobreescribir (override) el método execute, el cuál tiene como parámetro de entrada la SQL ya formada, por lo que sólo deberemos tratar la ristra y crear el PreparedStatement.

Veamos un ejemplo:

package com.fraguaDigital.log;

import java.io.StringReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.log4j.jdbc.JDBCAppender;

public class CLobJDBCAppender extends JDBCAppender {

 @Override
 protected void execute(String sql) throws SQLException {
  Connection con = null;
  Statement stmt = null;
  try {
   con = getConnection();

   if (sql.contains("#")){
    int initPos = sql.indexOf("#");
    int lastPos = sql.lastIndexOf("#");
    String clobData = sql.substring(initPos+1, lastPos);
    StringReader stringReader = new StringReader(clobData);

    sql = sql.substring(0,initPos) +"?"+ sql.substring(lastPos+1);
    stmt = con.prepareStatement(sql);
    ((PreparedStatement)stmt).setCharacterStream(1, stringReader, clobData.length());          
    ((PreparedStatement)stmt).execute();
   }else{
    stmt = con.createStatement();
    stmt.executeUpdate(sql);         
   }
  } catch (SQLException e) {
   if (stmt != null)
    stmt.close();
   throw e;
  }

  stmt.close();
  closeConnection(con);
 }
}

En este caso usamos el carácter "#" para marcar el comienzo y el fin del campo que es CLOB. Esto es sólo un ejemplo, pero puede generalizarse fácilmente.

Por último, tendríamos que añadir  en la configuración del log4j que use nuestro appender y definir en la cadena sql el campo CLOB usando las "#".

<appender name="LOG_BBDD" class="com.fraguaDigital.log.CLobJDBCAppender">
 <param name="Threshold" value="WARN"/>
 <param name="driver" value="${jdbc.driverClassName}" />
 <param name="URL" value="${jdbc.url}" />
 <param name="user" value="${jdbc.username}" />
 <param name="password" value="${jdbc.password}" />
 <layout class="org.apache.log4j.PatternLayout">
  <param name="ConversionPattern"
   value="INSERT INTO tabla (FECHA, PRIORIDAD, MENSAJE, CLASE, STACKTRACE, USUARIO) VALUES ('%d', '%p', '%m%n', '%C.%M(%L)', #%throwable#)" />
 </layout> 
</appender>

sábado, 21 de mayo de 2011

Anidar contenedores en Flex

     Cuando tenemos componentes anidados dentro de otros componentes a veces queremos que el componente ocupe el 100% del padre, por tanto definimos su ancho y alto al 100% (hasta ahí todo normal), para que al realizar el calculo de las dimensiones (método measure) se ajuste. Aún así, cuando usamos contenedores anidado podemos ver que siempre deja un pequeño espacio entre el borde del padre y el del contenedor hijo, por lo que creo es debido al padding, pero incluso modificado los valores del padding en el estilo (tag style o en el css) no conseguía que el contenedor hijo ocupara realmente el 100. Quedaba algo así:
     La solución que encontré a este problema es utilizar la propiedad clipContent. Establecido el valor de clipContent a true en el contenedor interior (contenedor 2) indicamos que  el contenido del hijo se puede extender fuera de sus limites, y clipContent a false en el contenedor padre (Contenedor 1), de forma que los componentes que se encuentren dentro del contenedor 2 podrán extenderse fuera de este pero no fuera del padre. Por tanto no tendremos la sensación de que los contenedores están identados unos dentro de otros. Quedando así:
Nota: El pequeño borde rojo alrededor de la segunda imagen lo he dejado para mostrar que el contenedor 1 sigue estando en el displayList.

jueves, 27 de enero de 2011

java.lang.OutOfMemoryError: PermGen

En algunas ocasiones tenemos problemas con el espacio de memoria permanente en nuestras aplicaciones Java. Durante la ejecución d cualquier método o servicio la aplicación lanza una excepción java.lang.OutOfMemoryError: PermGen. Esto sucede porque el espacio de memoria Permanente (no es el Heap que se usa  para asignar los objetos), que se usa para colocar contenido generado por la aplicación, como las clases que carga el ClassLoader o las cadenas internas (String.intern), que se usan para optimizar el manejo de las String, se llena y se queda sin espacio. El contenido cargado en este área de memoria no se libera (por defecto).

Sucede habitualmente durante los reinicios en caliente de los servidores de aplicaciones, ya que se producen fugas de memoria al reiniciar el servidor sin apagarlo, es decir como no se apaga no se libera la memoria permanente usada, y tras múltiples reinicios se llena. La solución obvia  en este caso es reiniciar.

También puede suceder por otras circunstancias, como que realmente se esté llenando el espacio de memoria permanente. La solución trivial es aumentar el tamaño de este espacio de memoria, para ello modificaremos las opciones de la Máquina Virtual de Java (JVM) usando la variable de entorno JAVA_OPTS aumentaremos el tamaño máximo con:

 -XX:MaxPermSize=256m

Esta solución puede solucionar el problema, si colocamos el tamaño adecuado, pero si aun así el espacio es insuficiente tan sólo lo retrasaremos. Para ello deberíamos saber cuánta memoria esta usando nuestra aplicación. Para saber cuánta memoria usa la aplcacion debemos añadir en JAVA_OPTS lo siguiente:

            -verbose:gc -XX:+PrintGCDetails

Con los valores anteriores indicamos a la JVM que se muestre los datos de recolector de basura y que muestre los detalles, algo así:

[Full GC [CMS: 168674K->168674K(379016K), 0.9549626 secs] 168688K->168674K(395336K), [CMS Perm : 72024K->72024K(122292K)], 0.9556050 secs]

El texto indicado tiene el siguiente patrón:

[CMS Perm : Memoria Antes(Kb)->Memoria después(Kb)(Memoria en uso(Kb))]

Con lo cuál veremos, cada vez que se ejecute el recolector de basura la memoria en uso, y sabremos que tamaño debemos poner al espacio PermGen.

Una solución mejor es permitir  el recolector de basura que elimine el contenido de la memoria PermGen que no esté en uso, hay muchas clases que se cargan y se quedan ahí sin usarlas más (esto sucede con asiduidad si usamos frameworks como Hibernate o Spring). Para ello debemos añadir a la variable JAVA_OPTS lo siguiente:

  • -XX:+UseConcMarkSweepGC --> Política del garbageCollector, para que trabaje de forma concurrente si hay múltiples procesadores.
  • -XX:+CMSPermGenSweepingEnabled --> Se habilita el barrido por el espacio de memoria permanente (PermGen).
  • -XX:+CMSClassUnloadingEnabled --> Se permite descargar clases que no se usen.


NOTA IMPORTANTE: Si estamos usando Maven para lanzar nuestro servidor de aplicaciones la variable de entrono que debemos modificar es MAVE_OPTS en lugar de JAVA_OPTS.

miércoles, 29 de diciembre de 2010

Fechas con un día menos en Flex y Java usando BlazeDS

   Cuando trabajamos con fechas en entornos con Flex y Java debemos tener cuidado de un curioso efecto, al cargar una fecha correcta en Java en Flex puede verse con un día menos.

   Este efecto se debe a como son transferidas las fechas entre un servidor Java y un cliente Flex, para ello se serializa la fecha en UTC, es decir sin información de la zona horaria. La conversión a la hora local se realiza a nivel del protocolo de forma automática, por lo tanto si el servidor no conoce la zona horaria del cliente no podrá transformarla adecuadamente. Dependiendo de la aplicación puede ser deseable almacenar/recuperar los datos en la zona horaria del cliente que los guarda, pero en la mayoría de los casos no lo es.

La solución (tal como se indica en el CookBook de Flex) consiste en forzar que la fecha se intercambie sin la información de la zona horaria. Para ello modificaremos los métodos accesores (getters y setters) de las fechas tanto del objeto java, como en el objeto en actionscript de forma que intercambiemos la fecha como el numero de milisegundos desde 1970 menos la cantidad añadida por la zona horaria (para que la trate como en GMT+0). Los objetos quedarían  así:
Java
package com.fraguaDigital;
 
import java.io.Serializable;
import java.util.Date;
import java.util.TimeZone;
 
public class MiClase implements Serializable {

    // ... Otros atributos ...
 
 private Date _fecha;
 public Date getFecha() {
  Date newDate = new Date(_fecha.getTime() - TimeZone.getDefault().getOffset(_fecha.getTime()));
  return newDate;
 }
 public void setFecha(Date value) {
  Date newDate = new Date(value.getTime() + TimeZone.getDefault().getOffset(value.getTime()));
  this._fecha = newDate;
 }
}

ActionScript (Flex)
package com.fraguaDigital
{
 [RemoteClass(alias="com.fraguaDigital.MiClase")]
 public class MiClase
 {
     // ... Otros atributos ...
  
  public var _fecha:Date;
  public function set fecha(value:Date):void {
   var newDate:Date = new Date(value.valueOf() - value.timezoneOffset);
   this._fecha = newDate;
  }
  
  public function get fecha():Date {
   var newDate:Date = new Date(_fecha.valueOf() + _fecha.timezoneOffset);
   return newDate;
  }
 }
}

Con esto ya las fechas llegarán correctamente al cliente.

sábado, 6 de noviembre de 2010

Características del sistema desde un cliente Flex/Flash

Cuando desarrollamos aplicaciones web debemos tener en cuenta que nuestra aplicación se puede utilizar desde diferentes plataformas, y por tanto tenemos que tener en cuenta las posibilidades de cada plataforma. Un ejemplo claro de está situación es la pantalla, para un dispositivo móvil puede tener resoluciones más pequeñas que para un ordenador de sobremesa, así mismo cada sistema puede tener configuradas diferentes resoluciones o profundidad de color.

Vista esta situación, cuando desarrollamos una aplicación, podemos adaptar nuestra aplicación al entorno en el que se ejecutará. Para esto, tenemos una gran ayuda en la clase flash.system.Capabilities, esta clase proporciona propiedades que describen el sistema y el runtime del host. Dicha clase nos permite conocer, entre otras cosas, lo siguiente: 
  • Tamaño de la pantalla: ancho y alto de la pantalla por searado en las variables screenResolutionX screenResolutionY respectivamente.
  • Si tenemos información de depuracion: variable isDebugger.
  • La arquitectura de la CPU: variable cpuArchitecture.
  • Si podemos usar audio, vídeo, si tenemos codecs para audio y vídeo, o si podemos reproducir streams de audio y/o vídeo.
  • Si el host puede imprimir
  • etc.
Podemos ver la lista completa de propiedades en la documentación de la clase.

Como veis, es una buena idea adaptar nuestras aplicaciones al host donde se ejecutará, ya sea la resolución, la reproducción de audio o vídeo (dependiendo de la naturaleza de la aplicación), para de esta forma conseguir una mejor experiencia de usuario.