Aplicaciones con Instancia Unica en Java

Estándar

Al momento de programar una aplicacion que controle algun mecanisno, lleve a cabo algun tipo de comunicación o acceda a cualquier tipo de sistema de almacenamiento de datos, sea una base de datos o archivos en disco duro podria ser deseable prevenir tener mas de una  instancia ejecutandose, esto con el fin de evitar corrupción, duplicidad de datos o que una de las instancias sobreescriba cambios realizados por la otra.

Hay varias formas de hacer estos, la mas comun parece ser la creación y eliminación de un archivo que actua como seguro, si bien este metodo el mas sencillo y el que menos demanda del sistema puede causar errores si por alguna razon la aplicación cierra o es cerrada sin poder borrar dicho archivo, como seria si el sistema sufre de un apagon.

Otra de las opciones es crear un Socket, imagine esto como una conexion de red, en un puerto especifico y cerrar dicho socket al terminar la aplicación, esto es muy similar al uso de un archivo como seguro, pero con la ventaja de que el socket desaparecera en el caso de un reinicio del sistema, como el caso del apagón ya mencionado.

Operación y Teoria

La forma en que usaremos el Socket como seguro es la siguiente

1 – Al iniciar la aplicación se creara un socket en un puerto establecido
2 – Si el socket se crea exitosamente, lease ese puerto de red no esta en uso se continua con la ejecución de la aplicación
3 – En el caso de no poderse crear el socket se lanza una excepción de tipo BindException
4 – Capturar dicha excepción y mostrar un mensaje adecuado al usuario
5 – Salir de la aplicación sin ningun error

Para ejemplificar esto crearemos un sencillo programa que despliega una ventana o un mensaje de error si ya hay una instancia en ejecución, este consistira en dos clases, una para la ventana principal y otra para contener la llamada al metodo main, el codigo de ambas se presenta a acontinuación:

package mx.hashSoft.Highlander;

import java.awt.BorderLayout;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.IOException;
import java.net.BindException;
import java.net.ServerSocket;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;

public class VentanaPrincipal extends JFrame implements WindowListener {

    private static final long serialVersionUID = -5385101636093904528L;
    private ServerSocket sello;
    private JLabel etiqueta;
    
    public VentanaPrincipal() {
        try {
            // Creamos el socket en el puerto 6660
            sello = new ServerSocket(6660);            
            etiqueta = new JLabel(“Esta aplicación usa ServerSocket para prevenir que se abran multiples instancias”);
            
            this.getContentPane().add(etiqueta, BorderLayout.CENTER);
            this.setTitle(“Highlander Sys.”);
            this.addWindowListener(this);
                        
            etiqueta.setBorder( BorderFactory.createEmptyBorder(20, 20, 20, 20) );
            
            this.pack();            
            
        }catch(BindException e) {
            // Esto se ejecuta si el Socket no se puede crear, usualmente esto sera por que el puerto ya esta en uso
            JOptionPane.showMessageDialog(this, “THERE CAN BE ONLY ONE!!”);
            System.exit(0);
            
        }catch(IOException s) {
            System.err.println(“Errr”);
        }
        
    }    
    
    // Metodos necesitados por WindowListener
    @Override
    public void windowActivated(WindowEvent e) {
        
    }

    @Override
    public void windowClosed(WindowEvent e) {
        
    }

    @Override
    public void windowClosing(WindowEvent e) {
        try {
            sello.close();
            
        } catch (IOException e1) {
            System.err.println(“Error al cerrar el socket”);
            e1.printStackTrace();
        }
        
    }

    @Override
    public void windowDeactivated(WindowEvent e) {
        
    }

    @Override
    public void windowDeiconified(WindowEvent e) {
        
    }

    @Override
    public void windowIconified(WindowEvent e) {
        
    }

    @Override
    public void windowOpened(WindowEvent e) {
        
    }

}

package mx.hashSoft.Highlander;

public class Main {

    public static void main(String[] args) {
        VentanaPrincipal vent = new VentanaPrincipal();
        
        vent.setVisible(true);

    }

}

sello = new ServerSocket(6660);Con esta linea creamos el nuevo socket para evitar problemas con servicios de red y similares, recordemos que es un puerto de red, se recomienda usar un numero bien arriba de los 2000

catch(BindException e) Ya que existe la posibilidad de que un puerto no se pueda abrir es necesario capturar esta exception, de modo que se pueda procesar el error en forma adecuada. En este ejemplo asi podemos indicar que ya esta en ejecucion el programa

Ya que esta aplicación necesita que tratemos de ejecutar dos instancias no la ejecutaremos desde el entorno de desarrollo, en su lugar generaremos el JAR executable con ayuda de la opcion exportar.

Figura 1 - Crear JAR

Figura 1 – Crear JAR

Hecho esto vamos a la ubicación del archivo Jar y lo ejecutamos dando doble click sobre este, lo que presentara una pantalla como la figura

Figura 2 - Programa en Ejecucion

Figura 2 – Programa en Ejecucion

Y si intentamos iniciar otra instancia del programa dando doble click al archivo jar  *mientras* tenemos una instancia en ejecución obtendremos

Figura 3 - Error de instancia Multiple

Figura 3 – Error de instancia Multiple

Que es el comportamiento deseado, pues previene multiples instancias.

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s