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.

viernes, 2 de octubre de 2009

Servlet para el envio de multiples reportes al cliente

Bueno en este post esta relacionado con el anterior en donde se envian los reportes al cliente para imprimirlos en su impresora, bueno pues el codigo que faltaba para la generacion de los reportes de jasper es lo que les voy a poner en este post y es lo siguiente :

/*Este metodo lo invocamos en doPost, doGet o ambos del servlet */
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Connection conection = null;
byte[] bytesReporte = null;
int in = 0;

try {
/*La clase HelpConnection devuelve la conexion jdbc para llenar el reporte*/
conection = HelpConnection.getConnection();

/*Aqui recibo unos ids que sera mi dato para llenar mi reporte,
* en mi caso tengo que llenar varios reportes de acuerdo a los id que recibo
* y de acuerdo a esos ids se generan n reportes */
String [] ids = request.getParameterValues("idusuario");

int numReportes = ids.length ;
/*Estos archivos temporales van a guardar los datos del reporte, lo queria hacer
* en memoria pero hubo algunos problemas. */
File files [] = new File [numReportes];
for(int i = 0; i < files.length; i++){
files[i] = getTempFile(i+"report","tmp");
}

String cs = File.separator;
String path = request.getSession().getServletContext().getRealPath("/");
/*La ruta de mi reporte*/
String rutaReporte = path + "reporte"+ cs + "user.jasper";
//JasperReport reporte = (JasperReport) JRLoader.loadObject(rutaReporte);
FileInputStream fileJasper = null;//new FileInputStream(rutaReporte);
JasperPrint jasperPrint = null;
HashMap paramReport = new HashMap();
JRXmlExporter exporter = new JRXmlExporter();
/*Se llenan los reportes y se guardan en los archivos temporales*/
for (int k = 0; k < numReportes; k++ ){
System.out.println("entro " + k);
fileJasper = new FileInputStream(rutaReporte);
paramReport.put("id", Integer.parseInt(ids[k]));
jasperPrint = JasperFillManager.fillReport(fileJasper, paramReport, conection);
exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
exporter.setParameter(JRExporterParameter.OUTPUT_FILE, files[k] );
exporter.exportReport();

}

/*y aqui los envio al cliente*/
OutputStream outStream = response.getOutputStream();
DataOutputStream dataOut = new DataOutputStream(outStream);
//envio el numero de archivos a enviar
dataOut.writeInt(numReportes);

FileInputStream fileStrm = null;
BufferedInputStream bufferStrm = null;
byte fileBytes[] = null;
for (int k = 0; k < numReportes; k++ ){
//envio nombre del archivo
dataOut.writeUTF(files[k].getName());
//envio tamño del archivo
dataOut.writeLong(files[k].length());
//abrimos el archivo a enviar
fileStrm = new FileInputStream(files[k]);
bufferStrm = new BufferedInputStream(fileStrm);
//Envia el archivo
while ((in = bufferStrm.read()) != -1){
dataOut.write(in);
}

outStream.flush();
fileStrm.close();
bufferStrm.close();
}
outStream.close();
dataOut.close();
outStream = null;
bufferStrm = null;
fileStrm = null;
dataOut = null;
for(int i = 0; i < files.length; i++){
files[i].delete();
}

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

El codigo funciona para 1 o n archivos, espero publicar la version del applet para un solo archivo, evidentemente eso es mas facil

Saludos