Configuración JDBC Progress v9+Glassfish sobre Windows

Variables de entorno :

-DLC: Directorio de instalación de progress

-JDK_HOME: Directorio de instalación de Java

-PATH: PATH=%PATH%;%DLC%\bin;%JDK_HOME%\bin

-GLASSFISH_HOME=Directorio de instalación de glassfish

Bibliotecas nativas:

Luego de instalar progress y configurar las variables de entorno hay que preparar el sistema operativo para efectuar una conexión via JDBC, a diferencia de otros drivers de JDBC autonomos, el driver de Progress se auxilia de bibliotecas nativas del sistema, las bibliotecas se encuentran en el directorio %DLC%\bin y son:

JdbcProgress.dll

procli92.dll

Estos archivos se deben copiar a la maquina CLIENTE dentro del directorio Windows\System32 para que esten a disposición de la JVM. Son dependientes de arquitectura y de sistema operativo. Tambien hay disponibles bibliotecas como archivos .so para *nix pero lastimosamente el servidor de prueba estaba en Windows.

Además para configurar un origen de datos en Glassfish estos mismos archivos .dll se tienen que copiar en %GLASSFISH_HOME%\lib

Configuración del pool JDBC:

Los drivers JDBC de progress se encuentran dentro de %DLC%\java, solo es necesario copiar los archivos jdbc.jar y progress.jar, al classpath de nuestro DOMINIO en glassfish, por ejemplo para dominio 1 seria:

%GLASSFISH_HOME%\domains\domain 1\lib

Estos drivers no funcionaran si no estan configuradas las .dll de manera adecuada y solo lanzaran una excepcion.

Configuración del pool de conexiones:

El estandar JDBC 2.0 describe dos tipos de drivers, drivers simples e implementaciones de DataSource para creación de pool de conexiones, la clase correcta para el pool de conexiones con progress es:

com.progress.sql.jdbcx.datasource.JdbcProgressDataSource

Esta clase implemente la interfaz javax.sql.ConnectionPoolDataSource, despues de crear un pool sin una plantilla especifica (sin seleccionar ningun DBMS en el asistente), se deben establecer las siguientes “Propiedades Adicionales” en el pool de conexiones:

User: Usuario que inicia el servicio en windows

Password: Password del usuario

DatabaseName: Nombre de la base de datos

ServerName: Dirección del servidor

PortNumber: Puerto de la base de datos

URL: jdbc:jdbcprogress:T:<servername>:<portnumber>:<databasename>

Ejecución remota desde Android con remote launcher y demonios Java en Linux

Hace tiempo tuve un control físico para xbmc y queria replicar eso mediante Android para lo cual encontre un cliente. El inconveniente que tenia con xbmc era el levantarme de mi cama para ejecutar el control en Android y asi fue como encontre Remote Launcher.

Remote Launcher es una especie de control para ordenar la ejecución de aplicaciones desde el smartphone, el cual solo necesita una contraparte en el servidor, tiene dos modos de ejecución un modo de ejecución gráfico y otro desde la terminal, la parte del servidor esta escrita en java.

Luego de un par de pruebas y de seguir los pasos de la configuración que hay en la pagina, ya podia ejecutar xbmc desde mi teléfono y podía controlarlo con el control de xbmc, sin embargo el problema ahora era ejecutar remote launcher en mi pc -_-, aunque mi primera opción fue agregarlo como programa al iniciar Gnome, perdia el poder de controlar la pc con pasarme a Fluxbox (mi otro escritorio) y mi solución final fue integrar Remote Launcher a los servicios del sistema en Gentoo.

Preeliminares

Aunque cada distribución tiene su propio sistema de arranque, la mayoria al final ejecutan una utilidad del sistema llamada start-stop-daemon (Debian y Gentoo por ejemplo) que como su nombre lo indica se encarga de gestionar la ejecución del programa en cuestión como un demonio y no como una aplicación normal. En mi caso la configuración que funciono fue la siguiente:

  • Configurar un nuevo servicio para ejecutar el .jar de Remote Launcher  Server como si se tratara de un servicio de sistema, en mi caso un nuevo servicio para openrc en Funtoo.
  • Agregar un lanzador para xbmc con la salvedad que la aplicación tiene que abrirse en la pantalla principal 0 de mi xserver (la pantalla por defecto para la mayoría de los usuarios).

Convertir un .jar en un servicio del sistema

Al utilizar start-stop-daemon su funcionamiento por defecto es verificar si el proceso ya se encuentra en la tabla de procesos en ejecución a nivel de sistema (para cualquier usuario), si utilizaramos este metodo con java start-stop-daemon verificaria si esta en ejecución /usr/bin/java y automaticamente detendria todas las instancias de maquina virtual que esten en ejecución asi que descarte este metodo.

El instalador de remote launcher, genera un script en /usr/bin/remote-launcher-server con el comando largo de java para la ejecución del programa, script que NO debe ser utilizado para crear el servicio porque al ejecutar el script el sistema genera un pid por la ejecución del script y otro pid para la ejecución de la maquina virtual de java, otra opción descartada.

La solución final entonces es basarme en el script para ejecutar el jar de remote launcher y convertirlo a un comando de start-stop-daemon:

start-stop-daemon –start –background –user tuxtor –make-pidfile –pidfile /var/run/remote-launcher-server/remote-launcher-server.pid  –exec /usr/bin/java — -jar «/usr/share/remotelauncherserver/Remote Launcher.jar» /usr/share/remotelauncherserver/ -d

Las opciones que utilice las explico a continuación:

–start – no necesita explicación o si?

–background – envía el proceso a ejecutarse a background, sin esta opcion mi script de inicio esperaria una señal del ejecutable para decir que esta listo para aceptar conexiones, señal que nunca existiria porque remote launcher no esta programado para eso

–user tuxtor – le indico que ejecute el comando con mi usuario, esto para mantener aislado el demonio de remote launcher y para que remote launcher lance los programas con mi usuario regular

— make-pidfile – workarround para que start-stop-daemon genere un archivo con el numero de pid del proceso de remote launcher

— pidfile – ruta en donde se generara el archivo con el numero de pid, no es necesario que el archivo exista PERO la ruta completa si debe de existir

— exec /usr/bin/java – el comando a ejecutar sera la maquina virtual de java

— «-jar . . .» las opciones de ejecución de remote launcher, le indico cual es el archivo .jar a ejecutar, el classpath y por ultimo la opción -d que hace que remote launcher inicie solo en linea de comandos.

Por ultimo para hacer que el servicio se detenga volvemos a utilizar start-stop-daemon, para lo cual usamos la opcion stop y le indicamos en donde se encuentra el archivo con el pid del proceso.

start-stop-daemon –stop –pidfile /var/run/remote-launcher-server/remote-launcher-server.pid

Especifico para Gentoo

En Gentoo el script completo a ubicar en init.d seria el siguiente, ademas de ejecutar el comando anterior me aseguro que la carpeta de destino del pidfile no genere problema. Como opción adicional hice parametrizable la dirección de destino del pidfile, la cual agrego como una variable en un nuevo archivo dentro de /etc/conf.d/ en mi caso se llama remote-launcher-server.

/etc/init.d/remote-launch-server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/sbin/runscript
# Android remote launcher server init script
# Distributed under the terms of the GNU General Public License v3
 
depend() {
need net
}
 
start() {
ebegin "Starting remote-launcher-server"
 
if [ ! -e /var/run/remote-launcher-server ] ; then
mkdir -p /var/run/remote-launcher-server
chown tuxtor:users /var/run/remote-launcher-server
fi
 
start-stop-daemon --start --background --user tuxtor --make-pidfile --pidfile ${REMOTE_LAUNCHER_SERVER_PIDFILE} \
--exec /usr/bin/java -- -jar "/usr/share/remotelauncherserver/Remote Launcher.jar" /usr/share/remotelauncherserver/ -d
eend $?
}
 
stop() {
ebegin "Stopping remote-launcher-server"
start-stop-daemon --stop --pidfile "${REMOTE_LAUNCHER_SERVER_PIDFILE}"
eend $?
}

/etc/conf.d/remote-launch-server

REMOTE_LAUNCHER_SERVER_PIDFILE=»/var/run/remote-launcher-server/remote-launcher-server.pid»

Agregar un lanzador para xbmc (o cualquier otro programa con GUI) con remote launcher server ejecutandose como servicio

Remote launcher server utiliza el archivo .remotelauncherserver dentro de la carpeta /home del usuario, en mi caso tuxtor. Aunque remote launcher server ya se encuentra ejecutandose bajo mis credenciales el siguiente inconveniente a resolver es que no se puede ejecutar un programa desde una terminal no grafica, hay que forzar la ejecución en la pantalla principal del X Server, esto se hace mediante la variable de entorno DISPLAY.

Por ejemplo un lanzador tradicional en el archivo de configuración de remote launcher server seria asi:

Xbmc
Xbox Media Center (Yes Xbox)
/usr/bin/xbmc

Asi que como paso adicional hay que generar un script con la variable de entorno DISPLAY apuntando a la pantalla principal, más o menos como el siguiente:

1
2
3
#!/bin/bash
export DISPLAY=:0.0
/usr/bin/xbmc

Lo ubicamos donde nos conventa (en mi caso /opt/xbmc_disp0) lo hacemos ejecutable (chmod +x /opt/xbmc_disp0) y reescribimos el lanzador.

Xbmc
Xbox Media Center (Yes Xbox)
/opt/xbmc_disp0

Y al conectarnos desde android deberiamos ver algo como esto:

 

Autenticación nativa de Trac y Svn en hosting compartido de Dreamhost

Dreamhost y sus famosos one-click instal son el reemplazo perfecto a los scripts fantastico que conocemos en todos los proveedores de hosting. Hace un año Trac no estaba disponible entre sus one-click install y después de hacer un par de hacks y workarrounds logre instalar mi propio sitio con Trac en mi cuenta de Dreamhost, sin embargo es un proceso un tanto doloroso a nivel de dificultad.

Recientemente Dreamhost añadio la posibilidad de instalar Trac con la salvedad que no podemos usar la autenticación de Trac como tal y en su lugar solo podemos vincularlo al archivo .passwd de nuestro repositorio y solo podremos restringir el acceso al sitio y no definir los niveles de autenticación de trac.

Si no saben de que hablo arriba pueden leer otros post 😀

Continue reading →

Netbeans 7 y Keyring de GNOME 3

Netbeans tiene una característica bastante útil para almacenar nuestras contraseñas de muchos servicios como:

  • Team server
  • Issue tracking
  • Respositorios de codigo
  • Conexiones a bases de datos

Para los que utilizamos Netbeans en sistemas GNU/Linux el almacenamiento de estas contraseñas por defecto se hace via keyring del sistema (kwallet para los que usan kde y gnome-keyring para los que usamos gnome).

Sin embargo con la nueva versión de Gnome, al intentar acceder a cualquier servicio con contraseña via keyring se obtiene el siguiente error de dbus:

arguments to dbus_message_iter_append_basic() were incorrect, assertion «*bool_p == 0 || *bool_p == 1»

Aunque aun no tengo una explicación clara para este error, solo estoy seguro que en Gnome 2 todo funcionaba sin inconvenientes :D. Mi solución temporal es deshabilitarlo y utilizar el keyring básico incluido en Netbeans.

Para deshabilitar la integración con el keyring del sistema podemos agregar la opción -J-Dnetbeans.keyring.no.native=true al parametro netbeans_default_options, si utilizamos el instalador binario de netbeans este archivo se encontrara en ~/netbeans-7.0/etc.

Por ejemplo en mi sistema el valor final de la variable es el siguiente:

netbeans_default_options=»-J-Dcom.sun.aas.installRoot=\»/home/tuxtor/glassfish-v2.1\» -J-client -J-Xss2m -J-Xms32m -J-XX:PermSize=32m -J-XX:MaxPermSize=384m -J-Dapple.laf.useScreenMenuBar=true -J-Dapple.awt.graphics.UseQuartz=true -J-Dsun.java2d.noddraw=true -J-Dnetbeans.keyring.no.native=true»

Una vez establecida esta opción obtendremos el siguiente administrador de contraseñas. Espero que la proxima versión de netbeans ya sea totalmente compatible con Gnome 3 :D.

Gmaps4jsf integrado en icefaces y con carga dinamica de Google API key

Recientemente tuve que integrar todas las tecnologías descritas en el titulo y aunque icefaces dispone de su propio control para Google maps, es en cierta forma limitado. La gracia de Java es que no hay o no debería haber vendor lock-in  y pude utilizar Gmaps4jsf sin problemas. Cada proyecto tiene una documentación bastante descriptiva para su uso asi que me centrare en definir algunas particularidades del caso.

Integración con paginas jsp bajo xml estricto

Para los que aun no lo sepan al escribir paginas jsp tenemos dos estilos de creación:

  • JSP Tradicionales: Ideales para scriptlets y lógica escrita en la pagina .jsp (php style le dicen algunos)
  • JSP XHTML: Estas solo permiten xml estricto, se utilizan en conjunto con backing beans y restringen al programador para que no escriba lógica en el archivo .jsp

La documentación de gmaps4jsf indica que para integrar el control a nuestra aplicación jsf basta con agregar el siguiente taglib:

<%@ taglib uri="http://code.google.com/p/gmaps4jsf/" prefix="m" %>

Sin embargo esto no se puede utilizar con paginas jsp escritas en xml estricto. ¿Solución?, traducir el tag a la siguiente estructura equivalente, esto es útil cuando nuestro generador de paginas (como el difunto plugin visual de icefaces en netbeans) genera paginas en xml estricto

<jsp:root version="2.1"
...
xmlns:m="http://code.google.com/p/gmaps4jsf/"
>

Integración con icefaces

Para que el control se integre bien en el ciclo de vida de los componentes de icefaces hay que utilizar el siguiente atributo propio de Gmaps4jsf  «renderOnWindowLoad», si no se utiliza este atributo el mapa no sera renderizado adecuadamente.

Ejemplo:

<m:map width="1000px" height="740px" zoom="16" type="G_HYBRID_MAP " renderOnWindowLoad="false">
....

Carga dinamica de la clave de Google api key

Aunque la documentación del control de icefaces indica que se debe establecer la clave en una sección del archivo web.xml y la documentacion de Gmaps4jsf indica que debe hacerse incluyendo el tag de script al inicio de la pagina podemos hacer algo más interesante, establecer la clave desde un backing bean. Para esto icefaces cuenta un mecanismo para acceder al contexto de javascript de nuestra pagina actual denominado JavascriptContext (logico no?).

Asi pues para cargar de manera dinamica la clave sin añadir ningun tag a nuestra pagina jsf se utilizaria un codigo similar al siguiente:

String googleAPIKey="seqwerui1298qrioyiuywopiuwrqepowqclaveDEEjempl0";
 
JavascriptContext.includeLib("http://maps.google.com/maps?file=api&amp;v=2&amp;key="+googleAPIKey, FacesContext.getCurrentInstance());

Con esto podemos cargar la clave sin necesidad de escribirla directamente en la pagina jsp

