pcollaog's blog

Linux, java, música y otras hierbas

jEnv Una Solución Para Múltiples Instalaciones De Java

| Comments

jEnv es una herramienta de linea de comandos que ayuda a mantener múltiples instalaciones de Java (versiones y/o sabores) y permite cambiar las versiones por linea de comandos para poder mantener diversos entornos de desarrollo.

Una de las funcionalidades interesantes de jEnv es que configura las variables de entorno de java JAVA_HOME al vuelo según una pequeña configuración que se agrega al proyecto. Más adelante veremos algunas gracias de jEnv.

Instalación

La instalación es bien sencilla y la pueden revisar en la página oficial de jEnv, acá dejo un resumen:

1
$ git clone https://github.com/gcuisinier/jenv.git ~/.jenv

Ahora debes ejecutar (dependiendo de tu shell) lo siguiente:

1
2
$ echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(jenv init -)"' >> ~/.bash_profile
1
2
$ echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.zshrc
$ echo 'eval "$(jenv init -)"' >> ~/.zshrc

Reiniciamos el terminal y/o cargamos las nueva variables de entorno de nuestra shell y primero probamos que todo funcione correctamente con el siguiente comando:

1
$ jenv --version

Si el comando te responde con la versión de jEnv es porque esta todo correctamente instalado.

Configuración

Luego tenemos que agregar las diferentes versiones de java que tengas instaladas en tu sistema de la siguiente forma:

1
2
3
4
5
$ jenv add /path/to/java6-oracle/java_home
$ jenv add /path/to/java6-openjdk/java_home
$ jenv add /path/to/java7-oracle/java_home
...
...

Una ves que hayas concluido la configuración de las diferentes versiones de Java de tu sistema, puedes listarlos con el siguiente comando:

1
$ jenv versions

Y aparecerá algo como esto:

1
2
3
4
5
6
7
  system
* 1.7 (set by /home/pcollaog/.jenv/version)
  1.7.0.79
  1.8
  1.8.0.45
  oracle64-1.7.0.79
  oracle64-1.8.0.45

Ahora corresponde configurar cual de todas esas instalaciones será la que funcionará de forma global, es decir, la configuración por omisión de Java. En este ejemplo se configura la versión oracle64-1.7.0.79 como global.

1
$ jenv global oracle64-1.7.0.79

Tambien se puede configurar de forma mas genérico, es decir, la última versión de java 7, por ejemplo:

1
$ jenv global 1.7

Con esto siempre tomará la versión mas nueva de java 7 que tengan previamente configurada.

Activar la configuración automática de JAVA_HOME

Para delegar la configuración de las variables de entorno de java a jEnv, debemos activar el plugin export de la siguiente forma:

1
$ jenv enable-plugin export

Nota: para que funcione correctamente el plugin, se debe eliminar la configuración de las variables de entorno que se tengan en los perfiles de bash (.bashrc) , zsh (.zshrc) o de su shell favorita.

Luego deben reiniciar su terminal o volver a cargar los perfiles de su shell y probar:

1
$ echo $JAVA_HOME

Debería aparecer algo como lo que sigue:

1
/home/pcollaog/.jenv/versions/1.7

Y si vemos la versión de java debería aparecer algo como esto:

1
2
3
4
$ java -version
java version "1.7.0_79"
Java(TM) SE Runtime Environment (build 1.7.0_79-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)

Configuración personalizada de Java

Si por alguna razón necesitas tener una versión especifica de Java en un proyecto de código fuente, debes configurar (estando dentro del directorio principal del proyecto) de la siguiente forma:

1
$ jenv local 1.7

Este comando creará un archivo en el directorio en el que te encuentras (directorio base de tu código fuente) llamado .java-version y cuyo contenido será la versión de java especificada. Con esto todos los subdirectorios (a partir de donde se encuentras este archivo) estarán configurados con la versión de java seleccionada. Si tienes correctamente configurado el plugin export, jEnv hará el trabajo sucio de configurar la variable JAVA_HOME.

Sus comentarios son bienvenidos!

Spring Constructor Namespace

| Comments

