Skip to content

Advanced Driver APIs#

The concourse-driver-java driver exposes a handful of APIs that sit above the standard Concourse surface. They’re designed for applications that need to manage connections in bulk, wrap the driver for cross-cutting concerns, or evaluate a Criteria locally without contacting the server.

Connection Pools#

For applications that issue many concurrent operations, a ConnectionPool amortizes the cost of establishing authenticated sessions. Two pool styles are available:

  • Cached pool — grows and shrinks on demand, suitable for bursty workloads.
  • Fixed pool — maintains a fixed number of connections, suitable for steady-state workloads where you want predictable concurrency.

Building a pool#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// Java

// Cached, reading defaults from concourse_client.yaml
ConnectionPool pool =
        ConnectionPool.newCachedConnectionPool();

// Cached, with explicit credentials
ConnectionPool pool = ConnectionPool.newCachedConnectionPool(
        "localhost", 1717, "admin", "admin");

// Fixed size of 8, scoped to an environment
ConnectionPool pool = ConnectionPool.newFixedConnectionPool(
        "localhost", 1717, "admin", "admin",
        "production", 8);

// Cached, copying connection info from an existing handler
Concourse template = ...;
ConnectionPool pool =
        ConnectionPool.newCachedConnectionPool(template);

The single-handler factories (newCachedConnectionPool(Concourse) and newFixedConnectionPool(Concourse, int)) reuse an existing handler’s credentials and environment, which is handy in tests or applications that already manage a reference connection.

Using a pool#

1
2
3
4
5
6
7
8
// Java
Concourse c = pool.request();
try {
    c.add("name", "Jeff", 1);
}
finally {
    pool.release(c);
}

Always pair request() with release() — the connection is not returned to the pool until released.

Auto-healing#

When a pooled connection is released after it has failed (for example, because the server restarted or the socket dropped), the pool detects the failure and silently replaces it with a fresh connection from the same supplier. No application code needs to change to participate: the next request() simply hands out a working connection.

Shutdown#

1
pool.close();

close() waits for every outstanding connection to be released. Closing a pool with leased connections throws rather than silently stranding them — design your application to release everything it holds before shutdown.


ForwardingConcourse Wrapper#

ForwardingConcourse is an abstract base class that delegates every Concourse method to a wrapped handler. Subclass it when you want to add behavior around a subset of methods without reimplementing the whole API.

Typical uses:

  • Caching read results in front of the driver.
  • Adding structured logging or metrics to every call.
  • Injecting an access-control check or tenant scoping.
  • Composing multiple wrappers (audit + cache + scope) through simple subclass chaining.

Example: a caching wrapper#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Java
public class CachingConcourse extends ForwardingConcourse {

    private final Map<String, Object> cache =
            new ConcurrentHashMap<>();

    public CachingConcourse(Concourse delegate) {
        super(delegate);
    }

    @Override
    public <T> T get(String key, long record) {
        String cacheKey = key + ":" + record;
        return (T) cache.computeIfAbsent(cacheKey,
                k -> super.get(key, record));
    }

    @Override
    protected ForwardingConcourse $this(Concourse delegate) {
        return new CachingConcourse(delegate);
    }
}

The $this(Concourse) hook lets the base class rewrap the delegate — for example, when a connection pool returns a fresh handler after a failure. Always implement it to return a subclass instance of the same type.


Local Criteria Evaluation#

Sometimes you have a Criteria (or CCL string) and a local dataset already in memory, and you want to check whether that data satisfies the condition without a round trip to the server. ConcourseCompiler compiles CCL into an AbstractSyntaxTree whose ConditionTree can be evaluated against local bindings.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Java
ConcourseCompiler compiler = ConcourseCompiler.get();

Multimap<String, Object> data = HashMultimap.create();
data.put("age", 32);
data.put("department", "Engineering");

AbstractSyntaxTree ast = compiler.parse(
        "age > 21 AND department = \"Engineering\"",
        data);
boolean matched = compiler.evaluate(
        (ConditionTree) ast, data);
// -> true

For locally-materialized records, the Association variant avoids the overhead of converting to a Multimap:

1
2
3
// Java
Association record = Association.from(selectResult);
boolean matched = compiler.evaluate(conditionTree, record);

When you only need to compare a single value against an operator, call Operators.evaluate(...) directly:

1
2
3
4
5
// Java
boolean over21 = Operators.evaluate(
        30,
        Operator.GREATER_THAN,
        ImmutableList.of(21));

This is useful in client-side filters, optimistic validation, and unit tests that need to model server-side semantics without a running server.