martes 14 de julio de 2009

Bordes de Celdas con Jasperreport 3

En este pequeño post les dare un tip de como ver los bordes de las celdas cuando generamos un reporte en excel con jasperreport, este es el trozo de codigo donde les explicare las propiedades,

String reportSource = "report.jasper";

 HashMap parameters= new HashMap();

//Esta propiedad es para que  todo el reporte se genere en sin espacios entre paginas de una misma hoja
  parameters.put(JRParameter.IS_IGNORE_PAGINATION, true);
  JasperReport jasperReport = (JasperReport) JRLoader.loadObject(reportSource);
  JasperPrint jasperPrint =
  JasperFillManager.fillReport(jasperReport, parameters, connection);

//Aqui generamos  la clase para generar el reporte a excel
  JExcelApiExporter exporter = new JExcelApiExporter();

//le pasamos algunos parametros
  exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
  exporter.setParameter(JRExporterParameter.OUTPUT_FILE_NAME, tempFile);

//este parametro es para decirle que todo el reporte se genera en una sola  hoja de excel
  exporter.setParameter(JRXlsExporterParameter.IS_ONE_PAGE_PER_SHEET, false);

//con este le decimos que  detecte el tipo de celda.
  exporter.setParameter(JRXlsExporterParameter.IS_DETECT_CELL_TYPE, true);

//El siguiente es para decirle que no ignore los bordes de las celdas, y el ultimo es para que no use un fondo blanco, con estos dos parametros jasperreport genera la hoja de excel con los bordes de las celdas.

  exporter.setParameter(JRXlsExporterParameter.IS_IGNORE_CELL_BORDER, false);
  exporter.setParameter(JRXlsExporterParameter.IS_WHITE_PAGE_BACKGROUND, false);


  exporter.exportReport();

Yo uso jasperreport 3, suerte !! 

jueves 9 de julio de 2009

Jboss Cache 1.4 y Hibernate 3.2

En este pequeño post veremos como configurar Jboss Cache 1.4.x (esta version viene por defecto en jboss 4.2.x ) como cache de segundo nivel para hibernate 3.2.x (yo probe con la version 3.2.5 de hibernate), esto puede parecer un poco complicado pero si siguen los siguientes pasos veran que en realidad es facil.

En este link podemos leer acerca de configurar jboss cache y recomienda usar jboss cache con hibernate si estamos usando entidades de hibernate en aplicaciones stand alone o si no usamos ejb-entity ya que jboss ya esta configurado para optimizar la cache en los ejb.

Algunos se preguntara para que sirve la cache de segundo nivel en hibernate, esta cache nos sirve basicamente para cargar datos en ella y de esta manera no tenemos que consultar de nuevo la base de datos, con lo que reducimos los tiempos de respuesta, y porque usar jboss-cache y no otra ?? Porque esta cache funciona en clusters, si nuestra aplicacion va a correr en cluster es mejor usar esta cache ya que la replicacion y todo lo que tiene que ver con cluster es transparente para nosotros (en este link pueden ver una tabla comparativa).

Requerimientos :

  • Jboss As 4.2.x (descarga)
  • Hibernate 3.2.x (descarga)
  • Hibernate cache provider (hibernate-jbc-cacheprovider-1.0.1.GA.jar descarga)

Configuracion del Servicio de Jboss Cache

Primero necesitamos tener un servicio de cache en jboss, les recomiendo usar la instancia all ya que ahi tiene todas las bibliotecas necesarias (prometo investigar los libs necesarios para el uso de jboss cache para poder usarla en el instancia default), en la carpeta deploy donde colocamos nuestros wars y datasource y algunas otras cosas crearemos un archivo con el nombre : jboss-cache-service.xml y en el pegaran lo siguiente (esta configuracion la tome de este ejemplo ) :

<?xml version="1.0" encoding="UTF-8"?>

<!-- ===================================================================== -->
<!-- -->
<!-- Sample TreeCache Service Configuration -->
<!-- Recommended for use as Hibernate's 2nd Level Cache -->
<!-- For use with JBossCache >= 1.3.0 ONLY!!! -->
<!-- -->
<!-- ===================================================================== -->

<server>

<!--classpath codebase="./lib" archives="jboss-cache.jar, jgroups.jar"/-->

