All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bridje.jdbc.impl.DataSourceImpl Maven / Gradle / Ivy

/*
 * Copyright 2016 Bridje Framework.
 *
 * 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.bridje.jdbc.impl;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sql.DataSource;
import org.bridje.jdbc.config.DataSourceConfig;

class DataSourceImpl implements DataSource
{
    private static final Logger LOG = Logger.getLogger(DataSourceImpl.class.getName());

    private final Deque freeConnections = new ConcurrentLinkedDeque<>();

    private final Deque usedConnections = new ConcurrentLinkedDeque<>();

    private DataSourceConfig config;
    
    private PrintWriter logWriter;
    
    private int loginTimeout;
    
    private boolean closed;
    
    private long lastCheck;
    
    public DataSourceImpl(DataSourceConfig config)
    {
        this.config = config;
        this.lastCheck = System.currentTimeMillis();
    }
    
    @Override
    public Connection getConnection() throws SQLException
    {
        if(closed)
        {
            throw new SQLException("The DataSource is close.");
        }
        Connection result = getFreeConnection();
        if(result != null)
        {
            checkIdleConnections();
            return result;
        }
        result = getNewConnection();
        if(result != null)
        {
            return result;
        }
        return waitFreeConnection();
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException
    {
        if(closed)
        {
            throw new SQLException("The DataSource is close.");
        }
        return createNewConnection(username, password);
    }

    private synchronized Connection getFreeConnection() throws SQLException
    {
        if(!freeConnections.isEmpty())
        {
            ConnectionImpl nextConnection = freeConnections.poll();
            if(needToReconnect(nextConnection))
            {
                if(!nextConnection.isValid(10))
                {
                    nextConnection.realClose();
                    nextConnection = createNewConnection();
                }
            }
            nextConnection.open();
            usedConnections.add(nextConnection);
            LOG.log(Level.FINE, String.format("Current free connections in %s: %s", config.getName(), freeConnections.size()) );
            return nextConnection;
        }
        return null;
    }

    private synchronized Connection getNewConnection() throws SQLException
    {
        if(usedConnections.size() < config.getMaxConnections())
        {
            ConnectionImpl newConnection = createNewConnection();
            newConnection.open();
            usedConnections.add(newConnection);
            LOG.log(Level.FINE, String.format("Current used connections in %s: %s, of %s", config.getName(), usedConnections.size(), config.getMaxConnections()));
            return newConnection;
        }
        return null;
    }

    private synchronized Connection waitFreeConnection() throws SQLException
    {
        Connection cnn = null;
        int count = 0;
        while(cnn == null)
        {
            try
            {
                wait(10 * 1000);
            }
            catch (InterruptedException ex)
            {
                LOG.log(Level.SEVERE, ex.getMessage(), ex);
            }
            cnn = getFreeConnection();
            if(cnn == null)
            {
                LOG.log(Level.WARNING, "Not connections availables.");
                count++;
                if(count >= 3) throw  new SQLException("Could not get a connection with the database.");
            }
        }
        return cnn;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException
    {
        return logWriter;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException
    {
        logWriter = out;
    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException
    {
        loginTimeout = seconds;
    }

    @Override
    public int getLoginTimeout() throws SQLException
    {
        return loginTimeout;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException
    {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public  T unwrap(Class iface) throws SQLException
    {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class iface) throws SQLException
    {
        return false;
    }

    private ConnectionImpl createNewConnection() throws SQLException
    {
        LOG.log(Level.INFO, String.format("Creating new connection for %s.", config.getName()));
        try
        {
            Class.forName(config.getDriver());
        }
        catch (ClassNotFoundException ex)
        {
            throw new SQLException(ex.getMessage(), ex);
        }
        Connection internalConnection = DriverManager.getConnection(config.getUrl(), config.getUser(), config.getPassword());
        return new ConnectionImpl(internalConnection, this);
    }

    private Connection createNewConnection(String user, String password) throws SQLException
    {
        try
        {
            Class.forName(config.getDriver());
        }
        catch (ClassNotFoundException ex)
        {
            throw new SQLException(ex.getMessage(), ex);
        }
        return DriverManager.getConnection(config.getUrl(), user, password);
    }

    protected synchronized void connectionClosed(ConnectionImpl closedConnection)
    {
        usedConnections.remove(closedConnection);
        freeConnections.add(closedConnection);
        LOG.log(Level.FINE, String.format("Current free connections in %s: %s", config.getName(), freeConnections.size() ));
        notifyAll();
    }

    protected synchronized void close() throws SQLException
    {
        closed = true;
        while(!usedConnections.isEmpty())
        {
            try
            {
                wait();
            }
            catch (InterruptedException e)
            {
                LOG.log(Level.SEVERE, e.getMessage(), e);
            }
        }
        for (ConnectionImpl freeConnection : freeConnections)
        {
            freeConnection.realClose();
        }
        freeConnections.clear();
        usedConnections.clear();
    }
    
    protected synchronized void reconnect(DataSourceConfig config)
    {
        try
        {
            close();
            this.config = config;
            closed = false;
        }
        catch (SQLException e)
        {
            LOG.log(Level.SEVERE, e.getMessage(), e);
        }
    }

    private void checkIdleConnections()
    {
        long idleTime = config.getIdleTime() * 1000;
        if( (System.currentTimeMillis() - lastCheck) > idleTime )
        {
            synchronized(this)
            {
                lastCheck = System.currentTimeMillis();
                List toRemove = new ArrayList<>();
                for (ConnectionImpl freeConnection : freeConnections)
                {
                    if( (System.currentTimeMillis() - freeConnection.getLastUse()) > idleTime )
                    {
                        try
                        {
                            int totalConnections = freeConnections.size() + usedConnections.size();
                            if(totalConnections - toRemove.size() <= config.getMinConnections())
                            {
                                break;
                            }
                            freeConnection.realClose();
                            toRemove.add(freeConnection);
                        }
                        catch (SQLException ex)
                        {
                            LOG.log(Level.SEVERE, ex.getMessage(), ex);
                        }
                    }
                }
                LOG.log(Level.FINE, String.format("Removing %s connections for %s.", toRemove.size(), config.getName()) );
                freeConnections.removeAll(toRemove);
                LOG.log(Level.FINE, String.format("Current total connections in %s: %s", config.getName(), freeConnections.size() + usedConnections.size()));
            }
        }
    }

    private boolean needToReconnect(ConnectionImpl connection) throws SQLException
    {
        long reTime = config.getReconnectTime() * 1000;
        long timePass = System.currentTimeMillis() - connection.getLastUse();
        return reTime < timePass || connection.isClosed();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy