package jef.database.datasource; import java.sql.Connection; import java.sql.SQLException; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.persistence.PersistenceException; import javax.sql.DataSource; import jef.common.Callback; import jef.common.CopyOnWriteMap; import jef.common.log.LogUtil; import jef.tools.Assert; /** * 支持路由的数据源,JEF要实现多数据源配置和XA,需要使用此类来作为初始化的datasource * * * * * @Date 2012-9-6 */ public class RoutingDataSource extends AbstractDataSource implements IRoutingDataSource{ //查找器1 protected DataSourceLookup dataSourceLookup; //查找器2:由于查找器返回的数据会经过解密处理,因此查找器找到的结果一定要缓存不能丢弃,否则下次再查找时会反复解密,引起出错 protected DataSourceInfoLookup dataSourceInfoLookup; //缓存已经查找到结果 protected Map<String, DataSource> resolvedDataSources=new CopyOnWriteMap<String, DataSource>(); //记录使用过的第一个数据源作为缺省的 protected Entry<String,DataSource> resolvedDefaultDataSource; //第一次成功的获取 protected Entry<String,DataSource> firstReturnDataSource; //记录当前datasource的状态,用于确定本次操作应该返回哪个数据源的连接 private final ThreadLocal<String> keys=new ThreadLocal<String>(); //初始化回调 protected Callback<String,SQLException> callback; /** * 空构造 */ public RoutingDataSource(){ } /** * 构造 * @param lookup */ public RoutingDataSource(DataSourceInfoLookup lookup){ this.dataSourceInfoLookup=lookup; } /** * 构造 * @param lookup */ public RoutingDataSource(DataSourceLookup lookup){ this.dataSourceLookup=lookup; } /** * {@inheritDoc} * * @throws IllegalStateException * if determineTargetDataSource() is null * @see #determineTargetDataSource() */ public Connection getConnection() throws SQLException { return determineTargetDataSource().getConnection(); } /** * {@inheritDoc} * * @throws IllegalStateException * if determineTargetDataSource() is null * @see #determineTargetDataSource() */ public Connection getConnection(String username, String password) throws SQLException { return determineTargetDataSource().getConnection(username, password); } /** * Retrieve the current target DataSource. Determines the * {@link #determineCurrentLookupKey() current lookup key}, performs a * lookup in the {@link #setTargetDataSources targetDataSources} map, falls * back to the specified {@link #setDefaultTargetDataSource default target * DataSource} if necessary. * * @see #determineCurrentLookupKey() * @throws IllegalStateException * If cannot determine target dataSource for the given lookupKey */ protected DataSource determineTargetDataSource() { //Assert.notNull(resolvedDataSources, "DataSource router not initialized"); String lookupKey = determineCurrentLookupKey(); if(lookupKey==null){ Entry<String,DataSource> result=getDefaultDatasource(); if(result==null) throw new IllegalArgumentException("Can not determine default datasource. avaliable datasoruces are:" + getDataSourceNames()); return result.getValue(); }else{ return getDataSource(lookupKey); } } /** * 返回DataSource * @param lookupKey * @return */ public DataSource getDataSource(String lookupKey){ DataSource dataSource = resolvedDataSources.get(lookupKey); if (dataSource == null && lookupKey == null) { throw new IllegalArgumentException("Can not lookup by empty Key");//不允许这样使用,这样做会造成上层无法得到default的key,从而会将null ,"" ,"DEFAULT"这种表示误认为是三个数据源,其实是同一个。 } if (dataSource == null) { dataSource=lookup(lookupKey); } if (dataSource == null) { throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]"); } //记录第一次成功获取的datasource,留作备用 if(firstReturnDataSource==null)firstReturnDataSource=new jef.common.Entry<String,DataSource>(lookupKey,dataSource); return dataSource; } public Entry<String,DataSource> getDefaultDatasource() { if(resolvedDefaultDataSource!=null){ //如果缺省数据源已经计算出来,那么直接返回。 return resolvedDefaultDataSource; } DataSource ds=null; if(dataSourceLookup!=null){ String defaultKey=dataSourceLookup.getDefaultKey(); //计算缺省数据源 if(defaultKey!=null){ LogUtil.info("Lookup key is null, using the default datasource:"+defaultKey); ds=resolvedDataSources.get(defaultKey); if(ds==null){ ds=lookup(defaultKey); } if(ds==null){ throw new NullPointerException("The default datasource '"+defaultKey+"' is not exist, please check you configuration!"); } resolvedDefaultDataSource =new jef.common.Entry<String,DataSource>(defaultKey,ds); //记录缺省数据源 return resolvedDefaultDataSource; } } if(dataSourceInfoLookup!=null && dataSourceInfoLookup!=dataSourceLookup){ String defaultKey=dataSourceInfoLookup.getDefaultKey(); //计算缺省数据源 if(defaultKey!=null){ LogUtil.info("Lookup key is null, using the default datasource:"+defaultKey); ds=resolvedDataSources.get(defaultKey); if(ds==null){ ds=lookup(defaultKey); } resolvedDefaultDataSource = new jef.common.Entry<String,DataSource>(defaultKey,ds);//记录缺省数据源 return resolvedDefaultDataSource; } } //无法计算和找到缺省数据源,将之前首次使用的数据源当作缺省数据源返回(可能为null) return firstReturnDataSource; } //去找寻数据源配置 private synchronized DataSource lookup(String lookupKey) { Assert.notNull(lookupKey);//不允许ket为空的查找 DataSource ds=null; if(dataSourceLookup!=null){ ds=dataSourceLookup.getDataSource(lookupKey); if(ds!=null)ds=checkDatasource(ds); } if(dataSourceInfoLookup!=null && dataSourceInfoLookup!=dataSourceLookup){ //因为有些类同时实现了两个接口,因此如果是同一对象就不要反复查找了 DataSourceInfo dsi=dataSourceInfoLookup.getDataSourceInfo(lookupKey); if(dsi!=null){ ds=createDataSource(dsi); } } if(ds!=null){ resolvedDataSources.put(lookupKey, ds); invokeCallback(lookupKey,ds); } return ds; } private void invokeCallback(String lookupKey, DataSource ds) { if(callback!=null){ try{ callback.call(lookupKey); }catch(SQLException e){ throw new PersistenceException(e); } } } /** * 供子类覆盖,用于挑选lookup返回的datasource是否合理。(检查实例,池,XA等特性) * * 有三种行为可供使用 * 1、返回再行包装后的Datasource * 2、返回Null,本次lookup作废,会尝试去用DataSourceInfo查找。 * 3、直接抛出异常,提示用户配置错误等. * @param ds * @return */ protected DataSource checkDatasource(DataSource ds) { return ds; } /** * 供子类覆盖用,用于将返回的数据库配置信息包装为DataSource * @param dsi * @return */ protected DataSource createDataSource(DataSourceInfo dsi) { return DataSources.getAsDataSource(dsi); } /** * 设置查找的数据源键 * @param key */ protected void setLookupKey(String key) { keys.set(key); } /** * 获取数据源的键 * @return */ protected String determineCurrentLookupKey() { return keys.get(); } public boolean isSingleDatasource() { Set<String> s=new HashSet<String>(); if(dataSourceLookup!=null){ s.addAll(dataSourceLookup.getAvailableKeys()); } if(dataSourceInfoLookup!=null){ s.addAll(dataSourceInfoLookup.getAvailableKeys()); } return s.size()<2; } /** * 获得所有已解析的DataSource名称 * @return */ public Set<String> getDataSourceNames() { Set<String> set=new HashSet<String>(resolvedDataSources.keySet()); if(dataSourceLookup!=null) set.addAll(dataSourceLookup.getAvailableKeys()); if(dataSourceInfoLookup!=null && dataSourceInfoLookup!=dataSourceLookup){ set.addAll(dataSourceInfoLookup.getAvailableKeys()); } return set; } @Override protected Class<? extends DataSource> getWrappedClass() { return null; } public void setDataSourceLookup(DataSourceLookup dataSourceLookup) { this.dataSourceLookup = dataSourceLookup; } public void setDataSourceInfoLookup(DataSourceInfoLookup dataSourceInfoLookup) { this.dataSourceInfoLookup = dataSourceInfoLookup; } public void setCallback(Callback<String, SQLException> callback) { this.callback = callback; } }