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

com.databricks.jdbc.pooling.DatabricksPooledConnection Maven / Gradle / Ivy

There is a newer version: 2.6.40-patch-1
Show newest version
package com.databricks.jdbc.pooling;

import com.databricks.jdbc.commons.LogLevel;
import com.databricks.jdbc.commons.util.LoggingUtil;
import com.databricks.jdbc.core.DatabricksSQLException;
import com.databricks.jdbc.core.IDatabricksConnection;
import com.databricks.jdbc.core.IDatabricksStatement;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Nullable;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.PooledConnection;
import javax.sql.StatementEventListener;

public class DatabricksPooledConnection implements PooledConnection {
  private final Set listeners = new HashSet<>();
  private Connection physicalConnection;
  private ConnectionHandler connectionHandler;

  public Connection getPhysicalConnection() {
    return this.physicalConnection;
  }

  /**
   * Creates a new PooledConnection representing the specified physical connection.
   *
   * @param physicalConnection connection
   */
  public DatabricksPooledConnection(Connection physicalConnection) {
    this.physicalConnection = physicalConnection;
  }

  /** Fires a connection closed event to all listeners. */
  void fireConnectionClosed() {
    LoggingUtil.log(LogLevel.DEBUG, "void fireConnectionClosed()");
    for (ConnectionEventListener listener : this.listeners) {
      listener.connectionClosed(new ConnectionEvent(this));
    }
  }

  /**
   * Fires a connection error event to all listeners
   *
   * @param e the SQLException to consider
   */
  private void fireConnectionError(SQLException e) {
    LoggingUtil.log(
        LogLevel.DEBUG,
        String.format("private void fireConnectionError(SQLException e = {})", e.toString()));
    for (ConnectionEventListener listener : this.listeners) {
      listener.connectionErrorOccurred(new ConnectionEvent(this, e));
    }
  }

  @Override
  public void addConnectionEventListener(ConnectionEventListener connectionEventListener) {
    listeners.add(connectionEventListener);
  }

  @Override
  public void removeConnectionEventListener(ConnectionEventListener connectionEventListener) {
    listeners.remove(connectionEventListener);
  }

  @Override
  public void removeStatementEventListener(StatementEventListener listener) {
    // Do nothing, not supported
  }

  @Override
  public void addStatementEventListener(StatementEventListener listener) {
    // Do nothing, not supported
  }

  /** Close the physical connection once the pooled connection is closed */
  @Override
  public void close() throws SQLException {
    LoggingUtil.log(LogLevel.DEBUG, "public void close()");
    if (connectionHandler != null && !connectionHandler.isClosed()) {
      connectionHandler.close();
    }
    if (physicalConnection == null) {
      return;
    }
    try {
      physicalConnection.close();
    } finally {
      physicalConnection = null;
    }
  }