<!-- ==================================================================== -->
<!-- Defines TreeCache configuration -->
<!-- ==================================================================== -->

<!--mbean code="org.jboss.cache.TreeCache"
name="jboss.cache:service=HibernateTreeCache"-->
<mbean code="org.jboss.cache.TreeCache"
name="portal:service=HibernateTreeCache,type=hibernate">

<depends>jboss:service=Naming</depends>
<depends>jboss:service=TransactionManager</depends>

<!--
Configure the TransactionManager
-->
<attribute name="TransactionManagerLookupClass">org.jboss.cache.GenericTransactionManagerLookup</attribute>


<!--
Node locking scheme:
OPTIMISTIC
PESSIMISTIC (default)
-->
<attribute name="NodeLockingScheme">OPTIMISTIC</attribute>

<!--
Note that this attribute is IGNORED if your NodeLockingScheme above is OPTIMISTIC.

Isolation level : SERIALIZABLE
REPEATABLE_READ (default)
READ_COMMITTED
READ_UNCOMMITTED
NONE
-->
<attribute name="IsolationLevel">REPEATABLE_READ</attribute>

<!--
Valid modes are LOCAL
REPL_ASYNC
REPL_SYNC
INVALIDATION_ASYNC
INVALIDATION_SYNC
-->

<!-- This should ideally be set to INVALIDATION_ASYNC but due to JBCACHE-806 this has to be REPL_ASYNC for now -->
<attribute name="CacheMode">REPL_ASYNC</attribute>

<!--
Just used for async repl: use a replication queue
-->
<attribute name="UseReplQueue">false</attribute>

<!--
Replication interval for replication queue (in ms)
-->
<attribute name="ReplQueueInterval">0</attribute>

<!--
Max number of elements which trigger replication
-->
<attribute name="ReplQueueMaxElements">0</attribute>

<!-- Name of cluster. Needs to be the same for all clusters, in order
to find each other
-->
<attribute name="ClusterName">TreeCache-Cluster</attribute>

<!-- JGroups protocol stack properties. Can also be a URL,
e.g. file:/home/bela/default.xml
<attribute name="ClusterProperties"></attribute>
-->

<attribute name="ClusterConfig">
<config>
<!-- UDP: if you have a multihomed machine,
set the bind_addr attribute to the appropriate NIC IP address -->
<!-- UDP: On Windows machines, because of the media sense feature
being broken with multicast (even after disabling media sense)
set the loopback attribute to true -->
<UDP mcast_addr="228.1.2.3" mcast_port="48866"
ip_ttl="64" ip_mcast="true"
mcast_send_buf_size="150000" mcast_recv_buf_size="80000"
ucast_send_buf_size="150000" ucast_recv_buf_size="80000"
loopback="false"/>
<PING timeout="2000" num_initial_members="3"
up_thread="false" down_thread="false"/>
<MERGE2 min_interval="10000" max_interval="20000"/>
<!-- <FD shun="true" up_thread="true" down_thread="true" />-->
<FD_SOCK/>
<VERIFY_SUSPECT timeout="1500"
up_thread="false" down_thread="false"/>
<pbcast.NAKACK gc_lag="50" retransmit_timeout="600,1200,2400,4800"
max_xmit_size="8192" up_thread="false" down_thread="false"/>
<UNICAST timeout="600,1200,2400" window_size="100" min_threshold="10"
down_thread="false"/>
<pbcast.STABLE desired_avg_gossip="20000"
up_thread="false" down_thread="false"/>
<FRAG frag_size="8192"
down_thread="false" up_thread="false"/>
<pbcast.GMS join_timeout="5000" join_retry_timeout="2000"
shun="true" print_local_addr="true"/>
<pbcast.STATE_TRANSFER up_thread="true" down_thread="true"/>
</config>
</attribute>

<!--
Whether or not to fetch state on joining a cluster
NOTE this used to be called FetchStateOnStartup and has been renamed to be more descriptive.
-->
<attribute name="FetchInMemoryState">false</attribute>

<!--
Number of milliseconds to wait until all responses for a
synchronous call have been received.
-->
<attribute name="SyncReplTimeout">20000</attribute>

<!-- Max number of milliseconds to wait for a lock acquisition -->
<attribute name="LockAcquisitionTimeout">15000</attribute>

<!--
The max amount of time (in milliseconds) we wait until the
initial state (ie. the contents of the cache) are retrieved from
existing members in a clustered environment
-->
<attribute name="InitialStateRetrievalTimeout">20000</attribute>

<!-- Name of the eviction policy class. -->
<attribute name="EvictionPolicyClass"></attribute>

<!--
Indicate whether to use region based marshalling or not. Set this to true if you are running under a scoped
class loader, e.g., inside an application server. Default is "false".
-->
<attribute name="UseRegionBasedMarshalling">false</attribute>

</mbean>
</server>

con esta configuracion ya tenemos un servicio de cache que puede ser utilizado por cualquiera de nuestras aplicaciones pero en este caso la usaremos como una cache de segundo nivel para hibernate,fijense bien en el nombre (portal:service=HibernateTreeCache,type=hibernate) pues con este nombre debemos configuralo en nuestra configuracion de hibernate pues si lo ponemos diferente no encontrara el servicio y nos marcara error. Cuando arrancamos jboss en la jmx-console al final podemos ver este servicio

Configuracion de Hibernate

Ahora que ya tenemos la configuracion de nuestra cache, lo siguiente es configurar hibernate para habilitar la segunda cache y decirle que tipo de cache y el servicio. Para eso colocaremos lo siquiente en el archivo hibernate.cfg.xml :

<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.datasource">java:/MySqlDS</property>

<property name="hibernate.cache.use_second_level_cache">true</property>
<!-- Provider is JBoss Cache with Optimistic locking -->
<property name="cache.provider_class">org.hibernate.cache.OptimisticTreeCacheProvider</property>
<property name="hibernate.cache.provider_class">
org.jboss.hibernate.jbc.cacheprovider.JmxBoundTreeCacheProvider
</property>
<property name="hibernate.treecache.mbean.object_name">
portal:service=HibernateTreeCache,type=hibernate
</property>

<!-- Transaction strategy configuration-->
  <property name="hibernate.transaction.factory_class">
  org.hibernate.transaction.JTATransactionFactory  </property>name="transaction.manager_lookup_class">org.hibernate.transaction.JBossTransactionManagerLookup</property>

Como observan en mi configuracion le digo a hibernate que usare MySQL y le paso un datasource que configure en jboss (mas abajo les pondre esa configuracion), despues le digo que habilite la cache de segundo nivel, posteriormente le indico la clase que me va a proveer el acceso a la cache y por ultimo es el nombre del servicio de jboss donde configure la cache.

En la penultima propiedad donde tiene el comentario "Transaction strategy configuration" es para decirle a hibernate la estrategia de transaccion que debe usar, si no colocamos esta propiedad hibernate utilizar por default : org.hibernate.transaction.JDBCTransactionFactory y esta estrategia no es adecuada para usar con la cache, ya que nuestro servidor de aplicaciones puede encargarse de esto entonces le decimos que lo haga colocando la propiedad con el valor  org.hibernate.transaction.JTATransactionFactory para decirle que delegue el manejo de las transacciones al servidor de aplicaciones.

En la ultima propiedad le indico la clase que administra las transacciones, como usamos jboss, pues le indicamos que use la clase org.hibernate.transaction.JBossTransactionManagerLookup, ya que es la adecuada a nuestro application server.

Al comienzo del post les menciono la biblioteca hibernate-jbc-cacheprovider-1.0.1.GA.jar, este jar lo debemos agregar a las bibliotecas de nuestra apliacion es decir en el directorio Myapp/WEB-INF/lib, ya que este jar contiene las clases que permiten a hibernate usar la cache de jboss.

Configuracion Mpping Clases

Ya que hemos configurado hibernate para usar la segunda cache solo nos resta indicar que clases seran almacenadas en la cache y para eso unicamente tenemos que añadir en nuestros archivos de mapeo la siguiente linea :

<cache usage="nonstrict-read-write"></cache>

Para usage tenemos las siguientes opciones que son las que definen el nivel de aislamiento:

  1. transactional: Garantiza un nivel de aislamiento hasta repeatable read. Es el nivel más estricto. Solamente se puede utilizar en clusters, es decir, con cachés distribuidas.
  2. read-write: Mantiene un aislamiento hasta el nivel de commited.
  3. nonstrict read-write: No ofrece garantía de consistencia entre el caché y la base de datos.Es una estrategia ideal para almacenar datos que no cambian habitualmente y que no sean demasiado críticos.
  4. read-only: Es la estrategia de concurrencia menos estricta. Recomendada para datos que nunca cambian

