close()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 4
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 1
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
1
/*
2
 * This file is part of Araknemu.
3
 *
4
 * Araknemu is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU Lesser General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * Araknemu is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with Araknemu.  If not, see <https://www.gnu.org/licenses/>.
16
 *
17
 * Copyright (c) 2017-2020 Vincent Quatrevieux
18
 */
19
20
package fr.quatrevieux.araknemu.core.dbal;
21
22
import fr.quatrevieux.araknemu.util.ExecutorFactory;
23
import org.apache.logging.log4j.Logger;
24
25
import java.sql.Connection;
26
import java.sql.SQLException;
27
import java.sql.Statement;
28
import java.util.ArrayList;
29
import java.util.List;
30
import java.util.concurrent.ScheduledExecutorService;
31
import java.util.concurrent.TimeUnit;
32
33
/**
34
 * Perform regularly ping on connections for keeping alive, or refresh if connection is lost
35
 */
36
public final class RefreshConnectionPool implements ConnectionPool {
37
    private final ConnectionPool pool;
38
    private final long interval;
39
    private final Logger logger;
40
41 1
    private final ScheduledExecutorService executorService = ExecutorFactory.createSingleThread();
42
43 1
    public RefreshConnectionPool(ConnectionPool pool, long interval, Logger logger) {
44 1
        this.pool = pool;
45 1
        this.interval = interval;
46 1
        this.logger = logger;
47 1
    }
48
49
    @Override
50
    public void initialize() throws SQLException {
51 1
        pool.initialize();
52 1
        executorService.schedule(this::refreshPool, interval, TimeUnit.SECONDS);
53 1
    }
54
55
    @Override
56
    public Connection acquire() throws SQLException {
57 1
        return pool.acquire();
58
    }
59
60
    @Override
61
    public void release(Connection connection) {
62 1
        pool.release(connection);
63 1
    }
64
65
    @Override
66
    public int size() {
67 1
        return pool.size();
68
    }
69
70
    @Override
71
    public <T> T execute(Task<T> task) throws SQLException {
72 1
        return pool.execute(task);
73
    }
74
75
    private void refreshPool() {
76 1
        logger.info("Refreshing pool");
77
78 1
        final int lastSize = pool.size();
79
80 1
        for (int i = 0; i < lastSize; ++i) {
81
            try {
82 1
                final Connection connection = acquire();
83
84 1
                if (connection.isClosed()) {
85 1
                    logger.warn("Closed connection detected");
86 1
                    continue;
87
                }
88
89 1
                try (Statement statement = connection.createStatement()) {
90 1
                    statement.execute("SELECT 1");
91 1
                    release(connection);
92
                }
93
            } catch (SQLException e) {
94
                logger.warn("Closed connection detected");
95 1
            }
96
        }
97
98 1
        if (pool.size() == 0) {
99 1
            final int newSize = Math.max(lastSize, 1);
100
101 1
            logger.info("Pool is empty : try to recreate {} connections", newSize);
102
103
            try {
104 1
                final List<Connection> newConnections = new ArrayList<>();
105
106 1
                for (int i = 0; i < newSize; ++i) {
107 1
                    newConnections.add(acquire());
108
                }
109
110 1
                for (Connection connection : newConnections) {
111 1
                    release(connection);
112 1
                }
113
            } catch (SQLException e) {
114
                logger.warn("Cannot recreate the pool", e);
115 1
            }
116
        }
117
118 1
        executorService.schedule(this::refreshPool, interval, TimeUnit.SECONDS);
119 1
    }
120
121
    @Override
122
    public void close() throws Exception {
123 1
        executorService.shutdownNow();
124 1
        pool.close();
125 1
    }
126
}
127