public RserveConnectionPool(String[] hosts, int numConnections) {
    this.hosts = hosts;
    this.numConnections = numConnections;

    final RserveConnectionFactory keyedConnectionFactory = new RserveConnectionFactory();

    final GenericKeyedObjectPool.Config config = new GenericKeyedObjectPool.Config();
    config.maxActive = numConnections;
    config.maxIdle = numConnections;
    config.minIdle = numConnections;
    config.whenExhaustedAction = GenericKeyedObjectPool.WHEN_EXHAUSTED_BLOCK;
    config.testOnBorrow = true;
    config.testOnReturn = true;
    config.testWhileIdle = true;
    config.timeBetweenEvictionRunsMillis = 1000; // enables eviction thread
    config.minEvictableIdleTimeMillis = -1; // disable eviction due to idle time
    config.numTestsPerEvictionRun = -1; // test all idle objects
    config.lifo = false;

    keyedConnectionPool =
        new GenericKeyedObjectPool<String, RConnection>(keyedConnectionFactory, config);

    // Initialise keyed object pool with each host
    for (String host : hosts) {
      keyedConnectionPool.preparePool(host, false);
    }

    // Initialise blocking queue with elements representing potential host connections.
    // There are n elements per host, where n is the number of connections permitted per host.
    // When connections are obtained, we take the host from the head of the queue and use this as
    // the key to the pool.
    // When connections are released, we return the host to the tail of the queue so future
    // connections can be obtained.
    // This implements a first-available-first-used (or first-in-first-out) model for host
    // connections.
    hostQueue = new ArrayBlockingQueue<String>(hosts.length * numConnections, true);
    for (String host : hosts) {
      for (int i = 0; i < numConnections; i++) {
        hostQueue.add(host);
      }
    }
  }