Por ejemplo :

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 7/07/2009 12:43:46 PM by Hibernate Tools 3.2.1.GA -->
<hibernate-mapping>
<class catalog="students" name="org.nl.pojos.People" table="people">
<cache usage="read-only"></cache>
<id name="id" type="java.lang.Integer">
<column name="id"/>
<generator class="identity"/>
</id>
<property name="name" type="string">
<column length="65535" name="name"/>
</property>
<property name="companyId" type="string">
<column length="65535" name="company_id"/>
</property>
</class>
</hibernate-mapping>

Appendice :

Datasources de MySql debe ser colocado en la carpeta deploy de nuestra instancia de jboss

<?xml version="1.0" encoding="UTF-8"?>

<!-- $Id: mysql-ds.xml 63175 2007-05-21 16:26:06Z rrajesh $ -->
<!-- Datasource config for MySQL using 3.0.9 available from:
http://www.mysql.com/downloads/api-jdbc-stable.html
-->

<datasources>
<local-tx-datasource>
<jndi-name>MySqlDS</jndi-name>
<connection-url>jdbc:mysql://localhost:3306/students</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>root</user-name>
<!--password>y</password-->
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
<!-- should only be used on drivers after 3.22.1 with "ping" support
<valid-connection-checker-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLValidConnectionChecker</valid-connection-checker-class-name>
-->
<!-- sql to call when connection is created
<new-connection-sql>some arbitrary sql</new-connection-sql>
-->
<!-- sql to call on an existing pooled connection when it is obtained from pool - MySQLValidConnectionChecker is preferred for newer drivers
<check-valid-connection-sql>some arbitrary sql</check-valid-connection-sql>
-->

<!-- corresponding type-mapping in the standardjbosscmp-jdbc.xml -->
<metadata>
<type-mapping>mySQL</type-mapping>
</metadata>
</local-tx-datasource>
</datasources>


Si alguien quiere mi proyecto de prueba escribame un comentario con su mail y se los envio

Referencias :

http://federicovarela.blogspot.com/2007/12/cache-en-hibernate.html

http://www.jboss.org/community/wiki/JBossCacheHibernate

http://www.jboss.org/community/wiki/NewJBossCache14xBasedHibernate32CacheProvider

http://www.jboss.org/community/wiki/JBossCacheHibernateTransactionsStandaloneExample

lunes 25 de mayo de 2009

Invocar metodos de una clase externa desde una clase Interna en Java (anidada)

En este pequeño post veremos como podemos invocar los métodos de una clase que contiene una clase anidada con nombres de metodos y miembros(varibles)  iguales, ya que las clases anidadas pueden acceder a cualquier método o miembro de la clase que la contiene lo único que nesecitamos para invocar metodos o miembros de la clase externa es usar el nombre de la varibale o metodo como si la clase interna fuera un metodo de la clase externa, pero cuando los metodos o miembros tienen los mismos nombres ocurre un problema pues se invocan los que estan definidos en la clase interna, entonces tenemos que tener cuidado con esto.

Para poder acceder a los miembros y métodos de la clase externa que tienen nombre igual al de la clase interna tenemos que colocar el nombre de la clase externa, despues un punto, despues la palabra reservada this y por ultimo el nombre de la variable o el método que deseamos invocar o acceder, por ejemplo :

         NombreClaseExterna.this.variableUno

ó

         NombreClaseExterna.this.miembroUno()

Bien les pongo un Ejemplo:

/**
 * Definimos una clase externa con una variable nombre
 * y definimos el get y set correspondiente
 */
public class ClaseExterna {
  
  private String nombre;

  public ClaseExterna() {
  }

  public String getNombre() {
  return nombre;
  }

  public void setNombre(String nombre) {
  this.nombre = nombre;
  }
  /*
  * Aqui definimos la clase interna, la cual contiene
  * una variable nombre, un metodo para obtener la variable
  * nombre y dos metodos que imprimen un mensaje
  */
  class ClaseInterna {

