/* * Copyright 1999-2012 Alibaba Group. * * Licensed 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 com.alibaba.cobar.manager.dao.delegate; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.sql.DataSource; import org.apache.log4j.Logger; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import com.alibaba.cobar.manager.dao.CobarAdapterDAO; /** * (created at 2010-7-26) * * @author <a href="mailto:shuo.qius@alibaba-inc.com">QIU Shuo</a> * @author haiqing.zhuhq 2011-7-21 */ public class AdapterDelegate implements InitializingBean, DisposableBean { private Logger logger = Logger.getLogger(getClass()); private DataSourceFactory dsFactory; private EvictThread evictThread; private long cobarNodeEvictThreadSweepInverval = 10 * 60 * 1000; //10min private long cobarNodeAdapterIdleTime = 40 * 60 * 1000; //40min public void setCobarNodeEvictThreadSweepInverval(long cobarNodeEvictThreadSweepInverval) { this.cobarNodeEvictThreadSweepInverval = cobarNodeEvictThreadSweepInverval; } public void setCobarNodeAdapterIdleTime(long cobarNodeAdapterIdleTime) { this.cobarNodeAdapterIdleTime = cobarNodeAdapterIdleTime; } public void setDsFactory(DataSourceFactory dsFactory) { this.dsFactory = dsFactory; } @Override public void afterPropertiesSet() throws Exception { evictThread = new EvictThread(); evictThread.setDaemon(true); evictThread.start(); } @Override public void destroy() throws Exception { evictThread.shutdown(); } public CobarAdapterDAO getCobarNodeAccesser(String ip, int port, String user, String password) { return getCobarNodeAdapterInternal(ip, port, user, password); } private ReadWriteLock adapterMapLock = new ReentrantReadWriteLock(true); private Map<CobarAdapterKey, TimestampPair> adapterMap = new HashMap<CobarAdapterKey, TimestampPair>(); protected CobarAdapter getCobarNodeAdapterInternal(String ip, int port, String user, String password) { final CobarAdapterKey key = new CobarAdapterKey(ip, port, user, password); TimestampPair rst = null; try { adapterMapLock.readLock().lock(); rst = adapterMap.get(key); if (rst != null) { return rst.refreshTime().getValue(); } } finally { adapterMapLock.readLock().unlock(); } try { adapterMapLock.writeLock().lock(); rst = adapterMap.get(key); if (rst != null) { return rst.refreshTime().getValue(); } rst = createAdapter(ip, port, user, password); adapterMap.put(key, rst); return rst.refreshTime().getValue(); } finally { adapterMapLock.writeLock().unlock(); } } private TimestampPair createAdapter(String ip, int port, String user, String password) { CobarAdapter adapter = null; try { DataSource ds = dsFactory.createDataSource(ip, port, user, password); adapter = new CobarAdapter(); adapter.setDataSource(ds); ((InitializingBean) adapter).afterPropertiesSet(); return new TimestampPair(adapter); } catch (Exception exception) { logger.error("ip=" + ip + ", port=" + port, exception); try { adapter.destroy(); } catch (Exception e) { } throw new RuntimeException(exception); } } private void sweepCobarNodeAdapter() { Map<CobarAdapterKey, TimestampPair> toClose = new HashMap<CobarAdapterKey, TimestampPair>(); try { if (logger.isDebugEnabled()) { logger.debug("evictThread sweep adapter begin, wating for write lock."); } adapterMapLock.writeLock().lock(); if (logger.isDebugEnabled()) { logger.debug("evictThread sweep adapter begin, acquired write lock."); } Iterator<Entry<CobarAdapterKey, TimestampPair>> iter = adapterMap.entrySet().iterator(); while (iter.hasNext()) { Entry<CobarAdapterKey, TimestampPair> entry = iter.next(); CobarAdapterKey key = entry.getKey(); TimestampPair pair = entry.getValue(); long time = pair.getCreatedTime(); if (System.currentTimeMillis() - time >= cobarNodeAdapterIdleTime) { toClose.put(key, pair); iter.remove(); } } } finally { adapterMapLock.writeLock().unlock(); } if (logger.isDebugEnabled()) { logger.debug("evictThread sweep adapter finished, toClose.size()=" + toClose.size() + ", toCloseSet=" + toClose.keySet()); } for (CobarAdapterKey key : toClose.keySet()) { try { toClose.get(key).getValue().destroy(); } catch (Exception e) { logger.warn(e); } } } private class EvictThread extends Thread { public volatile boolean keepRunning = true; public void shutdown() { keepRunning = false; interrupt(); } @Override public void run() { while (keepRunning) { try { sleep(cobarNodeEvictThreadSweepInverval); sweepCobarNodeAdapter(); } catch (InterruptedException e) { } } } } private class TimestampPair { private volatile long time; private final CobarAdapter value; public TimestampPair(CobarAdapter v) { time = System.currentTimeMillis(); value = v; } public TimestampPair refreshTime() { time = System.currentTimeMillis(); return this; } public long getCreatedTime() { return time; } public CobarAdapter getValue() { return value; } } }