Java tutorial
/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 com.alibaba.wasp.jdbcx; import com.alibaba.wasp.FConstants; import com.alibaba.wasp.jdbc.JdbcException; import com.alibaba.wasp.util.New; import org.apache.commons.lang.NotImplementedException; import org.apache.hadoop.conf.Configuration; import javax.sql.ConnectionEvent; import javax.sql.ConnectionEventListener; import javax.sql.ConnectionPoolDataSource; import javax.sql.DataSource; import javax.sql.PooledConnection; import java.io.PrintWriter; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; /** * A simple standalone JDBC connection pool. * */ public class JdbcConnectionPool implements DataSource, ConnectionEventListener { private final ConnectionPoolDataSource dataSource; private final ArrayList<PooledConnection> recycledConnections = New.arrayList(); private PrintWriter logWriter; private int maxConnections; private int timeout; private AtomicInteger activeConnections = new AtomicInteger(0); private boolean isDisposed; private Configuration conf; protected JdbcConnectionPool(ConnectionPoolDataSource dataSource, Configuration conf) { this.dataSource = dataSource; this.conf = conf; this.maxConnections = conf.getInt(FConstants.JDBC_POOL_MAX_CONNECTIONS, FConstants.DEFAULT_JDBC_POOL_MAX_CONNECTIONS); this.timeout = conf.getInt(FConstants.JDBC_CONNECTION_TIMEOUT, FConstants.DEFAULT_JDBC_CONNECTION_TIMEOUT); if (dataSource != null) { try { logWriter = dataSource.getLogWriter(); } catch (SQLException e) { // ignore } } } /** * Constructs a new connection pool. * * @param dataSource * the data source to create connections * @return the connection pool */ public static JdbcConnectionPool create(ConnectionPoolDataSource dataSource, Configuration conf) { return new JdbcConnectionPool(dataSource, conf); } /** * Constructs a new connection pool for wasp. * * @param url * the database URL of the wasp connection * @param user * the user name * @param password * the password * @return the connection pool */ public static JdbcConnectionPool create(String url, String user, String password, Configuration conf) { JdbcDataSource ds = new JdbcDataSource(conf); ds.setURL(url); ds.setUser(user); ds.setPassword(password); return new JdbcConnectionPool(ds, conf); } /** * Sets the maximum number of connections to use from now on. The default * value is 20 connections. * * @param max * the maximum number of connections */ public synchronized void setMaxConnections(int max) { if (max < 1) { throw new IllegalArgumentException("Invalid maxConnections value: " + max); } this.maxConnections = max; // notify waiting threads if the value was increased notifyAll(); } /** * Gets the maximum number of connections to use. * * @return the max the maximum number of connections */ public synchronized int getMaxConnections() { return maxConnections; } /** * Gets the maximum time in seconds to wait for a free connection. * * @return the timeout in seconds */ public synchronized int getLoginTimeout() { return timeout; } /** * Sets the maximum time in seconds to wait for a free connection. The default * timeout is 30 seconds. Calling this method with the value 0 will set the * timeout to the default value. * * @param seconds * the timeout, 0 meaning the default */ public synchronized void setLoginTimeout(int seconds) { if (seconds == 0) { seconds = FConstants.DEFAULT_JDBC_CONNECTION_TIMEOUT; } this.timeout = seconds; } /** * Closes all unused pooled connections. Exceptions while closing are written * to the log stream (if set). */ public synchronized void dispose() { if (isDisposed) { return; } isDisposed = true; ArrayList<PooledConnection> list = recycledConnections; for (int i = 0, size = list.size(); i < size; i++) { closeConnection(list.get(i)); } } /** * Retrieves a connection from the connection pool. If * <code>maxConnections</code> connections are already in use, the method * waits until a connection becomes available or <code>timeout</code> seconds * elapsed. When the application is finished using the connection, it must * close it in order to return it to the pool. If no connection becomes * available within the given timeout, an exception with SQL state 08001 and * vendor code 8001 is thrown. * * @return a new Connection object. * @throws java.sql.SQLException * when a new connection could not be established, or a timeout * occurred */ public Connection getConnection() throws SQLException { long max = System.currentTimeMillis() + timeout * 1000; do { synchronized (this) { if (activeConnections.get() < maxConnections) { return getConnectionNow(); } try { wait(1000); } catch (InterruptedException e) { // ignore } } } while (System.currentTimeMillis() <= max); throw new SQLException("get connection timeout. activeConnections=" + activeConnections + ". maxConnections=" + maxConnections, "08001", 8001); } /** * INTERNAL */ public Connection getConnection(String user, String password) { throw new UnsupportedOperationException(); } private Connection getConnectionNow() throws SQLException { if (isDisposed) { throw new IllegalStateException("Connection pool has been disposed."); } PooledConnection pc; if (!recycledConnections.isEmpty()) { pc = recycledConnections.remove(recycledConnections.size() - 1); } else { pc = dataSource.getPooledConnection(); } Connection conn = pc.getConnection(); activeConnections.incrementAndGet(); pc.addConnectionEventListener(this); return conn; } /** * This method usually puts the connection back into the pool. There are some * exceptions: if the pool is disposed, the connection is disposed as well. If * the pool is full, the connection is closed. * * @param pc * the pooled connection */ synchronized void recycleConnection(PooledConnection pc) { if (activeConnections.get() <= 0) { throw new AssertionError(); } activeConnections.decrementAndGet(); if (!isDisposed && activeConnections.get() < maxConnections) { recycledConnections.add(pc); } else { closeConnection(pc); } if (activeConnections.get() >= maxConnections - 1) { notifyAll(); } } private void closeConnection(PooledConnection pc) { try { pc.close(); } catch (SQLException e) { if (logWriter != null) { e.printStackTrace(logWriter); } } } /** * INTERNAL */ public void connectionClosed(ConnectionEvent event) { PooledConnection pc = (PooledConnection) event.getSource(); pc.removeConnectionEventListener(this); recycleConnection(pc); } /** * INTERNAL */ public void connectionErrorOccurred(ConnectionEvent event) { // not used } /** * Returns the number of active (open) connections of this pool. This is the * number of <code>Connection</code> objects that have been issued by * getConnection() for which <code>Connection.close()</code> has not yet been * called. * * @return the number of active connections. */ public int getActiveConnections() { return activeConnections.get(); } /** * INTERNAL */ public PrintWriter getLogWriter() { return logWriter; } /** * INTERNAL */ public void setLogWriter(PrintWriter logWriter) { this.logWriter = logWriter; } /** * [Not supported] Return an object of this class if possible. * * @param iface * the class */ // ## Java 1.6 ## public <T> T unwrap(Class<T> iface) throws SQLException { throw JdbcException.getUnsupportedException("unwrap"); } // */ /** * [Not supported] Checks if unwrap can return an object of this class. * * @param iface * the class */ // ## Java 1.6 ## public boolean isWrapperFor(Class<?> iface) throws SQLException { throw JdbcException.getUnsupportedException("isWrapperFor"); } // */ /** * [Not supported] */ // ## Java 1.7 ## public Logger getParentLogger() { throw new NotImplementedException(); } }