JNDI是为了一个最最核心的问题:是为了解耦,是为了开发出更加可维护、可扩展的系统
JNDI和JDBC起的作用类似:
JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC为工具/数据库开发人员提供了一个标准的API,据此可以构建更高级的工具和接口,使数据库开发人员能够用纯 Java API 编写数据库应用程序。
JNDI(Java Naming and Directory Interface)是一个应用程序设计的API,为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口,类似JDBC都是构建在抽象层上。
JNDI是 Java 命名与目录接口(Java Naming and Directory Interface),在J2EE规范中是重要的规范之一,不少专家认为,没有透彻理解JNDI的意义和作用,就没有真正掌握J2EE特别是EJB的知识。
那么,JNDI到底起什么作用?//带着问题看文章是最有效的
要了解JNDI的作用,我们可以从“如果不用JNDI我们怎样做?用了JNDI后我们又将怎样做?”这个问题来探讨。
没有JNDI的做法:
程序员开发时,知道要开发访问MySQL数据库的应用,于是将一个对 MySQL JDBC 驱动程序类的引用进行了编码,并通过使用适当的 JDBC URL 连接到数据库。
就像以下代码这样:
Connection con = null; Statement stmt = null; ResultSet rs = null; try { Class.forName("com.mysql.jdbc.Driver"); // con = DriverManager.getConnection("jdbc:mysql://localhost:3306/database", "user", "password"); con = DriverManager.getConnection("jdbc:mysql://localhost:3306/database?user=user&password=password"); stmt = con.createStatement();
这是传统的做法,也是以前非Java程序员(如Delphi、VB等)常见的做法。这种做法一般在小规模的开发过程中不会产生问题,只要程序员熟悉Java语言、了解JDBC技术和MySQL,可以很快开发出相应的应用程序。
没有JNDI的做法存在的问题:
1、数据库服务器名称MyDBServer 、用户名和口令都可能需要改变,由此引发JDBC URL需要修改;
2、数据库可能改用别的产品,如改用DB2或者Oracle,引发JDBC驱动程序包和类名需要修改;
3、随着实际使用终端的增加,原配置的连接池参数可能需要调整;
4、......
解决办法:
程序员应该不需要关心“具体的数据库后台是什么?JDBC驱动程序是什么?JDBC URL格式是什么?访问数据库的用户名和口令是什么?”等等这些问题,程序员编写的程序应该没有对 JDBC 驱动程序的引用,没有服务器名称,没有用户名称或口令 —— 甚至没有数据库池或连接管理。而是把这些问题交给J2EE容器来配置和管理,程序员只需要对这些配置和管理进行引用即可。
由此,就有了JNDI。
//看的出来,是为了一个最最核心的问题:是为了解耦,是为了开发出更加可维护、可扩展//的系统
用了JNDI之后的做法:
首先,在在J2EE容器中配置JNDI参数,定义一个数据源,也就是JDBC引用参数,给这个数据源设置一个名称;然后,在程序中,通过数据源名称引用数据源从而访问后台数据库。
//红色的字可以看出,JNDI是由j2ee容器提供的功能
在描述JNDI,例如获得数据源时,JNDI地址 有两种写法,例如同是 jdbc/testDS 数据源: A: java:comp/env/jdbc/testDS B: jdbc/testDS
这两种写法,配置的方式也不尽相同,第一种方法应该算是一种利于程序移植或迁移的方法,它的实现与“映射”的概念相 同,而B方法,则是一个硬引用。 java:comp/env 是环境命名上下文(environment naming context(ENC)),是在EJB规范1.1以后引入的,引入这个是为了解决原来JNDI查找所引起的冲突问题,也是为了提高EJB或者J2EE应 用的移植性。 在J2EE中的引用常用的有:
JDBC 数据源引用在java:comp/env/jdbc 子上下文中声明
JMS 连接工厂在java:comp/env/jms 子上下文中声明
JavaMail 连接工厂在java:comp/env/mail 子上下文中声明
URL 连接工厂在 java:comp/env/url子上下文中声明
java:comp/env和JNDI是不同的,很多人都有一些混淆,甚至认为这个就是JNDI名称。
其实,java:comp/env 是环境命名上下文(environment naming context(ENC),是在EJB规范1.1以后引入的,引入这个是为了解决原来JNDI查找所引起的冲突问题。比如你要把一个EJB的Jar包部署到两台Server,而这两台Server共享一台JNDI名字空间,此时问题就出来了,因为JNDI名字空间要求JNDI名字必须唯一。使用ENC查找,将可以避免这个冲突,EJB或者J2EE应用的移植性也提高了。 ENC是一个引用,引用是用于定位企业应用程序的外部资源的逻辑名。引用是在应用程序部署描述符文件中定义的。在部署时,引用被绑定到目标可操作环境中资源的物理位置( JNDI 名)。使用ENC是把对其它资源的JNDI查找的硬编码解脱出来,通过配置这个引用可以在不修改代码的情况下,将引用指向不同的EJB(JNDI)。
可以通过下面的结构示意来发现这两种描述的不同之处:
A: java:comp/env/jdbc/testDS(虚地址) ------> 映射描述符 ------> jdbc/testDS (实际的地址)
B: jdbc/testDS (实际的地址) 从这种结构上来看,A的确是便于移植的。
再来看一个例子: 假如你需要获取datasource,例如:dataSource = (DataSource) ctx.lookup("java:comp/env/jdbc/testDS"); 那么在配置文件中进行资源映射时,在web.xml中,
<resource-ref> <res-ref-name>jdbc/testDS</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref>
在相应的资源配置xml中(不同的应用服务器均不同,WSAD中,可以进行可视化的设置),
<reference-descriptor> <resource-description> <res-ref-name>jdbc/DBPool</res-ref-name> <jndi-name>OraDataSource</jndi-name> </resource-description> </reference-descriptor>
Tomcat5.5h server.xml中加入:
<Context docBase="D:/workspace/Hello/WebContent" path="/Hello" reloadable="true"> <Resource name="jdbc/DBPool" type="javax.sql.DataSource" maxActive="100" maxIdle="10" maxWait="3000" driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver" url="jdbc:sqlserver://192.168.1.37:1433;DatabaseName=xxx;user=sa;password=sa123"/> </Context>
实际服务器中的JNDI名字是OraDataSource,逻辑名jdbc/DBPool只是用来和它作映射的,这样做的好处是为了提高可移植性,移植的 时候只需要把配置文件改一下就可以,而应用程序可不用改动。
java:comp/env是标准的J2EE环境查找规则使用这种方式必须做一次环境名到JNDI名的映射这种隔离使得在写程序时不必关注真正的 JNDI名字其实说白了跟把JNDI名放到配置文件里是一样的用法,如把java:comp/env/my/datasource映射到 my.ora.dataource
补充一下不加的时候是全局的JNDI名,这样将造成应用间EJB的耦合太高,不建议使用
注:
java:comp/env/ 前面是固定的
java:comp/env是标准的J2EE环境查找规则
comp是company的缩写
env是environment的缩写
使用这种方式必须做一次环境名到JNDI名的映射 这种隔离使得在写程序时不必关注真正的JNDI名字 其实说白了跟把JNDI名放到配置文件里是一样的
其他扩展:
J2EE 1.3 ,资源的管理绑定一个资源,但是使用时应该先配置资源引用 。 你在 web.xml 中或者 ejb-jar.xml 上配置对 EJB 或者 DataSource 的引用才能使用相应的资源。 不管是资源的配置还是资源引用的配置都可以在布署的阶段来修改的, 但是程序可以不用改,你只要让引用不变就行了,因为你自己容器中将要放多少东西你写代码时就知道(就是一个项目要用的东西),但是你的服务器中将来要放多 少资源你写代码时是不知道的,因为资源是在整个服务器,很容易在将来的某个时候可能多得不可管理。
在 web.xml 和 ejb-jar.xml 都可以有个 mycompay/abc 这个资源引用,名字相同没关系,因为它们在不同的容器中,只要同一个容器中唯一就行了,资源引用与实际资源的JNDI 相同也没关系。
现在的应用服务器都支持 J2EE 1.3,都会有个把 资源引用对应到一个实际的资源的这么一个配置文件。 像 IBM WebSphere 在 web 项目中 /WEB-INF/ibm-web-bnd.xml 就是用来将一个资源引用绑定到实际的 JNDI 资源上去的, 而EJB 项目中是 ibm-ejb-jar-bnd.xml 。 我用 &Web&Sphere &Application &Developer 开发的时候, 在 web.xml 中添加一个资源引用,比如对数据源的引用 ,WSAD 会自动到 ibm-web-bnd.xml中添加一个相应的绑定条目,如果我在 ejb-jar.xml 中添加一个 Local Ejb Ref , WSAD 也会自动到 ibm-ejb-jar-bnd.xml 中添加一个相应的条目。
假如你写了一个EJB,获取datasource如:dataSource = (DataSource) ctx.lookup("java:comp/env/jdbc/DBPool"); 那么在配置文件中进行资源映射时,在ejb-jar.xml中,
<resource-ref> <res-ref-name>jdbc/DBPool</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref>
在weblogic-ejb-jar.xml中,
<reference-descriptor> <resource-description> <res-ref-name>jdbc/DBPool</res-ref-name> <jndi-name>OraDataSource</jndi-name> </resource-description> </reference-descriptor>
//转者注:如果是在jboss则在jboss.xml中做如下修改
<resource-managers> <resource-manager> <res-name>jdbc/DBPool</res-name> <res-jndi-name>OraDataSource</res-jndi-name> </resource-manager> </resource-managers>
实际服务器中的JNDI名字是OraDataSource,逻辑名jdbc/DBPool只是用来和它作映射的,这样做的好处是为了提高可移植性,移植的 时候只需要把配置文件改一下就可以,而应用程序可不用改动。
假如你写了一个一般的应用程序,想直接通过JNDI来获取数据源,那么直接lookup(“mytest”)就可以了(假如服务器上的JNDI为 mytest),用第一种写法反而会报错的。
http://blog.csdn.net/tony8829/article/details/7252651
具体操作如下(以JBoss为例):
1、配置数据源
在JBoss 的 D:\jboss420GA\docs\examples\jca 文件夹下面,有很多不同数据库引用的数据源定义模板。将其中的 mysql-ds.xml 文件Copy到你使用的服务器下,如 D:\jboss420GA\server\default\deploy。
修改 mysql-ds.xml 文件的内容,使之能通过JDBC正确访问你的MySQL数据库,如下:
<?xml version="1.0" encoding="UTF-8"?> <datasources> <local-tx-datasource> <jndi-name>MySqlDS</jndi-name> <connection-url>jdbc:mysql://localhost:3306/lw</connection-url> <driver-class>com.mysql.jdbc.Driver</driver-class> <user-name>root</user-name> <password>rootpassword</password> <exception-sorter-class-name> org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter </exception-sorter-class-name> <metadata> <type-mapping>mySQL</type-mapping> </metadata> </local-tx-datasource> </datasources>
这里,定义了一个名为MySqlDS的数据源,其参数包括JDBC的URL,驱动类名,用户名及密码等。
2、在程序中引用数据源:
Connection conn=null; try { Context ctx=new InitialContext(); Object datasourceRef=ctx.lookup("java:MySqlDS"); //引用数据源 DataSource ds=(Datasource)datasourceRef; conn=ds.getConnection(); ...... c.close(); } catch(Exception e) { e.printStackTrace(); } finally { if(conn!=null) { try { conn.close(); } catch(SQLException e) { } } }
直接使用JDBC或者通过JNDI引用数据源的编程代码量相差无几,但是现在的程序可以不用关心具体JDBC参数了。//解藕了,可扩展了
在系统部署后,如果数据库的相关参数变更,只需要重新配置 mysql-ds.xml 修改其中的JDBC参数,只要保证数据源的名称不变,那么程序源代码就无需修改。
由此可见,JNDI避免了程序与数据库之间的紧耦合,使应用更加易于配置、易于部署。
JNDI的扩展:
JNDI在满足了数据源配置的要求的基础上,还进一步扩充了作用:所有与系统外部的资源的引用,都可以通过JNDI定义和引用。
//注意什么叫资源
所以,在J2EE规范中,J2EE 中的资源并不局限于 JDBC 数据源。引用的类型有很多,其中包括资源引用(已经讨论过)、环境实体和 EJB 引用。特别是 EJB 引用,它暴露了 JNDI 在 J2EE 中的另外一项关键角色:查找其他应用程序组件。
EJB 的 JNDI 引用非常类似于 JDBC 资源的引用。在服务趋于转换的环境中,这是一种很有效的方法。可以对应用程序架构中所得到的所有组件进行这类配置管理,从 EJB 组件到 JMS 队列和主题,再到简单配置字符串或其他对象,这可以降低随时间的推移服务变更所产生的维护成本,同时还可以简化部署,减少集成工作。外部资源”。
总结:
J2EE 规范要求所有 J2EE 容器都要提供 JNDI 规范的实现。//sun 果然喜欢制定规范JNDI 在 J2EE 中的角色就是“交换机” —— J2EE 组件在运行时间接地查找其他组件、资源或服务的通用机制。在多数情况下,提供 JNDI 供应者的容器可以充当有限的数据存储,这样管理员就可以设置应用程序的执行属性,并让其他应用程序引用这些属性(Java 管理扩展(Java Management Extensions,JMX)也可以用作这个目的)。JNDI 在 J2EE 应用程序中的主要角色就是提供间接层,这样组件就可以发现所需要的资源,而不用了解这些间接性。
在 J2EE 中,JNDI 是把 J2EE 应用程序合在一起的粘合剂,JNDI 提供的间接寻址允许跨企业交付可伸缩的、功能强大且很灵活的应用程序。这是 J2EE 的承诺,而且经过一些计划和预先考虑,这个承诺是完全可以实现的。
从上面的文章中可以看出:
1、JNDI 提出的目的是为了解藕,是为了开发更加容易维护,容易扩展,容易部署的应用。
2、JNDI 是一个sun提出的一个规范(类似于jdbc),具体的实现是各个j2ee容器提供商,sun 只是要求,j2ee容器必须有JNDI这样的功能。
3、JNDI 在j2ee系统中的角色是“交换机”,是J2EE组件在运行时间接地查找其他组件、资源或服务的通用机制。
4、JNDI 是通过资源的名字来查找的,资源的名字在整个j2ee应用中(j2ee容器中)是唯一的。
再转一篇文章:
JNDI全称 Java Naming and Directory Interface
JNDI是Java平台的一个标准扩展,提供了一组接口、类和关于命名空间的概念。如同其它很多Java技术一样,JDNI是provider-based的技术,暴露了一个API和一个服务供应接口(SPI)。这意味着任何基于名字的技术都能通过JNDI而提供服务,只要JNDI支持这项技术。JNDI目前所支持的技术包括LDAP、CORBA Common Object Service(COS)名字服务、RMI、NDS、DNS、Windows注册表等等。很多J2EE技术,包括EJB都依靠JNDI来组织和定位实体。
JDNI通过绑定的概念将对象和名称联系起来。在一个文件系统中,文件名被绑定给文件。在DNS中,一个IP地址绑定一个URL。在目录服务中,一个对象名被绑定给一个对象实体。
JNDI中的一组绑定作为上下文来引用。每个上下文暴露的一组操作是一致的。例如,每个上下文提供了一个查找操作,返回指定名字的相应对象。每个上下文都提供了绑定和撤除绑定名字到某个对象的操作。JNDI使用通用的方式来暴露命名空间,即使用分层上下文以及使用相同命名语法的子上下文。
jndi的用途:
1。你可以用jndi来得到object类的属性
如:
Attribute attr =directory.getAttributes(personName).get("email"); String email = (String)attr.get();
2。你可以用jndi来搜索对象
如:
foxes = directory.search("o=Wiz,c=US", "sn=Fox", controls);
查找谁的名字叫Fox在wiz部门的员工?
3。你可以用jndi通过naming/directory服务查询像printers和databases的对象
如:查询 Printer
Printer printer = (Printer)namespace.lookup(printerName); printer.print(document);
4。你可以用jndi列表出命名空间的特殊级别的内容
如:
NamingEnumeration list = namespace.list("o=Widget, c=US"; while (list.hasMore()) { NameClassPair entry = (NameClassPair)list.next(); display(entry.getName(), entry.getClassName()); }
http://shitou521.iteye.com/blog/696006
http://www.cnblogs.com/lnlvinso/p/3818162.html
http://baike.baidu.com/link?url=MCs9DTepsOz1iQLvWMywRKYUu_aV4hxQ7Jc4x3AJupbs7tWcHeQ77T3Noyqfrf-0kH1NaI7-CmAML5Sz8BFyRq
http://www.cnblogs.com/lnlvinso/p/3818162.html
http://www.cnblogs.com/chinafine/archive/2010/06/16/1759246.html
http://www.cnblogs.com/lnlvinso/p/3818162.html
http://www.blogjava.net/Jack2007/archive/2007/11/07/158934.html
http://developer.zdnet.com.cn/2007/0902/485984.shtml
在服务器中配置JNDI数据源(javax.sql.DataSource)时,不同的服务器中lookup得到的Object是不同的.在Tomcat5中得到的是org.apache.commons.dbcp.BasicDataSource类,Tomcat把DataSource作为一种可配置的JNDI资源来处理,生成DataSource对象的工厂为org.apache.commons.dbcp.BasicDataSourceFactory。
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.commons.dbcp; import java.io.PrintWriter; import java.util.Properties; import java.util.Collection; import java.util.List; import java.util.ArrayList; import java.util.Iterator; import java.util.Collections; import java.sql.Connection; import java.sql.Driver; import java.sql.DriverManager; import java.sql.SQLException; import javax.sql.DataSource; import org.apache.commons.pool.KeyedObjectPoolFactory; import org.apache.commons.pool.impl.GenericKeyedObjectPool; import org.apache.commons.pool.impl.GenericKeyedObjectPoolFactory; import org.apache.commons.pool.impl.GenericObjectPool; /** * <p>Basic implementation of <code>javax.sql.DataSource</code> that is * configured via JavaBeans properties. This is not the only way to * combine the <em>commons-dbcp</em> and <em>commons-pool</em> packages, * but provides a "one stop shopping" solution for basic requirements.</p> * * <p>Users extending this class should take care to use appropriate accessors * rather than accessing protected fields directly to ensure thread-safety.</p> * * @author Glenn L. Nielsen * @author Craig R. McClanahan * @author Dirk Verbeeck * @version $Revision: 895844 $ $Date: 2010-01-04 20:50:04 -0500 (Mon, 04 Jan 2010) $ */ public class BasicDataSource implements DataSource { static { // Attempt to prevent deadlocks - see DBCP - 272 DriverManager.getDrivers(); } // ------------------------------------------------------------- Properties /** * The default auto-commit state of connections created by this pool. */ protected volatile boolean defaultAutoCommit = true; /** * Returns the default auto-commit property. * * @return true if default auto-commit is enabled */ public boolean getDefaultAutoCommit() { return this.defaultAutoCommit; } /** * <p>Sets default auto-commit state of connections returned by this * datasource.</p> * <p> * Note: this method currently has no effect once the pool has been * initialized. The pool is initialized the first time one of the * following methods is invoked: <code>getConnection, setLogwriter, * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p> * * @param defaultAutoCommit default auto-commit value */ public void setDefaultAutoCommit(boolean defaultAutoCommit) { this.defaultAutoCommit = defaultAutoCommit; this.restartNeeded = true; } /** * The default read-only state of connections created by this pool. */ protected transient Boolean defaultReadOnly = null; /** * Returns the default readOnly property. * * @return true if connections are readOnly by default */ public boolean getDefaultReadOnly() { Boolean val = defaultReadOnly; if (val != null) { return val.booleanValue(); } return false; } /** * <p>Sets defaultReadonly property.</p> * <p> * Note: this method currently has no effect once the pool has been * initialized. The pool is initialized the first time one of the * following methods is invoked: <code>getConnection, setLogwriter, * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p> * * @param defaultReadOnly default read-only value */ public void setDefaultReadOnly(boolean defaultReadOnly) { this.defaultReadOnly = defaultReadOnly ? Boolean.TRUE : Boolean.FALSE; this.restartNeeded = true; } /** * The default TransactionIsolation state of connections created by this pool. */ protected volatile int defaultTransactionIsolation = PoolableConnectionFactory.UNKNOWN_TRANSACTIONISOLATION; /** * Returns the default transaction isolation state of returned connections. * * @return the default value for transaction isolation state * @see Connection#getTransactionIsolation */ public int getDefaultTransactionIsolation() { return this.defaultTransactionIsolation; } /** * <p>Sets the default transaction isolation state for returned * connections.</p> * <p> * Note: this method currently has no effect once the pool has been * initialized. The pool is initialized the first time one of the * following methods is invoked: <code>getConnection, setLogwriter, * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p> * * @param defaultTransactionIsolation the default transaction isolation * state * @see Connection#getTransactionIsolation */ public void setDefaultTransactionIsolation(int defaultTransactionIsolation) { this.defaultTransactionIsolation = defaultTransactionIsolation; this.restartNeeded = true; } /** * The default "catalog" of connections created by this pool. */ protected volatile String defaultCatalog = null; /** * Returns the default catalog. * * @return the default catalog */ public String getDefaultCatalog() { return this.defaultCatalog; } /** * <p>Sets the default catalog.</p> * <p> * Note: this method currently has no effect once the pool has been * initialized. The pool is initialized the first time one of the * following methods is invoked: <code>getConnection, setLogwriter, * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p> * * @param defaultCatalog the default catalog */ public void setDefaultCatalog(String defaultCatalog) { if ((defaultCatalog != null) && (defaultCatalog.trim().length() > 0)) { this.defaultCatalog = defaultCatalog; } else { this.defaultCatalog = null; } this.restartNeeded = true; } /** * The fully qualified Java class name of the JDBC driver to be used. */ protected String driverClassName = null; /** * Returns the jdbc driver class name. * * @return the jdbc driver class name */ public synchronized String getDriverClassName() { return this.driverClassName; } /** * <p>Sets the jdbc driver class name.</p> * <p> * Note: this method currently has no effect once the pool has been * initialized. The pool is initialized the first time one of the * following methods is invoked: <code>getConnection, setLogwriter, * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p> * * @param driverClassName the class name of the jdbc driver */ public synchronized void setDriverClassName(String driverClassName) { if ((driverClassName != null) && (driverClassName.trim().length() > 0)) { this.driverClassName = driverClassName; } else { this.driverClassName = null; } this.restartNeeded = true; } /** * The class loader instance to use to load the JDBC driver. If not * specified, {@link Class#forName(String)} is used to load the JDBC driver. * If specified, {@link Class#forName(String, boolean, ClassLoader)} is * used. */ protected ClassLoader driverClassLoader = null; /** * Returns the class loader specified for loading the JDBC driver. Returns * <code>null</code> if no class loader has been explicitly specified. */ public synchronized ClassLoader getDriverClassLoader() { return this.driverClassLoader; } /** * <p>Sets the class loader to be used to load the JDBC driver.</p> * <p> * Note: this method currently has no effect once the pool has been * initialized. The pool is initialized the first time one of the * following methods is invoked: <code>getConnection, setLogwriter, * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p> * * @param driverClassLoader the class loader with which to load the JDBC * driver */ public synchronized void setDriverClassLoader( ClassLoader driverClassLoader) { this.driverClassLoader = driverClassLoader; this.restartNeeded = true; } /** * The maximum number of active connections that can be allocated from * this pool at the same time, or negative for no limit. */ protected int maxActive = GenericObjectPool.DEFAULT_MAX_ACTIVE; /** * <p>Returns the maximum number of active connections that can be * allocated at the same time. * </p> * <p>A negative number means that there is no limit.</p> * * @return the maximum number of active connections */ public synchronized int getMaxActive() { return this.maxActive; } /** * Sets the maximum number of active connections that can be * allocated at the same time. Use a negative value for no limit. * * @param maxActive the new value for maxActive * @see #getMaxActive() */ public synchronized void setMaxActive(int maxActive) { this.maxActive = maxActive; if (connectionPool != null) { connectionPool.setMaxActive(maxActive); } } /** * The maximum number of connections that can remain idle in the * pool, without extra ones being released, or negative for no limit. * If maxIdle is set too low on heavily loaded systems it is possible you * will see connections being closed and almost immediately new connections * being opened. This is a result of the active threads momentarily closing * connections faster than they are opening them, causing the number of idle * connections to rise above maxIdle. The best value for maxIdle for heavily * loaded system will vary but the default is a good starting point. */ protected int maxIdle = GenericObjectPool.DEFAULT_MAX_IDLE; /** * <p>Returns the maximum number of connections that can remain idle in the * pool. * </p> * <p>A negative value indicates that there is no limit</p> * * @return the maximum number of idle connections */ public synchronized int getMaxIdle() { return this.maxIdle; } /** * Sets the maximum number of connections that can remain idle in the * pool. * * @see #getMaxIdle() * @param maxIdle the new value for maxIdle */ public synchronized void setMaxIdle(int maxIdle) { this.maxIdle = maxIdle; if (connectionPool != null) { connectionPool.setMaxIdle(maxIdle); } } /** * The minimum number of active connections that can remain idle in the * pool, without extra ones being created, or 0 to create none. */ protected int minIdle = GenericObjectPool.DEFAULT_MIN_IDLE; /** * Returns the minimum number of idle connections in the pool * * @return the minimum number of idle connections * @see GenericObjectPool#getMinIdle() */ public synchronized int getMinIdle() { return this.minIdle; } /** * Sets the minimum number of idle connections in the pool. * * @param minIdle the new value for minIdle * @see GenericObjectPool#setMinIdle(int) */ public synchronized void setMinIdle(int minIdle) { this.minIdle = minIdle; if (connectionPool != null) { connectionPool.setMinIdle(minIdle); } } /** * The initial number of connections that are created when the pool * is started. * * @since 1.2 */ protected int initialSize = 0; /** * Returns the initial size of the connection pool. * * @return the number of connections created when the pool is initialized */ public synchronized int getInitialSize() { return this.initialSize; } /** * <p>Sets the initial size of the connection pool.</p> * <p> * Note: this method currently has no effect once the pool has been * initialized. The pool is initialized the first time one of the * following methods is invoked: <code>getConnection, setLogwriter, * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p> * * @param initialSize the number of connections created when the pool * is initialized */ public synchronized void setInitialSize(int initialSize) { this.initialSize = initialSize; this.restartNeeded = true; } /** * The maximum number of milliseconds that the pool will wait (when there * are no available connections) for a connection to be returned before * throwing an exception, or <= 0 to wait indefinitely. */ protected long maxWait = GenericObjectPool.DEFAULT_MAX_WAIT; /** * <p>Returns the maximum number of milliseconds that the pool will wait * for a connection to be returned before throwing an exception. * </p> * <p>A value less than or equal to zero means the pool is set to wait * indefinitely.</p> * * @return the maxWait property value */ public synchronized long getMaxWait() { return this.maxWait; } /** * <p>Sets the maxWait property. * </p> * <p>Use -1 to make the pool wait indefinitely. * </p> * * @param maxWait the new value for maxWait * @see #getMaxWait() */ public synchronized void setMaxWait(long maxWait) { this.maxWait = maxWait; if (connectionPool != null) { connectionPool.setMaxWait(maxWait); } } /** * Prepared statement pooling for this pool. When this property is set to <code>true</code> * both PreparedStatements and CallableStatements are pooled. */ protected boolean poolPreparedStatements = false; /** * Returns true if we are pooling statements. * * @return true if prepared and callable statements are pooled */ public synchronized boolean isPoolPreparedStatements() { return this.poolPreparedStatements; } /** * <p>Sets whether to pool statements or not.</p> * <p> * Note: this method currently has no effect once the pool has been * initialized. The pool is initialized the first time one of the * following methods is invoked: <code>getConnection, setLogwriter, * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p> * * @param poolingStatements pooling on or off */ public synchronized void setPoolPreparedStatements(boolean poolingStatements) { this.poolPreparedStatements = poolingStatements; this.restartNeeded = true; } /** * <p>The maximum number of open statements that can be allocated from * the statement pool at the same time, or non-positive for no limit. Since * a connection usually only uses one or two statements at a time, this is * mostly used to help detect resource leaks.</p> * * <p>Note: As of version 1.3, CallableStatements (those produced by {@link Connection#prepareCall}) * are pooled along with PreparedStatements (produced by {@link Connection#prepareStatement}) * and <code>maxOpenPreparedStatements</code> limits the total number of prepared or callable statements * that may be in use at a given time.</p> */ protected int maxOpenPreparedStatements = GenericKeyedObjectPool.DEFAULT_MAX_TOTAL; /** * Gets the value of the {@link #maxOpenPreparedStatements} property. * * @return the maximum number of open statements * @see #maxOpenPreparedStatements */ public synchronized int getMaxOpenPreparedStatements() { return this.maxOpenPreparedStatements; } /** * <p>Sets the value of the {@link #maxOpenPreparedStatements} * property.</p> * <p> * Note: this method currently has no effect once the pool has been * initialized. The pool is initialized the first time one of the * following methods is invoked: <code>getConnection, setLogwriter, * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p> * * @param maxOpenStatements the new maximum number of prepared statements * @see #maxOpenPreparedStatements */ public synchronized void setMaxOpenPreparedStatements(int maxOpenStatements) { this.maxOpenPreparedStatements = maxOpenStatements; this.restartNeeded = true; } /** * The indication of whether objects will be validated before being * borrowed from the pool. If the object fails to validate, it will be * dropped from the pool, and we will attempt to borrow another. */ protected boolean testOnBorrow = true; /** * Returns the {@link #testOnBorrow} property. * * @return true if objects are validated before being borrowed from the * pool * * @see #testOnBorrow */ public synchronized boolean getTestOnBorrow() { return this.testOnBorrow; } /** * Sets the {@link #testOnBorrow} property. This property determines * whether or not the pool will validate objects before they are borrowed * from the pool. For a <code>true</code> value to have any effect, the * <code>validationQuery</code> property must be set to a non-null string. * * @param testOnBorrow new value for testOnBorrow property */ public synchronized void setTestOnBorrow(boolean testOnBorrow) { this.testOnBorrow = testOnBorrow; if (connectionPool != null) { connectionPool.setTestOnBorrow(testOnBorrow); } } /** * The indication of whether objects will be validated before being * returned to the pool. */ protected boolean testOnReturn = false; /** * Returns the value of the {@link #testOnReturn} property. * * @return true if objects are validated before being returned to the * pool * @see #testOnReturn */ public synchronized boolean getTestOnReturn() { return this.testOnReturn; } /** * Sets the <code>testOnReturn</code> property. This property determines * whether or not the pool will validate objects before they are returned * to the pool. For a <code>true</code> value to have any effect, the * <code>validationQuery</code> property must be set to a non-null string. * * @param testOnReturn new value for testOnReturn property */ public synchronized void setTestOnReturn(boolean testOnReturn) { this.testOnReturn = testOnReturn; if (connectionPool != null) { connectionPool.setTestOnReturn(testOnReturn); } } /** * The number of milliseconds to sleep between runs of the idle object * evictor thread. When non-positive, no idle object evictor thread will * be run. */ protected long timeBetweenEvictionRunsMillis = GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; /** * Returns the value of the {@link #timeBetweenEvictionRunsMillis} * property. * * @return the time (in miliseconds) between evictor runs * @see #timeBetweenEvictionRunsMillis */ public synchronized long getTimeBetweenEvictionRunsMillis() { return this.timeBetweenEvictionRunsMillis; } /** * Sets the {@link #timeBetweenEvictionRunsMillis} property. * * @param timeBetweenEvictionRunsMillis the new time between evictor runs * @see #timeBetweenEvictionRunsMillis */ public synchronized void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) { this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; if (connectionPool != null) { connectionPool.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); } } /** * The number of objects to examine during each run of the idle object * evictor thread (if any). */ protected int numTestsPerEvictionRun = GenericObjectPool.DEFAULT_NUM_TESTS_PER_EVICTION_RUN; /** * Returns the value of the {@link #numTestsPerEvictionRun} property. * * @return the number of objects to examine during idle object evictor * runs * @see #numTestsPerEvictionRun */ public synchronized int getNumTestsPerEvictionRun() { return this.numTestsPerEvictionRun; } /** * Sets the value of the {@link #numTestsPerEvictionRun} property. * * @param numTestsPerEvictionRun the new {@link #numTestsPerEvictionRun} * value * @see #numTestsPerEvictionRun */ public synchronized void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) { this.numTestsPerEvictionRun = numTestsPerEvictionRun; if (connectionPool != null) { connectionPool.setNumTestsPerEvictionRun(numTestsPerEvictionRun); } } /** * The minimum amount of time an object may sit idle in the pool before it * is eligable for eviction by the idle object evictor (if any). */ protected long minEvictableIdleTimeMillis = GenericObjectPool.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS; /** * Returns the {@link #minEvictableIdleTimeMillis} property. * * @return the value of the {@link #minEvictableIdleTimeMillis} property * @see #minEvictableIdleTimeMillis */ public synchronized long getMinEvictableIdleTimeMillis() { return this.minEvictableIdleTimeMillis; } /** * Sets the {@link #minEvictableIdleTimeMillis} property. * * @param minEvictableIdleTimeMillis the minimum amount of time an object * may sit idle in the pool * @see #minEvictableIdleTimeMillis */ public synchronized void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) { this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; if (connectionPool != null) { connectionPool.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); } } /** * The indication of whether objects will be validated by the idle object * evictor (if any). If an object fails to validate, it will be dropped * from the pool. */ protected boolean testWhileIdle = false; /** * Returns the value of the {@link #testWhileIdle} property. * * @return true if objects examined by the idle object evictor are * validated * @see #testWhileIdle */ public synchronized boolean getTestWhileIdle() { return this.testWhileIdle; } /** * Sets the <code>testWhileIdle</code> property. This property determines * whether or not the idle object evictor will validate connections. For a * <code>true</code> value to have any effect, the * <code>validationQuery</code> property must be set to a non-null string. * * @param testWhileIdle new value for testWhileIdle property */ public synchronized void setTestWhileIdle(boolean testWhileIdle) { this.testWhileIdle = testWhileIdle; if (connectionPool != null) { connectionPool.setTestWhileIdle(testWhileIdle); } } /** * [Read Only] The current number of active connections that have been * allocated from this data source. * * @return the current number of active connections */ public synchronized int getNumActive() { if (connectionPool != null) { return connectionPool.getNumActive(); } else { return 0; } } /** * [Read Only] The current number of idle connections that are waiting * to be allocated from this data source. * * @return the current number of idle connections */ public synchronized int getNumIdle() { if (connectionPool != null) { return connectionPool.getNumIdle(); } else { return 0; } } /** * The connection password to be passed to our JDBC driver to establish * a connection. */ protected volatile String password = null; /** * Returns the password passed to the JDBC driver to establish connections. * * @return the connection password */ public String getPassword() { return this.password; } /** * <p>Sets the {@link #password}.</p> * <p> * Note: this method currently has no effect once the pool has been * initialized. The pool is initialized the first time one of the * following methods is invoked: <code>getConnection, setLogwriter, * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p> * * @param password new value for the password */ public void setPassword(String password) { this.password = password; this.restartNeeded = true; } /** * The connection URL to be passed to our JDBC driver to establish * a connection. */ protected String url = null; /** * Returns the JDBC connection {@link #url} property. * * @return the {@link #url} passed to the JDBC driver to establish * connections */ public synchronized String getUrl() { return this.url; } /** * <p>Sets the {@link #url}.</p> * <p> * Note: this method currently has no effect once the pool has been * initialized. The pool is initialized the first time one of the * following methods is invoked: <code>getConnection, setLogwriter, * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p> * * @param url the new value for the JDBC connection url */ public synchronized void setUrl(String url) { this.url = url; this.restartNeeded = true; } /** * The connection username to be passed to our JDBC driver to * establish a connection. */ protected String username = null; /** * Returns the JDBC connection {@link #username} property. * * @return the {@link #username} passed to the JDBC driver to establish * connections */ public String getUsername() { return this.username; } /** * <p>Sets the {@link #username}.</p> * <p> * Note: this method currently has no effect once the pool has been * initialized. The pool is initialized the first time one of the * following methods is invoked: <code>getConnection, setLogwriter, * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p> * * @param username the new value for the JDBC connection username */ public void setUsername(String username) { this.username = username; this.restartNeeded = true; } /** * The SQL query that will be used to validate connections from this pool * before returning them to the caller. If specified, this query * <strong>MUST</strong> be an SQL SELECT statement that returns at least * one row. */ protected volatile String validationQuery = null; /** * Returns the validation query used to validate connections before * returning them. * * @return the SQL validation query * @see #validationQuery */ public String getValidationQuery() { return this.validationQuery; } /** * <p>Sets the {@link #validationQuery}.</p> * <p> * Note: this method currently has no effect once the pool has been * initialized. The pool is initialized the first time one of the * following methods is invoked: <code>getConnection, setLogwriter, * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p> * * @param validationQuery the new value for the validation query */ public void setValidationQuery(String validationQuery) { if ((validationQuery != null) && (validationQuery.trim().length() > 0)) { this.validationQuery = validationQuery; } else { this.validationQuery = null; } this.restartNeeded = true; } /** * Timeout in seconds before connection validation queries fail. * * @since 1.3 */ protected volatile int validationQueryTimeout = -1; /** * Returns the validation query timeout. * * @return the timeout in seconds before connection validation queries fail. * @since 1.3 */ public int getValidationQueryTimeout() { return validationQueryTimeout; } /** * Sets the validation query timeout, the amount of time, in seconds, that * connection validation will wait for a response from the database when * executing a validation query. Use a value less than or equal to 0 for * no timeout. * <p> * Note: this method currently has no effect once the pool has been * initialized. The pool is initialized the first time one of the * following methods is invoked: <code>getConnection, setLogwriter, * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p> * * @param timeout new validation query timeout value in seconds * @since 1.3 */ public void setValidationQueryTimeout(int timeout) { this.validationQueryTimeout = timeout; restartNeeded = true; } /** * These SQL statements run once after a Connection is created. * <p> * This property can be used for example to run ALTER SESSION SET * NLS_SORT=XCYECH in an Oracle Database only once after connection * creation. * </p> * * @since 1.3 */ protected volatile List connectionInitSqls; /** * Returns the list of SQL statements executed when a physical connection * is first created. Returns an empty list if there are no initialization * statements configured. * * @return initialization SQL statements * @since 1.3 */ public Collection getConnectionInitSqls() { Collection result = connectionInitSqls; if (result == null) { return Collections.EMPTY_LIST; } return result; } /** * Sets the list of SQL statements to be executed when a physical * connection is first created. * <p> * Note: this method currently has no effect once the pool has been * initialized. The pool is initialized the first time one of the * following methods is invoked: <code>getConnection, setLogwriter, * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p> * * @param connectionInitSqls Collection of SQL statements to execute * on connection creation */ public void setConnectionInitSqls(Collection connectionInitSqls) { if ((connectionInitSqls != null) && (connectionInitSqls.size() > 0)) { ArrayList newVal = null; for (Iterator iterator = connectionInitSqls.iterator(); iterator.hasNext();) { Object o = iterator.next(); if (o != null) { String s = o.toString(); if (s.trim().length() > 0) { if (newVal == null) { newVal = new ArrayList(); } newVal.add(s); } } } this.connectionInitSqls = newVal; } else { this.connectionInitSqls = null; } this.restartNeeded = true; } /** * Controls access to the underlying connection. */ private boolean accessToUnderlyingConnectionAllowed = false; /** * Returns the value of the accessToUnderlyingConnectionAllowed property. * * @return true if access to the underlying connection is allowed, false * otherwise. */ public synchronized boolean isAccessToUnderlyingConnectionAllowed() { return this.accessToUnderlyingConnectionAllowed; } /** * <p>Sets the value of the accessToUnderlyingConnectionAllowed property. * It controls if the PoolGuard allows access to the underlying connection. * (Default: false)</p> * <p> * Note: this method currently has no effect once the pool has been * initialized. The pool is initialized the first time one of the * following methods is invoked: <code>getConnection, setLogwriter, * setLoginTimeout, getLoginTimeout, getLogWriter.</code></p> * * @param allow Access to the underlying connection is granted when true. */ public synchronized void setAccessToUnderlyingConnectionAllowed(boolean allow) { this.accessToUnderlyingConnectionAllowed = allow; this.restartNeeded = true; } // ----------------------------------------------------- Instance Variables // TODO: review & make isRestartNeeded() public, restartNeeded protected /** * A property setter has been invoked that will require the connection * pool to be re-initialized. Currently, restart is not triggered, so * this property has no effect. */ private volatile boolean restartNeeded = false; /** * Returns whether or not a restart is needed. * * Note: restart is not currently triggered by property changes. * * @return true if a restart is needed */ private boolean isRestartNeeded() { return restartNeeded; } /** * The object pool that internally manages our connections. */ protected volatile GenericObjectPool connectionPool = null; /** * 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(); /** * The data source we will use to manage connections. This object should * be acquired <strong>ONLY</strong> by calls to the * <code>createDataSource()</code> method. */ protected volatile DataSource dataSource = null; /** * The PrintWriter to which log messages should be directed. */ protected PrintWriter logWriter = new PrintWriter(System.out); // ----------------------------------------------------- DataSource Methods /** * Create (if necessary) and return a connection to the database. * * @throws SQLException if a database access error occurs * @return a database connection */ public Connection getConnection() throws SQLException { return createDataSource().getConnection(); } /** * <strong>BasicDataSource does NOT support this method. </strong> * * @param user Database user on whose behalf the Connection * is being made * @param pass The database user's password * * @throws UnsupportedOperationException * @throws SQLException if a database access error occurs * @return nothing - always throws UnsupportedOperationException */ public Connection getConnection(String user, String pass) throws SQLException { // This method isn't supported by the PoolingDataSource returned by // the createDataSource throw new UnsupportedOperationException("Not supported by BasicDataSource"); // return createDataSource().getConnection(username, password); } /** * <strong>BasicDataSource does NOT support this method. </strong> * * <p>Returns the login timeout (in seconds) for connecting to the database. * </p> * <p>Calls {@link #createDataSource()}, so has the side effect * of initializing the connection pool.</p> * * @throws SQLException if a database access error occurs * @throws UnsupportedOperationException If the DataSource implementation * does not support the login timeout feature. * @return login timeout in seconds */ public int getLoginTimeout() throws SQLException { // This method isn't supported by the PoolingDataSource returned by // the createDataSource throw new UnsupportedOperationException("Not supported by BasicDataSource"); //return createDataSource().getLoginTimeout(); } /** * <p>Returns the log writer being used by this data source.</p> * <p> * Calls {@link #createDataSource()}, so has the side effect * of initializing the connection pool.</p> * * @throws SQLException if a database access error occurs * @return log writer in use */ public PrintWriter getLogWriter() throws SQLException { return createDataSource().getLogWriter(); } /** * <strong>BasicDataSource does NOT support this method. </strong> * * <p>Set the login timeout (in seconds) for connecting to the * database.</p> * <p> * Calls {@link #createDataSource()}, so has the side effect * of initializing the connection pool.</p> * * @param loginTimeout The new login timeout, or zero for no timeout * @throws UnsupportedOperationException If the DataSource implementation * does not support the login timeout feature. * @throws SQLException if a database access error occurs */ public void setLoginTimeout(int loginTimeout) throws SQLException { // This method isn't supported by the PoolingDataSource returned by // the createDataSource throw new UnsupportedOperationException("Not supported by BasicDataSource"); //createDataSource().setLoginTimeout(loginTimeout); } /** * <p>Sets the log writer being used by this data source.</p> * <p> * Calls {@link #createDataSource()}, so has the side effect * of initializing the connection pool.</p> * * @param logWriter The new log writer * @throws SQLException if a database access error occurs */ public void setLogWriter(PrintWriter logWriter) throws SQLException { createDataSource().setLogWriter(logWriter); this.logWriter = logWriter; } private AbandonedConfig abandonedConfig; /** * Flag to remove abandoned connections if they exceed the * removeAbandonedTimout. * * Set to true or false, default false. * If set to true a connection is considered abandoned and eligible * for removal if it has been idle longer than the removeAbandonedTimeout. * Setting this to true can recover db connections from poorly written * applications which fail to close a connection. * <p> * Abandonded connections are identified and removed when * {@link #getConnection()} is invoked and the following conditions hold * <ul><li>{@link #getRemoveAbandoned()} = true </li> * <li>{@link #getNumActive()} > {@link #getMaxActive()} - 3 </li> * <li>{@link #getNumIdle()} < 2 </li></ul></p> */ public boolean getRemoveAbandoned() { if (abandonedConfig != null) { return abandonedConfig.getRemoveAbandoned(); } return false; } /** * @param removeAbandoned new removeAbandoned property value * @see #getRemoveAbandoned() */ public void setRemoveAbandoned(boolean removeAbandoned) { if (abandonedConfig == null) { abandonedConfig = new AbandonedConfig(); } abandonedConfig.setRemoveAbandoned(removeAbandoned); this.restartNeeded = true; } /** * Timeout in seconds before an abandoned connection can be removed. * * Defaults to 300 seconds. * @return abandoned connection timeout */ public int getRemoveAbandonedTimeout() { if (abandonedConfig != null) { return abandonedConfig.getRemoveAbandonedTimeout(); } return 300; } /** * @param removeAbandonedTimeout new removeAbandonedTimeout value */ public void setRemoveAbandonedTimeout(int removeAbandonedTimeout) { if (abandonedConfig == null) { abandonedConfig = new AbandonedConfig(); } abandonedConfig.setRemoveAbandonedTimeout(removeAbandonedTimeout); this.restartNeeded = true; } /** * <p>Flag to log stack traces for application code which abandoned * a Statement or Connection. * </p> * <p>Defaults to false. * </p> * <p>Logging of abandoned Statements and Connections adds overhead * for every Connection open or new Statement because a stack * trace has to be generated. </p> */ public boolean getLogAbandoned() { if (abandonedConfig != null) { return abandonedConfig.getLogAbandoned(); } return false; } /** * @param logAbandoned new logAbandoned property value */ public void setLogAbandoned(boolean logAbandoned) { if (abandonedConfig == null) { abandonedConfig = new AbandonedConfig(); } abandonedConfig.setLogAbandoned(logAbandoned); this.restartNeeded = true; } // --------------------------------------------------------- Public Methods /** * Add a custom connection property to the set that will be passed to our * JDBC driver. This <strong>MUST</strong> be called before the first * connection is retrieved (along with all the other configuration * property setters). Calls to this method after the connection pool * has been initialized have no effect. * * @param name Name of the custom connection property * @param value Value of the custom connection property */ public void addConnectionProperty(String name, String value) { connectionProperties.put(name, value); this.restartNeeded = true; } /** * Remove a custom connection property. * * @param name Name of the custom connection property to remove * @see #addConnectionProperty(String, String) */ public void removeConnectionProperty(String name) { connectionProperties.remove(name); this.restartNeeded = true; } /** * Sets the connection properties passed to driver.connect(...). * * Format of the string must be [propertyName=property;]* * * NOTE - The "user" and "password" properties will be added * explicitly, so they do not need to be included here. * * @param connectionProperties the connection properties used to * create new connections */ public void setConnectionProperties(String connectionProperties) { if (connectionProperties == null) throw new NullPointerException("connectionProperties is null"); String[] entries = connectionProperties.split(";"); Properties properties = new Properties(); for (int i = 0; i < entries.length; i++) { String entry = entries[i]; if (entry.length() > 0) { int index = entry.indexOf('='); if (index > 0) { String name = entry.substring(0, index); String value = entry.substring(index + 1); properties.setProperty(name, value); } else { // no value is empty string which is how java.util.Properties works properties.setProperty(entry, ""); } } } this.connectionProperties = properties; this.restartNeeded = true; } protected boolean closed; /** * <p>Closes and releases all idle connections that are currently stored in the connection pool * associated with this data source.</p> * * <p>Connections that are checked out to clients when this method is invoked are not affected. * When client applications subsequently invoke {@link Connection#close()} to return * these connections to the pool, the underlying JDBC connections are closed.</p> * * <p>Attempts to acquire connections using {@link #getConnection()} after this method has been * invoked result in SQLExceptions.<p> * * <p>This method is idempotent - i.e., closing an already closed BasicDataSource has no effect * and does not generate exceptions.</p> * * @throws SQLException if an error occurs closing idle connections */ public synchronized void close() throws SQLException { closed = true; GenericObjectPool oldpool = connectionPool; connectionPool = null; dataSource = null; try { if (oldpool != null) { oldpool.close(); } } catch(SQLException e) { throw e; } catch(RuntimeException e) { throw e; } catch(Exception e) { throw new SQLNestedException("Cannot close connection pool", e); } } /** * If true, this data source is closed and no more connections can be retrieved from this datasource. * @return true, if the data source is closed; false otherwise */ public synchronized boolean isClosed() { return closed; } /* JDBC_4_ANT_KEY_BEGIN */ public boolean isWrapperFor(Class<?> iface) throws SQLException { return false; } public <T> T unwrap(Class<T> iface) throws SQLException { throw new SQLException("BasicDataSource is not a wrapper."); } /* JDBC_4_ANT_KEY_END */ // ------------------------------------------------------ Protected Methods /** * <p>Create (if necessary) and return the internal data source we are * using to manage our connections.</p> * * <p><strong>IMPLEMENTATION NOTE</strong> - It is tempting to use the * "double checked locking" idiom in an attempt to avoid synchronizing * on every single call to this method. However, this idiom fails to * work correctly in the face of some optimizations that are legal for * a JVM to perform.</p> * * @throws SQLException if the object pool cannot be created. */ protected synchronized DataSource createDataSource() throws SQLException { if (closed) { throw new SQLException("Data source is closed"); } // Return the pool if we have already created it if (dataSource != null) { return (dataSource); } // create factory which returns raw physical connections ConnectionFactory driverConnectionFactory = createConnectionFactory(); // create a pool for our connections createConnectionPool(); // Set up statement pool, if desired GenericKeyedObjectPoolFactory statementPoolFactory = null; if (isPoolPreparedStatements()) { statementPoolFactory = new GenericKeyedObjectPoolFactory(null, -1, // unlimited maxActive (per key) GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL, 0, // maxWait 1, // maxIdle (per key) maxOpenPreparedStatements); } // Set up the poolable connection factory createPoolableConnectionFactory(driverConnectionFactory, statementPoolFactory, abandonedConfig); // Create and return the pooling data source to manage the connections createDataSourceInstance(); try { for (int i = 0 ; i < initialSize ; i++) { connectionPool.addObject(); } } catch (Exception e) { throw new SQLNestedException("Error preloading the connection pool", e); } return dataSource; } /** * Creates a JDBC connection factory for this datasource. This method only * exists so subclasses can replace the implementation class. */ protected ConnectionFactory createConnectionFactory() throws SQLException { // Load the JDBC driver class Class driverFromCCL = null; if (driverClassName != null) { try { try { if (driverClassLoader == null) { Class.forName(driverClassName); } else { Class.forName(driverClassName, true, driverClassLoader); } } catch (ClassNotFoundException cnfe) { driverFromCCL = Thread.currentThread( ).getContextClassLoader().loadClass( driverClassName); } } catch (Throwable t) { String message = "Cannot load JDBC driver class '" + driverClassName + "'"; logWriter.println(message); t.printStackTrace(logWriter); throw new SQLNestedException(message, t); } } // Create a JDBC driver instance Driver driver = null; try { if (driverFromCCL == null) { driver = DriverManager.getDriver(url); } else { // Usage of DriverManager is not possible, as it does not // respect the ContextClassLoader driver = (Driver) driverFromCCL.newInstance(); if (!driver.acceptsURL(url)) { throw new SQLException("No suitable driver", "08001"); } } } catch (Throwable t) { String message = "Cannot create JDBC driver of class '" + (driverClassName != null ? driverClassName : "") + "' for connect URL '" + url + "'"; logWriter.println(message); t.printStackTrace(logWriter); throw new SQLNestedException(message, t); } // Can't test without a validationQuery if (validationQuery == null) { setTestOnBorrow(false); setTestOnReturn(false); setTestWhileIdle(false); } // Set up the driver connection factory we will use String user = username; if (user != null) { connectionProperties.put("user", user); } else { log("DBCP DataSource configured without a 'username'"); } String pwd = password; if (pwd != null) { connectionProperties.put("password", pwd); } else { log("DBCP DataSource configured without a 'password'"); } ConnectionFactory driverConnectionFactory = new DriverConnectionFactory(driver, url, connectionProperties); return driverConnectionFactory; } /** * Creates a connection pool for this datasource. This method only exists * so subclasses can replace the implementation class. */ protected void createConnectionPool() { // Create an object pool to contain our active connections GenericObjectPool gop; if ((abandonedConfig != null) && (abandonedConfig.getRemoveAbandoned())) { gop = new AbandonedObjectPool(null,abandonedConfig); } else { gop = new GenericObjectPool(); } gop.setMaxActive(maxActive); gop.setMaxIdle(maxIdle); gop.setMinIdle(minIdle); gop.setMaxWait(maxWait); gop.setTestOnBorrow(testOnBorrow); gop.setTestOnReturn(testOnReturn); gop.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); gop.setNumTestsPerEvictionRun(numTestsPerEvictionRun); gop.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); gop.setTestWhileIdle(testWhileIdle); connectionPool = gop; } /** * Creates the actual data source instance. This method only exists so * subclasses can replace the implementation class. * * @throws SQLException if unable to create a datasource instance */ protected void createDataSourceInstance() throws SQLException { PoolingDataSource pds = new PoolingDataSource(connectionPool); pds.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed()); pds.setLogWriter(logWriter); dataSource = pds; } /** * Creates the PoolableConnectionFactory and attaches it to the connection pool. This method only exists * so subclasses can replace the default implementation. * * @param driverConnectionFactory JDBC connection factory * @param statementPoolFactory statement pool factory (null if statement pooling is turned off) * @param configuration abandoned connection tracking configuration (null if no tracking) * @throws SQLException if an error occurs creating the PoolableConnectionFactory */ protected void createPoolableConnectionFactory(ConnectionFactory driverConnectionFactory, KeyedObjectPoolFactory statementPoolFactory, AbandonedConfig configuration) throws SQLException { PoolableConnectionFactory connectionFactory = null; try { connectionFactory = new PoolableConnectionFactory(driverConnectionFactory, connectionPool, statementPoolFactory, validationQuery, validationQueryTimeout, connectionInitSqls, defaultReadOnly, defaultAutoCommit, defaultTransactionIsolation, defaultCatalog, configuration); validateConnectionFactory(connectionFactory); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new SQLNestedException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e); } } protected static void validateConnectionFactory(PoolableConnectionFactory connectionFactory) throws Exception { Connection conn = null; try { conn = (Connection) connectionFactory.makeObject(); connectionFactory.activateObject(conn); connectionFactory.validateConnection(conn); connectionFactory.passivateObject(conn); } finally { connectionFactory.destroyObject(conn); } } /** * Not used currently */ private void restart() { try { close(); } catch (SQLException e) { log("Could not restart DataSource, cause: " + e.getMessage()); } } protected void log(String message) { if (logWriter != null) { logWriter.println(message); } } }
BasicDataSource
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.commons.dbcp; import java.io.ByteArrayInputStream; import java.sql.Connection; import java.util.Enumeration; import java.util.Hashtable; import java.util.Properties; import java.util.StringTokenizer; import java.util.Collections; import javax.naming.Context; import javax.naming.Name; import javax.naming.RefAddr; import javax.naming.Reference; import javax.naming.spi.ObjectFactory; import javax.sql.DataSource; /** * <p>JNDI object factory that creates an instance of * <code>BasicDataSource</code> that has been configured based on the * <code>RefAddr</code> values of the specified <code>Reference</code>, * which must match the names and data types of the * <code>BasicDataSource</code> bean properties.</p> * * @author Craig R. McClanahan * @author Dirk Verbeeck * @version $Revision: 828639 $ $Date: 2009-10-22 06:27:43 -0400 (Thu, 22 Oct 2009) $ */ public class BasicDataSourceFactory implements ObjectFactory { private final static String PROP_DEFAULTAUTOCOMMIT = "defaultAutoCommit"; private final static String PROP_DEFAULTREADONLY = "defaultReadOnly"; private final static String PROP_DEFAULTTRANSACTIONISOLATION = "defaultTransactionIsolation"; private final static String PROP_DEFAULTCATALOG = "defaultCatalog"; private final static String PROP_DRIVERCLASSNAME = "driverClassName"; private final static String PROP_MAXACTIVE = "maxActive"; private final static String PROP_MAXIDLE = "maxIdle"; private final static String PROP_MINIDLE = "minIdle"; private final static String PROP_INITIALSIZE = "initialSize"; private final static String PROP_MAXWAIT = "maxWait"; private final static String PROP_TESTONBORROW = "testOnBorrow"; private final static String PROP_TESTONRETURN = "testOnReturn"; private final static String PROP_TIMEBETWEENEVICTIONRUNSMILLIS = "timeBetweenEvictionRunsMillis"; private final static String PROP_NUMTESTSPEREVICTIONRUN = "numTestsPerEvictionRun"; private final static String PROP_MINEVICTABLEIDLETIMEMILLIS = "minEvictableIdleTimeMillis"; private final static String PROP_TESTWHILEIDLE = "testWhileIdle"; private final static String PROP_PASSWORD = "password"; private final static String PROP_URL = "url"; private final static String PROP_USERNAME = "username"; private final static String PROP_VALIDATIONQUERY = "validationQuery"; private final static String PROP_VALIDATIONQUERY_TIMEOUT = "validationQueryTimeout"; /** * The property name for initConnectionSqls. * The associated value String must be of the form [query;]* * @since 1.3 */ private final static String PROP_INITCONNECTIONSQLS = "initConnectionSqls"; private final static String PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED = "accessToUnderlyingConnectionAllowed"; private final static String PROP_REMOVEABANDONED = "removeAbandoned"; private final static String PROP_REMOVEABANDONEDTIMEOUT = "removeAbandonedTimeout"; private final static String PROP_LOGABANDONED = "logAbandoned"; private final static String PROP_POOLPREPAREDSTATEMENTS = "poolPreparedStatements"; private final static String PROP_MAXOPENPREPAREDSTATEMENTS = "maxOpenPreparedStatements"; private final static String PROP_CONNECTIONPROPERTIES = "connectionProperties"; private final static String[] ALL_PROPERTIES = { PROP_DEFAULTAUTOCOMMIT, PROP_DEFAULTREADONLY, PROP_DEFAULTTRANSACTIONISOLATION, PROP_DEFAULTCATALOG, PROP_DRIVERCLASSNAME, PROP_MAXACTIVE, PROP_MAXIDLE, PROP_MINIDLE, PROP_INITIALSIZE, PROP_MAXWAIT, PROP_TESTONBORROW, PROP_TESTONRETURN, PROP_TIMEBETWEENEVICTIONRUNSMILLIS, PROP_NUMTESTSPEREVICTIONRUN, PROP_MINEVICTABLEIDLETIMEMILLIS, PROP_TESTWHILEIDLE, PROP_PASSWORD, PROP_URL, PROP_USERNAME, PROP_VALIDATIONQUERY, PROP_VALIDATIONQUERY_TIMEOUT, PROP_INITCONNECTIONSQLS, PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED, PROP_REMOVEABANDONED, PROP_REMOVEABANDONEDTIMEOUT, PROP_LOGABANDONED, PROP_POOLPREPAREDSTATEMENTS, PROP_MAXOPENPREPAREDSTATEMENTS, PROP_CONNECTIONPROPERTIES }; // -------------------------------------------------- ObjectFactory Methods /** * <p>Create and return a new <code>BasicDataSource</code> instance. If no * instance can be created, return <code>null</code> instead.</p> * * @param obj The possibly null object containing location or * reference information that can be used in creating an object * @param name The name of this object relative to <code>nameCtx</code> * @param nameCtx The context relative to which the <code>name</code> * parameter is specified, or <code>null</code> if <code>name</code> * is relative to the default initial context * @param environment The possibly null environment that is used in * creating this object * * @exception Exception if an exception occurs creating the instance */ public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception { // We only know how to deal with <code>javax.naming.Reference</code>s // that specify a class name of "javax.sql.DataSource" if ((obj == null) || !(obj instanceof Reference)) { return null; } Reference ref = (Reference) obj; if (!"javax.sql.DataSource".equals(ref.getClassName())) { return null; } Properties properties = new Properties(); for (int i = 0 ; i < ALL_PROPERTIES.length ; i++) { String propertyName = ALL_PROPERTIES[i]; RefAddr ra = ref.get(propertyName); if (ra != null) { String propertyValue = ra.getContent().toString(); properties.setProperty(propertyName, propertyValue); } } return createDataSource(properties); } /** * Creates and configures a {@link BasicDataSource} instance based on the * given properties. * * @param properties the datasource configuration properties * @throws Exception if an error occurs creating the data source */ public static DataSource createDataSource(Properties properties) throws Exception { BasicDataSource dataSource = new BasicDataSource(); String value = null; value = properties.getProperty(PROP_DEFAULTAUTOCOMMIT); if (value != null) { dataSource.setDefaultAutoCommit(Boolean.valueOf(value).booleanValue()); } value = properties.getProperty(PROP_DEFAULTREADONLY); if (value != null) { dataSource.setDefaultReadOnly(Boolean.valueOf(value).booleanValue()); } value = properties.getProperty(PROP_DEFAULTTRANSACTIONISOLATION); if (value != null) { int level = PoolableConnectionFactory.UNKNOWN_TRANSACTIONISOLATION; if ("NONE".equalsIgnoreCase(value)) { level = Connection.TRANSACTION_NONE; } else if ("READ_COMMITTED".equalsIgnoreCase(value)) { level = Connection.TRANSACTION_READ_COMMITTED; } else if ("READ_UNCOMMITTED".equalsIgnoreCase(value)) { level = Connection.TRANSACTION_READ_UNCOMMITTED; } else if ("REPEATABLE_READ".equalsIgnoreCase(value)) { level = Connection.TRANSACTION_REPEATABLE_READ; } else if ("SERIALIZABLE".equalsIgnoreCase(value)) { level = Connection.TRANSACTION_SERIALIZABLE; } else { try { level = Integer.parseInt(value); } catch (NumberFormatException e) { System.err.println("Could not parse defaultTransactionIsolation: " + value); System.err.println("WARNING: defaultTransactionIsolation not set"); System.err.println("using default value of database driver"); level = PoolableConnectionFactory.UNKNOWN_TRANSACTIONISOLATION; } } dataSource.setDefaultTransactionIsolation(level); } value = properties.getProperty(PROP_DEFAULTCATALOG); if (value != null) { dataSource.setDefaultCatalog(value); } value = properties.getProperty(PROP_DRIVERCLASSNAME); if (value != null) { dataSource.setDriverClassName(value); } value = properties.getProperty(PROP_MAXACTIVE); if (value != null) { dataSource.setMaxActive(Integer.parseInt(value)); } value = properties.getProperty(PROP_MAXIDLE); if (value != null) { dataSource.setMaxIdle(Integer.parseInt(value)); } value = properties.getProperty(PROP_MINIDLE); if (value != null) { dataSource.setMinIdle(Integer.parseInt(value)); } value = properties.getProperty(PROP_INITIALSIZE); if (value != null) { dataSource.setInitialSize(Integer.parseInt(value)); } value = properties.getProperty(PROP_MAXWAIT); if (value != null) { dataSource.setMaxWait(Long.parseLong(value)); } value = properties.getProperty(PROP_TESTONBORROW); if (value != null) { dataSource.setTestOnBorrow(Boolean.valueOf(value).booleanValue()); } value = properties.getProperty(PROP_TESTONRETURN); if (value != null) { dataSource.setTestOnReturn(Boolean.valueOf(value).booleanValue()); } value = properties.getProperty(PROP_TIMEBETWEENEVICTIONRUNSMILLIS); if (value != null) { dataSource.setTimeBetweenEvictionRunsMillis(Long.parseLong(value)); } value = properties.getProperty(PROP_NUMTESTSPEREVICTIONRUN); if (value != null) { dataSource.setNumTestsPerEvictionRun(Integer.parseInt(value)); } value = properties.getProperty(PROP_MINEVICTABLEIDLETIMEMILLIS); if (value != null) { dataSource.setMinEvictableIdleTimeMillis(Long.parseLong(value)); } value = properties.getProperty(PROP_TESTWHILEIDLE); if (value != null) { dataSource.setTestWhileIdle(Boolean.valueOf(value).booleanValue()); } value = properties.getProperty(PROP_PASSWORD); if (value != null) { dataSource.setPassword(value); } value = properties.getProperty(PROP_URL); if (value != null) { dataSource.setUrl(value); } value = properties.getProperty(PROP_USERNAME); if (value != null) { dataSource.setUsername(value); } value = properties.getProperty(PROP_VALIDATIONQUERY); if (value != null) { dataSource.setValidationQuery(value); } value = properties.getProperty(PROP_VALIDATIONQUERY_TIMEOUT); if (value != null) { dataSource.setValidationQueryTimeout(Integer.parseInt(value)); } value = properties.getProperty(PROP_ACCESSTOUNDERLYINGCONNECTIONALLOWED); if (value != null) { dataSource.setAccessToUnderlyingConnectionAllowed(Boolean.valueOf(value).booleanValue()); } value = properties.getProperty(PROP_REMOVEABANDONED); if (value != null) { dataSource.setRemoveAbandoned(Boolean.valueOf(value).booleanValue()); } value = properties.getProperty(PROP_REMOVEABANDONEDTIMEOUT); if (value != null) { dataSource.setRemoveAbandonedTimeout(Integer.parseInt(value)); } value = properties.getProperty(PROP_LOGABANDONED); if (value != null) { dataSource.setLogAbandoned(Boolean.valueOf(value).booleanValue()); } value = properties.getProperty(PROP_POOLPREPAREDSTATEMENTS); if (value != null) { dataSource.setPoolPreparedStatements(Boolean.valueOf(value).booleanValue()); } value = properties.getProperty(PROP_MAXOPENPREPAREDSTATEMENTS); if (value != null) { dataSource.setMaxOpenPreparedStatements(Integer.parseInt(value)); } value = properties.getProperty(PROP_INITCONNECTIONSQLS); if (value != null) { StringTokenizer tokenizer = new StringTokenizer(value, ";"); dataSource.setConnectionInitSqls(Collections.list(tokenizer)); } value = properties.getProperty(PROP_CONNECTIONPROPERTIES); if (value != null) { Properties p = getProperties(value); Enumeration e = p.propertyNames(); while (e.hasMoreElements()) { String propertyName = (String) e.nextElement(); dataSource.addConnectionProperty(propertyName, p.getProperty(propertyName)); } } // DBCP-215 // Trick to make sure that initialSize connections are created if (dataSource.getInitialSize() > 0) { dataSource.getLogWriter(); } // Return the configured DataSource instance return dataSource; } /** * <p>Parse properties from the string. Format of the string must be [propertyName=property;]*<p> * @param propText * @return Properties * @throws Exception */ static private Properties getProperties(String propText) throws Exception { Properties p = new Properties(); if (propText != null) { p.load(new ByteArrayInputStream(propText.replace(';', '\n').getBytes())); } return p; } }
BasicDataSourceFactory
在Jboss3.2中得到的是org.jboss.resource.adapter.jdbc.WrapperDataSource类,在WebOTX5.3中得到的是jp.co.nec.WebOTX.WODataSource类.所有的这些类都实现了javax.sql.DataSource接口,所以我们才可以Cast这个Object为javax.sql.DataSource.
Java异常处理的一个规则就是不要遮蔽异常.因为一旦出现这种情况,而客户程序又非常想知道错误原因,那么就可能丢失问题真正原因的线索.
一个设计良好的应用框架系统,是有必要建立自己的类装载系统的,而不能过分依赖系统CLASSPATH变量.
例如,Tomcat有自己的类装载机制,JBoss也是.Eclipse则干脆不用系统CLASSPATH变量.具体信息请参照它们的文档.
如果过分依赖CLASSPATH,就会有一个副作用,就是造成自身系统的不稳定.因为CLASSPATH是开放的,无法确保其它应用对它的修改不会对自身系统产生负面影响.
http://www.blogjava.net/ultramarine/articles/13822.html