Y por ultimo TADA!! tenemos nuestro mapa (convenientemente ubicado en alguna región no determinada):

 

 

¿Los servidores DNS que uso son decentes?

Hace casi 6 meses iniciamos la comunidad java Guatemala

Hace casi 3 meses alguien de la comunidad amablemente adquirio el dominio guate-jug.org.

Hace casi 2 meses este dominio se apunto hacia un nuevo servidor el cual es un procedimiento de rutina y regularmente no requiere más de 72 horas para que todos los dns tengan la nueva ip lista y fresca.

Hace un mes despues de detectar un problema con la actualizacion de DNS en un ISP con servidores DNS mediocres (telgua) hicimos de todo hasta cambiar de registrador de dominio.

Y el dia de hoy las cosas estan asi:

Google:

$ nslookup guate-jug.org 8.8.8.8
Server:        8.8.8.8
Address:    8.8.8.8#53

Non-authoritative answer:
Name:    guate-jug.org
Address: 173.236.29.66

$ nslookup guate-jug.org 8.8.4.4
Server:        8.8.4.4
Address:    8.8.4.4#53

Non-authoritative answer:
Name:    guate-jug.org
Address: 173.236.29.66

Opendns:

$ nslookup guate-jug.org 208.67.222.222
Server:        208.67.222.222
Address:    208.67.222.222#53

Non-authoritative answer:
Name:    guate-jug.org
Address: 173.236.29.66

$ nslookup guate-jug.org 208.67.220.220
Server:        208.67.220.220
Address:    208.67.220.220#53

Non-authoritative answer:
Name:    guate-jug.org
Address: 173.236.29.66

Pero si vamos por los locales:

Tigo:

$ nslookup guate-jug.org 200.49.161.131
Server:        200.49.161.131
Address:    200.49.161.131#53

Non-authoritative answer:
Name:    guate-jug.org
Address: 67.23.226.35

Telgua:

$ nslookup guate-jug.org ns.telgua.com.gt
Server:        ns.telgua.com.gt
Address:    216.230.128.33#53

** server can’t find guate-jug.org: REFUSED

$ nslookup guate-jug.org ns.comtech.net.gt
Server:        ns.comtech.net.gt
Address:    216.230.128.32#53

** server can’t find guate-jug.org: REFUSED

Y si puede parecer engreido que me queje tanto, pero despues de que todos dicen que es porque configure mal wordpress ¬¬, mientras yo con el uso de google dns he podido ver la pagina siempre. Tengo el derecho de quejarme que los tecnicos certificados CCNA/CCNP/CCNAlgo se dignen a optimizar el funcionamiento de sus servidores DNS o se rindan y preconfiguren en sus routers servidores DNS de verdad. Si hacen pruebas en los ISP locales a veces si resuelve la direccion . . . solo que a la IP equivocada.

Se aceptan donaciones para un guate-jug.net :/

Validando propiedades nulas de un objeto en Java

Todo programador java sabe que los tipos primitivos tienen un valor por defecto y los objetos por defecto tienen valor nulo (si no lo sabias ponte a estudiar).

A veces es necesario validar estos objetos para realizar operaciones sobre ellos y asegurarnos que nuestro usuario no reciba un magico nullPointerException. El metodo más rapido seria validar una a una las propiedades o la opción que me gusta, preguntarle a nuestro objeto que propiedades tiene y luego utilizar los metodos propios del objeto con Java Reflection.

Java Reflection es un API poco conocida por programadores principiantes, sin embargo es la unica salida en programas que requieren la habilidad de examinar un objeto en tiempo de ejecución. Si no han leido acerca de esto no se preocupen el Java Tutorial lo califica como avanzado.

Todos los detalles acerca de reflection se encuentra en el Java Tutorial sin embargo la clase que utilizo para validar nulos es la siguiente:

public class NullValidator {
 
