📄 namedconnectionpool.java
字号:
/*------------------------------------------------------------------------------Name: NamedConnectionPool.javaProject: xmlBlaster.orgCopyright: jutils.org, see jutils-LICENSE fileComment: Basic handling of a pool of limited resourcesAuthor: xmlBlaster@marcelruff.info------------------------------------------------------------------------------*/package org.xmlBlaster.protocol.jdbc;import org.xmlBlaster.util.XmlBlasterException;import org.xmlBlaster.util.def.ErrorCode;import java.util.logging.Logger;import java.util.logging.Level;import org.xmlBlaster.util.Global;import org.xmlBlaster.util.I_Timeout;import org.xmlBlaster.util.Timestamp;import org.xmlBlaster.util.pool.PoolManager;import org.xmlBlaster.util.pool.I_PoolManager;import org.xmlBlaster.util.pool.ResourceWrapper;import java.util.Hashtable;import java.util.Enumeration;import java.sql.Connection;import java.sql.DriverManager;/** * This is a specialized JDBC connection pool for xmlBlaster. * <p /> * It allows accessing any number of different databases with * arbitrary login users.<br /> * Every database user is separately pooled for maximum performance.<br /> * Every DB request needs to pass the DB-url, login name and password, * since the clients are not permanently connected.<br /> * Unused connection pools are freed after some time. * <p /> * The timeout parameters and pool size is adjustable. * <p /> * The connections are established on demand (lazy allocation). * Pre-allocation is currently not implemented. * The first SQL request (for example with Oracle) consumes about * 1 second to establish the connection, the following requests * get this connection from the pool, which is below 1 millisecond. * If more than one SQL requests are done simultaneously, the pool * increases the number of parallel connections. * <p /> * Load the drivers before using this pool, e.g.<br /> * <pre> * java -Djdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.taste.ourDriver ... * </pre> * or in xmlBlaster.properties, e.g.<br /> * <pre> * JdbcDriver.drivers=oracle.jdbc.driver.OracleDriver,org.gjt.mm.mysql.Driver:postgresql.Driver,de.sag.jdbc.adabasd.ADriver:sun.jdbc.odbc.JdbcOdbcDriver:com.sybase.jdbc2.jdbc.SybDriver * </pre> * You use reserve() to get a connection and need to call release() after using it, * note that the connection parameters are optional: * <pre> * String dbStmt = "select * from user_table"; * java.sql.Connection con = namedPool.reserve(dbUrl, dbUser, dbPasswd, 60*60*1000L, 100, 10*60*1000L); * java.sql.Statement stmt = null; * java.sql.ResultSet rs = null; * try { * stmt = con.createStatement(); * rs = stmt.executeQuery(dbStmt); * } finally { * if (rs!=null) rs.close(); * if (stmt!=null) stmt.close(); * if (con!=null) namedPool.release(dbUrl, user, pw, con); * } * </pre> * @see org.xmlBlaster.util.pool.PoolManager * @see org.xmlBlaster.util.pool.ResourceWrapper * @since xmlBlaster 0.78 */public class NamedConnectionPool { private static final String ME = "NamedConnectionPool"; private Global glob; private static Logger log = Logger.getLogger(NamedConnectionPool.class.getName()); private Hashtable namedPools = new Hashtable(); private final Object meetingPoint = new Object(); NamedConnectionPool(Global glob) { this.glob = glob; } /** * Use this method to get a JDBC connection. * <br /> * The pooling properties are set to default values. * @param dbUrl For example "jdbc:oracle:thin:@localhost:1521:mydb * @param dbUser The database user * @param dbPasswd The database password */ Connection reserve(String dbUrl, String dbUser, String dbPasswd) throws XmlBlasterException { return reserve(dbUrl, dbUser, dbPasswd, -1, -1, -1); } /** * Use this method to get a JDBC connection. * <br /> * Usually only the first time for a user, to specify all parameters. * @param dbUrl For example "jdbc:oracle:thin:@localhost:1521:mydb * @param eraseUnusedPoolTimeout Remove pool of a user if not in use, in ms * 0 switches it off, -1 uses default setting 1 hour (from xmlBlaster.properties) * @param maxInstances Default is max. 20 connections (from xmlBlaster.properties) * @param idleToErase in msec * 0 switches it off, -1 uses default setting 10 min. (from xmlBlaster.properties) */ Connection reserve(String dbUrl, String dbUser, String dbPasswd, long eraseUnusedPoolTimeout, int maxInstances, long idleToErase) throws XmlBlasterException { long busyToIdle = 0L; // On timeout it changes state from 'busy' to 'idle': switched off! UnnamedConnectionPool pool = getPool(dbUrl, dbUser, dbPasswd); if (pool == null) { // check before as well to increase performance synchronized(meetingPoint) { pool = getPool(dbUrl, dbUser, dbPasswd); if (pool == null) { if (dbPasswd == null) throw new XmlBlasterException(glob, ErrorCode.RESOURCE_CONFIGURATION, ME+".MissingPasswd", "Please give a password for '" + dbUser + "' when creating a JDBC pool"); pool = new UnnamedConnectionPool(this, dbUrl, dbUser, dbPasswd, eraseUnusedPoolTimeout, maxInstances, busyToIdle, idleToErase); namedPools.put(getKey(dbUrl, dbUser, dbPasswd), pool); } } } try { Connection con = pool.reserve(); if (log.isLoggable(Level.FINE)) log.fine("reserve(" + dbUrl + ", " + dbUser + ") con=" + con); return con; } catch(XmlBlasterException e) { throw e; } catch(Throwable e) { log.severe("Unexpected exception in connect(" + dbUrl + ", " + dbUser + "): " + e.toString()); throw new XmlBlasterException(glob, ErrorCode.RESOURCE_CONFIGURATION, ME+".NoOpen", "Couldn't open database connection: " + e.toString()); } } /** * Use this method to release a JDBC connection. */ void release(String dbUrl, String dbUser, String dbPasswd, Connection con) throws XmlBlasterException { UnnamedConnectionPool pool = getPool(dbUrl, dbUser, dbPasswd); if (pool != null) { pool.release(con); if (log.isLoggable(Level.FINE)) log.fine("release(" + dbUrl + ", " + dbUser + ") con=" + con); } } /** * Use this method to destroy the given JDBC connection. */ void eraseConnection(String dbUrl, String dbUser, String dbPasswd, Connection con) throws XmlBlasterException { UnnamedConnectionPool pool = getPool(dbUrl, dbUser, dbPasswd); if (pool != null) { if (log.isLoggable(Level.FINE)) log.fine("erase(" + dbUrl + ", " + dbUser + ") con=" + con); pool.erase(con); } } /** * Destroy the JDBC connection pool from a specific user. * The driver remains. * @param The UnnamedConnectionPool object */ void destroy(String dbUrl, String dbUser, String dbPasswd) throws XmlBlasterException { if (log.isLoggable(Level.FINE)) log.fine("Entering destroy() ..."); try { String key = getKey(dbUrl, dbUser, dbPasswd); UnnamedConnectionPool pool = (UnnamedConnectionPool)namedPools.remove(key); if (pool != null) pool.destroy(); if (log.isLoggable(Level.FINE)) log.fine("All JDBC connections for '" + dbUrl + "' destroyed"); } catch (Exception e) { log.severe("System Exception in destroy JDBC connection for '" + dbUrl + "': " + e.toString()); throw new XmlBlasterException(glob, ErrorCode.RESOURCE_DB_UNKNOWN, ME+".DestroyError", "System Exception in destroy JDBC connection for '" + dbUrl + "': " + e.toString()); } } /** * Destroy the complete named pool of all users. * This object is still valid for further use. */ void destroy() { for (Enumeration e = namedPools.elements() ; e.hasMoreElements() ;) { UnnamedConnectionPool pool = (UnnamedConnectionPool)e.nextElement(); pool.destroy(); } namedPools.clear(); } /** * @return instanceId <db_url>^<username>/<passwd>, e.g. "jdbc:oracle:thin:@localhost:1521:mydb^jack/secret" */ private String getKey(String dbUrl, String dbUser, String dbPasswd) { StringBuffer buf = new StringBuffer(80); return buf.append(dbUrl).append("^").append(dbUser).append("/").append(dbPasswd).toString(); } private UnnamedConnectionPool getPool(String dbUrl, String dbUser, String dbPasswd) { String key = getKey(dbUrl, dbUser, dbPasswd); return (UnnamedConnectionPool)namedPools.get(key); } /** * Dump state of this object into a XML ASCII string. */ public final String toXml() { StringBuffer buf = new StringBuffer(256); String offset = "\n"; buf.append(offset).append("<").append(ME).append(">"); buf.append(offset).append(" <namedPools num='").append(namedPools.size()).append("'>"); for (Enumeration e = namedPools.elements() ; e.hasMoreElements() ;) { UnnamedConnectionPool pool = (UnnamedConnectionPool)e.nextElement(); buf.append(pool.toXml()); } buf.append(offset).append(" </namedPools>"); buf.append(offset).append("</" + ME + ">"); return buf.toString(); } /** * Inner class, every user of the Named pool has its own connection pool. * <p /> * If the resource pool is exhausted, the request will poll for a connection * 5 times, with 1 sec sleeping in between.<br /> * This feature is adjustable in xmlBlaster.properties with:<br /> * <pre> * JdbcPool.maxResourceExhaustRetries=5 * JdbcPool.resourceExhaustSleepGap=1000 * </pre> */ private class UnnamedConnectionPool implements I_PoolManager, I_Timeout { private static final String ME = "UnnamedConnectionPool"; private NamedConnectionPool boss = null; PoolManager poolManager = null; private String dbUrl; private String dbUser; private String dbPasswd; private long eraseUnusedPoolTimeout; /** If the pool is exhausted, we poll the given times */ private int maxResourceExhaustRetries; /** If the pool is exhausted, we poll every given millis<br /> Please note that the current request thread will block for maxResourceExhaustRetries*resourceExhaustSleepGap millis. */ private long resourceExhaustSleepGap; private Timestamp timeoutHandle; private final Object timeoutMonitor = new Object(); private final Object meetingPoint = new Object(); /** * @param boss My manager * @param eraseUnusedPoolTimeout This pool is erased after given millis without activity of the owning user<br /> * 0 switches it off, -1 looks into env JdbcPool.eraseUnusedPoolTimeout setting (and defaults to one hour) * @param maxInstances Max. number of resources in this pool. * -1 uses default of 20 (xmlBlaster.properties)<br /> * @param busyToIdleTimeout Max. busy time of this resource in milli seconds<br /> * On timeout it changes state from 'busy' to 'idle'.<br /> * You can overwrite this value for each resource instance<br /> * 0 switches it off<br /> * -1 uses default (switched off)<br /> * You get called back through I_PoolManager.busyToIdle() on timeout * allowing you to code some specific handling. * @param idleToEraseTimeout Max. idle time span of this resource in milli seconds<br /> * On timeout it changes state from 'idle' to 'undef' (it is deleted).<br /> * You can overwrite this value for each resource instance<br /> * 0 switches it off<br /> * -1 uses default (10 min) (xmlBlaster.properties)<br /> * You get called back through I_PoolManager.toErased() on timeout * allowing you to code some specific handling. */ public UnnamedConnectionPool(NamedConnectionPool boss, String dbUrl, String dbUser, String dbPasswd, long eraseUnusedPoolTimeout, int maxInstances, long busyToIdle, long idleToErase) { this.boss = boss; this.dbUrl = dbUrl; this.dbUser = dbUser; this.dbPasswd = dbPasswd; this.eraseUnusedPoolTimeout = eraseUnusedPoolTimeout;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -