package com.rzg.zombieland.server.comunicacion;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
import com.rzg.zombieland.comunes.comunicacion.HiloEscucha;
import com.rzg.zombieland.comunes.comunicacion.HiloListener;
import com.rzg.zombieland.comunes.misc.Log;
import com.rzg.zombieland.comunes.misc.ZombielandException;
import com.rzg.zombieland.server.comunicacion.controlador.ControladorServidorFactory;
/**
* Escucha conexiones entrantes y lanza hilos para manejarlas.
*
* @author nicolas
*
*/
public class ServicioEscucha extends Thread implements HiloListener {
// El puerto por defecto para el constructor sin argumentos.
private final static int PUERTO_POR_DEFECTO = 2048;
// Generador de sockets.
private final ServerSocket serverSocket;
// Indica si el hilo est� corriendo.
private boolean corriendo;
// Mantiene una referencia a los hilos que cre� para cerrarlos.
private List<HiloEscucha> hilosEscucha;
// Bloquea la eliminaci�n de hilos.
private Semaphore semaforo;
// Indica si actualmente se est�n eliminado los hilos.
private boolean matandoHilos;
/**
* Crea un servicio de escucha para un puerto determinado.
* @param puerto
* @throws ZombielandException
*/
public ServicioEscucha(int puerto) throws ZombielandException {
super("ServicioEscucha");
Log.info("Arrancando servidor");
corriendo = true;
hilosEscucha = new ArrayList<HiloEscucha>();
try {
serverSocket = new ServerSocket(puerto);
} catch (IOException e) {
Log.error("No se pudo abrir el socket: ");
Log.error(e.getMessage());
e.printStackTrace();
throw new ZombielandException("No se pudo iniciar el servidor: "
+ e.getLocalizedMessage());
}
semaforo = new Semaphore(1);
matandoHilos = false;
}
/**
* Crea un servicio de escucha con el puerto por defecto.
* @throws ZombielandException
*/
public ServicioEscucha() throws ZombielandException {
this(PUERTO_POR_DEFECTO);
}
@Override
public void run() {
Log.info("Servidor arrancado");
while (corriendo) {
try {
ControladorServidorFactory factory = new ControladorServidorFactory();
HiloEscucha hilo = new HiloEscucha(serverSocket.accept(), factory);
hilo.addListener(this);
factory.setHiloEscucha(hilo);
hilo.start();
synchronized (this) {
hilosEscucha.add(hilo);
}
} catch (SocketException e) {
// Esperada. La usamos para salir.
try {
// Voy a arrancar a matar a los hilos, bloqueo las actualizaciones del listado.
semaforo.acquire();
matandoHilos = true;
for (HiloEscucha hilo : hilosEscucha) {
hilo.cerrar(false);
try {
// Permito que el hilo se cierre.
semaforo.release();
hilo.join(1000);
// Y lo vuelvo a bloquear.
semaforo.acquire();
} catch (InterruptedException e1) {
Log.error("No se pudo unir al hilo de escucha hijo:");
Log.error(e1.getMessage());
e1.printStackTrace();
}
}
semaforo.release();
} catch (InterruptedException e1) {
Log.error("Interrumpido al intentar cerrar: " + e1.getMessage());
}
return;
} catch (IOException e) {
Log.error("Ocurri� un error al intentar abrir la conexi�n con un nuevo cliente: ");
Log.error(e.getMessage());
e.printStackTrace();
}
}
}
/**
* Cierra al servicio de escucha, limpiando todos los recursos que adquiri�.
*/
public void cerrar() {
Log.info("Cerrando servidor");
corriendo = false;
if (serverSocket != null)
try {
serverSocket.close();
} catch (IOException e) {
}
}
/**
* @return los hilos de escucha.
*/
public List<HiloEscucha> getHilos() {
return hilosEscucha;
}
@Override
public synchronized void hiloCerrado(HiloEscucha hilo) {
try {
// Bloqueo si se est� operando sobre el listado de hilos.
semaforo.acquire();
} catch (InterruptedException e) {
Log.error("Interrumpido al adquirir el sem�foro para hiloCerrado: " + e.getMessage());
return;
}
// Me aseguro que no se est�n matando hilos para removerme del listado.
if (!matandoHilos)
hilosEscucha.remove(hilo);
semaforo.release();
}
}