docs: public Connection getConnection() throws SQLException {
  
          PooledConnection pooledConnection = null;
  
          synchronized (this) {
              if (!initialized) {
                  if (initialSize > maxPoolSize) {
                      throw new SQLException("Initial size of " + initialSize
                              + " exceeds max. pool size of " + maxPoolSize);
                  }
                  logInfo("Pre-initializing " + initialSize
                          + " physical connections");
  
                  for (int i = 0; i < initialSize; i++) {
                      connectionsInactive.add(createNewConnection());
                  }
                  initialized = true;
              }
  
              long loginTimeoutExpiration = calculateLoginTimeoutExpiration();
  
              //each waiting thread will spin around in this loop until either
              //
              // a) a connection becomes available
              // b) the login timeout passes. Results in SQLException.
              // c) the thread is interrupted while waiting for an available connection
              // d) the connection pool is closed. Results in SQLException.
              //
              // the reason the threads spin in a loop is that they should always check all options
              // (available + space for new  + abandoned) before waiting. In addition the threads will
              // repeat the close check before spinning a second time. That way all threads waiting
              // for a connection when the connection is closed will get an SQLException.
              while (pooledConnection == null) {
                  if (this.isPoolClosed) {
                      throw new SQLException(
                          "The pool is closed. You cannot get anymore connections from it.");
                  }
  
                  // 1) Check if any connections available in the pool.
                  pooledConnection = dequeueFirstIfAny();
  
                  if (pooledConnection != null) {
                      return wrapConnectionAndMarkAsInUse(pooledConnection);
                  }
  
                  // 2) Check if the pool has space for new connections and create one if it does.
                  if (poolHasSpaceForNewConnections()) {
                      pooledConnection = createNewConnection();
  
                      return wrapConnectionAndMarkAsInUse(pooledConnection);
                  }
  
                  // 3) Check if connection pool has session timeout. If yes,
                  // check if any connections has timed out. Return one if there are any.
                  if (this.sessionTimeout > 0) {
                      reclaimAbandonedConnections();
  
                      //check if any connections were closed during the time out check.
                      pooledConnection = dequeueFirstIfAny();
  
                      if (pooledConnection != null) {
                          return wrapConnectionAndMarkAsInUse(pooledConnection);
                      }
                  }
  
                  // 4) wait until a connection becomes available or the login timeout passes (if set).
                  doWait(loginTimeoutExpiration);
              }
  
              return wrapConnectionAndMarkAsInUse(pooledConnection);
          }
      }