com.alibaba.otter.node.etl.common.datasource.impl.DBDataSourceService.java Source code

Java tutorial

Introduction

Here is the source code for com.alibaba.otter.node.etl.common.datasource.impl.DBDataSourceService.java

Source

/*
 * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
 *
 * 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.otter.node.etl.common.datasource.impl;

import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import com.alibaba.otter.common.push.datasource.DataSourceHanlder;
import com.alibaba.otter.node.etl.common.datasource.DataSourceService;
import com.alibaba.otter.shared.common.model.config.data.DataMediaSource;
import com.alibaba.otter.shared.common.model.config.data.DataMediaType;
import com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;
import com.google.common.base.Function;
import com.google.common.collect.GenericMapMaker;
import com.google.common.collect.MapEvictionListener;
import com.google.common.collect.MapMaker;

/**
 * Comment of DataSourceServiceImpl
 * 
 * @author xiaoqing.zhouxq
 * @author zebinxu, add {@link DataSourceHanlder}
 */
public class DBDataSourceService implements DataSourceService, DisposableBean {

    private static final Logger logger = LoggerFactory.getLogger(DBDataSourceService.class);

    private List<DataSourceHanlder> dataSourceHandlers;

    private int maxWait = 60 * 1000;

    private int minIdle = 0;

    private int initialSize = 0;

    private int maxActive = 32;

    private int maxIdle = 32;

    private int numTestsPerEvictionRun = -1;

    private int timeBetweenEvictionRunsMillis = 60 * 1000;

    private int removeAbandonedTimeout = 5 * 60;

    private int minEvictableIdleTimeMillis = 5 * 60 * 1000;

    /**
     * pipeline?DataSource.<br>
     * key = pipelineId<br>
     * value = key(dataMediaSourceId)-value(DataSource)<br>
     */
    private Map<Long, Map<DbMediaSource, DataSource>> dataSources;

    public DBDataSourceService() {
        // soft
        GenericMapMaker mapMaker = new MapMaker().softValues();
        mapMaker = ((MapMaker) mapMaker)
                .evictionListener(new MapEvictionListener<Long, Map<DbMediaSource, DataSource>>() {

                    public void onEviction(Long pipelineId, Map<DbMediaSource, DataSource> dataSources) {
                        if (dataSources == null) {
                            return;
                        }

                        for (DataSource dataSource : dataSources.values()) {
                            // for filter to destroy custom datasource
                            if (letHandlerDestroyIfSupport(pipelineId, dataSource)) {
                                continue;
                            }
                            BasicDataSource basicDataSource = (BasicDataSource) dataSource;
                            try {
                                basicDataSource.close();
                            } catch (SQLException e) {
                                logger.error("ERROR ## close the datasource has an error", e);
                            }
                        }
                    }
                });

        // map
        dataSources = new MapMaker().makeComputingMap(new Function<Long, Map<DbMediaSource, DataSource>>() {

            public Map<DbMediaSource, DataSource> apply(final Long pipelineId) {
                // map
                return new MapMaker().makeComputingMap(new Function<DbMediaSource, DataSource>() {

                    public DataSource apply(DbMediaSource dbMediaSource) {

                        // ,? dataSource
                        DataSource customDataSource = preCreate(pipelineId, dbMediaSource);
                        if (customDataSource != null) {
                            return customDataSource;
                        }

                        return createDataSource(dbMediaSource.getUrl(), dbMediaSource.getUsername(),
                                dbMediaSource.getPassword(), dbMediaSource.getDriver(), dbMediaSource.getType(),
                                dbMediaSource.getEncode());
                    }

                });
            }
        });

    }

    public DataSource getDataSource(long pipelineId, DataMediaSource dataMediaSource) {
        Assert.notNull(dataMediaSource);
        return dataSources.get(pipelineId).get(dataMediaSource);
    }

    public void destroy(Long pipelineId) {
        Map<DbMediaSource, DataSource> sources = dataSources.remove(pipelineId);
        if (sources != null) {
            for (DataSource source : sources.values()) {
                try {
                    // for filter to destroy custom datasource
                    if (letHandlerDestroyIfSupport(pipelineId, source)) {
                        continue;
                    }

                    // fallback for regular destroy
                    // TODO need to integrate to handler
                    BasicDataSource basicDataSource = (BasicDataSource) source;
                    basicDataSource.close();
                } catch (SQLException e) {
                    logger.error("ERROR ## close the datasource has an error", e);
                }
            }

            sources.clear();
        }
    }

    private boolean letHandlerDestroyIfSupport(Long pipelineId, DataSource dataSource) {
        boolean destroied = false;

        if (CollectionUtils.isEmpty(this.dataSourceHandlers)) {
            return destroied;
        }
        for (DataSourceHanlder handler : this.dataSourceHandlers) {
            if (handler.support(dataSource)) {
                handler.destory(pipelineId);
                destroied = true;
                return destroied;
            }
        }
        return destroied;

    }

    public void destroy() throws Exception {
        for (Long pipelineId : dataSources.keySet()) {
            destroy(pipelineId);
        }
    }

    private DataSource createDataSource(String url, String userName, String password, String driverClassName,
            DataMediaType dataMediaType, String encoding) {
        BasicDataSource dbcpDs = new BasicDataSource();

        dbcpDs.setInitialSize(initialSize);// ?
        dbcpDs.setMaxActive(maxActive);// ?????
        dbcpDs.setMaxIdle(maxIdle);// ??
        dbcpDs.setMinIdle(minIdle);// ?0?
        dbcpDs.setMaxWait(maxWait);// ??-1?
        dbcpDs.setRemoveAbandoned(true);// ??removeAbandonedTimeout
        dbcpDs.setLogAbandoned(true);// ??
        dbcpDs.setRemoveAbandonedTimeout(removeAbandonedTimeout); // ?
        dbcpDs.setNumTestsPerEvictionRun(numTestsPerEvictionRun);// ??
        dbcpDs.setTestOnBorrow(false);// ??
        dbcpDs.setTestOnReturn(false);// ??
        dbcpDs.setTestWhileIdle(true);// ????
        dbcpDs.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); // ????????
        dbcpDs.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); // ???????

        // ??
        dbcpDs.setDriverClassName(driverClassName);
        dbcpDs.setUrl(url);
        dbcpDs.setUsername(userName);
        dbcpDs.setPassword(password);

        if (dataMediaType.isOracle()) {
            dbcpDs.addConnectionProperty("restrictGetTables", "true");
            dbcpDs.setValidationQuery("select 1 from dual");
        } else if (dataMediaType.isMysql()) {
            // open the batch mode for mysql since 5.1.8
            dbcpDs.addConnectionProperty("useServerPrepStmts", "false");
            dbcpDs.addConnectionProperty("rewriteBatchedStatements", "true");
            dbcpDs.addConnectionProperty("zeroDateTimeBehavior", "convertToNull");// 0000-00-00null
            dbcpDs.addConnectionProperty("yearIsDateType", "false");// ??year?date?
            dbcpDs.addConnectionProperty("noDatetimeStringSync", "true");// ,???
            if (StringUtils.isNotEmpty(encoding)) {
                if (StringUtils.equalsIgnoreCase(encoding, "utf8mb4")) {
                    dbcpDs.addConnectionProperty("characterEncoding", "utf8");
                    dbcpDs.setConnectionInitSqls(Arrays.asList("set names utf8mb4"));
                } else {
                    dbcpDs.addConnectionProperty("characterEncoding", encoding);
                }
            }
            dbcpDs.setValidationQuery("select 1");
        } else {
            logger.error("ERROR ## Unknow database type");
        }

        return dbcpDs;
    }

    /**
     * ,? dataSource
     */
    private DataSource preCreate(Long pipelineId, DbMediaSource dbMediaSource) {

        if (CollectionUtils.isEmpty(dataSourceHandlers)) {
            return null;
        }

        DataSource dataSource = null;
        for (DataSourceHanlder handler : dataSourceHandlers) {
            if (handler.support(dbMediaSource)) {
                dataSource = handler.create(pipelineId, dbMediaSource);
                if (dataSource != null) {
                    return dataSource;
                }
            }
        }
        return null;
    }

    public void setMaxWait(int maxWait) {
        this.maxWait = maxWait;
    }

    public void setMinIdle(int minIdle) {
        this.minIdle = minIdle;
    }

    public void setInitialSize(int initialSize) {
        this.initialSize = initialSize;
    }

    public void setMaxActive(int maxActive) {
        this.maxActive = maxActive;
    }

    public void setMaxIdle(int maxIdle) {
        this.maxIdle = maxIdle;
    }

    public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
        this.numTestsPerEvictionRun = numTestsPerEvictionRun;
    }

    public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {
        this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
    }

    public void setRemoveAbandonedTimeout(int removeAbandonedTimeout) {
        this.removeAbandonedTimeout = removeAbandonedTimeout;
    }

    public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {
        this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
    }

    public void setDataSourceHandlers(List<DataSourceHanlder> dataSourceHandlers) {
        this.dataSourceHandlers = dataSourceHandlers;
    }
}