  private String nombre;

  /* Constructor: Inicializa la variable nomnbre*/
  ClaseInterna(){
  nombre = "clase interna";
  }

  public String getNombre() {
  return nombre;
  }
   
  /* Aqui invocamos el miembro y el metodo de la clase interna*/
  public void hola(){
  System.out.println("Hola : " + nombre);
  System.out.println("Hola : " + getNombre());  
  }
   
  /* Aqui se invoca el miebro y el metodo de la clase externa*/
  public void holaDos(){
  System.out.println("Hola : " + ClaseExterna.this.nombre);
  System.out.println("Hola : " + ClaseExterna.this.getNombre());
  }

  }
  
  /*
  * Aqui probamos las clases, primero creamos una instancia
  * de la clase externa, despues inicializamos su variable nombre
  * posteriormente creamos una instancia de las clase interna,
  * y con el objeto que creamos de la clae interna invocamos sus
  * metodos.
  */
  public static void main(String args []){
  ClaseExterna ce = new ClaseExterna();
  ce.setNombre("Mundo");
  ClaseExterna.ClaseInterna ci = ce.new ClaseInterna();
   
  ci.hola();
  ci.holaDos();
   
  }
  
}

La salida del programa es :

Hola : clase internaHola : clase internaHola : Mundo
Hola : Mundo

Cualquier duda escriban un comentario ...

martes 21 de abril de 2009

Obtener Beans del IoC de Spring en JSP's

Bien en un modulo que estoy realizando unos cambios me encontre con la sorpresa de que se estaban instanciando Daos (data acces object) dentro del jsp y esto pues es un pequeño problema de performance pues cada vez que se presenta el jsp se crea un objeto.

Lo primero que hice fue declarar los Daos asi:

<%!DaoCatalogo daoCatalogo = new DaoCatalogo();%>

Con lo anterior aseguro que solo habrá una instancia del objeto para cada peticion al jsp, pero ya que estamos usando spring porque no mejor obtener los objetos del contenedor de spring (IoC).

Este pequeño detalle no se hubiera dado si el desarrollador anterior hubiera ocupado bien el springMVC, pero bueno lo que hice fue una clase estatica con un metodo estatico al cual le paso el nombre del bean definido en nuestro applicationContext y el ServletContext para poder obtener el objeto la clase es la siguiente

package com.example.nl;

import javax.servlet.ServletContext;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

public class SpringLocateBean {

public static Object getBean (String nameBean, ServletContext sc){

Object objBean = null;
ApplicationContext actx = WebApplicationContextUtils.
getWebApplicationContext(sc);
objBean = actx.getBean(nameBean);
return objBean;
}

}

Como se observa en la clase lo que hace es obtener el ApplicationContext con la ayuda de la clase WebApplicationContextUtils con el metodo getWebApplicationContext(ServletContext) al cual se le pase el contexto de nuestro servlet (jsp), una vez obtenido el ApplicationContext obtenemos el bean (objeto) que queremos a travez del nombre que le pasamos al metodo getBean(String), este metodo nos devuelve el bean que queremos pero en tipo Object por lo que en el jsp tenemos que hacer un cast al tipo de objeto adecuado

En el jsp tenemos lo siguiente :

<%@ page import="com.example.nl.SpringLocateBean"%>
<%!
DaoCatalogo daoCatalogo;
public void jspInit() {
daoCatalogo = (DaoCatalogo) SpringLocateBean.
getBean ("daoCatalogo", getServletConfig().getServletContext());
}
%>

Como vemos en el jsp importamos nuestra clase que obtiene los objetos de spring, despues declaramos la variable DaoCatalogo y posteriorme un metodo jspInit() en el cual se inicializa el objeto con nuestra clase SpringLocateBean ejecutando el metodo getBean y pasando el nombre del bean (ojo este nombre esta definido en el applicationContext.xml ) y el contexto del jsp

Nota: el metodo jspInit() se ocupa cuando ocupamos alguna libreria de etiquetas (custom tags) como por ejemplo JSTL, si no utilizamos ninguna en nuestro jsp podemos usar el metodo _jspInit(), esto si usas algun servidor basado en tomcat o tomcat en otros no he probado


