lunes, 27 de agosto de 2012

Java Compresion Alternativa gzip y zip

Saludos,  recientemente teníamos la necesidad de comprimir un archivo pdf, la primera prueba de compresión la hicimos con zip con las librerías nativas de java, el archivo era de aproximadamente 90 megas y el resultado era de 40, se necesitaba comprimir mucho mas, entre las alternativas que se considero pues era rar pero dado que habría que hacerse ejecutándolo fuera de java no me agradaba mucho, buscando en internet también encontramos LZMA SDK, la documentación me pareció un poco confusa y buscando ejemplos de como usarlo me tope con este link de stackoverflow http://stackoverflow.com/questions/5481487/how-to-use-lzma-sdk-to-compress-decompress-in-java en donde indicaba una implementacion nativa del algoritmo LZMA2 en java y dicha librería resulto muy fácil de usar, el link es http://tukaani.org/xz/java.html y en la misma pregunta de stackoverflow se encuentra un ejemplo muy sencillo de su uso.

Solo tenemos que baja el jar y agregarlo a nuestro proyecto, en caso de usar maven tambien se encuentra en el repositorio central y podemos agregarlo como dependencia

<dependency>  
<groupId>org.tukaani</groupId>
<artifactId>xz</artifactId>
<version>1.0</version>
</dependency>


y en el codigo seria algo asi  (tomado de la respuesta de stackoverlfow)


FileInputStream inFile = new FileInputStream("src.tar");
FileOutputStream outfile = new FileOutputStream("src.tar.xz");

LZMA2Options options = new LZMA2Options();

options.setPreset(7); // play with this number: 6 is default but 7 works better for mid sized archives ( > 8mb)
XZOutputStream out = new XZOutputStream(outfile, options);
byte[] buf = new byte[8192];
int size;
while ((size = inFile.read(buf)) != -1)
   out.write(buf, 0, size);

out.finish();

Con la compresion xz el archivo de 90 megas se redujo casi 6 megas, el archivo como lo mencione es un pdf que contiene texto e imagenes, si fuera solo texto pues bastaria usar zip, por eso opte por esta alternativa

jueves, 3 de mayo de 2012

Internacionalización JSF 2.0

Existe varios ejemplos de internacionalización de jsf 2.0, el inconveniente de estos ejemplos sencillos es que nos dejan con un problema ya que en la mayoría de ellos solo nos muestran como hacerlo por un solo request, hay algunas formas de modificar esto, la que más me agrado es la que voy poner aqui, es sencilla y no necesitan modificar sus archivos xhtml.

Vamos a utilizar un PhaseListener de jsf 2, El codigo es el siguiente

package org.nl.example;
import java.util.Locale;
import javax.el.ELContext;
import javax.el.ValueExpression;
import javax.faces.application.Application;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

public class LifeCycleListener implements PhaseListener {

public PhaseId getPhaseId() {
    return PhaseId.ANY_PHASE;
}

public void beforePhase(PhaseEvent event) {
    System.out.println("START PHASE " + event.getPhaseId());
    FacesContext context;

    if (event.getPhaseId() == PhaseId.RENDER_RESPONSE) {
        context = event.getFacesContext();
        Application application = context.getApplication();
        ValueExpression ve = application.getExpressionFactory()
                                        .createValueExpression(context.getELContext(),
                                                #{localeManagedBean.locale}", Locale.class);
        try {
             System.out.println("setting locale in phase listener");
             Locale localeToSet = (Locale) ve.getValue(context.getELContext());
             context.getViewRoot().setLocale(localeToSet);

        } catch (Exception e) {
             System.out.println("error in getting locale");
        }
    }
}

public void afterPhase(PhaseEvent event) {
    System.out.println("END PHASE " + event.getPhaseId());
}
}

El PhaseListener debe ser colocado en faces-config.xml asi:


<lifecycle>
    <phase-listener>org.nl.example.LifeCycleListener</phase-listener>
</lifecycle>


El PhaseListener uunicamente le importa la fase de RENDER_RESPONSE que es la fase donde faces crea el contenido de respuesta, y ahi obtiene un locale a partir de un managebean, en este caso el managebean es localeManageBean el cual su alcance es de sesion, este a su vez contienen una propiedad de tipo Locale, la cual tiene almacenado la locale de nuestro idioma, tambie podemos actualizarla con una nueva Locale correspondiente al nuevo idioma en el que se presenten las paginas, el codigo es mas o menos asi:

package org.nl.example;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.event.ValueChangeEvent;

@ManagedBean(name = "localeManagedBean")
@SessionScoped
public class LocaleManagedBean implements Serializable {

 private static final long serialVersionUID = 1L;

 private String localeCode;
 private Locale locale;

 private static Map countries;
 static {
  countries = new LinkedHashMap();
  countries.put("English", Locale.ENGLISH); // label, value
  countries.put("Spanish", new Locale("es"));
 }

        public LocaleManagedBean() {
  super();
  locale = (Locale) countries.get("Spanish");
 }

 public Map getCountriesInMap() {
  return countries;
 }

 public String getLocaleCode() {
  return localeCode;
 }

 public void setLocaleCode(String localeCode) {
  this.localeCode = localeCode;
 }

 public Locale getLocale() {
  return locale;
 }

 public void setLocale(Locale locale) {
  this.locale = locale;
 }

 public void countryLocaleCodeChanged(ValueChangeEvent e) {

  String newLocaleValue = e.getNewValue().toString();

  // loop country map to compare the locale code
  for (Map.Entry entry : countries.entrySet()) {

   if (entry.getValue().toString().equals(newLocaleValue)) {
    locale = (Locale) entry.getValue();
    break;
   }
  }
 }

}

Este MeanageBean puede ser utilizado para colocar el nuevo idioma con el metodo countryLocaleCodeChange, no estoy seguro si este método debe retornar a una pagina, dado que en mi caso yo obtengo la Locale a travez de una cookie que fue colocada por spring mvc, espero me comenten si tienen que hacer alguna adecuación.

Espero les sirva les dejo algunas ligas que me sirvieron

http://www.mkyong.com/jsf2/jsf-2-internationalization-example/

http://www.java.net/node/703940

martes, 7 de febrero de 2012

Eclipselink Join Fetch - Cargar Relaciones Jpa

En el post anterior les comentaba mi problema con un ejb remoto y la entidad jpa que este me devolvía, bueno al final les mencione que si tienen que acceder a las relaciones lazy deben inicializarlas, aquí les dejo como cargar las relaciones lazy con un query hint

Creamos el query

Query query = entityManager.createQuery("select p from Person p where p.id = :id");
query.setParameter("id",  10);

Para colocar el query hint hacemos los sigueinte

query.setHint("eclipselink.join-fetch", "p.telefonos" );

Person p = query.getSingleResult();

Ahora en el objeto p sus telefonos ya estan cargados, desafortunadamente si en telefonos su objeto tiene otra relación a la que queremos acceder tendremos que inicializarla también pero seria con código o bueno no encontré una forma de hacerlo con otro queryhint, si la encuentran pues  espero me la digan.

Esto solo tenemos que hacerlo cuando invocamos desde una aplicación un ejb remoto que devuelve una entidad jpa y usamos eclipselink de lo contrario al acceder a una relación no inicializada nos lanzara una excepción que nos indica que debemos inicializarla, si trabajamos las entidades en ejb locales no es necesario inicializar las relaciones, eclipselink lo hace en cuanto se intente acceder a ella

Glassfish, Eclipselink, Ejb Remoto Problema con Entidad

La semana pasada realizando una aplicación que se comunica con otra aplicación atraves de un ejb remoto el cual le devolvía una entidad jpa me resultaba en problemas de null pointer al querer acceder a las propiedades de dicha entidad. Viendo el modo debug me di cuenta de que estaba recibiendo del ejb remoto un objeto el cual tenia todas las propiedades nulas, comentando esto con otro compañero me comento que había tenido el mismo problema y la causa era que no tenían la misma versión de la clase de ese objeto en la aplicación que invoca al ejb remoto y la aplicación que contiene a este (Nota: en este caso  no se trata de una entidad jpa).

Verificando lo anterior revise que tuviera la misma versión de la entidad en ambas aplicaciones pero aun se mantenía el error, buscando en google me encontré que el problema era provocado por eclipselink (usamos eclipselink para jpa en esta aplicación), eclipselink tiene una optimización llamada weaving, esta optimización la realiza por default al cargar las clases en el classloader, hace modificaciones del bytecode por lo que al hacer esto ya no tenia la misma versión de las clases en la aplicaciones.

Para solucionarlo existen 2 opciones una es deshabilitar el weaving de eclipselink pero esta opción nos quita las optimizaciones por lo que puede impactar en el performance de la aplicación, y la otra opción es utilizar el weaving estático con esta opción se procesan las clases de nuestras entidades para utilizarlas en las aplicaciones por lo que tendríamos el mismo bytecode en cada aplicación.

Para deshabilitar el weaving dinámico podemos colocar la siguiente propiedad en nuestro persistence.xml

< property name="eclipselink.weaving" value="false" / >

Para habilitar el weaving estático son mas pasos, el primero colocar la propiedad de eclipse link con valor estático

< property name="eclipselink.weaving" value="static" / >

El segundo paso es "compilar" las classes entidad que tenemos, esto lo podemos hacer con ant, si tiene la ultima version (1.8.2) el build.xml es diferente al que esta en la pagina de eclipse link, seria así :

<project name="Weaving-Eclipselink" default="clean" basedir=".">
    <description>
        Build file to generate static weaving
    </description>
  <!-- set global properties for this build -->
  <property name="dist"  location="dist"/>
  <property name="jarToWeaving"  value="myjpa.jar"/>

