001/**
002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com).
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * http://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.jboot.db.driver;
017
018import com.google.common.collect.MapMaker;
019import org.slf4j.Logger;
020import org.slf4j.LoggerFactory;
021import ru.yandex.clickhouse.ClickHouseConnection;
022import ru.yandex.clickhouse.ClickHouseDriver;
023import ru.yandex.clickhouse.settings.ClickHouseProperties;
024import ru.yandex.clickhouse.util.LogProxy;
025
026import java.sql.DriverManager;
027import java.sql.SQLException;
028import java.util.concurrent.*;
029
030public class OfficialClickHouseDriver extends ClickHouseDriver {
031
032    private static final Logger logger = LoggerFactory.getLogger(ClickHouseDriver.class);
033    private static final ConcurrentMap<OfficialClickHouseConnection, Boolean> connections = new MapMaker().weakKeys().makeMap();
034
035    static {
036        try {
037            DriverManager.registerDriver(new OfficialClickHouseDriver());
038        } catch (SQLException e) {
039            throw new RuntimeException(e);
040        }
041    }
042
043    public OfficialClickHouseDriver() {
044        // 10 seconds
045        scheduleConnectionsCleaning(10, TimeUnit.SECONDS);
046    }
047
048
049    @Override
050    public ClickHouseConnection connect(String url, ClickHouseProperties properties) throws SQLException {
051        if (!acceptsURL(url)) {
052            return null;
053        }
054        OfficialClickHouseConnection connection = new OfficialClickHouseConnection(url, properties);
055        registerConnection(connection);
056        return LogProxy.wrap(ClickHouseConnection.class, connection);
057    }
058
059    private void registerConnection(OfficialClickHouseConnection connection) {
060        connections.put(connection, Boolean.TRUE);
061    }
062
063
064    /**
065     * Schedules connections cleaning at a rate. Turned off by default.
066     * See https://hc.apache.org/httpcomponents-client-4.5.x/tutorial/html/connmgmt.html#d5e418
067     *
068     * @param rate     period when checking would be performed
069     * @param timeUnit time unit of rate
070     */
071    @Override
072    public void scheduleConnectionsCleaning(int rate, TimeUnit timeUnit) {
073        ScheduledConnectionCleaner.INSTANCE.scheduleAtFixedRate(() -> {
074            try {
075                for (OfficialClickHouseConnection connection : connections.keySet()) {
076                    connection.cleanConnections();
077                }
078            } catch (Exception e) {
079                logger.error("error evicting connections: " + e);
080            }
081        }, 0, rate, timeUnit);
082    }
083
084
085    static class ScheduledConnectionCleaner {
086        static final ScheduledExecutorService INSTANCE = Executors.newSingleThreadScheduledExecutor(new DaemonThreadFactory());
087
088        static class DaemonThreadFactory implements ThreadFactory {
089            @Override
090            public Thread newThread(Runnable r) {
091                Thread thread = Executors.defaultThreadFactory().newThread(r);
092                thread.setDaemon(true);
093                return thread;
094            }
095        }
096    }
097
098
099}