<commit_before>/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.venky.swf.db;

import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
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.sql.Savepoint;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Stack;
import java.util.logging.Logger;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;

import com.venky.core.checkpoint.Checkpoint;
import com.venky.core.checkpoint.Checkpointed;
import com.venky.core.checkpoint.MergeableMap;
import com.venky.core.collections.IgnoreCaseMap;
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.model.Model;
import com.venky.swf.db.model.User;
import com.venky.swf.db.model.reflection.ModelReflector;
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 (getConnection() == null) {
			throw new RuntimeException("Failed to open connection to database");
		}
		this.currentUser = (User)currentUser;
	}
	
	public User getCurrentUser(){
		return currentUser;
	}
	
	public void close() {
		closeConnection();
		currentUser = null;
	}

	private Connection connection = null;
	private Connection getConnection() {
		if (connection == null) {
			try {
				connection = createConnection();
			} catch (SQLException ex) {
				throw new RuntimeException(ex);
			} catch (ClassNotFoundException ex) {
				throw new RuntimeException(ex);
			}
		}
		return connection;
	}

	private void closeConnection() {
		if (connection != null) {
			try {
				if (!transactionStack.isEmpty()){
					Logger.getLogger(Database.class.getName()).warning("Not all Transactions in the application has a finally rollback block! Any way. Recovering...");
					transactionStack.clear();
				}
				txnUserAttributes.rollback(); //All check points are clear.
				txnUserAttributes.getCurrentValue().clear(); // Now restore the initial value to a clear map.
				connection.rollback();
				connection.close();
			} catch (SQLException ex) {
				throw new RuntimeException(ex);
			} finally {
				connection = null;
			}
		}
	}



	private Stack<Transaction> transactionStack = new Stack<Transaction>();
	public Transaction createTransaction() {
		Transaction transaction = new Transaction();
		transactionStack.push(transaction);
		return transaction;
	}

	public Transaction getCurrentTransaction(){
		if (transactionStack.isEmpty()) {
			createTransaction();
		}
		return transactionStack.peek();
	}

	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 getCurrentTransaction().getCache(ref);
		}
	}

	public class Transaction implements _ITransaction{
		private Savepoint savepoint = null;
		private RuntimeException ex = null;
		private int transactionNo = -1 ;
		private Checkpoint<MergeableMap<String,Object>> checkpoint = null;

		private Savepoint setSavepoint(String name){
			try {
				return getConnection().setSavepoint(name);
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
			
		}
		private void releaseSavepoint(Savepoint sp){
			try {
				getConnection().releaseSavepoint(sp);
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
		}
		
		private void rollback(Savepoint sp){
			try {
				getConnection().rollback(sp);
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
		}
		
		public Transaction() {
			transactionNo = transactionStack.size();
            ex = new RuntimeException("Transaction " + transactionNo + " not completed ");
            savepoint = setSavepoint(String.valueOf(transactionNo));
            checkpoint = txnUserAttributes.createCheckpoint();
		}

		public void commit() {
			releaseSavepoint(savepoint);
            savepoint = setSavepoint(String.valueOf(transactionStack.size()));
			txnUserAttributes.commit(checkpoint);
            updateTransactionStack();
			if (transactionStack.isEmpty()){
				try {
					Registry.instance().callExtensions("before.commit",this);
					getConnection().commit();
					Registry.instance().callExtensions("after.commit",this);
				} catch (SQLException e) {
					throw new RuntimeException(e);
				}
			}
		}

		public void rollback() {
			rollback(savepoint);
			txnUserAttributes.rollback(checkpoint);
			updateTransactionStack();
		}

		private void updateTransactionStack() {
			Transaction completedTransaction = transactionStack.peek();
			if (completedTransaction != this) {
				throw ex;
			}
			transactionStack.pop();
		}

		public PreparedStatement createStatement(String sql) throws SQLException{
            return getConnection().prepareStatement(sql);
		}
        public PreparedStatement createStatement(String sql,String[] columnNames) throws SQLException{ 
			return getConnection().prepareStatement(sql, columnNames );
		}

		public QueryCache getCache(ModelReflector ref) {
			String tableName = ref.getTableName();
			
			QueryCache queryCache = (QueryCache)getAttribute(QueryCache.class.getName()+".for."+tableName);<commit_msg>Rollback when stack is empty<commit_after>/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.venky.swf.db;

import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
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.sql.Savepoint;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Stack;
import java.util.logging.Logger;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;

import com.venky.core.checkpoint.Checkpoint;
import com.venky.core.checkpoint.Checkpointed;
import com.venky.core.checkpoint.MergeableMap;
import com.venky.core.collections.IgnoreCaseMap;
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.model.Model;
import com.venky.swf.db.model.User;
import com.venky.swf.db.model.reflection.ModelReflector;
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 (getConnection() == null) {
			throw new RuntimeException("Failed to open connection to database");
		}
		this.currentUser = (User)currentUser;
	}
	
	public User getCurrentUser(){
		return currentUser;
	}
	
	public void close() {
		closeConnection();
		currentUser = null;
	}

	private Connection connection = null;
	private Connection getConnection() {
		if (connection == null) {
			try {
				connection = createConnection();
			} catch (SQLException ex) {
				throw new RuntimeException(ex);
			} catch (ClassNotFoundException ex) {
				throw new RuntimeException(ex);
			}
		}
		return connection;
	}

	private void closeConnection() {
		if (connection != null) {
			try {
				if (!transactionStack.isEmpty()){
					Logger.getLogger(Database.class.getName()).warning("Not all Transactions in the application has a finally rollback block! Any way. Recovering...");
					transactionStack.clear();
				}
				txnUserAttributes.rollback(); //All check points are clear.
				txnUserAttributes.getCurrentValue().clear(); // Now restore the initial value to a clear map.
				connection.rollback();
				connection.close();
			} catch (SQLException ex) {
				throw new RuntimeException(ex);
			} finally {
				connection = null;
			}
		}
	}



	private Stack<Transaction> transactionStack = new Stack<Transaction>();
	public Transaction createTransaction() {
		Transaction transaction = new Transaction();
		transactionStack.push(transaction);
		return transaction;
	}

	public Transaction getCurrentTransaction(){
		if (transactionStack.isEmpty()) {
			createTransaction();
		}
		return transactionStack.peek();
	}

	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 getCurrentTransaction().getCache(ref);
		}
	}

	public class Transaction implements _ITransaction{
		private Savepoint savepoint = null;
		private RuntimeException ex = null;
		private int transactionNo = -1 ;
		private Checkpoint<MergeableMap<String,Object>> checkpoint = null;

		private Savepoint setSavepoint(String name){
			try {
				return getConnection().setSavepoint(name);
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
			
		}
		private void releaseSavepoint(Savepoint sp){
			try {
				getConnection().releaseSavepoint(sp);
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
		}
		
		private void rollback(Savepoint sp){
			try {
				getConnection().rollback(sp);
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
		}
		
		public Transaction() {
			transactionNo = transactionStack.size();
            ex = new RuntimeException("Transaction " + transactionNo + " not completed ");
            savepoint = setSavepoint(String.valueOf(transactionNo));
            checkpoint = txnUserAttributes.createCheckpoint();
		}

		public void commit() {
			releaseSavepoint(savepoint);
            savepoint = setSavepoint(String.valueOf(transactionStack.size()));
			txnUserAttributes.commit(checkpoint);
            updateTransactionStack();
			if (transactionStack.isEmpty()){
				try {
					Registry.instance().callExtensions("before.commit",this);
					getConnection().commit();
					Registry.instance().callExtensions("after.commit",this);
				} catch (SQLException e) {
					throw new RuntimeException(e);
				}
			}
		}

		public void rollback() {
			rollback(savepoint);
			txnUserAttributes.rollback(checkpoint);
			updateTransactionStack();
			if (transactionStack.isEmpty()){
				try {
					getConnection().rollback();
				} catch (SQLException e) {
					throw new RuntimeException(e);
				}
			}
		}

		private void updateTransactionStack() {
			Transaction completedTransaction = transactionStack.peek();
			if (completedTransaction != this) {
				throw ex;
			}
			transactionStack.pop();
		}

		public PreparedStatement createStatement(String sql) throws SQLException{
            return getConnection().prepareStatement(sql);
		}
        public PreparedStatement createStatement(String sql,String[] columnNames) throws SQLException{ 
			return getConnection().prepareStatement(sql, columnNames );
		}

		public QueryCache getCache(ModelReflector ref) {
			String tableName = ref.getTableName();
			
			QueryCache queryCache = (QueryCache)getAttribute(QueryCache.class.getName()+".for."+tableName);
{'fraction_non_alphanumeric': 0.10992563853863563, 'fraction_numerical': 0.00016165535079211123, 'mean_word_length': 3.993139628732849, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3219285', 'n_tokens_mistral': 4064, 'n_tokens_neox': 3524, 'n_words': 801}