domino连接池解决方案(一)

来源:互联网 发布:kmp的next算法 编辑:程序博客网 时间:2024/06/12 23:04

Domino连接池解决方案(一)

一.     前言

前篇文章简单演示了一下Domino存取Oracle数据的操作方法及程序代码。虽然Domino存取Oracle数据这个问题解决了,但还有一个麻烦随之而来了。用代理调用Jdbc,有个问题,每次代理结束后都要释放连接。久而久之,对Oracle服务器的压力也不小,要是用的人多的话,那就更糟糕了。有没有解决办法那?有!使用连接池。下面就具体的应用进行分析。

二.        准备工作

先说一下我的测试环境。

操作系统:WindowsXP SP2 ,1G内存,Intel双核CPU

测试应用平台:Domino8.0

开发工具: Lotus Designer8.01 + Eclipse3.2.2  

           数据库:Oracle9i

      这里我说一句,MySQL数据库也可以。

      我们知道使用代理连接Oracle数据库,有这么几个步骤。Domino装入代理,然后执行代理。代理注册jdbc驱动,创建网络连接,然后就是存取数据,而后断开连接,反注册jdbc驱动,大致分这几个步骤。每个代理每次被调用都要走这么一个循环,效率低下是肯定的了。解决这个问题只有使用连接池。

      我的java水平不高,自己写连接池估计没戏了。只好上网查找,网络真是个好东西,被我找到了。虽然版本挺多,但是几乎都是一个版本派生出来的,因此都差不多。原创作者无法查证,咱先借花献佛吧。不过有个小问题,有些版本的连接池有问题,大家注意一下。我会在附件一中给出我使用的连接池版本,那是没问题的。将源文件编译后达成jar包,拷贝到domino服务器的jvm/lib/ext/目录下,同时也要在你的客户端的jvm/lib/ext下也拷贝一份,这样在编译代理时才不会出错。

三.测试连接池

好了,万事具备,只欠东风了。马上写代理验证连接池的可靠性和可用性,代理源程序见附件二。

调用web代理,我是通过在浏览器中输入代理的URL来进行访问的。这个就不用我罗唆了吧。输入URL后会在Domino服务器的控制台上输出数据库的记录信息。好了,到这里连接池测试是成功的。如果要用到商业上去,那么要注意几点。首先,我没有意见,大家随便用。其次,连接池作者有没有意见,我没办法确认。再次,一定要通过压力测试才能正式上线。

(同时发布于www.chinalotus.com ,www.csdn.net,www.chinaunix.net

 

附件一:一个完备的连接池类

package com.pool.domino;

import java.io.*;

import java.sql.*;

import java.util.*;

import java.util.Date;

 

/**

 * 管理类DBConnectionManager支持对一个或多个由属性文件定义的数据库连接

 * 池的访问.客户程序可以调用getInstance()方法访问本类的唯一实例.

 */

 

public class DBConnectionManager {

    static private DBConnectionManager instance; // 唯一实例

 

    static private int clients;

 

    private Vector drivers = new Vector();

 

    private PrintWriter log;

 

    private Hashtable pools = new Hashtable();

 

    /**

     * 返回唯一实例.如果是第一次调用此方法,则创建实例

     *

     * @return DBConnectionManager 唯一实例

     **/

    static synchronized public DBConnectionManager getInstance() {

       if (instance == null) {

           instance = new DBConnectionManager();

       }

       clients++;

       return instance;

    }

 

    /**

     * 建构函数私有以防止其它对象创建本类实例

     */

    private DBConnectionManager() {

       init();

    }

 

    /**

     * 将连接对象返回给由名字指定的连接池

     *

     * @param name 在属性文件中定义的连接池名字

     * @param con 连接对象

     **/

    public void freeConnection(String name, Connection con) {

       DBConnectionPool pool = (DBConnectionPool) pools.get(name);

       if (pool != null) {

           pool.freeConnection(con);

       }

    }

 

    /**

     * 获得一个可用的(空闲的)连接.如果没有可用连接,且已有连接数小于最大连接数

     * 限制,则创建并返回新连接

     *

     * @param name 在属性文件中定义的连接池名字

     * @return Connection 可用连接或null

     */

    public Connection getConnection(String name) {

       DBConnectionPool pool = (DBConnectionPool) pools.get(name);

       if (pool != null) {

           return pool.getConnection();

       }

       return null;

    }

 

    /**

     * 获得一个可用连接.若没有可用连接,且已有连接数小于最大连接数限制,

     * 则创建并返回新连接.否则,在指定的时间内等待其它线程释放连接.

     *

     * @param name 连接池名字

     * @param time 以毫秒计的等待时间

     * @return Connection 可用连接或null

     */

    public Connection getConnection(String name, long time) {

       DBConnectionPool pool = (DBConnectionPool) pools.get(name);

       if (pool != null) {

           return pool.getConnection(time);

       }

       return null;

    }

 

    /**

     * 关闭所有连接,撤销驱动程序的注册

     */

    public synchronized void release() {

       // 等待直到最后一个客户程序调用

       if (--clients != 0) {

           return;

       }

 

       Enumeration allPools = pools.elements();

       while (allPools.hasMoreElements()) {

           DBConnectionPool pool = (DBConnectionPool) allPools.nextElement();

           pool.release();

       }

       Enumeration allDrivers = drivers.elements();

       while (allDrivers.hasMoreElements()) {

           Driver driver = (Driver) allDrivers.nextElement();

           try {

              DriverManager.deregisterDriver(driver);

              log("撤销JDBC驱动程序 " + driver.getClass().getName() + "的注册");

           } catch (SQLException e) {

              log(e, "无法撤销下列JDBC驱动程序的注册: " + driver.getClass().getName());

           }

       }

    }

 

    /**

     * 根据指定属性创建连接池实例.

     *

     * @param props 连接池属性

     * @throws FileNotFoundException

     */

    private void createPools(Properties props) {

 

       Enumeration propNames = props.propertyNames();

       while (propNames.hasMoreElements()) {

           String name = (String) propNames.nextElement();

           if (name.endsWith(".url")) {

              String poolName = name.substring(0, name.lastIndexOf("."));

              String url = props.getProperty(poolName + ".url");

              if (url == null) {

                  log("没有为连接池" + poolName + "指定URL");

                  continue;

              }

              String user = props.getProperty(poolName + ".user");

              String password = props.getProperty(poolName + ".password");

              String maxconn = props.getProperty(poolName + ".maxconn", "0");

              int max;

               try {

                  max = Integer.valueOf(maxconn).intValue();

              } catch (NumberFormatException e) {

                  log("错误的最大连接数限制: " + maxconn + " .连接池: " + poolName);

                  max = 0;

              }

              DBConnectionPool pool = new DBConnectionPool(poolName, url, user, password, max);

              pools.put(poolName, pool);

              log("成功创建连接池" + poolName);

           }

       }

    }

 

    /**

     * 读取属性完成初始化

     */

    private void init() {

       InputStream is = getClass().getResourceAsStream("/db.properties");

       Properties dbProps = new Properties();

       try {

           dbProps.load(is);

       } catch (Exception e) {

           e.printStackTrace();

           System.err.println("不能读取属性文件. " + "请确保db.propertiesCLASSPATH指定的路径中");

           return;

       }

       String logFile = dbProps.getProperty("logfile", "DBConnectionManager.log");

       try {

           log = new PrintWriter(new FileWriter(logFile, true), true);

       } catch (IOException e) {

           System.err.println("无法打开日志文件: " + logFile);

           log = new PrintWriter(System.err);

       }

       loadDrivers(dbProps);

       createPools(dbProps);

    }

 

    /**

     * 装载和注册所有JDBC驱动程序

     *

     * @param props 属性

     */

    private void loadDrivers(Properties props) {

       String driverClasses = props.getProperty("drivers");

       StringTokenizer st = new StringTokenizer(driverClasses);

       while (st.hasMoreElements()) {

           String driverClassName = st.nextToken().trim();

           try {

              Driver driver = (Driver) Class.forName(driverClassName).newInstance();

              DriverManager.registerDriver(driver);

              drivers.addElement(driver);

              log("成功注册JDBC驱动程序" + driverClassName);

           } catch (Exception e) {

              log("无法注册JDBC驱动程序: " + driverClassName + ", 错误: " + e);

           }

       }

    }

 

    /**

     * 将文本信息写入日志文件

     */

    private void log(String msg) {

       log.println(new Date() + ": " + msg);

    }

 

    /**

     * 将文本信息与异常写入日志文件

     */

    private void log(Throwable e, String msg) {

       log.println(new Date() + ": " + msg);

       e.printStackTrace(log);

    }

 

    /**

     * 此内部类定义了一个连接池.它能够根据要求创建新连接,直到预定的最

     * 大连接数为止.在返回连接给客户程序之前,它能够验证连接的有效性.

     */

    class DBConnectionPool {

       private int checkedOut;

 

       private Vector freeConnections = new Vector();

 

       private int maxConn;

 

       private String name;

 

       private String password;

 

       private String URL;

 

       private String user;

 

       /**

        * 创建新的连接池

        *

        * @param name 连接池名字

        * @param URL 数据库的JDBC URL

        * @param user 数据库帐号, null

        * @param password 密码, null

        * @param maxConn 此连接池允许建立的最大连接数

        */

       public DBConnectionPool(String name, String URL, String user, String password, int maxConn) {

           this.name = name;

           this.URL = URL;

           this.user = user;

           this.password = password;

           this.maxConn = maxConn;

       }

 

       /**

        * 将不再使用的连接返回给连接池

        *

        * @param con 客户程序释放的连接

        */

       public synchronized void freeConnection(Connection con) {

           // 将指定连接加入到向量末尾

           freeConnections.addElement(con);

           checkedOut--;

           notifyAll();

       }

 

       /**

        * 从连接池获得一个可用连接.如没有空闲的连接且当前连接数小于最大连接

        * 数限制,则创建新连接.如原来登记为可用的连接不再有效,则从向量删除之,

        * 然后递归调用自己以尝试新的可用连接.

        */

       public synchronized Connection getConnection() {

           Connection con = null;

           if (freeConnections.size() > 0) {

              // 获取向量中第一个可用连接

              con = (Connection) freeConnections.firstElement();

              freeConnections.removeElementAt(0);

              try {

                  if (con.isClosed()) {

                     log("从连接池" + name + "删除一个无效连接");

                     // 递归调用自己,尝试再次获取可用连接

                     con = getConnection();

                  }

              } catch (SQLException e) {

                  log("从连接池" + name + "删除一个无效连接");

                  // 递归调用自己,尝试再次获取可用连接

                  con = getConnection();

              }

           } else if (maxConn == 0 || checkedOut < maxConn) {

              con = newConnection();

           }

           if (con != null) {

              checkedOut++;

           }

           return con;

       }

 

       /**

        * 从连接池获取可用连接.可以指定客户程序能够等待的最长时间

        * 参见前一个getConnection()方法.

        *

        * @param timeout 以毫秒计的等待时间限制

        */

       public synchronized Connection getConnection(long timeout) {

           long startTime = new Date().getTime();

           Connection con;

           while ((con = getConnection()) == null) {

              try {

                  wait(timeout);

              } catch (InterruptedException e) {

              }

              if ((new Date().getTime() - startTime) >= timeout) {

                  // wait()返回的原因是超时

                  return null;

              }

           }

           return con;

       }

 

       /**

        * 关闭所有连接

        */

       public synchronized void release() {

           Enumeration allConnections = freeConnections.elements();

           while (allConnections.hasMoreElements()) {

              Connection con = (Connection) allConnections.nextElement();

              try {

                  con.close();

                  log("关闭连接池" + name + "中的一个连接");

              } catch (SQLException e) {

                  log(e, "无法关闭连接池" + name + "中的连接");

              }

           }

           freeConnections.removeAllElements();

       }

 

       /**

        * 创建新的连接

        /**/

       private Connection newConnection() {

           Connection con = null;

           try {

              if (user == null) {

                  con = DriverManager.getConnection(URL);

              } else {

                  con = DriverManager.getConnection(URL, user, password);

              }

              log("连接池" + name + "创建一个新的连接");

           } catch (SQLException e) {

              log("无法创建下列URL的连接: " + URL + "/n/t" + e.getNextException());

              return null;

           }

           return con;

       }

    }

 

    public static void main(String args[]) {

       DBConnectionManager dm = DBConnectionManager.getInstance();

       Connection conn = dm.getConnection("jtds_root");

       if (conn != null) {

           System.out.println("OK");

       } else {

           System.out.println("NUll");

       }

 

       String strSQL = null;

       try {

           strSQL = "SELECT * from tb_maincontrol";

 

           Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

           ResultSet rs = stmt.executeQuery(strSQL);

           if (rs.next()) {

              System.out.println(rs.getString(1));

           }

       } catch (Exception sqe) {

           sqe.printStackTrace();

       }

    }

 

}

 

 

附件二:测试用java代理。

import java.sql.Connection;

import java.sql.ResultSet;

import java.sql.Statement;

import lotus.domino.*;

import com.pool.domino.*;

 

public class testpool extends AgentBase {

 

    public void NotesMain() {

       Session session = null;

       AgentContext agentContext = null;

       try {

           session = getSession();

           agentContext = session.getAgentContext();

 

           // (Your code goes here)

           DBConnectionManager dm = DBConnectionManager.getInstance();

           Connection conn = dm.getConnection("jtds_root");

 

           String strSQL = null;

           try {

              strSQL = "SELECT * from JDBCTEST";

 

              Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);

               ResultSet rs = stmt.executeQuery(strSQL);

              if (rs.next()) {

                  System.out.println(rs.getString(1));

              }

              rs.close();

              stmt.close();

              conn.close();

           } catch (Exception sqe) {

              sqe.printStackTrace();

           }

          

 

       } catch(Exception e) {

           e.printStackTrace();

       }finally{

           try{

              agentContext.recycle();

              session.recycle();

           }catch(Exception e){

             

           }

       }

    }

}