Bueno esto les puede servir si necesitan incorporar Spring a una aplicacion web con servltes y jsp pero sin usar springMVC.

lunes 13 de abril de 2009

Problema texto largo en jasperreports y linux

Les cuento que en la aplicacion que desarrollo tengo unos reportes y usamos jasperreports3.0 para generar archivos pdf, bien teniamos un problema con estos reportes en un campo de texto que podia ser corto o muy largo, el problema es que a veces el texto se cortaba, en windows funcionaba muy bien, todo el texto se mostraba perfecto, pero en linux no, entonces escribi a jasperreport.org y me contestaro que se debia a un problema con las fuentes, en este campo la fuente definida es Arial y en windows esta fuente trabaja muy bien pues es nativa de este sisteama operativo pero en linux no la tiene ya que es una fuente propietaria, y bueno para esto installe la fuente Arial en linux y el problema se soluciono.

Para saber mas acerca de este bug pueden leer en este FAQ de jasper, en la parte donde dice:

Why is my text not displayed correctly in PDF?

Para instalar la fuente arial segui los siguientes pasos (el servidor linux que usamos es un red hat enterprise 5):

1.- Crear una carpeta en la ruta /usr/share/fonts
mkdir windowsfonts

2.- Copiar las fuentes Arial de la carpeta c:\WINDOWS\fonts a la carpeta creada en el punto 1 (windowsfonts)

3.- Cambiar el propietario y los permisos de las fuentes que se copiaron
Ejecutar :
cd /usr/share/fonts/windowsfonts
chown root.root *.tff
chmod 644 *.ttf

4.- Ejecutar el commando mkfontdir dentro de la carpeta que contiene las fuentes (windowsfonts)

5.- Cambiar al directorio padre (fonts), ejecutando cd ..

6.- Ejecutar el comando fc-cache windowsfonts

Para mas referencias sobre installar la fuente arial aqui estan estos links:

http://linuxhelp.blogspot.com/2005/12/adding-windows-fonts-in-linux.html

Este es de redhat
http://www.redhat.com/docs/en-US/Red_Hat_Enterprise_Linux/5.2/html/Deployment_Guide/s3-x-fonts-fontconfig-add.html


miércoles 25 de marzo de 2009

Substance, JComboBox, Problema al colocar una fuente en JComboBox

En cierta aplicacion de swing me asignaron implementar el evento enter del teclado para que al dar enter se cambiara de componente en componente de acuerdo a un orden establecido, bien esto lo solucione con keyListeners en los respectivos objetos que se despliegan en la pantalla, pero con el JComboBox tuvimos algunos problemas.
Para empezar un JComboBox esta compuesto por otros objetos (una lista desplegable y una caja de texto), cuando queria agregar el keyLystener lo hacia directo a mi objeto JComboBox y cuando lo ejecutaba pues no se realizaba la accion del enter ya que no estaba dando enter al JComboBox sino a su campo de texto, entonces para solucionar esto añadi el listener a el campo de texto, asi:

JTextComponent editor;
//myJcomboBox es el objeto JComboBox
editor = (JTextComponent) myJcomboBox.getEditor().getEditorComponent();
final ComboBoxEditor cmboxEditor = this.getEditor();
editor.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
System.out.println("KeyAdapter keypressed " + e.getKeyCode() );
}
});

como la aplicacion usa substance para hacer mas amigable la intefaz, cuando la aplicacion despliega algunos JComboBox les coloca un tipo de fuente y al hacer eso y estar utilizando substance podemos decir que se rehacia ese objeto por lo cual los listeners que le colocaba desaparecian, bien para solucionar esto debemos añadir un PropertyChangeListener a el JComboBox :

JComboBox myComboBox = new MyComboBox();
//al colocar la fuente los listeners se pierden
myComboBox.setFont(new Font("Arial",Font.BOLD,12));
//Se coloca final para poder utilizar esta variable dentro de la clase anonima
final JComboBox mycmb = myComboBox;
//Se agrega un PropertyChangeListener para añadir el listener del teclado
myComboBox.addPropertyChangeListener( new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
String property = propertyChangeEvent.getPropertyName();
System.out.println("En property changer " + property);
if ("editor".equals(property)) {
// Si queremos obtener la propiedad editor en este punto
// Obtenemos un nullpointer exception
System.out.println("Entra en property changer " + property);
}
if ("UI".equals(property)) {
System.out.println("Entra en property changer " + property);
System.out.println(mycmb);
//aqui es donde se agrega el listener del teclado
mycmb.getEditor().getEditorComponent()
.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
System.out.println("KeyAdapter keypressed " + e.getKeyCode() );
}
});
}
}
} );

Y asi los listeners que le agrego al combobox ya no se pierden y esta resuelto el problema

Les dejo el codigo que estuve probando pero necesitaran substance aqui esta su link https://substance.dev.java.net/

Y el codigo es el siguiente :

import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;import javax.swing.*;
import javax.swing.text.JTextComponent;

public class ExampleJComboBox extends JFrame {
MyComboBox myComboBox;
public ExampleJComboBox() {
String[] names = { "hemanth", "Shawn", "Hunter", "Undertaker", "Big Show" }; myComboBox = new MyComboBox();
myComboBox.setFont(new Font("Arial",Font.BOLD,12));
getContentPane().add(myComboBox, BorderLayout.NORTH);

JButton jbnOk = new JButton("Ok");
getContentPane().add(jbnOk, BorderLayout.SOUTH);
JTextComponent editor;
editor = (JTextComponent) myComboBox.getEditor().getEditorComponent();
final JTextComponent jedit = editor;
editor.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
System.out.println("KeyAdapter keypressed " + e.getKeyCode() ); } });
// Print Name of the Selected Combo Box Item to Console when OK button is pressed
jbnOk.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println( myComboBox.getSelectedItem() ); }
});
// Print Name of the Selected Combo Box Item to Console when Enter is pressed myComboBox.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println( myComboBox.getSelectedItem() );
}
});
final MyComboBox mycmb = myComboBox;
myComboBox.addPropertyChangeListener( new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
String property = propertyChangeEvent.getPropertyName();
System.out.println("En property changer " + property);
if ("editor".equals(property)) {
System.out.println("Entra en property changer " + property);
System.out.println(mycmb);
}
if ("UI".equals(property)) {
System.out.println("Entra en property changer " + property);
System.out.println(mycmb);
mycmb.getEditor().getEditorComponent()
.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
System.out.println("KeyAdapter keypressed " + e.getKeyCode() );
}
});
}
}
}
);
}
class MyComboBox extends JComboBox {
public MyComboBox(){
super( new Object[] {"Ester", "Jordi", "Jordina", "Jorge", "Sergi"});
// has to be editable
this.setEditable(true);
// JTextComponent editor;
// editor = (JTextComponent) this.getEditor().getEditorComponent();
// final ComboBoxEditor cmboxEditor = this.getEditor();
// editor.addKeyListener(new KeyAdapter() {
// public void keyPressed(KeyEvent e) {
// System.out.println("KeyAdapter keypressed " + e.getKeyCode() );
// }
// });
}
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel("org.jvnet.substance.skin.SubstanceMistAquaLookAndFeel");
} catch (UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
} catch (IllegalAccessException ex) {
ex.printStackTrace();
} catch (InstantiationException ex) {
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
ExampleJComboBox frame = new ExampleJComboBox();
frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
frame.pack(); frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
});
}
}

Nota: no olviden agregar substance.jar a su classpath

martes 24 de febrero de 2009

Hola mundo Flex compilado con Ant

En este post vamos a ver como hacer un hola mundo en flex y construirlo con ant, esto les puede servir si no pueden obtener el flex builder o el pluguin para eclipse, pues de esta manera podriamos construir nuestros proyectos sin embargo sera un poco mas de trabajo.

Lo que necesitamos :

Que es flex ?

Flex es un conjunto de librerías o framework – para desarrollo de interfaces de usuario. Este framework usa ActionScript 3.0 (AS3). Si el código AS3 que se programe utiliza los componentes y métodos definidos en el Flex framework, entonces se esta usando Flex. Es decir, AS3 no es por si solo flex.

Tendremos la siguiente estructura de nuestro proyecto

  • Carpeta raiz del proyecto: hello_flex_ant
  • dentro de la carpeta raiz tendremos los archivos build.xml, build.properties, y una subcarpeta src
  • dentro de la subcarpeta src creamos una subcarpeta flex y dentro de esta crearemos el archivo HelloFlexAnt.mxml

