Evitando que un error detenga un Timer

Error, pero el programa continua funcionando
Error, pero el programa continua funcionando

La razón mas común por la que usuaria un Timer en su aplicación Java es el tener funciones corriendo en segundo plano, usualmente para transmitir o recibir datos en forma periódica durante un periodo prolongado, usualmente todo el día y sin demasiada vigilancia por parte de un usuario y esto implica preparar su aplicación para que una excepción no trabe o cierra la aplicación, por que le garantizo eso siempre pasara en el peor momento posible.

¿Que detiene un Timer?

El Timer se detiene en el caso de que una excepción no manejada ocurra dentro del método run del TimerTask siendo ejecutado por el Timer, esto es una excepción que no tenga un catch para manejarla, esta excepción saltara mas arriba en su código y en el proceso detendrá el Timer.

¿Como nos afecta esto? bueno recuerde que aunque el compilador le exige capturar algunas excepciones hay varias que pueden aparecer sin avisar como CommunicationsException (la cual aparece si la conexion a una base de datos se pierde por causa de la red) o un error al convertir un JSON a objeto, o una excepción aritmética resultado de una división con mas decimales de lo esperado.

Por fortuna prevenir esto es muy sencillo.

Prevenir que una excepción detenga el Timer.

La mejor forma de prevenir el Timer de ser detenido por una excepción es capturar todas las excepciones posibles y aunque eso es posible capturando cualquier objeto de la clase o subclase Exception se recomienda aprovechar que cada excepción significa algo diferente y excepto algunas cosas como fallos de red o de conexión a base de datos seguro habrá varios casos que pueda corregir

Pero suficiente teoría pasemos a un ejemplo.

Ejemplo.

Usaremos de ejemplo un caso que seguramente le a pasado, que la conexión a una base de datos ser pierda por un momento, esto lo simularemos parando el servicio MySQL mientras el programa corre.

package mx.hashCode.TimerError;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;

public class TimerSQL extends TimerTask {
    static private final Logger LOGGER = Logger.getLogger("mx.hashCode.TimerError.TimerSQL");

    public Connection crearConexion() throws SQLException, ClassNotFoundException {
        Class.forName("com.mysql.cj.jdbc.Driver");
        String usuario = "archivista";
        String passwd = "123456789";

        Connection cx = DriverManager.getConnection ("jdbc:mysql://127.0.0.1:3306/"+ "?useSSL=false&" + "user="+ usuario + "&" + "password=" + passwd + "");

        return cx;
    }

    @Override
    public void run() {
        try {
            String sql = "SELECT * FROM pruebas.juegos;";            
            
            try(Connection cx = this.crearConexion(); Statement consulta = cx.createStatement()) {
                ResultSet data = consulta.executeQuery(sql);

                LOGGER.log(Level.INFO, "Presentaremos los datos");
                System.out.println("");
                while(data.next() == true) {
                    System.out.print(data.getString("nombre") + " ");
                    System.out.println(data.getString("consola"));
                }
                System.out.println("");
                data.close();
            }

        } catch (SQLException | ClassNotFoundException e) {
            LOGGER.log(Level.SEVERE, "OCURRIO UN ERROR DE ACCESO A DB!!!");
            LOGGER.log(Level.SEVERE, "Pero el ciclo deberia repetirse sin problema");
            //LOGGER.log(Level.SEVERE, null, e);
        }
    }

}
package mx.hashCode.TimerError;

import java.util.Timer;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Hello world!
 *
 */
public class App 
{
    private static final Logger LOGGER = Logger.getLogger("mx.hashCode.TimerError.App");

    public static void main( String[] args )
    {
        LOGGER.log(Level.INFO, "Inicializamos el timer");
        Timer timer = new Timer();
        TimerSQL timerSQL = new TimerSQL();

        timer.schedule(timerSQL, 0, 1000*10);
    }
}

Esta es la tabla que consulta el programa.

CREATE TABLE `juegos` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `consola` varchar(100) DEFAULT NULL,
  `nombre` varchar(100) DEFAULT NULL,
  `obtenido` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1

Obviamente deberá modificar los datos de conexión poner algún que otro dato en la tabla, hecho eso corra el programa con el servicio MySQL activo y vera la siguiente salida

Salida Correcta
Salida Correcta

Ahora desactive el servicio MySQL (esto variara dependiendo de su sistema operativo) y vera

Salida sin MySQL
Salida sin MySQL

Y sin embargo el programa no se a caído, como puede ver espera el tiempo que le indicamos, realiza la conexión y si algo sale mal (cosa que se indica con una excepción) el bloque catch correspondiente nos lo informa que ocurrió algo y previene que se detenga el ciclo.

No solo eso, sino que como es en el run donde se efectúa todo el proceso si inicia de nuevo el servicio MySQL

Tras reiniciar MySQL
Tras reiniciar MySQL

El programa vuelve a funcionar sin problema.

Obviamente para sacarle ventaja a todo esto debe diseñar su programa para que una excepción de ese tipo no vaya a causar errores de calculo o duplicidad de datos, la solución mas adecuada dependerá del caso especifico que este manejando.


Espero que esta entrada les fuera de utilidad y si fue así y desean cooperar con la causa.

ko-fi

Anuncios

Ejecutar periódicamente una función en Java

Función periodica
Función periódica

Una de las situaciones con las que seguro tendrá que lidiar al realizar una aplicación es ejecutar un método en forma periódica cada vez que pase un intervalo de tiempo especificado, ya sea para verificar el estado de un sensor, consultar o actualizar una base de datos, generar un reporte o enviar datos a un servidor, pero no se preocupe, hacer esto en Java es muy sencillo gracias a las clases Timer y TimerTask.

Timer

La clase Timer nos permite ejecutar una función en forma periódica a un intervalo especificado, su uso es bastante sencillo basta con crear un objeto Timer y usar el método scheduleAtFixedRate el cual toma tres argumentos que son los siguientes:

  • task, un objeto TimerTask cuyo método run se ejecutara al intervalo indicado
  • delay, la cantidad de milisegundos que queremos esperar antes de comenzar
  • period, cada cuanto en milisegundo queremos ejecutar el método run del objeto TimerTask

Esto en código se ve de la siguiente manera:

temporizador.scheduleAtFixedRate(tarea, 0, 1000*segundos);

Como notara no es nada del otro mundo, basta con pasarle los parámetros indicados y una vez pasado el retraso indicado por delay la función se llamara cada tantos milisegundos hasta que termine el programa o cancele la ejecución del objeto Timer llamando al método cancel.

TimerTask

Ya definimos cuando y cada cuanto queremos que una función se ejecute, ahora llego el momento de definir dicha función, para hacer esto debemos crear una subclase de TimerTask y redefinir el método run de modo que ejecute el código que nosotros queramos, como se ve en el siguiente ejemplo

package mx.com.hash.tareaprogramada;

import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author david
 */
public class Tarea extends TimerTask {
    static private final Logger LOGGER = Logger.getLogger("mx.com.hash.tareaprogramada.Tarea");
    private Integer contador;    

    public Tarea() {
        contador = 0;
    }

    @Override
    public void run() {
        LOGGER.log(Level.INFO, "Numero de ejecución {0}", contador);
        contador++;
    }

}

De nuevo dentro del método run puede poner el código que quiera, llamar a otras clases y demas, no sienta que debe limitarse a funciones de la subclase de TimerTask.

Otro detalle a recordar es que el Timer llama al método run del objeto que le pasamos, de modo que si almacena información en ese objeto esta estará disponible entre cada ejecución del Timer, esto quedara mas claro en el ejemplo.

Ejemplo

Para dejar mas en claro todo esto hagamos un pequeño ejemplo, llamando a una función cada 5 segundos que nos escriba en pantalla cuantas veces hemos llamado a la función, para esto usaremos el siguiente código.

package mx.com.hash.tareaprogramada;

import java.util.Timer;
import java.util.logging.Logger;

/**
 *
 * @author david
 */
public class TareaProgramada {
    static private final Logger LOGGER = Logger.getLogger("mx.com.hash.tareaprogramada.TareaProgramada");

    static public void main(String[] args){
        Tarea tarea = new Tarea();
        Timer temporizador = new Timer();
        Integer segundos = 5;

        temporizador.scheduleAtFixedRate(tarea, 0, 1000*segundos);
    }
}
package mx.com.hash.tareaprogramada;

import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author david
 */
public class Tarea extends TimerTask {
    static private final Logger LOGGER = Logger.getLogger("mx.com.hash.tareaprogramada.Tarea");
    private Integer contador;    

    public Tarea() {
        contador = 0;
    }

    @Override
    public void run() {
        LOGGER.log(Level.INFO, "Numero de ejecución {0}", contador);
        contador++;
    }

}

Y al ejecutarlo veremos la siguiente salida.

Ejemplo de función periodica
Ejemplo de función periódica

Como puede ver el objeto tarea no se destruye durante la ejecución del Timer, por lo que la información en el persiste e ejecución en ejecución.

¿Que pasa si la función tarda mucho en ejecutarse?

Un caso que puede presentarse es que la función tarde tanto en ejecutarse que llegue el momento de volverla a ejecutar y aun no halla acabado, cosa muy posible si depende de conexiones a base de datos, servidores externos o conexiones, ¿En ese caso que pasaría?

Bueno hagamos la prueba, modifiqué la clase Tarea para que quede así

package mx.com.hash.tareaprogramada;

import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author david
 */
public class Tarea extends TimerTask {
    static private final Logger LOGGER = Logger.getLogger("mx.com.hash.tareaprogramada.Tarea");
    private Integer contador;    

    public Tarea() {
        contador = 0;
    }

    @Override
    public void run() {
        LOGGER.log(Level.INFO, "Numero de ejecución {0}", contador);
        contador++;

        try {
            // Con esto hacemos que la funcion tarde *mas* en ejecutarse que
            // el periodo especificado
            Thread.sleep(10000);
        } catch (InterruptedException ex) {
            LOGGER.log(Level.SEVERE, "Error de interrupcion");
        }
    }

}<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>

Lo que hacemos aquí es agregar un retraso  a la función run de modo que tarde 10 segundos en ejecutarse pero no modificamos lo demas, de modo que el temporizador ejecutara cada 5 segundos una función que tarda 10 segundos en ejecutarse, el resultado se ve a continuación.

Una función que tarda mas en ejecutarse
Una función que tarda mas en ejecutarse

Como ve el retraso entre llamadas es el indicado por la función, el Timer no llamara al método run sino hasta que este halla acabado por lo que no debe preocuparse de que queden cosas incompletas, lo que si puede pasar es que si ya paso el periodo entre llamadas el método run se llame inmediatamente después de terminar la llamada que tardo demas.

El código de este ejemplo lo puede encontrar aquí: https://gitlab.com/ticomWebcomic/tareaprogramada


Espero que esta entrada fuera de utilidad y si lo fue y quiere cooperar con la causa tengo una pagina en Ko-fi para aceptar donativos

ko-fi

Habilidad Geriatrica

Habilidad Geriatrica
Habilidad Geriatrica

La sabiduría de los ancianos

Transcript

Mike: ¡ESA COSA ES DEL DIABLO!

Mike: ¡¡¡COMO EL POKE MONGO Y EL IMSS TANGRAM!!

Narrador: Los mayores no sabran de técnologia, pero son expertos en detectar que es del Diablo.


Y si también pueden visiten mi pagina en Ko-fi y cooperen con la causa 😀

ko-fi

Yokai Gameplay Volumen 1

Se me a olvidado ponerlo aquí, pero he estado haciendo un webcomic basado en un gameplay de Yokai Watch, aquí estan los comics cubriendo el primer capitulo del juego.

Si le interesa como va este proyecto aca es donde puede ver el comic 🙂

yokai_watch

Si puede dele una visita, se actualiza (en lo posible :P) cada Martes y Jueves


Y si también pueden visiten mi pagina en Ko-fi y cooperen con la causa 😀

ko-fi

 

Materiales de dibujo

tools

Y finalmente me doy cuenta por que se hicieron TAN populares las tabletas digitalizadoras, no solo dejan deshacer cada error y ahorran hacer el escaneo sino que además evita el circo que es tratar de conseguir los materiales usuales de dibujo.

Sonara como broma para literalmente fue comprar la tinta en un Office Max, buscar plumillas en una tienda de arte, solo para ver que costaban un ojo de la cara y luego buscar un estilografo en otro Office Depot, por que no tenian ni en el Office Max ni en la tienda de arte.

Pero bueno, ya conseguí los materiales que necesitaba, a dibujar 😛

 

 

Convertir objetos Java a JSON y de regreso

JSON
JSON

El formato JSON se a convertido rápidamente en un estándar en como enviar y recibir información a y desde un webservice, si bien es un formato que se originó en JavaScript usarlo en Java es muy sencillo gracias a la librería Gson.

Gson