  /**
   * Gets a handle for a client to use. This is a wrapper around the physical connection, so the
   * client can call close, and it will just return the connection to the pool without really
   * closing the physical connection.
   *
   * 

According to the JDBC 4.3 Optional Package spec (11.4), only one client may have an active * handle to the connection at a time, so if there is a previous handle active when this is * called, the previous one is forcibly closed. */ @Override public Connection getConnection() throws SQLException { LoggingUtil.log(LogLevel.DEBUG, "public PooledConnection getConnection()"); if (physicalConnection == null) { // Before throwing the exception, notify the listeners DatabricksSQLException sqlException = new DatabricksSQLException("This PooledConnection has already been closed."); fireConnectionError(sqlException); throw sqlException; } // Only one connection can be open at a time from this PooledConnection if (connectionHandler != null && !connectionHandler.isClosed()) { connectionHandler.close(); } connectionHandler = new ConnectionHandler(physicalConnection); return connectionHandler.getVirtualConnection(); } /** * Instead of declaring a class implementing Connection, use a dynamic proxy to handle all calls * through the Connection interface. */ private class ConnectionHandler implements InvocationHandler { private Connection physicalConnection; private Connection virtualConnection; // the Connection the client is currently using, which is not a physical // connection ConnectionHandler(Connection physicalConnection) { this.physicalConnection = physicalConnection; // Use a proxy connection object as a virtual connection, so that we do not close the physical // connection this.virtualConnection = (Connection) Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {Connection.class, IDatabricksConnection.class}, this); } @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { LoggingUtil.log( LogLevel.DEBUG, String.format( "public Object invoke(Object proxy, Method method = {%s}, Object[] args = {%s})", method, args)); final String methodName = method.getName(); if (method.getDeclaringClass() == Object.class) { if (methodName.equals("toString")) { return "Pooled connection wrapping physical connection " + physicalConnection; } if (methodName.equals("equals")) { return proxy == args[0]; } if (methodName.equals("hashCode")) { return System.identityHashCode(proxy); } try { return method.invoke(physicalConnection, args); } catch (InvocationTargetException e) { // throwing.nullable throw e.getTargetException(); } } if (methodName.equals("isClosed")) { return physicalConnection == null || physicalConnection.isClosed(); } // Do not close the physical connection, remove reference and fire close event if (methodName.equals("close")) { if (physicalConnection != null) { physicalConnection = null; virtualConnection = null; connectionHandler = null; fireConnectionClosed(); } return null; } if (physicalConnection == null || physicalConnection.isClosed()) { throw new DatabricksSQLException("Connection has been closed."); } // From here on in, we invoke via reflection and catch exceptions try { Class statementClass; switch (methodName) { case "createStatement": statementClass = Statement.class; break; case "prepareCall": statementClass = CallableStatement.class; break; case "prepareStatement": statementClass = PreparedStatement.class; break; default: return method.invoke(physicalConnection, args); } Statement st = (Statement) method.invoke(physicalConnection, args); return Proxy.newProxyInstance( getClass().getClassLoader(), new Class[] {statementClass, IDatabricksStatement.class}, new StatementHandler(this, st)); } catch (final InvocationTargetException ite) { final Throwable targetException = ite.getTargetException(); if (targetException instanceof SQLException) { fireConnectionError((SQLException) targetException); } throw targetException; } } Connection getVirtualConnection() { return virtualConnection; } public void close() { LoggingUtil.log(LogLevel.DEBUG, "public void close()"); physicalConnection = null; virtualConnection = null; // No close event fired here: see JDBC 4.3 Optional Package spec section 11.4 } public boolean isClosed() { return physicalConnection == null; } } /** * Instead of declaring classes implementing Statement, use a dynamic proxy to handle all calls * through the Statement interfaces. The StatementHandler is required in order to return the * proper Connection proxy for the getConnection method. */ private class StatementHandler implements InvocationHandler { private ConnectionHandler conHandler; private Statement physicalStatement; StatementHandler(ConnectionHandler conHandler, Statement physicalStatement) { this.conHandler = conHandler; this.physicalStatement = physicalStatement; } @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { LoggingUtil.log( LogLevel.DEBUG, String.format( "public Object invoke(Object proxy, Method method = {%s}, Object[] args = {%s})", method, Arrays.toString(args))); final String methodName = method.getName(); if (method.getDeclaringClass() == Object.class) { if (methodName.equals("toString")) { return "Pooled statement wrapping physical statement " + physicalStatement; } if (methodName.equals("hashCode")) { return System.identityHashCode(proxy); } if (methodName.equals("equals")) { return proxy == args[0]; } return method.invoke(physicalStatement, args); } if (methodName.equals("isClosed")) { return physicalStatement == null || physicalStatement.isClosed(); } if (methodName.equals("close")) { if (physicalStatement == null || physicalStatement.isClosed()) { return null; } conHandler = null; physicalStatement.close(); physicalStatement = null; return null; } if (physicalStatement == null || physicalStatement.isClosed()) { throw new DatabricksSQLException("Statement has been closed."); } if (methodName.equals("getConnection")) { return conHandler .getVirtualConnection(); // the virtual connection from the connection handler } // Delegate the call to the physical Statement. try { return method.invoke(physicalStatement, args); } catch (final InvocationTargetException ite) { final Throwable targetException = ite.getTargetException(); if (targetException instanceof SQLException) { fireConnectionError((SQLException) targetException); } throw targetException; } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy