/*
 *  Copyright 2008 biaoping.yin
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.frameworkset.commons.dbcp;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.sql.DataSource;

/**
 * 
 * <p>Title: NativeDataSource.java</p>
 * 
 * <p>Description: 用户非连接池模式的数据源管理，当用户在连接池的配置文件指定
 * usepool=false时，系统创建NativeDataSource类型的数据源
 * </p>
 *
 * <p>Copyright: Copyright (c) 2007</p>
 * @Date 2011-3-4 下午02:06:01
 * @author biaoping.yin
 * @version 1.0
 */
public class NativeDataSource implements DataSource {
	private StaticCount staticCount;
	public static class StaticCount
	{
		private List<AbandonedTrace> traces = new ArrayList<AbandonedTrace>();
		private boolean logAbandoned = false;
		public StaticCount(boolean logAbandoned)
		{
			this.logAbandoned = logAbandoned;
		}
		
		/**
		 * 非链接池数据源活动链接数
		 */
		private volatile int activeConnections = 0;
		/**
		 * 非链接池数据源高峰链接数
		 */
		private volatile int heapConnections = 0;
		private Object lock = new Object();
		public void increments(AbandonedTrace trace)
		{
			synchronized(lock)
			{
				if(logAbandoned)
					addTraceObject(trace);
				activeConnections ++;
				if(heapConnections < activeConnections)
					heapConnections = activeConnections;
			}
		}
		
		public void decrements(AbandonedTrace trace)
		{
			synchronized(lock)
			{
				if(logAbandoned)
					removeTraceObject(trace);
				activeConnections --;
				
			}
		}

		protected int getActiveConnections() {
			return activeConnections;
		}

		protected int getHeapConnections() {
			return heapConnections;
		}
		
		private void addTraceObject(AbandonedTrace trace)
		{
			this.traces.add(trace);
		}
		
		private void removeTraceObject(AbandonedTrace trace)
		{
			this.traces.remove(trace);
		}
		
		
		public List<AbandonedTrace> getTraceObjects()
		{
			
			List dest = new ArrayList();
			try
			{
				if(traces != null)
					dest.addAll(traces);
			}
			catch(Exception e)
			{
				
			}
			return dest;
			
		}
	}
	private String driver;
	private String url;
	private String user;
	private String password;
	private boolean defaultAutoCommit;
	private boolean defaultReadOnly;
	private int defaultTransactionIsolation;
	private String defaultCatalog;
	private DriverConnectionFactory driverConnectionFactory;
	protected AbandonedConfig abandonedConfig = null;
	private boolean logAbandoned = false;
	
	/**
	 * The connection properties that will be sent to our JDBC driver when
	 * establishing new connections. <strong>NOTE</strong> - The "user" and
	 * "password" properties will be passed explicitly, so they do not need to
	 * be included here.
	 */
	protected Properties connectionProperties = new Properties();

	private PrintWriter logWriter;
	
	
	

	public Connection getConnection() throws SQLException {
		this.createDataSource();
//		Connection connection = this.driverConnectionFactory.createConnection();
		StaticDelegateConnection connection = new StaticDelegateConnection(this.driverConnectionFactory.createConnection(),abandonedConfig,this.staticCount);
		this.staticCount.increments(connection);
		return connection;
	}

	public Connection getConnection(String username, String password)
			throws SQLException {
		throw new UnsupportedOperationException("Not supported by BasicDataSource");
		
	}

	public PrintWriter getLogWriter() throws SQLException {
		// TODO Auto-generated method stub
		return this.logWriter;
	}

	public int getLoginTimeout() throws SQLException {
		// TODO Auto-generated method stub
		return 0;
	}

	public void setLogWriter(PrintWriter out) throws SQLException {
		this.logWriter = out;

	}

	public void setLoginTimeout(int seconds) throws SQLException {

	}

	private void log(String message) {
		if (logWriter != null) {
			logWriter.println(message);
		}
	}

	private boolean created = false;

