/**
* This file is part of Waarp Project.
*
* Copyright 2009, Frederic Bregier, and individual contributors by the @author tags. See the
* COPYRIGHT.txt in the distribution for a full listing of individual contributors.
*
* All Waarp Project is free software: you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* Waarp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Waarp. If not, see
* <http://www.gnu.org/licenses/>.
*/
package org.waarp.common.database;
import java.sql.SQLException;
import java.util.ConcurrentModificationException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timer;
import org.waarp.common.database.exception.WaarpDatabaseNoConnectionException;
import org.waarp.common.database.exception.WaarpDatabaseSqlException;
import org.waarp.common.database.model.DbModel;
import org.waarp.common.database.model.DbModelFactory;
import org.waarp.common.database.model.DbType;
import org.waarp.common.database.model.EmptyDbModel;
import org.waarp.common.logging.WaarpLogger;
import org.waarp.common.logging.WaarpLoggerFactory;
import org.waarp.common.utility.UUID;
import org.waarp.common.utility.WaarpThreadFactory;
/**
* Class for access to Database
*
* @author Frederic Bregier
*
*/
public class DbAdmin {
/**
* Internal Logger
*/
private static final WaarpLogger logger = WaarpLoggerFactory
.getLogger(DbAdmin.class);
public static int RETRYNB = 3;
public static long WAITFORNETOP = 100;
/**
* Database type
*/
protected final DbType typeDriver;
/**
* DbModel
*/
private final DbModel dbModel;
/**
* DB Server
*/
private final String server;
/**
* DB User
*/
private final String user;
/**
* DB Password
*/
private final String passwd;
/**
* Is this DB Admin connected
*/
private boolean isActive = false;
/**
* Is this DB Admin Read Only
*/
private boolean isReadOnly = false;
/**
* session is the Session object for all type of requests
*/
private DbSession session = null;
/**
* Number of HttpSession
*/
private static int nbHttpSession = 0;
protected static final Timer dbSessionTimer = new HashedWheelTimer(new WaarpThreadFactory("TimerClose"),
50, TimeUnit.MILLISECONDS, 1024);
/**
* @return the session
*/
public DbSession getSession() {
return session;
}
/**
* @param session the session to set
*/
public void setSession(DbSession session) {
this.session = session;
}
/**
* @return True if the connection is ReadOnly
*/
public boolean isReadOnly() {
return isReadOnly;
}
/**
* @return the isActive
*/
public boolean isActive() {
return isActive;
}
/**
* @param isActive the isActive to set
*/
public void setActive(boolean isActive) {
this.isActive = isActive;
}
/**
* Validate connection
*
* @throws WaarpDatabaseNoConnectionException
*/
public void validConnection() throws WaarpDatabaseNoConnectionException {
try {
dbModel.validConnection(getSession());
} catch (WaarpDatabaseNoConnectionException e) {
getSession().setDisActive(true);
setActive(false);
throw e;
}
getSession().setDisActive(false);
setActive(true);
}
/**
* Use a default server for basic connection. Later on, specific connection to database for the
* scheme that provides access to the table R66DbIndex for one specific Legacy could be done.
*
* A this time, only one driver is possible! If a new driver is needed, then we need to create a
* new DbSession object. Be aware that DbSession.initialize should be call only once for each
* driver, whatever the number of DbSession objects that could be created (=> need a hashtable
* for specific driver when created). Also, don't know if two drivers at the same time (two
* different DbSession) is allowed by JDBC.
*
* @param model
* @param server
* @param user
* @param passwd
* @throws WaarpDatabaseNoConnectionException
*/
public DbAdmin(DbModel model, String server, String user, String passwd)
throws WaarpDatabaseNoConnectionException {
this.server = server;
this.user = user;
this.passwd = passwd;
this.dbModel = model;
this.typeDriver = model.getDbType();
if (typeDriver == null) {
logger.error("Cannot find TypeDriver");
throw new WaarpDatabaseNoConnectionException(
"Cannot find database drive");
}
setSession(new DbSession(this, false));
getSession().setAdmin(this);
isReadOnly = false;
validConnection();
getSession().useConnection(); // default since this is the top connection
}
/**
* Use a default server for basic connection. Later on, specific connection to database for the
* scheme that provides access to the table R66DbIndex for one specific Legacy could be done.
*
* A this time, only one driver is possible! If a new driver is needed, then we need to create a
* new DbSession object. Be aware that DbSession.initialize should be call only once for each
* driver, whatever the number of DbSession objects that could be created (=> need a hashtable
* for specific driver when created). Also, don't know if two drivers at the same time (two
* different DbSession) is allowed by JDBC.
*
* @param model
* @param server
* @param user
* @param passwd
* @param write
* @throws WaarpDatabaseSqlException
* @throws WaarpDatabaseNoConnectionException
*/
public DbAdmin(DbModel model, String server, String user, String passwd,
boolean write) throws WaarpDatabaseNoConnectionException {
this.server = server;
this.user = user;
this.passwd = passwd;
this.dbModel = model;
this.typeDriver = model.getDbType();
if (typeDriver == null) {
logger.error("Cannot find TypeDriver");
throw new WaarpDatabaseNoConnectionException(
"Cannot find database driver");
}
if (write) {
for (int i = 0; i < RETRYNB; i++) {
try {
setSession(new DbSession(this, false));
} catch (WaarpDatabaseNoConnectionException e) {
logger.warn("Attempt of connection in error: " + i, e);
continue;
}
isReadOnly = false;
getSession().setAdmin(this);
validConnection();
getSession().useConnection(); // default since this is the top
// connection
return;
}
} else {
for (int i = 0; i < RETRYNB; i++) {
try {
setSession(new DbSession(this, true));
} catch (WaarpDatabaseNoConnectionException e) {
logger.warn("Attempt of connection in error: " + i, e);
continue;
}
isReadOnly = true;
getSession().setAdmin(this);
validConnection();
getSession().useConnection(); // default since this is the top
// connection
return;
}
}
setSession(null);
setActive(false);
logger.error("Cannot connect to Database!");
throw new WaarpDatabaseNoConnectionException(
"Cannot connect to database");
}
/**
* Empty constructor for no Database support (very thin client)
*/
public DbAdmin() {
// not true but to enable pseudo database functions
setActive(false);
typeDriver = DbType.none;
DbModelFactory.classLoaded.add(DbType.none.name());
dbModel = new EmptyDbModel();
server = null;
user = null;
passwd = null;
}
/**
* Close the underlying session. Can be call even for connection given from the constructor
* DbAdmin(Connection, boolean).
*
*/
public void close() {
if (getSession() != null) {
getSession().endUseConnection(); // default since this is the top
// connection
getSession().forceDisconnect();
setSession(null);
}
setActive(false);
}
/**
* Commit on connection (since in autocommit, should not be used)
*
* @throws WaarpDatabaseNoConnectionException
* @throws WaarpDatabaseSqlException
*
*/
public void commit() throws WaarpDatabaseSqlException,
WaarpDatabaseNoConnectionException {
if (getSession() != null) {
getSession().commit();
}
}
/**
* @return the server
*/
public String getServer() {
return server;
}
/**
* @return the user
*/
public String getUser() {
return user;
}
/**
* @return the passwd
*/
public String getPasswd() {
return passwd;
}
/**
* @return the associated dbModel
*/
public DbModel getDbModel() {
return dbModel;
}
/**
* @return the typeDriver
*/
public DbType getTypeDriver() {
return typeDriver;
}
@Override
public String toString() {
return "Admin: " + typeDriver.name() + ":" + server + ":" + user + ":" + (passwd.length());
}
/**
* List all Connection to enable the close call on them
*/
private static ConcurrentHashMap<UUID, DbSession> listConnection = new ConcurrentHashMap<UUID, DbSession>();
/**
* Increment nb of Http Connection
*/
public static void incHttpSession() {
nbHttpSession++;
}
/**
* Decrement nb of Http Connection
*/
public static void decHttpSession() {
nbHttpSession--;
}
/**
* @return the nb of Http Connection
*/
public static int getHttpSession() {
return nbHttpSession;
}
/**
* Add a Connection into the list
*
* @param id
* @param session
*/
public static void addConnection(UUID id, DbSession session) {
listConnection.put(id, session);
}
/**
* Remove a Connection from the list
*
* @param id
* Id of the connection
*/
public static void removeConnection(UUID id) {
listConnection.remove(id);
}
/**
*
* @return the number of connection (so number of network channels)
*/
public static int getNbConnection() {
return listConnection.size() - 1;
}
/**
* Close all database connections
*/
public static void closeAllConnection() {
for (DbSession session : listConnection.values()) {
logger.debug("Close (all) Db Conn: " + session.getInternalId());
try {
session.getConn().close();
} catch (SQLException e) {
} catch (ConcurrentModificationException e) {
}
}
listConnection.clear();
for (DbModel dbModel : DbModelFactory.dbModels) {
if (dbModel != null) {
dbModel.releaseResources();
}
}
dbSessionTimer.stop();
}
/**
* Check all database connections and try to reopen them if disActive
*/
public static void checkAllConnections() {
for (DbSession session : listConnection.values()) {
try {
session.checkConnection();
} catch (WaarpDatabaseNoConnectionException e) {
logger.error("Database Connection cannot be reinitialized");
}
}
}
/**
*
* @return True if this driver allows Thread Shared Connexion (concurrency usage)
*/
public boolean isCompatibleWithThreadSharedConnexion() {
return (typeDriver != DbType.MariaDB && typeDriver != DbType.MySQL && typeDriver != DbType.Oracle && typeDriver != DbType.none);
}
}