  <target name="init">
    <!-- Create the time stamp -->
    <tstamp/>
    <mkdir dir="${dist}"/>
    <!--copy file="${jarToWeaving}" todir="${dist}"/-->
    <copy todir="${dist}" overwrite="true" >
         <fileset dir=".">
             <include name="${jarToWeaving}" />
         </fileset>
    </copy>
  </target>

  <taskdef name="weave" classname="org.eclipse.persistence.tools.weaving.jpa.StaticWeaveAntTask"/>
 
  <target name="weaving" description="perform weaving" >
    <weave source="${jarToWeaving}"
           target="${dist}/${jarToWeaving}"
           persistenceinfo="./persistence">
      <classpath>
        <pathelement path="./lib/javax.persistence.jar"/>
      </classpath>
    </weave>
  </target>

  <target name="clean" description="clean up" >
    <!-- Delete the ${build} and ${dist} directory trees -->
    <delete dir="${dist}"/>
  </target>

</project>

Donde myjpa.jar es el jar que contiene las clases que deseo procesar para generar el weaving estático, se crea una carpeta dist y ahí se genera el nuevo jar con el weaving estático, otro punto importante es persisteninfo ahí le indico el archivo persistence.xml en el que se basara para generar el weaving estático, en mi caso cree la carpeta persistence al mismo nivel del build.xml y dentro de ella tengo META-INF y dentro de META-INF se encuentra mi archivo persistence.xml (eclipselink requiere la carpeta META-INF). El classpath de la tarea weaving no me funciono muy bien por lo que tuve que pasar las lib en la ejecución de ant

ant -lib ./lib/eclipselink.jar:./lib/javax.persistence.jar weaving




Una vez que tenemos el jar con el weaving estatico podemos incluir este en las aplicaciones que usan las entidades y con esto al invocar el ejb remoto obtenemos correctamente la entidad, bueno hay que tener cuidado con las relaciones lazy hay que inicializarlas en el ejb remoto si las vamos a utilizar.

Mas informacion de eclipselink weaving

http://wiki.eclipse.org/Using_EclipseLink_JPA_Extensions_%28ELUG%29#Using_EclipseLink_JPA_Weaving

jueves, 12 de mayo de 2011

Parameters in sql of JdbcCursorItemReader

Si no conocen spring batch pueden mirar los siguientes enlaces :
http://static.springsource.org/spring-batch/index.html
http://www.dosideas.com/cursos/course/view.php?id=7

Estaba realizando algunos pruebas con esta herramienta, cuando me tope con un pequeño detalle al usar JdbcCursorItemReader, esta clase es una implementacion de spring-batch para leer datos de la bd, entre sus propiedades tiene una nombrada "sql", y es en esta donde se declara el query que sera ejecutado, hasta ahi todo bien pero me surgio la inquietud de pasarle parametros a ese query buscando en google encontre algunas sugerencias pero siempre me falto algo para que funcionara el paso de parametros, bien les dejo la configuracion para poder hacerlo; en la declaración del itemReader debemos asignarle el scope de tipo step, en nuestro query colocamos el signo de ? en donde queremos meter el parámetro, después colocamos la propiedad preparedStatementSetter que es de tipo ListPreparedStatementSetter que a su vez es una lista por lo que podemos asignarle varios valores, para agregar el parámetro que se recibe al ejecutar el Job de spring usamos spring(#{...}) EL para pasar el valor del parámetro a la lista, esto se realica en tiempo de ejecucion.

<bean id="bdItemReader" class="org.springframework.batch.item.database.JdbcCursorItemReader" scope="step">
<property name="dataSource" ref="dataSource" />
<property name="sql">
<value>
<![CDATA[
select *
from foo
where propertyStr like ?
]]>
</value>
</property>
<property name="preparedStatementSetter">
<bean class="org.springframework.batch.core.resource.ListPreparedStatementSetter">
<property name="parameters">
<list>
<value>#{jobParameters[propertyStr]}</value>
</list>
</property>
</bean>
</property>
<property name="rowMapper">
<bean id="beneficiarioMapper" class="org.springframework.jdbc.core.BeanPropertyRowMapper">
<property name="mappedClass">
<value type="java.lang.Class">pgs.example.Foo</value>
</property>
</bean>
</property>
</bean>

y al lanzar el Job debemos pasar el parametro propertyStr


       JobParametersBuilder builder = new JobParametersBuilder();
        builder.addDate("StartTime", new Date());
        builder.addString("jobName", "Export");
        builder.addString("propertyStr", "condition");
        JobParameters parameters = builder.toJobParameters();

        jobLauncher.run(job, parameters);

y listo con estos, el query recibe el parámetro.

Nota: Probado con spring-batch-2.1.7

jueves, 18 de marzo de 2010

Ejemplo Message Drive Bean con Jboss 4.3

Vamos a realizar un pequeño ejemplo de Message Drive Bean en jboss este ejemplo aparece en el libro de Mastering Enterprise Java Beans 3.0 cuarta edición de Rima Patel Sriganesh, Geral Brose y Mica Silverman, y el tema se encuentra en el capitulo 7 el cual es una introducción a Message Drive Bean, el código del libro se realizo con glassfish como servidor de aplicaciones, he usado el mismo código adaptando solo algunas partes de acuerdo a la implementacion de jboss.

El código  de nuestro Message Bean es el siguiente:

package examples.messaging;

import javax.annotation.PreDestroy;
import javax.jms.*;
import javax.ejb.*;

@MessageDriven (
  activationConfig={
  @ActivationConfigProperty(propertyName="destination",
  propertyValue="topic/testTopic"),
  @ActivationConfigProperty(propertyName="destinationType",
  propertyValue="javax.jms.Topic")
  }
  )
public class LogBean implements MessageListener {

  public LogBean() {
  System.out.println("Log Bean Created");
  }

  public void onMessage(Message message) {
  if (message instanceof TextMessage) {
  try {
  TextMessage txtMessage = (TextMessage) message;
  String text = txtMessage.getText();
  System.out.println("Recived new message : " + text);
  } catch (JMSException ex) {
  ex.printStackTrace();
  }
  }
  }

  @PreDestroy
  public void remove(){
  System.out.println("Log Bean Destroyed");
  }
}

Estamos utilizando anotaciones para realizar la configuración de nuestro message bean, si no queremos usar estas anotaciones podemos usar el descriptor de despliegue (ejb-jar.xml) .

La primera anotación  declara la configuración necesaria, primero el destino de nuestros mensajes y después el tipo. En el código utiliza la configuración de jms que jboss tiene por defecto, podemos crear las nuestras y cambiarlas.

Esta clase tenemos que compilarla y empaquetarla en un jar, las dependencias se encuentran en las lib de jboss, colocamos el jar en nuestra carpeta deploy de nuestra configuración.

El código del cliente es el siguiente, es un cliente standalone :

package org.nl.examples.client;

import java.util.Properties;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.naming.Context;
import javax.naming.InitialContext;

/**
 *
 * @author usuario
 * This class is a simple client for a stateful session bean
 * To ilustrate how passivation works, configure your EJB server
 * to allow only two stateful session beans in memory (Consult your
 * vendor documentation for details on how to do this). We create
 * 3 beans in this example to see how and when beans are passivated.
 */
public class LogClient {
 
  public static final int noOfClients = 10;

  public static void main(String args[]) {
  try{

  /*Estas propiedades nos sirven para conectarnos a jboss desde nuestro cliente standalone,
si el cliente corre dentro de jboss esto no seria necesario*/
  Properties properties = new Properties();
  properties.put("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory");
  properties.put("java.naming.factory.url.pkgs","=org.jboss.naming:org.jnp.interfaces");
  properties.put("java.naming.provider.url","localhost:1099");

  Context ctx = new InitialContext(properties);
  //ctx.lookup(CountBean.class.getSimpleName()+"/remote");
  // 1: Lookup the connection factory
  TopicConnectionFactory factory = (TopicConnectionFactory) ctx.lookup("ConnectionFactory");
  // 2: Use connection factory to create a JMS connection
  TopicConnection connection = factory.createTopicConnection();
  // 3: Use connection to create a session
  TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
  // 4: Lookup destination
  Topic topic = (Topic) ctx.lookup("topic/testTopic");
  // 5: Create a message publisher
  TopicPublisher publisher = session.createPublisher(topic);
  // 6: create and publish a message
  TextMessage msg = session.createTextMessage();
  msg.setText("This is a test message.");
  publisher.send(msg);
  // finish
  publisher.close();

  System.out.println("Message published. Please check aplication server's console to see the response from MDB");

  } catch(Exception e){
  e.printStackTrace();
  }
  }

}

 Para compilarlo y ejecutarlo se necesitan las bibliotecas:
-jboss-aop.jar
-jbossall-client.jar
-javassist.jar
-trove.jar
-log4j.jar

 El libro que les mencione antes pueden descargarlo de masteringEJB3,

Problemas con charset en Spring MVC

El problema que tenia es que mi forma de spring mvc no me estaba aceptando caracteres como la ñ los acentos, y algunos otros, cuando hacia el submit de mi forma al llegar al controller los datos no llegaban correctamente. A pesar de que mi jsp tenia correctamente el charset los caracteres eran sustituidos para solucionar el problema unicamente tenemos que agregar un filtro de spring mvc, este filtro es CharacterEncodingFilter el cual se encargara de verificar que todo valla bien, la forma de acerlo es como sigue:

<filter>
  <filter-name>charsetFilter</filter-name>
  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  <init-param>
  <param-name>encoding</param-name>
  <param-value>UTF-8</param-value>
  </init-param>
 </filter>
 <filter-mapping>
  <filter-name>charsetFilter</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>

Y eso es todo con este filtro podemos reciber correctamente los caracteres de nuestra forma.