	protected DataSource createDataSource() throws SQLException {
		if (created)
			return this;

		synchronized (this) {
			if (created)
				return this;
			// Load the JDBC driver class
			if (driver != null) {
				try {
					Class.forName(driver);
				} catch (Throwable t) {
					String message = "Cannot load JDBC driver class '" + driver
							+ "'";
					logWriter.println(message);
					t.printStackTrace(logWriter);
					throw new SQLNestedException(message, t);
				}
			}

			// Create a JDBC driver instance
			Driver driver = null;
			try {
				driver = DriverManager.getDriver(url);
			} catch (Throwable t) {
				String message = "Cannot create JDBC driver of class '"
						+ (driver != null ? driver : "")
						+ "' for connect URL '" + url + "'";
				logWriter.println(message);
				t.printStackTrace(logWriter);
				throw new SQLNestedException(message, t);
			}

			// Set up the driver connection factory we will use
			if (user != null) {
				connectionProperties.put("user", user);
			} else {
				log("DBCP DataSource configured without a 'username'");
			}

			if (password != null) {
				connectionProperties.put("password", password);
			} else {
				log("DBCP DataSource configured without a 'password'");
			}

			driverConnectionFactory = new DriverConnectionFactory(driver, url,
					connectionProperties);
			staticCount = new StaticCount(this.isLogAbandoned());
			this.created = true;
		}
		return this;
	}

	/**
	 * @return the driver
	 */
	public String getDriver() {
		return driver;
	}

	/**
	 * @param driver
	 *            the driver to set
	 */
	public void setDriver(String driver) {
		this.driver = driver;
	}

	/**
	 * @return the url
	 */
	public String getUrl() {
		return url;
	}

	/**
	 * @param url
	 *            the url to set
	 */
	public void setUrl(String url) {
		this.url = url;
	}

	/**
	 * @return the user
	 */
	public String getUser() {
		return user;
	}

	/**
	 * @param user
	 *            the user to set
	 */
	public void setUser(String user) {
		this.user = user;
	}

	/**
	 * @return the password
	 */
	public String getPassword() {
		return password;
	}

	/**
	 * @param password
	 *            the password to set
	 */
	public void setPassword(String password) {
		this.password = password;
	}

	/**
	 * @return the defaultAutoCommit
	 */
	public boolean isDefaultAutoCommit() {
		return defaultAutoCommit;
	}

	/**
	 * @param defaultAutoCommit
	 *            the defaultAutoCommit to set
	 */
	public void setDefaultAutoCommit(boolean defaultAutoCommit) {
		this.defaultAutoCommit = defaultAutoCommit;
	}

	/**
	 * @return the defaultReadOnly
	 */
	public boolean isDefaultReadOnly() {
		return defaultReadOnly;
	}

	/**
	 * @param defaultReadOnly
	 *            the defaultReadOnly to set
	 */
	public void setDefaultReadOnly(boolean defaultReadOnly) {
		this.defaultReadOnly = defaultReadOnly;
	}

	/**
	 * @return the defaultTransactionIsolation
	 */
	public int getDefaultTransactionIsolation() {
		return defaultTransactionIsolation;
	}

	/**
	 * @param defaultTransactionIsolation
	 *            the defaultTransactionIsolation to set
	 */
	public void setDefaultTransactionIsolation(int defaultTransactionIsolation) {
		this.defaultTransactionIsolation = defaultTransactionIsolation;
	}

	/**
	 * @return the defaultCatalog
	 */
	public String getDefaultCatalog() {
		return defaultCatalog;
	}

	/**
	 * @param defaultCatalog
	 *            the defaultCatalog to set
	 */
	public void setDefaultCatalog(String defaultCatalog) {
		this.defaultCatalog = defaultCatalog;
	}

	/**
	 * @return the logAbandoned
	 */
	public boolean isLogAbandoned() {
		if (abandonedConfig != null) {
            return abandonedConfig.getLogAbandoned();
        }
        return false;
	}

	/**
	 * @param logAbandoned the logAbandoned to set
	 */
	public void setLogAbandoned(boolean logAbandoned) {
		if (abandonedConfig == null) {
            abandonedConfig = new AbandonedConfig();
        }
		if(logAbandoned)
		{
			abandonedConfig.setRemoveAbandoned(true);
			 abandonedConfig.setLogAbandoned(logAbandoned);
		}
		this.logAbandoned = logAbandoned;
	}

	public void addConnectionProperty(String propertyName, String propertyValue) {
		 connectionProperties.put(propertyName, propertyValue);
		
	}
	
	
	public int getNumActive()
	{
		if(this.staticCount != null)
		{
			return this.staticCount.getActiveConnections();
		}
		return 0;
	}
	public int getMaxNumActive()
	{
		if(this.staticCount != null)
		{
			return this.staticCount.getHeapConnections();
		}
		return 0;
	}
	
	public List getTraces()
	{
		if(this.staticCount != null)
		{
			return this.staticCount.getTraceObjects();
		}
		return new ArrayList();
	}
	
	
	
	   

}