En esta oportunidad les escribo sobre un nuevo namespace que apareció en Spring 3.1 (y me dí cuenta recién XD ) y sirve para configurar los beans haciendo uso de su constructor. Como su nombre lo indica este es un namespace que opera sólo sobre los constructores de beans y así permitir la inyección de beans o valores.

Les dejo una imagen de como se activa este nuevo namespace en el STS.

Veamos un ejemplo simple de cómo usar namespace C.

Supongamos la siguiente clase:

Clase de ejemplo con un constructor
1
2
3
4
5
6
7
8
public class SimpleSPImpl extends StoredProcedure
        implements SimpleSP {

    public SimpleSPImpl(DataSource ds, String spName) {
        super(ds, spName);
    }

}
Ejemplo de configuración de constructores (versión anterior)
1
2
3
4
5
6
7
8
9
10
<bean id="bean_id" class="com.example.SimpleSPImpl">
    <constructor-arg name="ds" ref="datasource" />
    <constructor-arg name="spName" value="spNameTest" />
</bean>

<!-- Definición del datasource de ejemplo -->
<bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
...
...
</bean>

Ahora usando el namespace c quedaría algo mas simple:

Ejemplo de uso de constructores namespace C
1
2
3
4
5
6
7
8
<bean id="bean_id" class="com.example.SimpleSPImpl 
    c:ds-ref="datasource" c:spName="spNameTest" />

<!-- Definición del datasource de ejemplo -->
<bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
...
...
</bean>

Como pueden notar en el ejemplo, el namespace C permite configurar los constructores de los beans y tiene dos formas básicas, por valor y referencia. Para inyectar una instancia preconfigurada se debe usar el sufijo -ref para hacer alusión a que es una referencia. Si se desea inyectar un valor, sólo se usa el nombre del argumento del constructor.

Como siempre sus comentarios son bienvenidos.

Checked Y Unchecked Exception

| Comments

Dado que varios de mis lectores me han solicitado un post sobre tipos de excepciones en Java, les dejaré un par de notas para que consideren al momento de diseñar soluciones y por su puesto el cómo manejar los errores. Aquí vamos!

Primero que todo, un par de definiciones básicas y características antes de partir.

Jerarquía de Excepciones

Esta es la jerarquía de excepciones de mas alto nivel que encontramos en Java.

Unchecked Exception

Generalmente este tipo de excepciones son lanzadas por la aplicación y se generan a partir de errores en tiempo de Runtime. Este tipo de excepciones representan errores en el código y que la aplicación no es capaz de controlar. Algunos de errores causados y que lanzan este tipo de excepciones, por ejemplo, argumentos inválidos pasados a un método (argumentos null pueden causar NullPointerException), otro error común son la excepciones del tipo IndexOutOfBoundsException y que son lanzadas cuando se quieren obtener elementos de una lista y el índice que se entrega está fuera del tamaño del arreglo. Como podrán ver, son errores de programación y que generarán defectos en momento de correr la aplicación (no así al compilar).

Unchecked runtime exceptions represent conditions that, generally speaking, reflect errors in your program's logic and cannot be reasonably recovered from at runtime.

Gosling, Arnold and Holmes The Java Programming Language

Las excepciones de tipo Unchecked son subclases que heredan desde RuntimeException. Además este tipo de excepciones no tienen la obligación de ser declaradas con la cláusula throws en la cabecera del método. Otra característica es que tampoco se tiene la obligación de atraparlas con un catch como se muestra en el ejemplo siguiente:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
 * Ejemplo de exception tipo {@link RuntimeException}
 *
 */
public class RuntimeDemo {

    /**
     * Método principal
     */
    public void mainMethod() {
        methodThowsRuntimeException();
        methodThowsRuntimeException2();
    }

    /**
     * Este método lanzará una excepción de tipo runtime no declarada en su
     * firma
     */
    public void methodThowsRuntimeException() {
        throw new ExampleRuntimeException();
    }

    /**
     * Este método lanzará una excepción de tipo runtime está declarada en su
     * firma (no es obligación) pero deja mas claro al desarrollador las
     * excepciones que debería manejar con la API.
     * 
     * @throws ExampleRuntimeException
     *             en caso de error
     */
    public void methodThowsRuntimeException2() throws ExampleRuntimeException {
        throw new ExampleRuntimeException();
    }

    /**
     * Clase de error tipo Runtime
     */
    public static class ExampleRuntimeException extends RuntimeException {

        public ExampleRuntimeException() {
            super();
        }
    }
}

Checked Exception

Este tipo de excepciones representan condiciones inválidas en el contexto de la línea de ejecución y que están fuera del control de dicho contexto, como por ejemplo, problemas con la base de datos, problemas de red, acceso a los archivos. También pueden ser condiciones de ingreso al sistema en donde el sistema no tiene ninguna participación, como por ejemplo, ingresar un nombre de usuario y contraseña incorrectos.

Contexto de ejecución ó scope: Corresponde al las líneas de código que están encerradas en un bloque de código, como por ejemplo, un método, un try/catch, bloque estático, etc.

Este tipo de excepciones deben ser declaradas en la firma del método. Además deben ser atrapadas dentro de los bloques de código donde se invoque un método que contenga la clausula throws.

Todas las excepciones de este tipo son subclases que heredan desde Exception, como por ejemplo:

Otra característica de este tipo de excepciones es que existe una probabilidad de recuperación de la ejecución y el método puede realizar alguna acción correctiva y/o informativa (log) en el bloque catch o simplemente relanzar la excepción y confiar en que el método invocante la atrape y haga algo con ella.

Para enviar el stacktrace al sistema de log (systemout) pueden usar los métodos de Throwable y en específico al método printStackTrace).

Un pequeño ejemplo de checked exception:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
 * Clase de ejemplo para checked exceptions
 */
public class CheckedExample {

    /**
     * Método que atrapa una checked exception
     */
    public void catchCheckedException() {
        try {
            throwCheckedException();
        } catch (ExampleCheckedException e) {
            // TODO: hacer algo en caso de error (recuperacion)
        }
    }

    /**
     * Método que relanza la checked exception a un método superior
     * 
     * @throws ExampleCheckedException
     *             checked exception error
     */
    public void rethrowCheckedException() throws ExampleCheckedException {
        throwCheckedException();
    }

    /**
     * Método que lanza una checked exception
     * 
     * @throws ExampleCheckedException
     *             checked exception error
     */
    private void throwCheckedException() throws ExampleCheckedException {
        throw new ExampleCheckedException();
    }

    /**
     * Clase que representa la excepcion de ejemplo
     */
    private static final class ExampleCheckedException extends Exception {

        public ExampleCheckedException() {
            super();
        }

    }
}

Error

Las excepciones de tipo Error son excepciones en las que el sistema no puede hacer nada con ellas, son clasificadas como errores irreversibles y que en su mayoría provienen desde la JVM, como por ejemplo: IOError, NoClassDefFoundError, NoSuchMethodError, OutOfMemoryError y VirtualMachineError por mencionar algunos de los errores.

Un poco de diseño y consejos para el manejo de excepciones

Para iniciar esta última parte, comenzaré con algunos ejemplos de malas prácticas con las que siempre nos encontramos cuando programamos, les dejo unas pocas:

java Ejemplo 1: El catch que no hace nada
1
2
3
4
5
try {
    //ejecución que lanza checked exceptions
} catch (ExampleCheckedException e) {
    // No hace nada
}

En el ejemplo de arriba claramente no se hace nada con la excepción dentro del try, lo recomendable es que si de verdad no vas a hacer nada con la excepción, al menos debes enviarla a tu sistema de Logger favorito con algún nivel de debug aceptable para poder revisar el log. Siempre se recomienda hacer algo en el bloque catch ya que ocurrió un error que debe ser controlado.

Ejemplo 2: Relanzar la excepción en el catch
1
2
3
4
5
try {
    //ejecución que lanza checked exceptions
} catch (ExampleCheckedException e) {
    throw e;
}

Es similar ejemplo 1 pero lo que se hace es relanzar la excepción atrapada hacia el método invocante. La recomendación es que nunca hagas eso ya que se presta para confusión al leer el código fuente y en la practica se estarían ejecutando dos bloques catch para la misma excepción (el directo y el del método invocante).

Ejemplo 3: Atrapar todas las excepciones.
1
2
3
4
5
try {
    //ejecución que lanza checked exceptions
} catch (Exception e) {
    // alguna lógica de negocio
}

Es recomendable que nunca atrapen todas las excepciones en un bloque catch y básicamente porque uno pierde la noción de por qué se produjo la excepción. Además en ese bloque también se atrapan las excepciones de tipo Runtime y ya mencionamos que estas excepciones significan errores en tu programa y que deben ser depurados (no escondidos debajo de la alfombra). Lo mejor es atrapar cada una de las excepciones y darle un tratamiento a cada una, si necesitan agrupar usen la herencia/jerarquía de las excepciones.

Ejemplo 4: instanceof en el catch
1
2
3
4
5
6
7
8
9
10
11
try {
    //ejecución que lanza checked exceptions
} catch (Exception e) {
    // alguna lógica de negocio

    if (e instanceof BlaException ){

    } else if (e instanceof FooException) {

    } else if .....
}

Ese bloque de if’s compuestos se debe transformar en varios catch para cada una de las excepciones lanzadas. No usen un control de errores manual, es mejor usar las herramientas que te provee el lenguaje.

Ejemplo 5: No abusar de las checked exceptions
1
2
3
4
5
6
7
8
9
10
11
12
13
try {
    //ejecución que lanza checked exceptions
} catch (ErrorBlaException e) {
    ...
} catch (ErrorFooException e) {
    ...
} catch (ErrorOMGException e) {
    ...
} catch (SDWException e) {
    ...
} catch (Exception e) {
    ...
}

No abusar de las checked exceptions ya que hacen nuestro código confuso y poco mantenible. Si bien es cierto, es la herramienta que nos provee el lenguaje, no abusemos de ella y convirtamos los bloques catch en pseudo programas y rutinas anexas a la lógica de negocio (que es la que vale).

Ejemplo 6: Lanzar el misil "Exception"
1
2
3
private void foo() throws Exception {
 // código de negocio   
}

Nunca lancen Exception como una excepción de su lógica de negocio y es que básicamente los catch están pensados en atrapar excepciones particulares y al lanzar Exception (de la mas alta jerarquía) jamás entrarás al bloque catch que corresponda y que pueda gestionar el error. Por otro lado el programador pierde la visibilidad de los errores particulares que debe gestionar.

Ejemplo 7: Lanzar el misil nuclear "RuntimeException"
1
2
3
4
5
6
7
private void foo() throws Exception {
    try {
        // logica de negocio
    } catch (ParserConfigurationException e) {
        throw new RuntimeException("Error");
    }
}

Jamás se debe hacer esto, jamás!. Esto romperá todo tu programa ya que al lanzar la excepción RuntimeException esta llegará sin control a la capa mas alta provocando un error. Recuerden que ese tipo de excepciones son errores sin recuperación y justamente estamos tratando de hacer lo contrario gestionar los errores de lógica de negocio.

 Mantener un árbol de excepciones

Esta sección del post quizás sea el más polémico ya que no hay receta perfecta para el manejo de excepciones y daré mis consejos (personales), puede que estén de acuerdo como puede que no.

Les recomiendo siempre mantener un árbol de excepciones que representen los errores (de negocio y de ejecución) de tu aplicación. Para que sea mas simple la mantención del árbol de excepciones, usen polimorfismo, herencia y todas las herramientas que ofrece OOP. En este punto siempre hay detractores de los árboles de excepciones con la excusa de su mantención.

Contraria a mi propuesta de manejo de errores, existen quienes mantienen sólo 1 excepción y a dicha excepción le agregan atributos y cuanta metadata puedan agregar. Qué se consigue finalmente con ese esquema de errores, es llenarte de IF por todos lados mirando los atributos que contiene la instancia de excepción y haciendo todo un control de errores manual.

Palabras al cierre

Quedan muchas cosas por mencionar de las excepciones y ahondar mucho mas en cómo diseñar y construir un árbol de excepciones, creo que sera materia para otro artículo. Demás esta decirles que esta abierta la discusión. Los comentarios bienvenidos sean.

Ud. No Lo Haga - Parte 1

| Comments

En este post, pondré algunas capturas de pantallas de cosas que Ud. como desarrollador (java) no debe hacer (en algunos casos debe evitar hacer).

Constantes