    public static Object validarCampos(Object pObjeto) {
        Object objeto = pObjeto;
        Method metodos[] = pObjeto.getClass().getMethods();
        for (int i = 0; i < metodos.length; i++) {
            Method metodo = metodos[i];
            //Si es un metodo get o is lo utilizo con su equivalente set
            if ((metodo.getName().substring(0, 3).equalsIgnoreCase("get") || metodo.getName().substring(0, 2).equalsIgnoreCase("is")) && !metodo.getName().equals("getClass")) {
                String methodNameSet = "";
                if(metodo.getName().substring(0, 3).equalsIgnoreCase("get")){
                    methodNameSet = metodo.getName().replaceFirst("get", "set");
                }else{
                    methodNameSet = methodNameSet.replaceFirst("is", "set");
                }
                try {
                    Method metodoSet = pObjeto.getClass().getMethod(methodNameSet, metodo.getReturnType());
                    if (metodoSet != null) {
                        //Datos numericos
                        //Si es byte
                        if (metodo.getReturnType().equals(java.lang.Byte.class)) {
                            Byte valor = (Byte)metodo.invoke(pObjeto, new Object[0]);
                            if(valor==null){
                                metodoSet.invoke(pObjeto, 0);
                            }
                        }
                        //Si es bigDecimal
                        if (metodo.getReturnType().equals(java.math.BigDecimal.class)) {
                            BigDecimal valor = (BigDecimal)metodo.invoke(pObjeto, new Object[0]);
                            if(valor==null){
                                metodoSet.invoke(pObjeto, new BigDecimal(0));
                            }
                        }
                        // Si es Double
                        if (metodo.getReturnType().equals(java.lang.Double.class)) {
                            Double valor = (Double)metodo.invoke(pObjeto, new Object[0]);
                            if(valor==null){
                                metodoSet.invoke(pObjeto, new Double(0));
                            }
                        }
                        //Si es un string
                        if (metodo.getReturnType().equals(java.lang.String.class)) {
                            String valor = (String)metodo.invoke(pObjeto, new Object[0]);
                            if(valor==null){
                                metodoSet.invoke(pObjeto, "");
                            }
                        }
                        //Si es una lista
                        if (metodo.getReturnType().equals(java.util.List.class)) {
                            List objetosList = (List)metodo.invoke(pObjeto, new Object[0]);
                            for(Object objetoFromList:objetosList){
                                NullValidator.validarCampos(objetoFromList);
                            }
                        }
                        //Si es date
                        if (metodo.getReturnType().equals(java.util.Date.class)) {
                            Date valor = (Date)metodo.invoke(pObjeto, new Object[0]);
                            if(valor==null){
                                metodoSet.invoke(pObjeto, new Date());
                            }
                        }
                        //Si es primitivo
                        if (metodo.getReturnType().isPrimitive()) {
                            //los primitivos no permiten null
                        }
                    }
                } catch (Exception e) {
                }
            }
        }
        return objeto;
    }
 
}

¿Como funciona?

  1. Primero capturo los metodos que tiene el objeto
  2. Luego si es un metodo get procedo a utilizar su equivalente is o set
  3. Por ultimo compruebo el tipo de dato y si es nulo, si es nulo procedo a darle un valor inicial

Luego basta con invocar el metodo validarCampos (porque además es estatico) y listo nuestro objeto sera validado contra valores nulos.

 

Cyanogenmod 7 y Google Apps para paises con market restringido

El día de hoy se publicaron los primeros release candidate de mi ROM preferido para smartphones Android, esta vez con un cambio de versión mayor acorde al cambio de ROM base. Los nuevos ROMS del proyecto cyanogenmod de ahora en adelante estarán basados en Android Gingerbread.

Dejando a un lado los bombos y platillos, siguiendo mi tradición gentoosera bleeding edge procedi a instalar un release candidate :P.

Los pasos detallados para una instalación nueva se encuentran en la wiki del proyecto. Y para los usuarios actuales de cyanogenmod ya esta disponible la actualizacion desde el ROM manager.

