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.


lunes, 23 de agosto de 2010

Identificador único en objetos Java

Recientemente hice una pequeña investigación sobre la mejor forma  de identificar una instancia de un objeto de forma única, de forma que podamos utilizar dicho UID (Unique identifier). Este identificador puede ser muy útil a la hora de cachear instancias , por ejemplo en un HashMap.

Documentándome un poco por la red, he leído muchas "soluciones", algunas más acertadas y otras menos, y he aquí el verdadero motivo de que escriba esta entrada.

Cualquier desarrollador con un poco de experiencia en Java sabrá que esto se resuelve usualmente mediante el método hashCode, el cuál genera un código de hash para un objeto, que nos permite indexarlo fácilmente. Pero, ¿que pasa cuando se han sobrecargado dicho método generando un override del objeto que cambia su funcionalidad?. Este escenario es bastante habitual, ya que se suelen generar los métodos equals y hashCode, para poder comparar objetos y saber a igualdad de datos si se trata del mismo objeto.

Una cosa que ha llamado mi atención en particular es que en muchos sitios proponen usar el método toString, para obtener a un identificador único, siempre y cuando no se halla sobreescrito dicho método en las clases. Esta suposición no es errónea per se,  pero resulta curiosa la forma de usar dicho método, y las limitaciones que conlleva (ya que no podremos modificar el método toString). Este método se basa en la implementación del método toString en la clase java.lang.Object, el cuál concatena el nombre de la clase con el hashcode y no con la dirección de memoria como cree mucha gente. El formato de la String devuelta  puede verse en la documentación de Java):

public String toString() {
   return getClass().getName() + '@' + Integer.toHexString(hashCode());
 }

Si esto es así, dicha solución tampoco funcionará en todos los casos, si hemos hecho un override del método hashCode basándonos en algunos datos, o cuando menos no podemos decir que la "resistencia a colisiones" sea alta.

En estas situaciones lo mejor sería utilizar el método hashCode original de la clase Object, el cual asegura que dos instancias distintas, aún cuando tengan los mismo datos, devolverán un hashCode diferente. Esto se debe a que el calculo de los datos se hace de forma nativa en base a la dirección de memoria donde se encuentra la instancia:

public int hashCode() {
   return VMMemoryManager.getIdentityHashCode(this);
 }

Para acceder al método de forma independiente de si lo hemos sobreescrito podemos usar la clase System, llamando al metodo identityHashCode.

System.identityHashCode(object);

Otra opción un poco más "delicada" es usar la clase sun.misc.Unsafe, para obtener la dirección real de memoria, podemos leer un poco más sobre ello aquí. esto es algo totalmente irrecomendable, que tiene cabida sólo como investigación para aquellos que gusten de saber como funcionan las cosas (Para que la llamaron Unsafe si no querían que trasteáramos con ella. XD).