En Java las constantes por lo general se declaran en estilo uppercase usando como separador de palabras el underscore ALGO_COMO_ESTO, en este ejemplo, se pueden notar que agregan un underscore al principio (No lo haga!). Generalmente se usa underscore al principio para nombrar los atributos de una clase, de esta forma no usas this para identificar un atributo de clase.

El falso catch

Si va hacer algún tipo de control sobre una exception, pués hágalo. En este ejemplo sólo se captura la exception y se vuelve a lanzar. Si realmente quiere hacer eso, no haga el catch] de la exception*, déjela salir libremente.

La estética del código si importa

Si esta programando y al final de su algoritmo, le queda algo parecido a lo que sale en la imagen, recapacite, tome aire y refactorice su código. Hay algunas alertas que se pueden ver fácilmente con la estética del código, es decir, al cómo queda escrito (forma, silueta). Hay algunos desarrolladores que les encanta tener sus líneas de código hasta el infinito, lo que dificulta su lectura cuando tienes una pantalla distinta a la del desarrollador.

Algunos consejos que te ayudarán a darte cuenta de errores en tu código de forma visual, aquí los dejo:

  • Ajusta tu IDE para que te corte las lineas en los 80 caracteres. Con esto podrás tener como buena práctica nombrar bien tus variables (cortas y precisas) y es un buen límite cuando empiezas a avanzar en la identación del código producto de los if/else/for/while/try/catch. Si terminas escribiendo código cerca de los 80 caracteres es que algún problema tienes en tu código y necesita refactorización.
  • Si trabajas en equipo, es indispensable que todos tengan los mismos settings para la identación y el encoding, de esta forma, no tendrás problemas al comparar código (diff).

Estos son mis ajustes en el STS/Eclipse:

Estilo Visual Basic

Si esta escribiendo código Java, por favor no cometa este error. Si va a declarar una variable, hágalo en el lugar donde se utilizará, de esta forma los refactoring de código son mas simples. Por otro lado, estéticamente queda feo tu código. Ahora si entramos en el micro manejo de memoria, posiblemente estas reservando memoria que no utilizarás en todo el método. En este ejemplo, se declaran muchas variables con un valor, pero que pasa si salta una exception? o algún control de flujo que no considere todas las variables?, habrás perdido innecesariamente un par de bytes.

WTF!!!

Nunca, pero nunca, asignes a una variable el valor de una constante, no tiene ningún sentido. Además en este ejemplo, podrán notar que hay código que no tiene ningún sentido, nameCombobox nunca jamás en la vida va a ser null por lo tanto ese if esta de sobra. Escriba código que realmente es útil y que funciona.

Hay algunas cosas que no tienen una explicación razonable, como instanciar un objeto para luego no utilizarlo. Esto sólo provoca perdida de preciados bytes y ciclos de procesador.

Creo que califica en la misma descripción de arriba, escriba código que funcione.

Si va a utilizar StringBuffer hágalo de la forma correcta, se merece un mínimo de respeto dicha clase.

Evite el código que está de más, la API de commons-lang StringUtils.isEmpty() evalua que sea null y vacío. Ahora bien en el código podrían utilizar de la misma API StringUtils.isNotEmpty() y con eso le sacan el signo ! y todo queda mas bonito, por su puesto que la primera parte del if vuela también del código.

Finalmente esto se traduce en:

1
2
3
if (StringUtils.isNotEmpty(codeAdditional)){
  ....
}

Espero les sirvan estos anti-ejemplos de código fuente. A medida que siga revisando código iré agregando algún otro post con mas código para el bronce. La discusión esta abierta por si quieren agregar algún otro tip.

PD: Las variables han sido renombradas para proteger a los verdaderos autores, cualquier coincidencia con la realidad es casualmente cierta y verídica.

¿Ha Muerto El Diseño?

| Comments

Este artículo es sólo un copy&paste de un viejo articulo de Martin Fowler titulado ¿Ha muerto el diseño?, todo esto porque el otro día lo anduve buscando en español y el link esta roto.

Aún recuerdo cuando entré a trabajar como informático y me recibieron con este artículo. (ping @ferchik)

Pues aquí mi grano de arena para que este artículo no se pierda. Aquí va el artículo.