
com.venky.swf.db.Database Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of swf-db Show documentation
Show all versions of swf-db Show documentation
Succinct Web Framework - Db
The newest version!
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.venky.swf.db;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import com.venky.cache.Cache;
import com.venky.core.collections.IgnoreCaseMap;
import com.venky.core.collections.SequenceSet;
import com.venky.core.log.SWFLogger;
import com.venky.core.log.TimerUtils;
import com.venky.core.util.ObjectUtil;
import com.venky.extension.Registry;
import com.venky.swf.configuration.Installer;
import com.venky.swf.db.annotations.model.CONFIGURATION;
import com.venky.swf.db.jdbc.ConnectionManager;
import com.venky.swf.db.jdbc.TransactionManager;
import com.venky.swf.db.model.Model;
import com.venky.swf.db.model.User;
import com.venky.swf.db.model.reflection.ModelReflector;
import com.venky.swf.db.model.reflection.TableReflector;
import com.venky.swf.db.table.QueryCache;
import com.venky.swf.db.table.Table;
import com.venky.swf.routing.Config;
/**
*
* @author venky
*/
public class Database implements _IDatabase{
private Database() {
}
private User currentUser ;
public void open(Object currentUser) {
if (connectionCache.get(ModelReflector.instance(User.class).getPool()) == null) {
throw new RuntimeException("Failed to open connection to database: " + getCaller());
}
this.currentUser = (User)currentUser;
}
public User getCurrentUser(){
return currentUser;
}
private TransactionManager tm = null;
public TransactionManager getTransactionManager(){
if (tm == null){
tm = new TransactionManager();
}
return tm;
}
public Transaction getCurrentTransaction() {
return getTransactionManager().getCurrentTransaction();
}
public boolean isActiveTransactionPresent(){
return getTransactionManager().isActiveTransactionPresent();
}
public void close() {
closeConnections();
currentUser = null;
if (context != null){
context.clear();
context = null;
}
}
private void closeConnections() {
List pools = new ArrayList(connectionCache.keySet());
for (String pool : pools){
Connection connection = connectionCache.get(pool);
try {
if (!connection.isClosed()){
try {
connection.rollback();
}catch (SQLException ex){
Config.instance().getLogger(Database.class.getName()).fine("Rollback Failed!! Closing anyway to release locks " + getCaller());
}finally {
connection.close();
}
Config.instance().getLogger(Database.class.getName()).fine("Connection closed : " + getCaller());
}
} catch (SQLException ex) {
throw new RuntimeException(ex);
} finally {
connectionCache.remove(pool);
}
}
if (!pools.isEmpty()){
getTransactionManager().completeAllTransaction();
}
}
private Cache connectionCache = new Cache(){
/**
*
*/
private static final long serialVersionUID = 8256289464354769723L;
protected Connection getValue(String pool) {
try {
return createConnection(pool);
} catch (SQLException ex) {
throw new RuntimeException(ex);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
}
};
public Set getActivePools(){
return connectionCache.keySet();
}
private boolean registeringActivePool = false; // Prevent Recursion from createTransaction , setSavePoint calling getConnection.
public Connection getConnection(String pool){
return getConnection(pool,true);
}
public Connection getConnection(String pool,boolean registerActivePool){
Connection conn = connectionCache.get(pool);
try {
if (conn.getTransactionIsolation() != getTransactionIsolationLevel() ){
if ( conn.getMetaData().supportsTransactionIsolationLevel(getTransactionIsolationLevel())) {
conn.setTransactionIsolation(getTransactionIsolationLevel());
}
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
if (registerActivePool && !registeringActivePool){
registeringActivePool = true;
try {
getCurrentTransaction().registerActivePool(pool);
}finally {
registeringActivePool = false;
}
}
return conn;
}
public void registerLockRelease(){
for (QueryCache cache: configQueryCacheMap.values()){
cache.registerLockRelease();
}
}
public QueryCache getCache(ModelReflector ref) {
String tableName = ref.getTableName();
if (ref.isAnnotationPresent(CONFIGURATION.class)){
QueryCache cacheEntry = configQueryCacheMap.get(tableName);
if (cacheEntry == null) {
synchronized (configQueryCacheMap) {
cacheEntry = configQueryCacheMap.get(tableName);
if (cacheEntry == null) {
cacheEntry = new QueryCache(tableName);
configQueryCacheMap.put(tableName, cacheEntry);
}
}
}
return cacheEntry;
} else {
return getTransactionManager().getCurrentTransaction().getCache(ref);
}
}
public PreparedStatement createStatement(String pool, String sql) throws SQLException{
return getConnection(pool).prepareStatement(sql);
}
public PreparedStatement createStatement(String pool, String sql,String[] columnNames) throws SQLException{
return getConnection(pool).prepareStatement(sql, columnNames );
}
// Class level methods and variables.
private static final Map configQueryCacheMap = new HashMap();
private static Map>> tablesInPool = new HashMap>>();
public static Map> getTables(String pool) {
Map> map = tablesInPool.get(pool);
if (map == null){
map = new IgnoreCaseMap>();
tablesInPool.put(pool,map);
}
return tablesInPool.get(pool);
}
@SuppressWarnings("unchecked")
public static Table getTable(Class modelClass) {
return (Table) getTable(ModelReflector.instance(modelClass).getPool(),Table.tableName(modelClass));
}
public static Table getTable(String table){
SequenceSet> set = new SequenceSet<>();
for (String pool:tablesInPool.keySet()){
Table t = getTable(pool, table);
if (t != null){
set.add(t);
if (t.getModelClass()!= null){
return t;
}
}
}
if (set.size() > 0){
return set.first();
}
return null;
}
@SuppressWarnings("unchecked")
public static Table getTable(String pool , String tableName) {
return (Table) tablesInPool.get(pool).get(tableName);
}
public static Set getTableNames() {
Set tableNames = new HashSet();
for (String pool :tablesInPool.keySet()){
tableNames.addAll(tablesInPool.get(pool).keySet());
}
return tableNames;
}
public static void migrateTables() {
boolean dbModified = false;
loadTables(dbModified);
for (String pool: tablesInPool.keySet()) {
for (Table> table : tablesInPool.get(pool).values()) {
if (table.isVirtual()){
continue;
}
Config.instance().getLogger(Database.class.getName()).info("Table " + table.getRealTableName() + " :" + pool + "Model " + table.getModelClass() + " :" + table.getPool());
if (!table.isExistingInDatabase() && ObjectUtil.equals(table.getReflector().getPool(),pool)) {
dbModified = table.createTable() || dbModified;
} else if (table.getModelClass() == null || !ObjectUtil.equals(table.getReflector().getPool(),pool)) {
dbModified = table.dropTable() || dbModified ;
} else {
dbModified = table.sync() || dbModified;
}
}
}
loadTables(dbModified);
}
public static void loadTables(boolean reload) {
if (reload) {
tablesInPool.clear();
}
if (!tablesInPool.isEmpty()) {
return;
}
loadTablesFromModel();
loadTablesFromDB();
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private static void loadTablesFromModel() {
List modelClasses = Config.instance().getModelClasses();
for (String className : modelClasses) {
try {
Class extends Model> modelClass = (Class extends Model>) Class.forName(className);
if (!className.equals(Model.class.getName()) && modelClass.isInterface() && Model.class.isAssignableFrom(modelClass)){
Table table = new Table(modelClass);
table.setExistingInDatabase(false);
String tableName = table.getTableName();
String pool = ModelReflector.instance(modelClass).getPool();
if (table.getRealTableName() != null && !getTables(pool).containsKey(tableName)) {
getTables(pool).put(table.getTableName(),table);
}
}
} catch (ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
}
}
private static void loadTablesFromDB() {
for (String pool : ConnectionManager.instance().getPools()){
loadTablesFromDB(pool);
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private static void loadTablesFromDB(String pool) {
ResultSet tablesResultSet = null;
SWFLogger cat = Config.instance().getLogger(Database.class.getName());
try {
Connection conn = getInstance().getConnection(pool);
DatabaseMetaData meta = conn.getMetaData();
cat.info("Starting To Load Tables from " + pool );
tablesResultSet = meta.getTables(null, getSchema(pool), "%", new String[]{"TABLE"});
while (tablesResultSet.next()) {
String tableName = tablesResultSet.getString("TABLE_NAME");
Table table = getTables(pool).get(tableName);
cat.info("Loading " + pool +"." + tableName);
if (table == null){ //
table = new Table(tableName,pool);
getTables(pool).put(tableName, table);
}
table.setExistingInDatabase(true);
ResultSet columnResultSet = null;
cat.info("Loading Columns for " + pool +"." + tableName);
try {
columnResultSet = meta.getColumns(null,getSchema(pool), tableName, null);
while (columnResultSet.next()) {
String columnName = columnResultSet.getString("COLUMN_NAME");
table.getColumnDescriptor(columnName,true).load(columnResultSet);
}
}finally {
if (columnResultSet != null){
columnResultSet.close();
}
}
}
} catch (SQLException ex) {
throw new RuntimeException(ex);
}finally {
if (tablesResultSet != null){
try {
tablesResultSet.close();
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
}
}
}
public void evictTableFromPool(String pool,String tableName){
//If app determines that a table is dropped (like archived) etc. this may be called.
getTables(pool).remove(tableName);
}
public void loadFactorySettings() {
List installerNames = Config.instance().getInstallers();
try {
for (String installerName : installerNames){
Config.instance().getLogger(Database.class.getName()).info("Installing ... " + installerName );
try{
Installer installer = (Installer)Class.forName(installerName).newInstance();
installer.install();
}catch (Exception ex){
Config.instance().getLogger(Database.class.getName()).log(Level.WARNING,"Installer " + installerName + " failed !" , ex);
throw new RuntimeException(ex);
} finally {
Config.instance().getLogger(Database.class.getName()).info(installerName + "done!");
}
}
} catch (Exception e) {
Config.instance().getLogger(Database.class.getName()).log(Level.WARNING,"Installers Failed!",e);
throw new RuntimeException(e);
}
}
private static ThreadLocal _instance = new ThreadLocal();
public static Database getInstance() {
return getInstance(false);
}
Map context = null;
public void setContext(String name, T value){
if (context == null){
context = new HashMap<>();
}
context.put(name,value);
}
public T getContext(String name){
if (context == null){
return null;
}
return (T)context.get(name);
}
public static Database getInstance(boolean migrate) {
if (_instance.get() == null) {
Database db = new Database();
_instance.set(db);
}
if (migrate) {
migrateTables();
} else {
loadTables(false);
}
return _instance.get();
}
public static JdbcTypeHelper getJdbcTypeHelper(String pool) {
return ConnectionManager.instance().getJdbcTypeHelper(pool);
}
private static String getSchema(String pool) {
return Config.instance().getProperty(ConnectionManager.instance().getNormalizedPropertyName("swf.jdbc."+ pool + ".dbschema"));
}
private static boolean isSchemaToBeSetOnConnection(String pool) {
return Boolean.valueOf(Config.instance().getProperty(ConnectionManager.instance().getNormalizedPropertyName("swf.jdbc."+pool+".dbschema.setonconnection")));
}
private static Connection createConnection(String pool) throws SQLException, ClassNotFoundException{
Connection conn = ConnectionManager.instance().createConnection(pool);
conn.setAutoCommit(false);
if (isSchemaToBeSetOnConnection(pool)) {
String schemaSettingCommand = Config.instance().getProperty(ConnectionManager.instance().getNormalizedPropertyName("swf.jdbc."+ pool +".set.dbschema.command"), "set schema ?");
PreparedStatement stmt = conn.prepareStatement(schemaSettingCommand);
if (schemaSettingCommand.indexOf('?') >= 0) {
stmt.setString(1, getSchema(pool));
}
stmt.executeUpdate();
conn.commit();
}
Config.instance().getLogger(Database.class.getName()).fine("Opened Connection:" + getCaller());
return conn;
}
public static void shutdown() {
ConnectionManager.instance().close();
for (String pool:ConnectionManager.instance().getPools()){
String jdbcurl = Config.instance().getProperty(ConnectionManager.instance().getNormalizedPropertyName("swf.jdbc."+pool+".url"));
String driver = Config.instance().getProperty(ConnectionManager.instance().getNormalizedPropertyName("swf.jdbc."+pool+ ".driver"));
if (driver.equals("org.apache.derby.jdbc.EmbeddedDriver")) {
try {
jdbcurl = jdbcurl + ";shutdown=true";
String userid = Config.instance().getProperty(ConnectionManager.instance().getNormalizedPropertyName("swf.jdbc."+pool+".userid"));
String password = Config.instance().getProperty(ConnectionManager.instance().getNormalizedPropertyName("swf.jdbc." + pool + ".password"));
Properties info = new Properties();
info.setProperty("user", userid);
info.setProperty("password", password);
DriverManager.getConnection(jdbcurl, info);
} catch (SQLException ex) {
if (ex.getSQLState().equals("08006") && ex.getErrorCode() == 45000){
System.out.println("Derby db closed!");
} else {
throw new RuntimeException(ex);
}
}
}
}
}
public static void dispose(){
Registry.instance().callExtensions("com.venky.swf.db.Database.beforeClose");
for (String pool : tablesInPool.keySet()){
tablesInPool.get(pool).clear();
}
tablesInPool.clear();
for (String key : configQueryCacheMap.keySet()){
configQueryCacheMap.get(key).clear();
}
configQueryCacheMap.clear();
ConnectionManager.instance().close();
ModelReflector.dispose();
TableReflector.dispose();
}
public static String getCaller(){
StackTraceElement[] e = new Exception().getStackTrace();
for (StackTraceElement elem : e) {
if (elem.getClassName().startsWith("sun.") || elem.getClassName().startsWith("java.") || elem.getClassName().startsWith("jdk.")) {
continue;
}
if (elem.getClassName().matches( "com\\.venky\\.swf\\.(routing|db|sql)\\.")){
continue;
}
return elem.toString();
}
StringWriter w = new StringWriter();
new Exception().printStackTrace(new PrintWriter(w));
return w.toString();
}
public void resetIdGeneration(){
for (String pool : ConnectionManager.instance().getPools()){
JdbcTypeHelper helper = getJdbcTypeHelper(pool);
for (Table extends Model> table : Database.getTables(pool).values()){
if (table.isReal() && table.isExistingInDatabase()){
helper.updateSequence(table);
}
}
}
}
int transactionIsolationLevel = Connection.TRANSACTION_READ_COMMITTED;
public int getTransactionIsolationLevel(){
return transactionIsolationLevel;
}
public void setTransactionIsolationLevel(int newIsolationLevel) {
if (transactionIsolationLevel == newIsolationLevel) {
return;
}
if (isActiveTransactionPresent()){
throw new RuntimeException("Cannot Set Isolation Level when a Transaction is going on");
}
transactionIsolationLevel = newIsolationLevel;
Config.instance().getLogger(getClass().getName()).info ("Set transaction isolation level to " + newIsolationLevel);
}
public void resetTransactionIsolationLevel(){
setTransactionIsolationLevel(Connection.TRANSACTION_READ_COMMITTED);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy