package com.cidat.proclets;

import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;

public class DBConnectionManager {
	private static DBConnectionManager instance;
	private static int clients;
	private Vector drivers = new Vector();
	private PrintWriter log;
	private Hashtable pools = new Hashtable();

	public static synchronized DBConnectionManager getInstance() {
		if (instance == null) {
			instance = new DBConnectionManager();
		}

		return instance;
	}

	private DBConnectionManager() {
		init();
	}

	public void freeConnection(String name, Connection con) {
		DBConnectionPool pool = (DBConnectionPool) this.pools.get(name);
		if (pool != null) {
			pool.freeConnection(con);
		}
	}

	public Connection getConnection(String name) {
		DBConnectionPool pool = (DBConnectionPool) this.pools.get(name);
		if (pool != null) {
			return pool.getConnection(5000L);
		}
		return null;
	}

	public Connection getConnection(String name, long time) {
		DBConnectionPool pool = (DBConnectionPool) this.pools.get(name);
		if (pool != null) {
			return pool.getConnection(time);
		}
		return null;
	}

	public synchronized void release() {
		Enumeration allPools = this.pools.elements();
		while (allPools.hasMoreElements()) {
			DBConnectionPool pool = (DBConnectionPool) allPools.nextElement();
			pool.release();
		}
		Enumeration allDrivers = this.drivers.elements();
		while (allDrivers.hasMoreElements()) {
			Driver driver = (Driver) allDrivers.nextElement();
			try {
				DriverManager.deregisterDriver(driver);
				log("Deregistered JDBC driver " + driver.getClass().getName());
			} catch (SQLException e) {
				log(e, "Can't deregister JDBC driver: "
						+ driver.getClass().getName());
			}
		}
	}

	private void createPools(Properties props) {
		Enumeration propNames = props.propertyNames();
		while (propNames.hasMoreElements()) {
			String name = (String) propNames.nextElement();
			if (name.endsWith(".url")) {
				String poolName = name.substring(0, name.lastIndexOf("."));
				String url = props.getProperty(poolName + ".url");
				if (url == null) {
					log("No URL specified for " + poolName);
				} else {
					String user = props.getProperty(poolName + ".user");
					String password = props.getProperty(poolName + ".password");
					String maxconn = props.getProperty(poolName + ".maxconn",
							"0");
					int max;
					try {
						max = Integer.valueOf(maxconn).intValue();
					} catch (NumberFormatException e) {
						log("Invalid maxconn value " + maxconn + " for "
								+ poolName);
						max = 0;
					}
					DBConnectionPool pool = new DBConnectionPool(poolName, url,
							user, password, max);

					this.pools.put(poolName, pool);
					log("Initialized pool " + poolName);
				}
			}
		}
	}

	private void init() {
		InputStream is = getClass().getResourceAsStream(
				"/proclets.db.properties");
		Properties dbProps = new Properties();
		try {
			dbProps.load(is);
		} catch (Exception e) {
			System.err.println(is + ". Can't read the properties file. "
					+ "Make sure proclets.db.properties is in the CLASSPATH");

			e.printStackTrace();
			return;
		}
		String logFile = dbProps.getProperty("logfile",
				"DBConnectionManager.log");
		try {
			this.log = new PrintWriter(new FileWriter(logFile, true), true);
		} catch (IOException e) {
			System.err.println("Can't open the log file: " + logFile);
			this.log = new PrintWriter(System.err);
		}
		log("Leyendo proclets.db.properties");
		loadDrivers(dbProps);
		createPools(dbProps);
	}

	private void loadDrivers(Properties props) {
		String driverClasses = props.getProperty("drivers");
		StringTokenizer st = new StringTokenizer(driverClasses);
		while (st.hasMoreElements()) {
			String driverClassName = st.nextToken().trim();
			try {
				Driver driver = (Driver) Class.forName(driverClassName)
						.newInstance();

				DriverManager.registerDriver(driver);
				this.drivers.addElement(driver);
				log("Registerd JDBC driver " + driverClassName);
			} catch (Exception e) {
				log("Can't register JDBC driver: " + driverClassName
						+ ", Exception: " + e);
			}
		}
	}

	public int getCountPool() {
		return this.pools.size();
	}

	public Enumeration getNamePool() {
		return this.pools.keys();
	}

	public int getConsOcupadas(String name) {
		DBConnectionPool pool = (DBConnectionPool) this.pools.get(name);
		return pool.getConsOcupadas();
	}