En mi caso ya era usuario de cyanogenmod la instalación fue transparente, solo actualice ClockworkMod, luego el correspondiente backup y despues de descargar la imagen del ROM todo fue bien a excepcion de un inconveniente: Instalación aplicaciones de google

El problema es que aunque las aplicaciones de Google sean descargadas mediante el instalador del Rom Manager, necesitan ser instaladas via Android Market pero solo son visibles para usuarios de tiendas de aplicaciónes «premium» (con aplicaciones de pago)

Solucion 1 (Sin ejecutar el Android Market):

Si despues de instalar aun no han ejecutado el android market deben cargar por medio de sideload la aplicacion Market Enabler y obtener un perfil de cualquier pais con un Android Market «premium».

Por ejemplo si busco gmail con el Market normal de Guatemala obtengo:

Y despues de la ejecucion del Market Enabler:

Solucion 2 (Ya habiendo ejecutado el Android Market):

Si ya han ejecutado el Android Market y el instalador no pudo encontrar las Google Apps el procedimiento es descargarlas, instalarlas nuevamente via ROM Manager en la opcion «Install ROM From SD Card» y podemos buscar de nuevo las google apps en el Market e instalarlas sin problema

¿Que pasa si unicamente las busco en el Android Market?

En mi caso todas las aplicaciones descargadas (Gmail por ejemplo) no funcionaron hasta instalar el ROM descrito anteriormente.

Y con esto soy un usuario feliz de Cyanogenmod en mi nexus one :).

Querys con multiples columnas en JPA (JPA Projection)

Un error bastante común al desarrollar aplicaciones con JPA es volvernos perezosos y acostumbrarnos a siempre traer de vuelta los objetos enteros aun cuando solo necesitemos un par de propiedades.

Otro error común cuando somos principiantes en JPA (más que todo por ignorancia) es hacer varias consultas para obtener varios atributos desde la base de datos sin obtener el objeto completo.

Sin embargo podemos jugar un poco con JPA utilizando JPA projection para obtener solo los atributos que queremos

Imaginemos el siguiente escenario, donde tenemos una clase Foo equivalente a la tabla foo con 5 atributos-columnas.

Asi pues tendriamos

public class Foo{
private int id;
private String codigoUniversalProducto;
private String nombreUniversalProducto;
private BigDecimal existenciaActual;
private BigDecimal pedidosPendientes;
/*Getters y setters*/
}

Ahora bien imaginemos que nuestro servidor es de bajo rendimiento y por cada atributo nuestra implementacion de JPA tarda alrededor de 0.02 segundos en hacer el mapeo, por cada fila que obtengamos de la base de datos tardaremos 0.1 segundos.

Si esto lo traducimos a un resultset de 5000 filas tendriamos automaticamente 500 segundos solo para efectuar el mapeo. Obviamente estos tiempos son sobredimensionados pero imaginemos que este resultset es parte de una logica más grande y que es necesario hacer varias consultas a la misma tabla para algun procedimiento dentro de un EJB donde SOLO necesitamos los atributos de nombre y de existencia actual.

Si hacemos cuentas al solo obtener de regreso dos columnas de la base de datos en lugar del objeto completo, la operacion de mapeo tardaria 0.04 segundos en lugar de 0.1 segundos, con el resultset de 5000 filas tendriamos 200 segundos de operaciones de mapeo, interesante reducción no?.

¿Solución?

Solo obtener las columnas que necesitamos y eso lo hacemos con JPA projection, JPA projection es una funcionalidad de JPA para realizar mapeos de varias columnas dentro de una tabla y obtenerlas de tal forma que sean manipulables como objetos (el objetivo de los ORM), en JPA tenemos dos modos de funcionamiento SELECT con expresiones multiples, y utilizando constructores.

SELECT con expresiones multiples

