org.hsweb.web.datasource.dynamic.DynamicDataSourceServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.hsweb.web.datasource.dynamic.DynamicDataSourceServiceImpl.java

Source

/*
 * Copyright 2015-2016 http://hsweb.me
 *
 * 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 org.hsweb.web.datasource.dynamic;

import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.atomikos.jdbc.AtomikosSQLException;
import org.hsweb.concurrent.lock.LockFactory;
import org.hsweb.web.bean.po.datasource.DataSource;
import org.hsweb.web.core.datasource.DynamicDataSource;
import org.hsweb.web.core.exception.NotFoundException;
import org.hsweb.web.service.datasource.DataSourceService;
import org.hsweb.web.service.datasource.DynamicDataSourceService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.io.Closeable;
import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.ReadWriteLock;

@Service("dynamicDataSourceService")
public class DynamicDataSourceServiceImpl implements DynamicDataSourceService {

    private Logger logger = LoggerFactory.getLogger(this.getClass());
    @Resource
    private DataSourceService dataSourceService;

    @Autowired(required = false)
    protected DynamicDataSource dynamicDataSource;

    @Autowired
    private LockFactory lockFactory;

    private ConcurrentMap<String, CacheInfo> cache = new ConcurrentHashMap<>();

    @Override
    public javax.sql.DataSource getDataSource(String id) {
        return getCache(id).getDataSource();
    }

    @Override
    @PreDestroy
    public void destroyAll() throws Exception {
        cache.values().stream().map(CacheInfo::getDataSource).forEach(this::closeDataSource);
        cache.clear();
    }

    protected void closeDataSource(javax.sql.CommonDataSource ds) {
        if (ds instanceof AtomikosDataSourceBean) {
            closeDataSource(((AtomikosDataSourceBean) ds).getXaDataSource());
            ((AtomikosDataSourceBean) ds).close();
        } else if (ds instanceof Closeable) {
            try {
                ((Closeable) ds).close();
            } catch (IOException e) {
                logger.error("close datasource error", e);
            }
        }
    }

    protected CacheInfo getCache(String id) {
        DynamicDataSource.useDefault();
        try {
            DataSource old = dataSourceService.selectByPk(id);
            if (old == null || old.getEnabled() != 1)
                throw new NotFoundException("????");

            //?
            ReadWriteLock readWriteLock = lockFactory.createReadWriteLock("dynamic.ds." + id);

            readWriteLock.readLock().tryLock();
            CacheInfo cacheInfo = null;
            try {
                cacheInfo = cache.get(id);
                // ,hash
                if (cacheInfo != null && cacheInfo.getHash() == old.getHash())
                    return cacheInfo;
            } finally {
                try {
                    readWriteLock.readLock().unlock();
                } catch (Exception e) {
                }
            }
            readWriteLock.writeLock().tryLock();
            try {
                if (cacheInfo != null) {
                    closeDataSource(cacheInfo.getDataSource());
                }
                //datasource
                javax.sql.DataSource dataSource = createDataSource(old);
                cacheInfo = new CacheInfo(old.getHash(), dataSource);
                cache.put(id, cacheInfo);
            } finally {
                try {
                    readWriteLock.writeLock().unlock();
                } catch (Exception e) {
                }
            }
            return cacheInfo;
        } finally {
            DynamicDataSource.useLast();
        }
    }

    @Autowired
    private DataSourceProperties properties;

    protected javax.sql.DataSource createDataSource(DataSource dataSource) {
        AtomikosDataSourceBean dataSourceBean = new AtomikosDataSourceBean();
        Properties xaProperties = new Properties();
        if (dataSource.getProperties() != null)
            xaProperties.putAll(dataSource.getProperties());
        if (dataSource.getDriver().contains("mysql")) {
            dataSourceBean.setXaDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlXADataSource");
            xaProperties.put("pinGlobalTxToPhysicalConnection", true);
            xaProperties.put("user", dataSource.getUsername());
            xaProperties.put("password", dataSource.getPassword());
            xaProperties.put("url", dataSource.getUrl());
        } else {
            dataSourceBean.setXaDataSourceClassName(properties.getXa().getDataSourceClassName());
            xaProperties.put("username", dataSource.getUsername());
            xaProperties.put("password", dataSource.getPassword());
            xaProperties.put("url", dataSource.getUrl());
            xaProperties.put("driverClassName", dataSource.getDriver());
        }
        dataSourceBean.setXaProperties(xaProperties);
        dataSourceBean.setUniqueResourceName("ds_" + dataSource.getId());
        dataSourceBean.setMaxPoolSize(200);
        dataSourceBean.setMinPoolSize(5);
        dataSourceBean.setBorrowConnectionTimeout(60);
        boolean[] success = new boolean[1];
        //?
        new Thread(() -> {
            try {
                dataSourceBean.init();
                success[0] = true;
            } catch (AtomikosSQLException e) {
                closeDataSource(dataSourceBean);
                cache.remove(dataSource.getId());
            }
        }).start();
        //?
        new Thread(() -> {
            try {
                Thread.sleep(10000);
                if (!success[0]) {
                    logger.error("?jdbc:{}", dataSourceBean);
                    closeDataSource(dataSourceBean);
                    cache.remove(dataSource.getId());
                }
            } catch (Exception e) {

            }
        }).start();
        return dataSourceBean;
    }

    @PostConstruct
    public void init() {
        if (null != dynamicDataSource && dynamicDataSource instanceof DynamicXaDataSourceImpl)
            ((DynamicXaDataSourceImpl) dynamicDataSource).setDynamicDataSourceService(this);
    }

    class CacheInfo {
        int hash;

        javax.sql.DataSource dataSource;

        public CacheInfo(int hash, javax.sql.DataSource dataSource) {
            this.hash = hash;
            this.dataSource = dataSource;
        }

        public int getHash() {
            return hash;
        }

        public javax.sql.DataSource getDataSource() {
            return dataSource;
        }
    }

}