Les debe quedar asi:



En el archivo HelloFlexAnt.mxml colocaremos lo siguiente:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
viewSourceURL="src/HelloWorld/index.html"
horizontalAlign="center" verticalAlign="middle"
width="300" height="160"
>
<mx:Panel
paddingTop="10" paddingBottom="10"
paddingLeft="10" paddingRight="10"
title="My Application"
>
<mx:Label text="Hello World!" fontWeight="bold"
fontmxmlString">24"/>
</mx:Panel>
</mx:Application>

En al archivo build.properties colocaremos la ruta donde tengamos instalado el SDK de flex, o podemos ponerlo directamente el el archivo build.xml

Ejemplo en build.properties :

ruta.FLEX_HOME = C:/flex_sdk_3

Ejemplo en build.xml

<property name="FLEX_HOME" location="/Users/mykola/java/flex">

Si colocamos la ruta de flex en el build properties entonces en nuestro build.xml tendremos la siguientes linas:

<property file="build.properties">

<property name="FLEX_HOME" location="${ruta.FLEX_HOME}">

El build.xml contendra lo siguiente:

<?xml version="1.0"?>
<project name="HelloFlexAnt" default="all">
<!-- Init the build process -->
<target name="init" unless="initialized">
<!-- Name of project and version -->
<property name="FLEX_HOME" location="/Users/mykola/java/flex"/>
<property name="proj.name" value="${ant.project.name}" />
<property name="proj.shortname" value="${ant.project.name}" />
<property name="version.major" value="0" />
<property name="version.minor" value="9" />
<property name="version.revision" value="0" />
<property name="APP_TITLE" value="Sample Application" />
<property name="APP_WIDTH" value="800" />
<property name="APP_HEIGHT" value="600" />
<!-- Global properties for this build -->
<property name="build.dir" location="${basedir}/build" />
<property name="flex_src" location="${basedir}/src" />
<path id="project.classpath">
<pathelement path="${java.class.path}" />
</path>
<taskdef resource="flexTasks.tasks"
classpath="${FLEX_HOME}/ant/lib/flexTasks.jar" />
<echoproperties/>
<property name="initialized" value="true" />
<mkdir dir="${build.dir}" />
</target>
<!-- Default target: clean and build the application -->
<target name="all" depends="init">
<antcall target="clean" />
<antcall target="build" />
</target>
<!-- Compile Flex files -->
<target name="compile.flex" depends="init">
<property name="module"
value="${ant.project.name}"
description="The name of the application module." />
<mxmlc file="${flex_src}/${module}.mxml"
keep-generated-actionscript="true"
output="${build.dir}/${ant.project.name}/${module}.swf"
actionscript-file-encoding="UTF-8"
incremental="true"
context-root="${ant.project.name}"
debug="true">
<load-config filename="${FLEX_HOME}/frameworks/flex-config.xml" />
<source-path path-element="${FLEX_HOME}/frameworks" />
<compiler.source-path path-element="${flex_src}" />
</mxmlc>
<html-wrapper title="${APP_TITLE}"
file="index.html"
application="app"
swf="${module}"
width="${APP_WIDTH}"
height="${APP_HEIGHT}"
version-major="${version.major}"
version-minor="${version.minor}"
version-revision="${version.revision}"
history="true"
template="express-installation"
output="${build.dir}/${ant.project.name}/" />
</target>
<!-- Build the application -->
<target name="build" depends="init">
<antcall target="compile.flex" />
</target>
<!-- Clean build files -->
<target name="clean" depends="init">
<delete dir="${basedir}/generated" />
<delete dir="${build.dir}" />
</target>
<target name="usage" description="Usage documentation">
<echo>
all - clean and build the project
</echo>
</target>
</project>

Este build lo encontre en este blog : blog on programming

Para compilar nuestro hola mundo abrimos una consola de comandos, y nos vamos hasta la carpeta de nuestro proyecto donde se encuentra nuestro build xml, y ahi ejecutamos ant all, para observar lo que se ha compilado entramos a la carpeta build y ahi podemos darle en el index.html o darle en el archivo swf que se genera si tenemos el reproductor de flash

El codigo del hola mundo desde la pagina de flex hola mundo flex

Y al final debemos tener lo siguiente: