TDD

Metodología de pruebas bastante extendida cuyo acrónimo significa:

  • Inglés: Test Driven Development.
  • Español: Desarrollo guiado por pruebas.

Se utiliza generalmente para el desarrollo de pruebas unitarias y no es dependiente de ningún lenguaje. El proceso de desarrollo siguiendo esta metodología se compone de tres etapas diferentes:

  1. Se escriben las pruebas y se comprueba que fallan.
  2. Se implementa el mínimo código que hace que las pruebas se pasen exitosamente.
  3. Se refactoriza el código escrito.

La idea de la metodología es que los requisitos de negocio sean transformados en pruebas asegurando de este modo que al final el desarrollo, todos los requisitos se cumplen. También es evidente que utilizando esta metodología, otro de los objetivos que se alcanzan es el de tener una amplia cobertura de pruebas en nuestros proyectos, ya que nunca se va a desarrollar nada para lo que no se hayan escrito sus pruebas previamente. Esto deriva en que en las siguientes iteraciones del proyecto, el desarrollador que quiere introducir nuevos cambios puede confiar en que el código que va a modificar o va a utilizar en su desarrollo funciona e implementa de forma correcta los requisitos de negocio.

A la hora de la implementación, se pretende que el código siga dos principios básicos y ampiamente conocidos como son:

  • KISS: “keep is simple stupid”. Literalemnte traducido por “Mantenlo sencillo estupido”, aunque prefiero la traducción “Mantenlo estúpidamente sencillo”. Que nos viene a instar a mantener nuestros sistemas lo más simples posibles a través de buenas prácticas de diseño.
  • YAGNI: “You aren´t gonna need it”. Literalmente traducido por “No lo vas a necesitar”. Este principio nos insta a no implementar cosas que no se van a utilizar, ni siquiera aunque pensemos que en un futuro quizás lo vayamos a hacer, porque en la mayoría de las ocasiones, ese futuro nunca llega.

Pero bueno, después de toda esta introducción teórica, vamos a implementar algo.

Primera Fase: Escribir las pruebas y comprobar que fallan.

public interface Calculator {
    int add(int a, int b);
    int subs(int a, int b);
    ....
}
public class MyCalculator implements Calculator {
    public int add(int a, int b) {
        return 0;
    }

    public int subs(int a, int b) {
        return 0;
    }

    ...
}
public class MyCalculatorTest {

    Calculator calc;

    @Before
    public void setUp() {
        calc = new MyCalculator();
    }

    @Test
    public void addTest() {
        assertEquals(2, calc.add(1, 1));
    }

    @Test
    public void subsTest() {
        assertEquals(1, calc.subs(2, 1));
    }

    ...
}

Segunda Fase: Implementar el mínimo código

public class MyCalculator implements Calculator {
    public int add(int a, int b) {
        return a + b;
    }

    public int subs(int a, int b) {
        return a - b;
    }

    ...
}

Tercera fase: Refactorizar nuestro código

En este caso, está tercera fase obviamente no tiene mucho sentido ya que el código se ha hecho lo más simple posible para que nos fijemos solo en el proceso de TDD y no en el código. Pero por si alguno no está familiarizado con el término refactorizar, os dejo el enlace de su definición: Refactorización

Tras esto tendríamos listo nuestro código con nuestras pruebas y la implementación de nuestra lógica de negocio, con lo que podríamos pasar al siguiente requisito.

Se que es una pequeña (muy pequeña) introducción al término de TDD pero creo que puede ser útil para aquellos que lo desconozcan o nunca lo hayan utilizado. Nos vemos.

TDD

Añadir una libraría a nuestro repositorio maven

Aunque muchas veces lo parezca, maven no tiene todas las librerías que podamos necesitar, generalmente por problemas de licencias y demás, o simplemente, si queremos añadir a nuestro repositorio local una librería que hemos creado nosotros mismos para poder utilizarla en otros proyectos, en ambos casos, podemos instalar de forma manual dicha librería en nuestros repositorios locales.

¿Qué necesitamos para ello? Nada más fácil, la librería, un pequeño comando de consola, y saber un poco que estamos haciendo, que es lo que pretendo explicar en este artículo.

Imaginemos que como librería para el ejemplo vamos a utilizar una llamada “example-1.0.jar”, y que dicha librería es la que queremos añadir a nuestro repositorio local.

El comando a ejecutar, es tan simple como:

mvn install:install-file -Dfile=<libraryName> -DgroupId=<groupId> -DartifactId=<artifactId> -Dversion=<version> -Dpackaging=jar

Como veis es bastante simple, y cualquiera ligeramente familiarizado con maven puede reconocer que son cada uno de los parámetros, ya que son los habituales datos que rellenamos cuando añadimos las dependencias en nuestro “pom.xml”.

Con nuestra librería ejemplo, esto quedaría algo así:

mvn install:install-file -Dfile=example-1.0.jar -DgroupId=org.example -DartifactId=example -Dversion=1.0 -Dpackaging=jar

Tras esto, solo tendríamos que ir a nuestro proyecto e incluir la dependencia de la forma habitual:

<dependency>
    <groupId>org.example</groupId>
    <artifactId>example</artifactId>
    <version>1.0</version>
</dependency>

Y con esto ya estaríamos. Nos vemos.

Añadir una libraría a nuestro repositorio maven

MyBatis: Herencia de objectos

Hoy tengo uno de esos casos algo peculiares para los cuales no hay una solución buena o quizás todas sean buenas, nunca se sabe. De lo que estoy seguro es de que hay otras soluciones, y seguro que muchas de ellas mejores, sobre todo, porque yo estoy empezando a trabajar ahora con MyBatis y mis conocimientos sobre este framework no son muy extensos aún. Pero bueno, como ya sabéis el motivo del blog es aprender, y una forma de hacer esto es escribir lo que uno aprende y, posteriormente, revisar las cosas cuando uno adquiere más conocimiento.