Gson es una librería en Java creada por Google que permite convertir un objeto Java a JSON y un JSON a un objeto Java, su uso es increíblemente sencillo, por eso se lo recomiendo.

Convirtiendo un Objeto Java a JSON.

Como primer ejemplo veamos como se convierte un objeto Java a JSON, para esto necesitara crear un objeto de clase Gson el cual se encargara de realizar la conversión y además deberá de asegurarse que la clase del objeto que desea convertir tenga todos los getter y setter necesarios, como se ve a continuación.

package mx.com.pydee.jsonexample;

import java.math.BigDecimal;

/**
 *
 * @author David
 */
public class DetallesVenta {
    private String producto;
    private BigDecimal importe;
    private BigDecimal precioUnitario;
    private Integer cantidad;
    
    public DetallesVenta(String prod, BigDecimal pu, BigDecimal imp, Integer cant) {
        producto = prod;
        precioUnitario = pu;
        importe = imp;
        cantidad = cant;
    }

    /**
     * @return the producto
     */
    public String getProducto() {
        return producto;
    }

    /**
     * @param producto the producto to set
     */
    public void setProducto(String producto) {
        this.producto = producto;
    }

    /**
     * @return the importe
     */
    public BigDecimal getImporte() {
        return importe;
    }

    /**
     * @param importe the importe to set
     */
    public void setImporte(BigDecimal importe) {
        this.importe = importe;
    }

    /**
     * @return the precioUnitario
     */
    public BigDecimal getPrecioUnitario() {
        return precioUnitario;
    }

    /**
     * @param precioUnitario the precioUnitario to set
     */
    public void setPrecioUnitario(BigDecimal precioUnitario) {
        this.precioUnitario = precioUnitario;
    }

    /**
     * @return the cantidad
     */
    public Integer getCantidad() {
        return cantidad;
    }

    /**
     * @param cantidad the cantidad to set
     */
    public void setCantidad(Integer cantidad) {
        this.cantidad = cantidad;
    }
}

Esto es necesario ya que de otro modo le objeto Gson mandara errores de acceso a las propiedades del objeto.

Ya que halla cumplido esos dos detalles la conversión es tan sencilla como llamar al método toJson del objeto Gson, como se ve a continuación.

String JSON = gson.toJson(detalle1);

Y el resultado se vera mas o menos asi

{"producto":"Aceite","importe":120.0,"precioUnitario":12.00,"cantidad":10}

Si el objeto que convirtió a JSON contenia un arreglo de objetos estos se agregaran con la notación adecuada, como se ve a continuación

{
	"fecha": "Jul 10, 2018 1:12:58 AM",
	"cliente": "CLIENTE DE PRUEBA",
	"detalle": [{
		"producto": "Aceite",
		"importe": 120.0,
		"precioUnitario": 12.00,
		"cantidad": 10
	}, {
		"producto": "Anticongelante",
		"importe": 90.0,
		"precioUnitario": 45.00,
		"cantidad": 2
	}, {
		"producto": "Reparacion Rin",
		"importe": 400.0,
		"precioUnitario": 400.00,
		"cantidad": 1
	}]
}

Convirtiendo de JSON a Objetos Java

Al usar un webservice no solo enviaremos información en forma de JSON sino que también la recibiremos en el mismo forma, asi que veamos como convertir de JSON a un objeto Java, la misma observación aplica aquí, el objeto receptor debe de tener todos los getter y setters necesarios o tendrá un error de conversión

El método usado para realizar la conversión es fromJson el cual toma dos argumentos, la cadena String con el JSON y la clase del objeto al que queremos convertir el Json.

Con la cadena de texto debe recordar un detalle importante y es que las comillas dobles son caracteres reservados en Java por lo que recomiendo revisar el Json que regreso el webservice y remplazar esas por comillas simples, para evitar problemas de conversión.

Con respecto a la clase del objeto esa se obtiene facilmente con la propiedad .class de la clase que deseamos usar

Para simplificar las cosas a continuación se presenta un ejemplo

String jsonComplejo = "{'fecha':'Jul 9, 2018 3:37:49 PM','cliente':'LIMPIEZA Y ASEO PROFESIONAL','detalle':[{'producto':'Cloro','importe':150.0,'precioUnitario':15.00,'cantidad':10},{'producto':'Escoba','importe':30,'precioUnitario':30.00,'cantidad':1},{'producto':'Aromatizante ambiental','importe':100.0,'precioUnitario':10.00,'cantidad':10}]}";

Venta venta2 = gson.fromJson(jsonComplejo, Venta.class);

Como notara la conversión puede encargarse sin problemas de un arreglo contenido

El ejemplo completo con lo puede encontrar aquí: https://gitlab.com/ticomWebcomic/JsonExample

Referencias.

Pagina oficial de Gson: https://github.com/google/gson


Espero que esta entrada fuera de utilidad y si lo fue y quiere cooperar con la causa tengo una pagina en Ko-fi para aceptar donativos

ko-fi

Marcar registros modificados en MySQL

Trigger
Trigger

Supongamos el siguiente problema, tiene una tabla MySQL que va a ser modificada, a la brava por un programa tercero y cada vez que se modifique esa tabla debe usar esos valores para recalcular un total en otro lado, ¿Como le hace?

Si bien lo obvio es tener algún programa que revise esa tabla en forma regular queda el problema de como saber cual registro se modificó, bueno hay una solución que aprovecha una característica de MySQL, los disparadores o triggers.

Triggers

Estos son funciones que se llaman automáticamente al insertar, actualizar o borrar un registro de una tabla y nos permiten efectuar comparaciones y modificar los valores justo antes o después de que ocurra uno de los eventos mencionados, como se ven el la figura con los seis triggers disponibles: before insert, after insert ,before update, after update, before delete y after delete.

Para nuestro caso de marcar un registro modificado usaremos el trigger before update.

Trigger Before Update

Este es el trigger en particular que usaremos para detectar y marcar modificaciones en la tabla, se activa justo antes de aplicar los cambios en base de datos, al momento en el que ambos, el registro actualizado y el registro anterior, están en la memoria de modo que podemos acceder a los valores de ambos, usando NEW y OLD para representar los registros antes y después de la actualización, para efectuar comparaciones.

Ejemplo

Como un ejemplo para dejar las cosas en claro crearemos un trigger que cambie el campo modificado de cero a uno cada vez que se modifique ese registro y que nos deje cambiar el campo modificado a cero otra vez.

Sobra decir que la tabla donde quiera aplicar este trigger debe tener un campo que se llame modificado y que acepte 0 y 1 como valores, si no lo tiene modifique una tabla que tenga para hacer el experimento.

Para crear el trigger usaremos el siguiente comando:

CREATE DEFINER=`root`@`localhost` TRIGGER `test`.`folios_test_BEFORE_UPDATE` BEFORE UPDATE ON `folios_test` FOR EACH ROW
BEGIN
if old.modificado = 0 then
set new.modificado = 1;
end if;
END

Puede hacerlo directamente desde las consultas de MySQL Workbench o desde las propiedades de la tabla en la pestaña Triggers como se ve en la figura al inicio.

Lo que estamos haciendo aquí es lo siguiente:

  1. Antes de aplicar la actualización vemos si el campo modificado del registro previo a la actualización (OLD) esta en cero, si lo esta procedemos al paso 2 sino se deja como esta, esto es necesario para poder regresar el registro de uno a cero.
  2. Cambiamos el valor de modificado a 1 en el registro actualizado (NEW)
  3. Se actualiza el registro con los valores de NEW, esto lo hace automáticamente la base de datos, pero lo pongo para que sepa por que cambiamos el valor de modificado usando NEW

Ya que aplique el trigger, sea corriendo el comando o desde las propiedades de la tabla, abra la tabla donde aplicó el trigger y modifique un registro.

Tabla antes de modificar
Tabla antes de modificar
Tabla despues de modificar
Tabla despues de modificar

Notara que el valor de modificado cambio de 0 a 1, ahora mueva el valor de modificado de vuelta a 0.

Modificado en cero
Modificado en cero

Notara que aunque modificamos el registro el valor de modificado no paso a 1 automáticamente, esto se debe al if que pusimos en el trigger que solo permite cambiar el valor de modificado si este esta en cero.

No hay un limite a la cantidad de instrucciones que puede poner dentro de un trigger y tampoco se limitan a esa misma tabla, por lo que podría usar un trigger para modificar otra tabla al instante sin problemas, nada mas recuerde no excederse o podría volver muy lentas las operaciones donde se corra el trigger.


Espero que esta entrada fuera de utilidad y si lo fue y quiere cooperar con la causa tengo una pagina en Ko-fi para aceptar donativos

ko-fi