Analogo a lo que realizamos con sql plano con JPA podemos hacer una consulta sobre las columnas que necesitamos. Por ejemplo

SELECT f.codigoUniversalProducto, f.nombreUniversalProducto FROM Foo AS f

Si ejecutamos esta query lo que obtendremos sera un listado de objetos de tipo generico Object. Dependiendo si fueron una o más  columnas este Object representara otro objeto de tipo Object[] o un tipo de dato en particular si nuestra query fue sobre una sola columna.

Por ejemplo:

List Object[] results =  em.createQuery("SELECT f.codigoUniversalProducto, f.nombreUniversalProducto FROM Foo AS f").getResultList();
for (Object[] result : results) {
System.out.println("Codigo: " + result[0] + ", Nombre: " + result[1]);
}

SELECT utilizando constructor
Sin embargo si queremos ir un poco más alla tambien podemos utilizar los constructores de nuestros objetos (mi metodo preferido). Con esta alternativa estariamos utilizando de nuevo los POJO’s que utilizabamos sin rediseñar nuestra arquitectura, por ejemplo con la query anterior deberiamos agregar el siguiente constructor a nuestro POJO:

public foo(String codigoUniversalProducto, String nombreUniversalProducto)
{
this.codigoUniversalProducto=codigoUniversalProducto;
this.nombreUniversalProducto=nombreUniversalProducto;
}

Y utilizamos este constructor desde nuestra expresión JPA

SELECT NEW org.shekalug.tuxtor.Foo(f.codigoUniversalProducto, c.nombreUniversalProducto)FROM Foo AS f;

La query anterior es totalmente equivalente y un ejemplo de uso seria:

String queryStr = "SELECT NEW org.shekalug.tuxtor.Foo(f.codigoUniversalProducto, c.nombreUniversalProducto)FROM Foo AS f;"
List Foo resultado = em.createQuery(queryStr).getResultList();
for(Foo fooObjeto:resultado)
{
System.out.println("Codigo: "+foo.getCodigo()+" Nombre: "+foo.getNombre());
}

Vale la pena resaltar que se puete utilizar cualquier POJO independientemente si represente una tabla o no.

Misma cara nuevo peinado

Después de casi un año regreso \o/, aun no se ingles pero si he mejorado y seguiré con el experimento y al fin veo la luz al final del tunel en la Univerisidad. Creo que ya es tiempo de retomar la escritura aca.

Luego de que toda Guatemala dejara la onda blog y se metiera en la onda social media (porque aceptemoslo es más difícil encadenar 3 párrafos coherentes que 250 caracteres con estupidez permitida), he visto que un par de bloggers de vieja escuela regresan y creo que eso me motiva(no pseudobloggers que copiaban noticias esos ahora ya solo ponen links en twitter).

Un par de cosas que han pasado en estos dias:

¿Sigo usando FLOSS?

Si

¿Trabajas con FLOSS?

Tambien

¿En serio trabajas con FLOSS?

Si

¿Promoves FLOSS?

Si pero de diferente manera

¿Otros blogs, cual es cual?

-Este blog (donde el TODO #1 es poner más cosas sobre java para aportar a Guatejug)

En ingles (que seguira enfocado en Gentoo y en reflexiones persionales)

Mi fotolog

¿Nuevo peinado?

-Si, ahora el blog es compatible con social media para estar cool, pero no le cambie el template porque me gusta

¿Que esperar de ahora en adelante?

Más java, más codigo, mismas reflexiones, menos eventos y . . .

las mismas locuras que hicieron que 70 personas en algun momento de su existencia me agregaran a su rss y/o entrega via mail.

Y por ultimo gracias a todos por no aprovechar ninguna vulnerabilidad en mi wordpress porque lo tenia desatendido.

Si aun no se han dado cuenta todas las lineas a excepción del saludo tienen menos de 240 caracteres (sarcasm mode off).

Saludos 🙂