001/*
002MIT License
003
004Copyright (c) 2020 FBSQL Team
005
006Permission is hereby granted, free of charge, to any person obtaining a copy
007of this software and associated documentation files (the "Software"), to deal
008in the Software without restriction, including without limitation the rights
009to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010copies of the Software, and to permit persons to whom the Software is
011furnished to do so, subject to the following conditions:
012
013The above copyright notice and this permission notice shall be included in all
014copies or substantial portions of the Software.
015
016THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
022SOFTWARE.
023
024Home:   https://fbsql.github.io
025E-Mail: fbsql.team@gmail.com
026*/
027
028package org.fbsql.connection_pool;
029
030import java.io.FileNotFoundException;
031import java.io.IOException;
032import java.security.NoSuchAlgorithmException;
033import java.security.NoSuchProviderException;
034import java.sql.Connection;
035import java.sql.DriverManager;
036import java.sql.SQLException;
037import java.util.ArrayList;
038import java.util.Collection;
039import java.util.Collections;
040import java.util.Iterator;
041import java.util.List;
042import java.util.Properties;
043
044/**
045 * Connection pool used to connect to database using a pre-created set of reusable connections.
046 * When a new connection is required, an arbitrary available connection is retrieved from the pool.
047 * When connection closed, it is returned to connection pool for future use.
048 */
049public class ConnectionPoolManager implements AutoCloseable {
050        private int initialPoolSize;
051        private int maxPoolSize;
052        //
053        private String     jdbcUrl;
054        private String     jdbcUser;
055        private String     jdbcPassword;
056        private Properties jdbcProperties;
057        //
058        private List<DbConnection>       availableList;
059        private Collection<DbConnection> inUseList;
060
061        /**
062         * Constructs ConnectionPoolManager
063         * 
064         * @param jdbcUrl
065         * @param jdbcUser
066         * @param jdbcPassword
067         * @param initialPoolSize
068         * @param maxPoolSize
069         */
070        public ConnectionPoolManager(String jdbcUrl, String jdbcUser, String jdbcPassword, Properties jdbcProperties, int initialPoolSize, int maxPoolSize) {
071                this.jdbcUrl         = jdbcUrl;
072                this.jdbcUser        = jdbcUser;
073                this.jdbcPassword    = jdbcPassword;
074                this.jdbcProperties  = jdbcProperties;
075                this.initialPoolSize = initialPoolSize;
076                this.maxPoolSize     = maxPoolSize;
077        }
078
079        /**
080         * Initialize ConnectionPoolManager
081         * 
082         * @throws NoSuchAlgorithmException
083         * @throws NoSuchProviderException
084         * @throws SQLException
085         * @throws FileNotFoundException
086         * @throws IOException
087         */
088        public void init() throws NoSuchAlgorithmException, NoSuchProviderException, SQLException, FileNotFoundException, IOException {
089                inUseList     = Collections.synchronizedList(new ArrayList<>(initialPoolSize));
090                availableList = Collections.synchronizedList(new ArrayList<>(initialPoolSize));
091                for (int i = 0; i < initialPoolSize; i++)
092                        addNewConnection();
093        }
094
095        /**
096         * Close ConnectionPoolManager
097         */
098        @Override
099        public void close() throws SQLException {
100                for (Iterator<DbConnection> iterator = inUseList.iterator(); iterator.hasNext();) {
101                        DbConnection dbConnection = iterator.next();
102                        dbConnection.getConnection().commit();
103                        availableList.add(dbConnection);
104                        iterator.remove();
105                }
106                for (Iterator<DbConnection> iterator = availableList.iterator(); iterator.hasNext();) {
107                        iterator.next().getConnection().close();
108                        iterator.remove();
109                }
110        }
111
112        /**
113         * Get available connection from pool
114         * 
115         * @return
116         * @throws SQLException
117         */
118        public synchronized DbConnection getConnection() throws SQLException {
119                if (availableList.isEmpty())
120                        if (inUseList.size() < maxPoolSize)
121                                addNewConnection();
122                        else // no available connections
123                                throw new SQLException("Connection pool size exceeds maximum value");
124                DbConnection dbConnection = availableList.remove(availableList.size() - 1);
125                inUseList.add(dbConnection);
126                return dbConnection;
127        }
128
129        /**
130         * Release connection
131         * 
132         * @param dbConnection
133         * @return true if connection successfully released
134         * @throws SQLException
135         */
136        public boolean releaseConnection(DbConnection dbConnection) throws SQLException {
137                dbConnection.getConnection().commit();
138                availableList.add(dbConnection);
139                return inUseList.remove(dbConnection);
140        }
141
142        /**
143         * Add new connection to pool
144         * 
145         * @throws SQLException
146         */
147        private void addNewConnection() throws SQLException {
148                Connection connection;
149                if (jdbcProperties != null && !jdbcProperties.isEmpty())
150                        connection = DriverManager.getConnection(jdbcUrl, jdbcProperties);
151                else if (jdbcUser != null)
152                        connection = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);
153                else
154                        connection = DriverManager.getConnection(jdbcUrl);
155
156                connection.setAutoCommit(false);
157
158                DbConnection dbConnection = new DbConnection(connection);
159                availableList.add(dbConnection);
160        }
161
162}
163
164/*
165Please contact FBSQL Team by E-Mail fbsql.team@gmail.com
166or visit https://fbsql.github.io if you need additional
167information or have any questions.
168*/
169
170/* EOF */