Class DataSourceResourceCaptureScope

java.lang.Object
com.isomorphic.datasource.DataSourceResourceCaptureScope
All Implemented Interfaces:
com.isomorphic.datasource.ResourceCaptureScope

public class DataSourceResourceCaptureScope extends Object implements com.isomorphic.datasource.ResourceCaptureScope
Tracks DataSource instances borrowed during an HTTP request and ensures they are returned to the pool when the request ends — even if the caller never explicitly frees them.

Pool capture. The scope captures the PoolManager that was current when it started. All borrows and returns within the scope use the captured pool, ensuring hot-swap safety: replacing the global pool mid-request cannot cause a DS to be borrowed from one pool and returned to another.

Name-based caching. When the same DataSource name is requested multiple times within a scope (a common occurrence - for example, when DataSources contain includeFrom fields), the first request borrows from the pool and caches the instance; subsequent requests return the cached instance without re-borrowing. Peak pool usage is bounded by the number of distinct DataSource names in the request, not the number of get() calls.

Instance tracking. Every borrowed instance is recorded so that free() can route returns to the captured pool, and so that the outermost scope's cleanup can return any instances that were never explicitly freed (leak safety net).

Inner scopes. When a scope is started while an outer scope is already active, the inner scope shares the outermost scope's tracking set, name cache, and pool reference. Nesting is transparent to resource management; it exists purely for robustness, allowing the system to work correctly regardless of which layer (filter, servlet, RPCManager, DSRequest) happens to be outermost.

Isolated scopes. A scope created with isolated=true (used by DSRequest(dsName, opType, false)) behaves as an outermost scope even when an outer scope is already active. The outer scope is suspended for the isolated scope's lifetime and restored when it stops. This lets a one-off DSRequest bypass the surrounding request's DS cache without affecting the surrounding request's DS instances.

freeOnExecute=false lifecycle. When an isolated DSRequest's execute() finishes but freeOnExecute=false, the isolated scope stays open after execute() returns: the DS instance holds an open JDBC connection and must remain out of the pool for exactly as long as the connection is open. To prevent a leak, a deferred cleanup callback is registered on the outer scope. When the outer scope stops at the end of the HTTP request, it runs all pending callbacks, each of which calls freeResources() on the isolated DSRequest, releasing the JDBC connection and returning the DS instance to the pool.

The deferred callback is idempotent: if the caller explicitly calls freeResources() before the outer scope stops, the callback becomes a no-op. Explicit early cleanup is always preferred when the caller knows the DS is no longer needed, as it returns the JDBC connection to the pool sooner.

If there is no outer scope, no deferred cleanup is registered and the caller must call freeResources() explicitly. DSRequest.finalize() provides a last-resort safety net, but Java finalizer timing is unreliable and pool exhaustion may occur before GC runs.