/** * Copyright (c)2010-2011 Enterprise Website Content Management System(EWCMS), All rights reserved. * EWCMS PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * http://www.ewcms.com */ package com.ewcms.plugin.externalds.generate.factory.jdbc; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.sql.DataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.ewcms.plugin.BaseRuntimeException; import com.ewcms.plugin.externalds.generate.factory.DataSourceFactoryable; import com.ewcms.plugin.externalds.generate.factory.dbcp.DbcpDataSourceFactoryable; import com.ewcms.plugin.externalds.generate.service.EwcmsDataSourceServiceable; import com.ewcms.plugin.externalds.generate.service.dbcp.DbcpDataSourceable; import com.ewcms.plugin.externalds.generate.service.jdbc.JdbcDataSourceService; import com.ewcms.plugin.externalds.model.BaseDS; import com.ewcms.plugin.externalds.model.JdbcDS; /** * Jdbc数据源工厂模式 * * @author 吴智俊 */ @Service public class JdbcDataSourceFactory implements DataSourceFactoryable { private static final Logger logger = LoggerFactory.getLogger(JdbcDataSourceFactory.class); protected static class PooledDataSourcesCache { protected static class DataSourceEntry { final Object key; final DbcpDataSourceable ds; DataSourceEntry next, prev; long lastAccess; public DataSourceEntry(Object key, DbcpDataSourceable ds) { this.key = key; this.ds = ds; } public void access(long now) { lastAccess = now; } } final Map<Object, DataSourceEntry> cache; DataSourceEntry first, last; public PooledDataSourcesCache() { cache = new HashMap<Object, DataSourceEntry>(); first = last = null; } public DbcpDataSourceable get(Object key, long now) { DataSourceEntry entry = (DataSourceEntry) cache.get(key); if (entry == null) { return null; } moveFirst(entry); entry.access(now); return entry.ds; } private void moveFirst(DataSourceEntry entry) { entry.next = first; entry.prev = null; if (first != null) { first.prev = entry; } first = entry; if (last == null) { last = entry; } } public void put(Object key, DbcpDataSourceable ds, long now) { DataSourceEntry entry = new DataSourceEntry(key, ds); moveFirst(entry); entry.access(now); cache.put(key, entry); } public List<DbcpDataSourceable> removeExpired(long now, int timeout) { List<DbcpDataSourceable> expired = new ArrayList<DbcpDataSourceable>(); DataSourceEntry entry = last; long expTime = now - timeout * 1000; while (entry != null && entry.lastAccess < expTime) { expired.add(entry.ds); remove(entry); entry = entry.prev; } return expired; } protected void remove(DataSourceEntry entry) { cache.remove(entry.key); if (entry.prev != null) { entry.prev.next = entry.next; } if (entry.next != null) { entry.next.prev = entry.prev; } if (first == entry) { first = entry.next; } if (last == entry) { last = entry.prev; } } } @Autowired private DbcpDataSourceFactoryable dbcpDataSourceFactory; private PooledDataSourcesCache poolDataSources; private int poolTimeout; public JdbcDataSourceFactory() { poolDataSources = new PooledDataSourcesCache(); } public void setDbcpDataSourceFactory( DbcpDataSourceFactoryable dbcpDataSourceFactory) { this.dbcpDataSourceFactory = dbcpDataSourceFactory; } public DbcpDataSourceFactoryable getDbcpDataSourceFactory() { return dbcpDataSourceFactory; } @Override public EwcmsDataSourceServiceable createService(BaseDS alqcDataSource) { if (!(alqcDataSource instanceof JdbcDS)) { logger.error("无效的Jdbc数据库"); throw new BaseRuntimeException("无效的Jdbc数据库", new Object[]{alqcDataSource.getClass()}); } JdbcDS jdbcDataSet = (JdbcDS) alqcDataSource; DataSource dataSource = getPoolDataSource(jdbcDataSet.getDriver(), jdbcDataSet.getConnUrl(), jdbcDataSet.getUserName(), jdbcDataSet.getPassWord()); return new JdbcDataSourceService(dataSource); } protected DataSource getPoolDataSource(String driverClass, String url, String username, String password) { Object poolKey = createJdbcPoolKey(driverClass, url, username, password); DbcpDataSourceable dataSource; List<DbcpDataSourceable> expired = null; long now = System.currentTimeMillis(); synchronized (poolDataSources) { dataSource = poolDataSources.get(poolKey, now); if (dataSource == null) { logger.info("建立Pool数据连接 driver=\"" + driverClass + "\", url=\"" + url + "\", username=\"" + username + "\"."); dataSource = dbcpDataSourceFactory.createPooledDataSource(driverClass, url, username, password); poolDataSources.put(poolKey, dataSource, now); } if (getPoolTimeout() > 0) { expired = poolDataSources.removeExpired(now, getPoolTimeout()); } } if (expired != null && !expired.isEmpty()) { for (DbcpDataSourceable ds : expired) { try { ds.release(); } catch (Exception e) { logger.error("Error while releasing connection pool.", e); } } } return dataSource.getDataSource(); } protected Object createJdbcPoolKey(String driverClass, String url, String username, String password) { return new JdbcPoolKey(driverClass, url, username, password); } protected static class JdbcPoolKey { private final String driverClass; private final String url; private final String username; private final String password; private final int hash; public JdbcPoolKey(String driverClass, String url, String username, String password) { this.driverClass = driverClass; this.url = url; this.username = username; this.password = password; int hashCode = 559; if (driverClass != null) { hashCode += driverClass.hashCode(); } hashCode *= 43; if (url != null) { hashCode += url.hashCode(); } hashCode *= 43; if (username != null) { hashCode += username.hashCode(); } hashCode *= 43; if (password != null) { hashCode += password.hashCode(); } hash = hashCode; } @Override public boolean equals(Object obj) { if (!(obj instanceof JdbcPoolKey)) { return false; } if (this == obj) { return true; } JdbcPoolKey key = (JdbcPoolKey) obj; return (driverClass == null ? key.driverClass == null : (key.driverClass != null && driverClass.equals(key.driverClass))) && (url == null ? key.url == null : (key.url != null && url.equals(key.url))) && (username == null ? key.username == null : (key.username != null && username.equals(key.username))) && (password == null ? key.password == null : (key.password != null && password.equals(key.password))); } @Override public int hashCode() { return hash; } } public int getPoolTimeout() { return poolTimeout; } public void setPoolTimeout(int poolTimeout) { this.poolTimeout = poolTimeout; } }