	public int getConsLibres(String name) {
		DBConnectionPool pool = (DBConnectionPool) this.pools.get(name);
		return pool.getConsLibres();
	}

	public int getConsCreadas(String name) {
		DBConnectionPool pool = (DBConnectionPool) this.pools.get(name);
		return pool.getConsCreadas();
	}

	public int getConsPedidas(String name) {
		DBConnectionPool pool = (DBConnectionPool) this.pools.get(name);
		return pool.getConsPedidas();
	}

	public int getConsDevueltasOk(String name) {
		DBConnectionPool pool = (DBConnectionPool) this.pools.get(name);
		return pool.getConsDevueltasOk();
	}

	public int getConsDevueltasError(String name) {
		DBConnectionPool pool = (DBConnectionPool) this.pools.get(name);
		return pool.getConsDevueltasError();
	}

	public Date getFechaCreacion(String name) {
		DBConnectionPool pool = (DBConnectionPool) this.pools.get(name);
		return pool.getFechaCreacion();
	}

	public synchronized void liberarCons(String name) {
		DBConnectionPool pool = (DBConnectionPool) this.pools.get(name);
		log("Pool " + name
				+ " -> liberacion de las conexiones a peticion de CONTROL");
		pool.release();
	}

	private void log(String msg) {
		this.log.println(new Date() + ": " + msg);
	}

	private void log(Throwable e, String msg) {
		this.log.println(new Date() + ": " + msg);
		e.printStackTrace(this.log);
	}

	class DBConnectionPool {
		private int checkedOut;
		private Vector freeConnections = new Vector();
		private int maxConn;
		private String name;
		private String password;
		private String URL;
		private String user;
		private volatile int totConexionesCreadas;
		private volatile int totPeticionesConexion;
		private volatile int totRespuestasCorrectas;
		private volatile int totRespuestasError;
		private Date fechaCreacion;

		public DBConnectionPool(String name, String URL, String user,
				String password, int maxConn) {
			this.checkedOut = 0;

			this.name = name;
			this.URL = URL;
			this.user = user;
			this.password = password;
			this.maxConn = maxConn;

			this.totConexionesCreadas = 0;
			this.totPeticionesConexion = 0;
			this.totRespuestasCorrectas = 0;
			this.totRespuestasError = 0;
			this.fechaCreacion = new Date();
		}

		public synchronized void freeConnection(Connection con) {
			boolean conOk = con != null;
			try {
				if (!conOk) {
					throw new Exception(
							"Conexión devuelta en freeConnection es nula");
				}

				if ((conOk) && (con.isClosed())) {
					conOk = false;

					throw new Exception(
							"Conexión devuelta en freeConnection está cerrada");
				}
			} catch (SQLException sqle) {
				conOk = false;
				DBConnectionManager.this
						.log("Pool "
								+ this.name
								+ " -> SQLException al preguntar si la conexión devuelta estaba cerrada. SQLExcepción: "
								+ sqle);
			} catch (Exception e) {
				DBConnectionManager.this
						.log(e,
								"Pool "
										+ this.name
										+ " -> Conexión devuelta en freeConnection con error. Exception: "
										+ e);
			}

			if (conOk) {
				this.freeConnections.addElement(con);
			}
			this.checkedOut -= 1;
			if (this.checkedOut < 0) {
				DBConnectionManager.this.log("Pool " + this.name
						+ " -> Error: checkedOut = " + this.checkedOut);
				DBConnectionManager.this
						.log("Pool "
								+ this.name
								+ " -> Liberacion de las conexiones por error checkedOut < 0");
				release();
				this.checkedOut = 0;
			}
			notifyAll();
		}

		private synchronized Connection getConnection() {
			Connection con = null;

			while ((con == null) && (this.checkedOut < this.maxConn)) {
				if (this.freeConnections.size() > 0) {
					con = (Connection) this.freeConnections.firstElement();
					this.freeConnections.removeElementAt(0);
					try {
						if (con == null) {
							DBConnectionManager.this
									.log("Pool "
											+ this.name
											+ " -> getConnection: Conexion sacada de freeConnections es nula. Buscando nueva conexion...");
						} else if (con.isClosed()) {
							DBConnectionManager.this
									.log("Pool "
											+ this.name
											+ " -> getConnection: Conexion sacada de freeConnections estaba cerrada. Buscando nueva conexion...");

							con = null;
						}

					} catch (SQLException sqle) {
						DBConnectionManager.this
								.log("Pool "
										+ this.name
										+ " -> getConnection: Removed bad connection from "
										+ this.name);

						con = null;
					} catch (Exception e) {
						DBConnectionManager.this.log(e, "Pool " + this.name
								+ " -> getConnection: Traza");
					}

				} else if ((this.maxConn == 0)
						|| (this.checkedOut < this.maxConn)) {
					con = newConnection();

					if (con == null)
						return null;

				}

			}

			if (con != null) {
				this.checkedOut += 1;
			}
			return con;
		}

		public synchronized Connection getConnection(long timeout) {
			this.totPeticionesConexion += 1;
			Connection con;
			if ((con = getConnection()) != null) {
				this.totRespuestasCorrectas += 1;
			} else {
				DBConnectionManager.this.log("Pool " + this.name
						+ " -> Conexion obtenida en getConnection es null.");
				DBConnectionManager.this.log("Pool " + this.name
						+ " -> CheckedOut: " + this.checkedOut + " ; MaxConn: "
						+ this.maxConn);
				this.totRespuestasError += 1;
			}

			return con;
		}

		public synchronized void release() {
			Enumeration allConnections = this.freeConnections.elements();
			while (allConnections.hasMoreElements()) {
				Connection con = (Connection) allConnections.nextElement();
				try {
					con.close();
					DBConnectionManager.this.log("Pool " + this.name
							+ " -> Closed connection for pool " + this.name);
				} catch (SQLException e) {
					DBConnectionManager.this.log(e, "Pool " + this.name
							+ " -> Can't close connection for pool "
							+ this.name);
				}
			}
			DBConnectionManager.this
					.log("Pool " + this.name + " -> Fecha de creacion: "
							+ this.fechaCreacion.toString());
			DBConnectionManager.this.log("Pool " + this.name
					+ " -> Conexiones creadas en total: "
					+ this.totConexionesCreadas);
			DBConnectionManager.this.log("Pool " + this.name
					+ " -> Conexiones libres antes de cerrarlas...: "
					+ this.freeConnections.size());
			DBConnectionManager.this.log("Pool " + this.name
					+ " -> Conexiones ocupadas ahora: " + this.checkedOut);
			DBConnectionManager.this.log("Pool " + this.name
					+ " -> Peticiones recibidas en total: "
					+ this.totPeticionesConexion);
			DBConnectionManager.this.log("Pool " + this.name
					+ " -> Respuestas devueltas con conexión correcta: "
					+ this.totRespuestasCorrectas);
			DBConnectionManager.this.log("Pool " + this.name
					+ " -> Respuestas devueltas con conexión erronea: "
					+ this.totRespuestasError);
			this.freeConnections.removeAllElements();
		}

		private Connection newConnection() {
			DBConnectionManager.this.log("Pool " + this.name
					+ " -> Intentando crear una conexion");

			Connection con = null;
			try {
				if (this.user == null) {
					con = DriverManager.getConnection(this.URL);
				} else {
					con = DriverManager.getConnection(this.URL, this.user,
							this.password);
				}
				DBConnectionManager.this.log("Pool " + this.name
						+ " -> Created a new connection in pool " + this.name);
				this.totConexionesCreadas += 1;
			} catch (SQLException e) {
				DBConnectionManager.this.log(e, "Pool " + this.name
						+ " -> Can't create a new connection for " + this.URL);
				return null;
			}
			return con;
		}

		public int getConsOcupadas() {
			return this.checkedOut;
		}

		public int getConsLibres() {
			return this.freeConnections.size();
		}

		public int getConsCreadas() {
			return this.totConexionesCreadas;
		}

		public int getConsPedidas() {
			return this.totPeticionesConexion;
		}

		public int getConsDevueltasOk() {
			return this.totRespuestasCorrectas;
		}

		public int getConsDevueltasError() {
			return this.totRespuestasError;
		}

		public Date getFechaCreacion() {
			return this.fechaCreacion;
		}
	}
}

/*
 * Location:
 * D:\svn\ejie\q53.trunk\codigo\q53\q53ItourbaskWar\WebContent\WEB-INF\
 * lib\proclets_wl81.jar Qualified Name: com.cidat.proclets.DBConnectionManager
 * JD-Core Version: 0.6.0
 */