Por ponernos en situación, el caso es este: Imaginemos que tenemos una aplicación con tres clases, las cuales extienden unas de otras, de forma que:

  • La clase A no extiende de ninguna.
  • La clase B extiende a A.
  • La clase C extiende a B.

En esta aplicación se entiende como un objeto completo a la clase C, pero a lo largo de la aplicación en determinadas ocasiones solo se requieren los datos que posee A o B, no siendo necesario tener el objeto al completo.

Además de este esquema de clases, en la BBDD, se decidió representar esta jerarquía de clases de igual modo, de forma que tenemos una tabla por cada clase y todas ellas comparten el mismo identificador.

Tras todo esto, se decidió implementar en MyBatis cumpliendo todas las restricciones y necesidades.

Antes de seguir leyendo, este artículo no es un tutorial de MyBatis (probablemente escriba uno, pero no hoy). Este artículo es solo la resolución de un caso muy concreto, otra de estas cosas que surgen y hay que solucionar sin poderse replantear el diseño de la aplicación.

Para ver como resolver esto, y repito, habrá muchas y mejores soluciones, vamos a hacer un pequeño proyecto donde:

  • La clase A será la clase “Mammal”. Con una propiedad “age” y un “id”.
  • La clase B será la clase “Human”. Con una propiedad “name” y un “id”.
  • La clase C será la clase “Gender”. Con una propiedad “gender” y un “id”.

No hagáis mucho caso a los nombre de las clases y centrados en las relaciones.

Para el ejemplo, vamos a utilizar Java y una BBDD PostgreSQL.

Lo primero, es crea un pequeño proyecto maven y añadirle las siguientes dependencias:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>MyBatis</artifactId>
    <version>1.0</version>
    <packaging>war</packaging>
    <name>MyBatis</name>
	
    <properties>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
	
    <dependencies>
	
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.2.7</version>
        </dependency>

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>9.3-1101-jdbc41</version>
        </dependency>
	
    </dependencies>
	
    <build>
        <finalName>mybatis</finalName>
    </build>
</project>

El siguiente paso, si no lo hemos hecho ya, es crear una BBDD para nuestro ejemplo:

drop table if exists mammal;

create table mammal (
    id integer primary key, 
    age integer
);

drop table if exists human;

create table human (
    id integer primary key, 
    name varchar(50)
);

drop table if exists gender;

create table gender (
    id integer primary key, 
    gender varchar(50)
);

Tras esto, vamos a hacer una configuración básica de MyBatis para que conecte con nuestra BBDD:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <!-- Alias generation -->
    <typeAliases>
        <package name="org.example.model" />
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="UNPOOLED">
                <property name="driver" value="org.postgresql.Driver" />
                <property name="url" value="jdbc:postgresql:earthlife" />
                <property name="username" value="postgres" />
                <property name="password" value="toor" />
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="org/example/model/MammalMapper.xml" />
        <mapper resource="org/example/model/HumanMapper.xml" />
        <mapper resource="org/example/model/GenderMapper.xml" />
    </mappers>

</configuration>

Y ya nos lanzamos a la implementación.

En primer lugar nuestros objectos:

package org.example.model;

public class Mammal {
    private int id;
    private int age;

    public int getId() {
        return this.id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return this.age;
    }

    public void setAge(int age) {
        this.age = age;
    }

}
package org.example.model;

public class Human extends Mammal {
    private int id;
    private String name;

    @Override
    public int getId() {
        return this.id;
    }

    @Override
    public void setId(int id) {
        this.id = id;
        super.setId(id);
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
package org.example.model;

public class Gender extends Human {
    private int id;
    private String gender;

    @Override
    public int getId() {
        return this.id;
    }

    @Override
    public void setId(int id) {
        this.id = id;
        super.setId(id);
    }

    public String getGender() {
        return this.gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

}

En segundo lugar, los interfaces de los Mappers:

package org.example.mappers;

import java.util.List;

import org.example.model.Mammal;

public interface MammalMapper {
    Mammal getMammalById(int id);

    List<Mammal> getAllMammals();

    int insertMammal(Mammal mammal);

    int updateMammal(Mammal mammal);

    int deleteMammal(int id);
}
package org.example.mappers;

import java.util.List;

import org.example.model.Human;

public interface HumanMapper {
    Human getHumanById(int id);

    List<Human> getAllHumans();

    int insertHuman(Human human);

    int updateHuman(Human human);

    int deleteHuman(int id);

    Human getHumanMammalById(int id);

    List<Human> getAllHumanMammals();

    int insertHumanMammal(Human human);

    int updateHumanMammal(Human human);

    int deleteHumanMammal(int id);
}
package org.example.mappers;

import java.util.List;

import org.example.model.Gender;

public interface GenderMapper {
    Gender getGenderById(int id);

    List<Gender> getAllGenders();

    int insertGender(Gender gender);

    int updateGender(Gender gender);

    int deleteGender(int id);

    Gender getGenderHumanById(int id);

    List<Gender> getAllGenderHumans();

    int insertGenderHuman(Gender gender);

    int updateGenderHuman(Gender gender);

    int deleteGenderHuman(int id);

    Gender getGenderHumanMammalById(int id);

    List<Gender> getAllGenderHumanMammals();

    int insertGenderHumanMammal(Gender gender);

    int updateGenderHumanMammal(Gender gender);

    int deleteGenderHumanMammal(int id);
}

Y, por último, los Mappers en XML:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="org.example.mappers.MammalMapper" >

    <resultMap type="Mammal" id="MammalResultMap">
        <id column="id" property="id" jdbcType="INTEGER" />
        <result column="age" property="age" jdbcType="INTEGER" />
    </resultMap>
    
    <select id="getMammalById" parameterType="int" resultMap="MammalResultMap">
        select id,
               age
          from mammal
         where id = #{id}
    </select>
    
    <select id="getAllMammals" parameterType="int" resultMap="MammalResultMap">
        select id,
               age
          from mammal
    </select>
    
    <insert id="insertMammal" parameterType="Mammal">
        insert into mammal
                    (id, age)
             values (#{id, jdbcType=INTEGER}, #{age, jdbcType=INTEGER})
    </insert>
    
    <update id="updateMammal" parameterType="Mammal">
        update mammal
           set age = #{age, jdbcType=INTEGER}
         where id = #{id, jdbcType=INTEGER}
    </update>
    
    <delete id="deleteMammal" parameterType="int">
        delete from mammal
         where id = #{id, jdbcType=INTEGER}
    </delete>
    
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="org.example.mappers.HumanMapper" >

    <resultMap type="Human" id="HumanResultMap">
        <id column="id" property="id" jdbcType="INTEGER" />
        <result column="name" property="name" jdbcType="VARCHAR" />
    </resultMap>
    
    <resultMap type="Human" id="HumanMammalResultMap">
        <id column="id" property="id" jdbcType="INTEGER" />
        <result column="age" property="age" jdbcType="INTEGER" />
        <result column="name" property="name" jdbcType="VARCHAR" />
    </resultMap>
    
    <!-- Human -->
    
    <select id="getHumanById" parameterType="int" resultMap="HumanResultMap">
        select id,
               name
          from human
         where id = #{id}
    </select>
    
    <select id="getAllHumans" parameterType="int" resultMap="HumanResultMap">
        select id,
               name
          from human
    </select>
    
    <insert id="insertHuman" parameterType="Human">
        insert into human
                    (id, name)
             values (#{id, jdbcType=INTEGER}, #{name, jdbcType=VARCHAR})
    </insert>
    
    <update id="updateHuman" parameterType="Human">
        update human
           set name = #{name, jdbcType=VARCHAR}
         where id = #{id, jdbcType=INTEGER}
    </update>
    
    <delete id="deleteHuman" parameterType="int">
        delete from human
         where id = #{id, jdbcType=INTEGER}
    </delete>
    
    <!-- Human + Mammal -->
    
    <select id="getHumanMammalById" parameterType="int" resultMap="HumanMammalResultMap">
        select h.id,
               m.age,
               h.name
          from human h
          left outer join mammal m on h.id = m.id
         where h.id = #{id}
    </select>
    
    <select id="getAllHumanMammals" parameterType="int" resultMap="HumanMammalResultMap">
        select h.id,
               m.age,
               h.name
          from human
          left outer join mammal m on h.id = m.id
    </select>
    
    <insert id="insertHumanMammal" parameterType="Human">
        insert into mammal
                    (id, age)
             values (#{id, jdbcType=INTEGER}, #{age, jdbcType=INTEGER});
        insert into human
                    (id, name)
             values (#{id, jdbcType=INTEGER}, #{name, jdbcType=VARCHAR})
    </insert>
    
    <update id="updateHumanMammal" parameterType="Human">
        update mammal
           set age = #{age, jdbcType=INTEGER}
         where id = #{id, jdbcType=INTEGER};
        update human
           set name = #{name, jdbcType=VARCHAR}
         where id = #{id, jdbcType=INTEGER}
    </update>
    
    <delete id="deleteHumanMammal" parameterType="int">
        delete from human
         where id = #{id, jdbcType=INTEGER};
        delete from mammal
         where id = #{id, jdbcType=INTEGER}
    </delete>
    
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="org.example.mappers.GenderMapper" >

    <resultMap type="Gender" id="GenderResultMap">
        <id column="id" property="id" jdbcType="INTEGER" />
        <result column="gender" property="gender" jdbcType="VARCHAR" />
    </resultMap>
    
    <resultMap type="Gender" id="GenderHumanResultMap">
        <id column="id" property="id" jdbcType="INTEGER" />
        <result column="name" property="name" jdbcType="VARCHAR" />
        <result column="gender" property="gender" jdbcType="VARCHAR" />
    </resultMap>
    
    <resultMap type="Gender" id="GenderHumanMammalResultMap">
        <id column="id" property="id" jdbcType="INTEGER" />
        <result column="age" property="age" jdbcType="INTEGER" />
        <result column="name" property="name" jdbcType="VARCHAR" />
        <result column="gender" property="gender" jdbcType="VARCHAR" />
    </resultMap>
    
    <!-- Gender -->
    
    <select id="getGenderById" parameterType="int" resultMap="GenderResultMap">
        select id,
               gender
          from gender
         where id = #{id}
    </select>
    
    <select id="getAllGenders" parameterType="int" resultMap="GenderResultMap">
        select id,
               gender
          from gender
    </select>
    
    <insert id="insertGender" parameterType="Human">
        insert into gender
                    (id, gender)
             values (#{id, jdbcType=INTEGER}, #{gender, jdbcType=VARCHAR})
    </insert>
    
    <update id="updateGender" parameterType="Human">
        update gender
           set gender = #{gender, jdbcType=VARCHAR}
         where id = #{id, jdbcType=INTEGER}
    </update>
    
    <delete id="deleteGender" parameterType="int">
        delete from gender
         where id = #{id, jdbcType=INTEGER}
    </delete>
    
    <!-- Gender + Human -->
    
    <select id="getGenderHumanById" parameterType="int" resultMap="GenderHumanResultMap">
        select g.id,
               h.name,
               g.gender
          from gender g
          left outer join human h on g.id = h.id
         where g.id = #{id}
    </select>
    
    <select id="getAllGenderHumans" parameterType="int" resultMap="GenderHumanResultMap">
        select g.id,
               h.name,
               g.gender
          from gender g
          left outer join human h on g.id = h.id
    </select>
    
    <insert id="insertGenderHuman" parameterType="Gender">
        insert into human
                    (id, name)
             values (#{id, jdbcType=INTEGER}, #{name, jdbcType=VARCHAR});
        insert into gender
                    (id, gender)
             values (#{id, jdbcType=INTEGER}, #{gender, jdbcType=VARCHAR})
    </insert>
    
    <update id="updateGenderHuman" parameterType="Gender">
        update human
           set name = #{name, jdbcType=VARCHAR}
         where id = #{id, jdbcType=INTEGER};
        update gender
           set gender = #{gender, jdbcType=VARCHAR}
         where id = #{id, jdbcType=INTEGER}
    </update>
    
    <delete id="deleteGenderHuman" parameterType="int">
        delete from gender
         where id = #{id, jdbcType=INTEGER};
        delete from human
         where id = #{id, jdbcType=INTEGER}
    </delete>
    
    <!-- Gender + Human + Mammal -->
    
    <select id="getGenderHumanMammalById" parameterType="int" resultMap="GenderHumanMammalResultMap">
        select g.id,
               m.age,
               h.name,
               g.gender
          from gender g
          left outer join human h on g.id = h.id
          left outer join mammal m on g.id = m.id
         where g.id = #{id}
    </select>
    
    <select id="getAllGenderHumanMammals" parameterType="int" resultMap="GenderHumanMammalResultMap">
        select g.id,
               m.age,
               h.name,
               g.gender
          from gender g
          left outer join human h on g.id = h.id
          left outer join mammal m on g.id = m.id
    </select>
    
    <insert id="insertGenderHumanMammal" parameterType="Gender">
        insert into mammal
                    (id, age)
             values (#{id, jdbcType=INTEGER}, #{age, jdbcType=INTEGER});
        insert into human
                    (id, name)
             values (#{id, jdbcType=INTEGER}, #{name, jdbcType=VARCHAR});
        insert into gender
                    (id, gender)
             values (#{id, jdbcType=INTEGER}, #{gender, jdbcType=VARCHAR});
    </insert>
    
    <update id="updateGenderHumanMammal" parameterType="Gender">
        update mammal
           set age = #{age, jdbcType=INTEGER}
         where id = #{id, jdbcType=INTEGER};
        update human
           set name = #{name, jdbcType=VARCHAR}
         where id = #{id, jdbcType=INTEGER};
        update gender
           set gender = #{gender, jdbcType=VARCHAR}
         where id = #{id, jdbcType=INTEGER}
    </update>
    
    <delete id="deleteGenderHumanMammal" parameterType="int">
        delete from gender
         where id = #{id, jdbcType=INTEGER};
        delete from human
         where id = #{id, jdbcType=INTEGER};
        delete from mammal
         where id = #{id, jdbcType=INTEGER}
    </delete>
    
</mapper>

Como podéis ver, todo está preparado para poder trabajar con los tres objetos a lo largo de todas la aplicación, y que para aquellos que heredan se pueda obtener el objeto entero con todas sus propiedades, o solo partes de dicho objeto.

La única cosa digna de mención, ya que el código es fácilmente entendible, es la utilización del objeto “super” en los constructores, para poder hacer una asignación encadenada de dicho “id” ya que este es compartido por los tres objetos en las tres tablas. Si no lo añadiéramos, tendríamos en muchas ocasiones los ids de los objetos padres a null.

Aunque otra cosa que debería de nombrar es que el código no es independiente de la BBDD que utilicemos. Quiero decir, PostgreSQL soporte la ejecución consecutiva de varias instrucciones tal y como están en los Mappers, pero si utilizáramos una BBDD Oracle, por ejemplo, tendríamos que encerrar esas sentencias consecutivas entre un “begin” y un “end;”:

begin
    select ...;
    select ...;
    ...
end;

Estoy casi seguro de que se puede acortar mucho más el código trabajando con herencia de los objetos resultado en MyBatis o quizás reutilizando las sentencias SQL de unos mappers a otros, pero de momento, eso, está aún por investigar.

Nos vemos.

MyBatis: Herencia de objectos

El límite está en tu mente no en el lenguaje

Se que por el título del artículo, más de uno pensará que me voy a poner a filosofar sobre unas cosas u otras, pero nada dista más de mi intención. Lo que pasa es que al igual que no se porque he escrito la porción de código que os traigo hoy, no sabía que título ponerle.

Hoy es uno de esos días donde en una discusión sobre diseño, salta alguien diciendo si tal cosa o tal otra se puede o no se puede hacer, y si Java no lo soporta y ese tipo de discusiones que no llegan a ningún lado. En concreto, era el paso como parámetro de una función (véase que estamos por razones de proyecto usando la versión 1.7 y no la 1.8). Es cierto, que en este punto no te queda más remedio que concederle la razón, sobre todo si entra en comparaciones con JavaScript (salvando las distancias). Pero, he aquí que a uno después de estas discusiones, se le queda un no sé qué que que se yo que tiene que ponerse a escribir algo de código a ver que puede hacer.

La idea, era escribir una operación de CRUD que en ciertas ocasiones necesita crear una transacción y en otras no. Lo más común es duplicar los métodos, en uno la lógica y en otro el manejo de la transacción y la llamada al que tiene la lógica (seguro que habrán métodos mejores, peores y de todo tipo, pero no es el punto de este artículo).

Pues bien, para hacer una aproximación de como pasar funciones como parámetros (sin que el lenguaje lo permita) yo me he decantado por una clase abstracta. A continuación, os pongo el código y luego, lo explicaré un poco, aunque es muy básico.

package com.wordpress.infow.chorradas;

import org.apache.log4j.Logger;

import com.wordpress.infow.db.TransactionManager;

public abstract class GenericExec {

    static Logger logger = Logger.getLogger(GenericExec.class);

    public static final int CREATE = 0;
    public static final int READ = 1;
    public static final int UPDATE = 2;
    public static final int DELETE = 3;

    abstract public String create(Object obj, TransactionManager tr);

    abstract public Object read(Object obj, TransactionManager tr);

    abstract public void update(Object obj, TransactionManager tr);

    abstract public void delete(Object obj, TransactionManager tr);

    public Object withTransaction(GenericExec generic, Object data, int operation) {
        Object obj = null;

        TransactionManager tr = new TransactionManager();

        try {
            tr.initTransaction();

            switch (operation) {
                case CREATE:
                    obj = generic.create(data, tr);
                    break;

                case READ:
                    obj = generic.read(data, tr);
                    break;

                case UPDATE:
                    obj = generic.update(data, tr);
                    break;

                case DELETE:
                    obj = generic.delete(data, tr);
                    break;
            }

            tr.commit();
        } catch (Exception e) {
            GenericExec.logger.error(generic.getClass() + "::" + operation + " " + e.getMessage());
        } finally {
            tr.closeTransaction();
        }

        return obj;
    }
}

Como se puede ver, la clase abstracta obliga a implementar los métodos que deseamos, las operaciones de CRUD, y posee la implementación de la función que nos va a permitir ejecutar estas con una transacción alrededor. Todo muy básico. Además, de esta clase van a extender muchas otras que va a recibir parámetros de tipo diferente y devolver parámetros de tipo diferente para cada una de las clases que heredan.

Yo creo que no necesita más explicación. Tiene algunas pegas, como que hay que tener la precaución de realizar los casting apropiados desde el objeto Object al que deseamos utilizar, pero por lo demás es bastante manejable.

De nuevo, recordados que esto solo es un juguete, una chorrada implementada a partir de un debate sobre algo, y sin más sentido que como reto o idea, y ver que aunque muchas veces un lenguaje no nos provee de algo, siempre podemos implementarnos algo, como reza el título del artículo, los límites muchas veces están en la mente del desarrollado no en el lenguaje. Y no hablo de inteligencia, sino de ganas de jugar un rato con el lenguaje o falta de tiempo, o cosas de este estilo.

Bueno, espero que como curiosidad os haya gustado. Si alguien tiene alguna duda, preguntad sin miedo. Nos vemos.

El límite está en tu mente no en el lenguaje

Planificar tareas en Java

En algunas ocasiones nos vemos en la necesidad de que nuestras aplicaciones lancen alguna tarea en segundo plano para llevar a cabo algún procedimiento, generar algún informe, recolectar nuevas alertas, … este tipo de cosas. En este caso lo mejor es lanzarlas en segundo plano y no en el hilo principal. Para esto, Java nos ofrece tres posibilidades diferentes que vamos a ver a continuación a modo de chuleta de fácil y rápido acceso.

La primera de las formas es usando un simple hilo. Crearíamos el hilo con la tareas, lo lanzaríamos indefinidamente y le asignaríamos un intervalo de espera. Esto quedaría algo así:

public class Example1 {
    public static void main(String[] args) {
        final long interval = 2000; // Each 2 seconds
        Runnable runnable = new Runnable() {
            public void run() {
                while (true) {
                    System.out.println("I am here!!!");
                    try {
                        Thread.sleep(interval);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        Thread thread = new Thread(runnable);
        thread.start();
    }
}

Como podéis ver es bastante simple. Basta con crear el objeto Runnable, crear el comportamiento que deseamos y lanzarlo. No tenemos mucho control sobre cuando empezar o parar el hilo y el proceso se lanzará de forma inmediata, pero el objetivo era ser simple.

El siguiente ejemplo que vamos a ver cubre las necesidades que no nos ofrece la solución anterior, que en definitiva son, tener un poco más de control sobre muestro proceso. Para ello vamos a utilizar las clases Timer y TimerTask. Además, la instancia de Timer es thread-safe.

public class Example2 {
    public static void main(Strings[] args) {
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println("I am here!!!");
            }
        };

        Timer timer = new Timer();
        long delay = 0;
        long interval = 2000;

        timer.scheduleAtFixedRate(task, delay, interval);
    }
}

Como se puede ver en este ejemplo, aunque un poquito más complejo, nos ofrece la posibilidad de definir más parámetros para nuestra tarea.

El tercer y último método que vamos a ver, nos permite aún más flexibilidad de la que ya teníamos en el caso anterior. Las clases que nos permiten la implementación de esta última fueron añadidas en Java SE 5.

public class Example3 {
    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            public void run() {
                system.out.println("I am here!!!");
            }
        };

        ScheduledExecutorService ses = Executors.
                newSingleThreadScheduledExecutor(runnable, 0, 1, TimeUnit.SECONDS);
    }
}

Este como vemos es en el que menos código escribimos, el más configurable a la hora de lanzar nuestro proceso y, además, aunque aquí no lo hemos usado, nos ofrece la posibilidad de tener un Pool de hilos para las ejecuciones.

Bueno, hasta aquí todo por hoy. Nos vemos.

Nota: Basado en el post How to Schedule a task to run in an interval.

Planificar tareas en Java

JavaFX – Nuevo repositorio

Hace ya un par de meses os anuncié que había creado una cuenta en Bitbucket donde albergar repositorios Git, algunos de ellos públicos otros privados, donde iba a albergar cosillas que fuera haciendo. De hecho, en el blog hay publicadas varias entradas con ejemplos en JavaEE y su código fuente correspondiente está albergado en estos repositorios.

Pues bien, hoy uno de esos repositorios que he creado públicos es el que va a estar dedicado a JavaFX, podéis encontrarlo aquí. De momento, solo he añadido los proyectos que se corresponde con el tutorial de JavaFX de Oracle en su sección “Getting Started…“, pero espero ir añadiendo más cosillas poco a poco.

Bueno, solo era comentar simplemente esto. Nos vemos.

JavaFX – Nuevo repositorio

Simplificando proyectos

Hoy, solo os traigo una charla de Adam Bien sobre el código que sobra en nuestros proyectos y la enorme cantidad de veces que tenemos en ellos mucho más de lo que necesitamos. Me ha sorprendio, entre otras cosas a parte de resultarme interesante, la cantidad de cosas que comenta que no se deberían hacer y que si que se hacen en la mayoría de proyectos grandes en los que he participado. Obviamente, esto es una opinión de él, pero con bastante sentido común muchas de las cosas que comenta.

Para quien no lo conozca, es un Freelancer que lleva trabajando muchísimos años en Java, y como él dice, aún lo disfruta. Además, ha escrito un par de libros (que tengo en mi lista para leer) y, de vez en cuando, da charlas sobre JavaEE. Los libros son:

Pero bueno, os dejo su página si queréis echarle un ojo: Adam Bien

El video, como ya he dicho, es bastante interesante. Su duración es de menos de una hora (unos 45 minutos) y, aunque está en inglés, es fácimente entendible.

Espero que os guste. Nos vemos.

Simplificando proyectos

Node.js

Hoy vamos a hacer nuestra primera prueba con Node.js. Para quien no lo conozca o haya oido hablar de él, es una tecnología de servidor basada en JavaScript. Sí, habéis oido bien: JavaScript. Node.js esta implementado con una arquitectura orientada a eventos lo que lo hace altamente escalable y soprendentemente rápido. Ademas, se basa en el motor de JavaScript V8 creado por Google y que sigue evolucionando. Al contrario que el JavaScript al que estamos acostumbrado Node.js no trabaja en el navegador (N. del A: ¡bien!) si no en el lado del servidor. Así que, todos aquellos que os dedicabais a back-end y creíais que os ibais a librar de JavaScript, vais a tener que replanteároslo. Y la verdad, es que nunca está de más conocerlo ya que muchas empresas punteras lo usan y muchas lo están adoptando.

Node.js viene por defecto con muchos módulos incoporados que nos harán la vida muy fácil, ya que podremos usarlos simplemente importándolos en nuestro código. Además, tiene un gestor de módulos (npm) que nos permite incorporar módulos de terceros. Sobre todo esto, os recomiendo leer algo de documentación sobre Node.js, ya que el objetivo de este post, es solo hacer un pequeño ejemplo y no explicar ampliamente Node.js. Evidentemente, intentaré explicar todo aquello que se haga en el código lo mejor posible.

Antes de empezar, no prometo el mejor código, ni el más limpio y el más optimo, pero si que intentaré explicarlo todo como ya he dicho. Al igual que para muchos de vosotros, esta es una de mis primeras pruebas.

Vamos a ello.

La aplicación que vamos a realizar tendrá un total de cuatro ficheros y será la típica aplicación con un formulario y que mostrará un mensaje. No he hecho ningún tipo de separación MVC, ni he utilizado ninguna librería de terceros ni extraña, he intentado hacerla lo más simple posible, pero a la vez, que se pueda escalar y trabajar de forma cómoda con ella. Los ficheros son los siguientes:

  • app.js: Fichero que realizará el importe de los módulos implementados por nosotros mismos y que lanzará el servidor implementado con Node.js.
  • server.js: Será el módulo que contenga el servidor que atenderá las peticiones y procesará dicha petición para obtener todos los datos.
  • router.js: Será el módulo que envíe la petición a una función u otra según la URL solicitada. Será una especie de controlador.
  • requestHandlers.js: Será el módulo que tenga las funciones que procesen las distintar peticiones y a las que llegaremos desde el “controlador”.

Vamos a empezar por el fichero “app.js” que será una especie de main de nuestra aplicación.

El código del fichero “app.js” será el siguiente:


var server = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");

var handle = {};
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/show"] = requestHandlers.show;

server.start(router.route, handle);

En las primeras líneas vemos como se importan varios módulos que vamos a crear posteriormente, ya explicaremos como.

Luego se ha definido una especie de mapa, que relacionará la ruta de nuestra petición con una función del manejador que va a ser llamada.

Y finalmente, lanzaremos la instancia de nuestro servidor para que empiece a atender peticiones.

El siguiente paso es el fichero “server.js”. Como ya he dicho va a ser un módulo que arrancará nuestro servidor y procesará las peticiones.

Lo primero que vamos a hacer es ver como definir un módulo que podamos usar nosotros e importar en otros fichero más tarde de forma fácil. Para crear un módulo tendremos que tener dos cosas en cuenta:

  1. Necesitaremos una función que será la que contenga el código de nuestro módulo.
  2. Necesitaremos hacer un export al final del fichero para poderlo importar en otros ficheros.

El código del fichero “server.js” será el siguiente:


var http = require("http");
var url = require("url");

function start(route, handle) {
    function onRequest(request, response) {
        var postData = "";
        var pathname = url.parse(request.url).pathname;
        console.log("Request received for " + pathname + ".");

        request.setEncoding("utf8");
        request.addListener("data", function(postPiece) {
            postData += postPiece;
            console.log("Recibido trozo POST '" + postPiece + "'.");
        });

        request.addListener("end", function() {
            route(handle, pathname, response, postData);
        });
    }

    http.createServer(onRequest).listen(8888);
    console.log("Server started.");
}

exports.start = start;

Las dos primeras líneas son imports de módulos que trae consigo Node.js. Nos ayudarán a montar nuestro servidor y a procesar las peticiones.

En segundo lugar tenemos la función “start” que es la que contendrá el código de nuestro módulo donde definiremos una función para procesar las peticiones y crearemos el servidor.

Dentro de la función “onRequest”, crearemos dos listeners para procesar la petición:

  • Listener “data”: Procesará la petición mientras va llegando la información ya que no sabemos el tamaño que tiene dicha información.
  • Listener “end”: Evento que se lanza cuando ha llegado toda la información.

Finalmente, la penúltima línea crea nuestro servidor poniendolo a la escucha en el puerto 8888 y recibe como parámetro nuestro método para procesar peticiones.

Como habéis podido observar, cuando el evento de recepción completa de la petición (end) se lanza, se llama a la función “route” que está definida por nosotros.

Siguiendo nuestro avance, vamos a generar el fichero “router.js” que tendrá ese enrutador o controlador que será el que le diga a cada petición que función tiene que llamar. Como al principio definimos nuestra variable manejador (handler) esto será bastante fácil y el código bastante simple.

El código del fichero “router.js” será el siguiente:


function route(handle, pathname, response, postData) {
    console.log("Starting to enroute request from " + pathname);
    if (typeof handle[pathname] === 'function') {
        return handle[pathname](response, postData);
    } else {
        console.log("Handler not found for " + pathname);
        response.writeHead(404, {"Content-Type": "text/html"});
        response.write("404 Not found");
        response.end();
    }
}

exports.route = route;

Aquí de nuevo hemos creado la clase en forma de módulo para poder importarlo posteriormente. en la función no hay más que una pequeña comprobación de si con la ruta se puede llegar a alguna función y la llamada a esta. En caso de error, simplemente nos mostrará el error típico de página no encontrada.

Y finalmente, tendremos el módulo que contendrá las funciones que se ejecutarán según la petición que nos haya llegado. Aquí yo voy a meter las vistas y demás (sin MVC) ya que esto es un ejemplo básico.

El código del fichero “requestHandler.js” será el siguiente:


var querystring = require("querystring");

function start(response, postData) {
    console.log("Start handler has been called.");

    var body = '<html>' +
        '<head>' +
        '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />' +
        '</head>' +
        '<body>' +
        '<form action="/show" method="post">' +
        '<input type="text" name="text" />' +
        '<input type="submit" value="Submit text" />' +
        '</form>' +
        '</body>' +
        '</html>';

    response.writeHead(200, {"Content-Type": "text/html"});
    response.write(body);
    response.end();
}

function show(response, postData) {
    console.log("Show handler has been called.");

    var text = querystring.parse(postData)["text"] || '';
    if (text === '') {
        response.writeHead(301, {Location: '/start'});
        response.end();
    } else {
        response.writeHead(200, {"Content-Type": "text/html"});
        response.write("You have sent: " + querystring.parse(postData)["text"]);
        response.end();
    }
}

exports.start = start;
exports.show = show;

Aquí hay más bien poco que comentar. Se declaran las funciones y se les asigna contenido. Una de ellas muestra un pequeño formulario y la otra procesa la petición mostrando un mensaje.

Como habréis notado, estas son las funciones que hemos linkado con las rutas de las peticiones en el objeto manejador en el fichero “app.js”.

Sobre el código no hay que decir mucho más, ya que en si es bastante simple, pero si que quiero añadir un par de notas más para que las tengáis en cuenta a la hora de diseñas vuestras primeras aplicaciones.

En algún sitio leí que todo el código en Node.js se ejecuta en paralelo menos el nuestro. Así dicho, suena un poco confuso, pero pensad por un momento que nuestras funciones en la clase requestHandler devolvieran las respuestas en un return para que otra función, más arriba, los pintará (evitando así tener que pasar el objeto request, por ejemplo). Aunque dichas funciones se podrían ejecutar en paralelo todas según el número de peticiones que haya, nuestro método encargado de pintarlas, solo lo podría hacer de una en una. Si añadimos a esto que podríamos ejecutar una función que tarde mucho en devolver un valor, tendríamos todas las peticiones paradas sin sentido.

Un metáfora de esto que leí, lamentablemente no recuerdo donde pero se me quedo grabada es: Imagina un rey que se levanta y le manda una lista de tareas a los sirvientes, estos podrán hacerlas todas de forma paralela, pero el rey solo podrá recibir los reportes de uno en uno cuando los sirvientes hayan terminado.

Como útlima cosa, os dejo un enlace de un tutorial bastante útil, bien explicado y en varios idiomas que también podéis seguir.

The Node Beginner Book

Nos vemos.

Node.js

Preguntas en entrevistas Java

Como ya sabéis no me gusta poner contenido que no haya sido escrito, desarrollado o al menos trabajado por mi, pero hoy vamos a hacer una excepción. Uno de los blogs que sigo es el de Java Code Geeks y hoy, ha publicado un interesante artículo con multitud de preguntas que te pueden aparecer en entrevista relacionadas con Java. No dejaría de ser otra recopilación de las que ya hay muchas, pero está en concreto me ha llamado mucho la atención porque muchas de las preguntas las reconozco de entrevistas que he ido haciendo en los últimos meses, cosa que no me había pasado con otras recopilaciones de este tipo. Por esto, porque considero que es una lista muy interesante y muy acertada, os dejo el enlace aquí para que podáis echarle un ojo. Me temo que está en el lenguaje de Shakespeare, pero incluso si vuestro nivel no es muy bueno, yo creo que seréis capaces de entenderlas.

Core Java Interview Questions

Espero que os sea de utilidad. Nos vemos.

Preguntas en entrevistas Java

MariaBD

Hoy he tenido que preparar un pequeño entorno de desarrollo para proyectos personales ya que el que tenía montado hasta ahora era un poco chapucerillo y, entre otras cosas, he tenido que instalar un servidor de BBDD. En principio, la fuerza de la costumbre ha hecho mis dedos volar sobre la consola y, sin apenas darme cuenta, ya tenía instalado MySQL para cubrir este aspecto. Pero, pensando un poco más, en estos últimos días he estado leyendo sobre MariaDB y he decidido instalarlo. Así que este pequeño post no son más que unos pequeños pasos de cómo instalarlo en una distribución Debian Squeeze y una pequeña opinión sobre lo poco visto hasta ahora.

Para el que no lo sepa, MariaDB es el fork que se hizo de MySQL cuando Oracle adquirió SUN. Si realmente no conocíais este proyecto, os recomiendo que leáis un poco las razones de porque se hizo, solo para que tengáis una versión sobre ello y podáis opinar. Pero bueno, sin entretenernos más, vamos a ello.

La instalación de este gestor de BBDD es bastante simple, aunque obviamente no tanto como la de MySQL, la principal razón para esto es que aún no figura entre los paquetes por defecto que llevan las distintas distribuciones, cosa que MySQL si que hace. Como suelen decir,  “la veteranía es un grado”. Pero bueno, la ventaja es que si que existen repositorios para varias distribuciones muy fácilmente añadibles. Tenemos repositorios para: CentOS, Debian, Fedora, Mageia, Mint, openSUSE, RedHat, Ubuntu. Echo en falta alguna como Arch Linux, pero más o menos están todas las grandes y alguna que no conocía ¿Mageia?

En esta dirección perteneciente a la página del proyecto, podéis encontrar un configurador de repositorios para, en unos pocos clicks de ratón, tener el vuestro. En ella solo tenéis que hacer click en vuestra distribución, versión de está, versión del MariaDB que queréis instalar y localización de los repositorios. El resultado será algo así:

# MariaDB 10.0 repository list - created 2013-03-31 07:43 UTC
# http://mariadb.org/mariadb/repositories/
deb http://mirror.netcologne.de/mariadb/repo/10.0/debian squeeze main
deb-src http://mirror.netcologne.de/mariadb/repo/10.0/debian squeeze main

Tras esto, solo habrá que añadir estas líneas al resto de repositorios que tenemos en nuestra máquina. En el caso de Debian, donde lo estoy haciendo yo en estos momentos, basta con añadir estas líneas al final del fichero:

/etc/apt/sources.list

Para el resto de distribuciones es algo similar, o podemos echar mano de los gestores gráficos de paquetes que vienen en ellas para añadir el repositorio. No suele tener mayor dificultad.

Tras esto, deberemos añadir las claves del repositorio para que el aptitude no nos muestre mensajes de advertencia:

apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 0xcbcb082a1bb943db

Finalmente, para instalarlo en nuestro sistema no necesitaremos más que instalar el servidor con sus paquetes correspondientes:

aptitude install mariadb-server

Esto instalará el servidor y sus dependencias. Aquí ha sucedido algo que no me ha acabado de convencer, y es que como ya he mencionado antes, tenía instalado MySQL y el problema es que para instalar MariaDB se ha tenido que desinstalar forzosamente. Entiendo que al ser un fork una de la otra, los paquetes, las configuraciones y demás son comunes, pero ¿qué pasa si solo tengo una máquina y ya varios proyecto en MySQL en ella y quiero empezar uno nuevo con MariaDB? Creo que deberían buscar alguna forma de convivir, pero bueno, esto es solo una opinión personal.

Tras esto, he estado leyendo un poco de documentación y he hecho una mini aplicación para probar, algo así como un “Hola Mundo” para lo cual he seguido los siguientes pasos y consideraciones.

Como herramienta de administración he cogido el propio Workbench de MySQL que ya tenía instalado en mi máquina y que en la documentación de MariaDB he leído que era compatible. Ningún problema.

El conector de Java está disponible en su pagina y lo podéis descargar de la siguiente dirección:

Conector Java

Es un jar que solamente debéis añadir como librería al igual que solemos hacer con el conector de MySQL.

A partir de aquí, el proceso es el mismo que con una aplicación de MySQL así que no creo que haga falta entrar en detalle sobre una aplicación.

A modo de opinión personal, y para terminar el artículo, simplemente añadir que las similitudes entre ambos proyecto son muchísimas y desde el punto de vista técnico, a día de hoy no veo la necesidad de cambiar una por la otra. Entiendo las razones por las que se hizo el fork, pero creo que el proyecto tendría que centrarse en desarrollas su propia documentación más extensa y organizada ya que con el tiempo, el fork irá difiriendo más y más de MySQL. Por lo demás, el proyecto parece que se está consolidando poco a poco y quizás algún día pase a ser uno de los grandes, dependiendo de las acciones que lleve a cabo Oracle sobre MySQL. Nos vemos.

MariaBD