demo.socket.pool.SocketsPool.java Source code

Java tutorial

Introduction

Here is the source code for demo.socket.pool.SocketsPool.java

Source

package demo.socket.pool;

/*
 *    Copyright 2009-2012 The MyBatis Team
 *
 *    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.
 */

import static demo.util.Util.*;
import static demo.util.socket.SocketUtil.*;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

import org.apache.commons.logging.Log;

// by: org.apache.ibatis.datasource.pooled.PooledDataSource
/**
 * This is a simple, synchronous, thread-safe socket pool
 */
public class SocketsPool {

    private static final Log log = getLog(SocketsPool.class);

    private final PoolState state = new PoolState();

    protected String host;
    protected int port;

    protected int poolMaximumActiveConnections = 10;
    protected int poolMaximumIdleConnections = 5;
    protected int poolMaximumCheckoutTime = 20000;
    protected int poolTimeToWait = 20000;
    protected Integer socketSoTimeout;

    public SocketsPool(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public SocketConn getConnection() throws IOException {
        return popConnection();
    }

    public <T> T invoke(SocketConnHandler<T> handler) throws IOException {
        SocketConn conn = null;
        try {
            conn = getConnection();
            return handler.handle(conn);
        } catch (Throwable t) {
            return handler.onException(conn, t);
        } finally {
            if (conn != null)
                conn.close();
        }
    }

    public void setHost(String host) {
        this.host = host;
        forceCloseAll();
    }

    public void setPort(int port) {
        this.port = port;
        forceCloseAll();
    }

    /**
     * The maximum number of active connections
     */
    public void setPoolMaximumActiveConnections(int poolMaximumActiveConnections) {
        this.poolMaximumActiveConnections = poolMaximumActiveConnections;
        forceCloseAll();
    }

    /**
     * The maximum number of idle connections
     */
    public void setPoolMaximumIdleConnections(int poolMaximumIdleConnections) {
        this.poolMaximumIdleConnections = poolMaximumIdleConnections;
        forceCloseAll();
    }

    /**
     * The maximum time a connection can be used before it *may* be given away again
     */
    public void setPoolMaximumCheckoutTime(int poolMaximumCheckoutTime) {
        this.poolMaximumCheckoutTime = poolMaximumCheckoutTime;
        forceCloseAll();
    }

    /**
     * The time to wait before retrying to get a connection
     */
    public void setPoolTimeToWait(int poolTimeToWait) {
        this.poolTimeToWait = poolTimeToWait;
        forceCloseAll();
    }

    public String getHost() {
        return host;
    }

    public int getPort() {
        return port;
    }

    public String getRemoteAddress() {
        return getHost() + ":" + getPort();
    }

    public int getPoolMaximumActiveConnections() {
        return poolMaximumActiveConnections;
    }

    public int getPoolMaximumIdleConnections() {
        return poolMaximumIdleConnections;
    }

    public int getPoolMaximumCheckoutTime() {
        return poolMaximumCheckoutTime;
    }

    public int getPoolTimeToWait() {
        return poolTimeToWait;
    }

    public Integer getSocketSoTimeoutForNewConn() {
        return socketSoTimeout;
    }

    public void setSocketSoTimeoutForNewConn(Integer socketSoTimeout) {
        this.socketSoTimeout = socketSoTimeout;
    }

    /**
     * Closes all active and idle connections in the pool
     */
    public void forceCloseAll() {
        synchronized (state) {
            for (int i = state.activeConnections.size(); i > 0; i--) {
                try {
                    PooledSocketConn conn = state.activeConnections.remove(i - 1);
                    conn.invalidate();
                    close(conn.getSocket());
                } catch (Exception e) {
                    // ignore
                }
            }
            for (int i = state.idleConnections.size(); i > 0; i--) {
                try {
                    PooledSocketConn conn = state.idleConnections.remove(i - 1);
                    conn.invalidate();
                    close(conn.getSocket());
                } catch (Exception e) {
                    // ignore
                }
            }
        }
    }

    public PoolState getPoolState() {
        return state;
    }

    protected void pushConnection(PooledSocketConn conn) throws IOException {

        synchronized (state) {
            state.activeConnections.remove(conn);
            if (conn.isValid()) {
                if (state.idleConnections.size() < poolMaximumIdleConnections) {
                    state.accumulatedCheckoutTime += conn.getCheckoutTime();
                    PooledSocketConn newConn = new PooledSocketConn(conn);
                    state.idleConnections.add(newConn);
                    conn.invalidate();
                    state.notifyAll();
                } else {
                    state.accumulatedCheckoutTime += conn.getCheckoutTime();
                    conn.getSocket().close();
                    conn.invalidate();
                }
            } else {
                //A bad connection attempted to return to the pool, discarding connection
                state.badConnectionCount++;
                close(conn.getSocket());
            }
        }
    }

    private PooledSocketConn popConnection() throws IOException {
        boolean countedWait = false;
        PooledSocketConn conn = null;
        long t = System.currentTimeMillis();
        int localBadConnectionCount = 0;

        //LOOP for find conn
        while (conn == null) {
            synchronized (state) {
                // Pool has available connection
                if (state.idleConnections.size() > 0) {
                    conn = state.idleConnections.remove(0);
                }
                // Pool does not have available connection
                else {
                    // Can create new connection
                    if (state.activeConnections.size() < poolMaximumActiveConnections) {
                        conn = createConn(state.activeConnections.size() == 0);
                    }
                    // Cannot create new connection
                    else {
                        PooledSocketConn oldestActiveConnection = state.activeConnections.get(0);
                        long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
                        // Can claim overdue connection
                        if (longestCheckoutTime > poolMaximumCheckoutTime) {
                            state.claimedOverdueConnectionCount++;
                            state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
                            state.accumulatedCheckoutTime += longestCheckoutTime;
                            state.activeConnections.remove(oldestActiveConnection);
                            conn = new PooledSocketConn(oldestActiveConnection);
                            oldestActiveConnection.invalidate();
                        }
                        // Must wait
                        else {
                            try {
                                if (!countedWait) {
                                    state.hadToWaitCount++;
                                    countedWait = true;
                                }
                                long wt = System.currentTimeMillis();
                                state.wait(poolTimeToWait);
                                state.accumulatedWaitTime += System.currentTimeMillis() - wt;
                            } catch (InterruptedException e) {
                                break;
                            }
                        }
                    }
                }
                if (conn != null) {
                    if (conn.isValid()) {
                        conn.setCheckoutTimestamp(System.currentTimeMillis());
                        conn.setLastUsedTimestamp(System.currentTimeMillis());
                        state.activeConnections.add(conn);
                        state.requestCount++;
                        state.accumulatedRequestTime += System.currentTimeMillis() - t;
                    } else {
                        state.badConnectionCount++;
                        localBadConnectionCount++;
                        conn = null;
                        if (localBadConnectionCount > (poolMaximumIdleConnections + 3)) {
                            throw new IllegalStateException("Could not get a good connection");
                        }
                    }
                }
            }

        }

        if (conn == null) {
            throw new IllegalStateException(
                    "Unknown severe error condition.  The connection pool returned a null connection.");
        }

        return conn;
    }

    private PooledSocketConn createConn(boolean printLog) throws IOException {

        Socket s = new Socket(host, port);
        if (socketSoTimeout != null)
            s.setSoTimeout(socketSoTimeout);

        InputStream is = s.getInputStream();
        OutputStream os = s.getOutputStream();
        if (printLog)
            log.info("Connected to " + host + ":" + port + "." + " Pool props [" + "maxActiveConnections="
                    + poolMaximumActiveConnections + ", maxIdleConnections=" + poolMaximumIdleConnections
                    + ", maxCheckoutTime=" + poolMaximumCheckoutTime + ", timeToWait=" + poolTimeToWait + "]");
        return new PooledSocketConn(s, is, os, this);
    }

    @Override
    protected void finalize() throws Throwable {
        forceCloseAll();
    }

    public int getActiveConnectionsCount() {
        synchronized (state) {
            return state.getActiveConnectionCount();
        }
    }

    public int getIdleConnections() {
        synchronized (state) {
            return state.getIdleConnectionCount();
        }
    }

}