Class DaoFactoryPool<T extends PooledResourceUser>
- All Implemented Interfaces:
AutoCloseable
DAO_POOL_MAX_SIZE, makes borrowers wait
when saturated, and shrinks back to DAO_POOL_MIN_IDLE once traffic stops.
Wraps Apache Commons Pool 2. That dependency is optional in this module — nothing here
is loaded unless generated code was built with DAO_POOL=YES, so a deployment that does not
pool never needs the jar.
Normal use is withFactory(com.mcpdbwizard.pub.DaoFactoryPool.PoolTask<T, R>), which borrows, runs, and returns in one call:
String theJson = thePool.withFactory(theFactory -> theFactory.getFooTableDAO().get(id).toJson());
borrow() and release(T) are exposed for callers whose control flow will not fit
that shape, but they must be paired in a finally — a factory that is never returned is a
leaked Oracle session, and the pool cannot reclaim it.
What happens on the return leg
- The borrower's transaction is settled — committed or rolled back per
DaoFactoryPoolConfig.isCommitOnReturn(), but always rolled back if the borrower threw. Committing half a failed unit of work would be worse than either policy. - Statements and DAOs are not released. Keeping them parsed is the entire
reason to pool factories instead of connections; see
PooledResourceUser. - A factory whose connection has gone bad is destroyed rather than returned. An application error — a PL/SQL exception, a constraint violation — leaves the connection perfectly usable, so it is not grounds for discarding a warm factory.
This class is thread-safe. The factories it hands out are not, which is what the pool is for: exactly one borrower holds a given factory at a time. Copyright 2003-2026 ATB Consultancy Services Ltd (formerly Orinda Software Ltd, Dublin, Ireland)
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic interfaceA unit of work to run against a borrowed factory. -
Field Summary
Fields -
Constructor Summary
ConstructorsConstructorDescriptionDaoFactoryPool(Supplier<T> theSupplier, DaoFactoryPoolConfig theConfig, LogInterface theLog) -
Method Summary
Modifier and TypeMethodDescriptionborrow()Take a factory out of the pool, creating one if the pool is below its maximum and none is free.voidclose()Close every factory, connected or idle, and stop the evictor.longTotal borrows since startup.The settings this pool is running with.longFactories created since startup — how often the pool had to grow.longCreations that threw since start-up — a refused or failed logon, not a destroyed factory.longFactories closed since startup: evicted when idle, or discarded when broken.intThe configured ceiling, so a status page can show "3 of 10".intFactories currently checked out.intFactories currently sitting in the pool, connected and warm.voidinvalidate(T theFactory) Discard a borrowed factory instead of returning it, freeing its slot.static booleanisConnectionFatal(Throwable theFailure) Could this failure have broken the connection the work ran on?voidGive a factory back after successful work.A one-line, machine-readable snapshot of this pool, for a generated server to log periodically:<R> RwithFactory(DaoFactoryPool.PoolTask<T, R> theTask) Borrow a factory, run the work against it, and return it — the safe shape, because the return happens in afinally.
-
Field Details
-
STATS_PREFIX
Marks a line ofstatsLine()output. Public because it is a wire format between a generated server and whatever reads its log — the web module's Runtime page parses these — so both ends must agree on it in one place rather than by two matching string literals.- See Also:
-
-
Constructor Details
-
DaoFactoryPool
- Parameters:
theSupplier- creates a fresh, unconnected factory — typically() -> new DaoFactory(theLog)theConfig- sizing and lifetime settings; validated heretheLog- where the pool reports evictions and discarded factories. May benullfor silence.
-
-
Method Details
-
withFactory
Borrow a factory, run the work against it, and return it — the safe shape, because the return happens in afinally.Exceptions from the work propagate unchanged, so callers keep whatever
CSExceptionhandling they already had.- Parameters:
theTask- the work to run- Returns:
- whatever the work returned
- Throws:
CSPoolExhaustedException- if no factory became free withinDAO_POOL_MAX_WAIT_MSException- whatever the work threw
-
borrow
Take a factory out of the pool, creating one if the pool is below its maximum and none is free. Must be paired withrelease(T)in afinally.- Returns:
- a connected, validated factory owned exclusively by the caller until it is returned
- Throws:
CSPoolExhaustedException- if no factory became free withinDAO_POOL_MAX_WAIT_MSCSException- if a new factory could not be created or connected
-
release
Give a factory back after successful work. Settles the transaction per the configured policy and keeps the factory's parsed statements for the next borrower.- Parameters:
theFactory- a factory previously returned byborrow();nullis ignored so callers can return from afinallywithout a null check
-
invalidate
-
getNumActive
public int getNumActive()Factories currently checked out. -
getNumIdle
public int getNumIdle()Factories currently sitting in the pool, connected and warm. -
getMaxSize
public int getMaxSize()The configured ceiling, so a status page can show "3 of 10". -
getCreateFailedCount
public long getCreateFailedCount()Creations that threw since start-up — a refused or failed logon, not a destroyed factory. Climbing whilegetCreatedCount()is flat means the database is turning us away. -
getBorrowedCount
public long getBorrowedCount()Total borrows since startup. -
getCreatedCount
public long getCreatedCount()Factories created since startup — how often the pool had to grow. -
getDestroyedCount
public long getDestroyedCount()Factories closed since startup: evicted when idle, or discarded when broken. -
getConfig
The settings this pool is running with. -
statsLine
A one-line, machine-readable snapshot of this pool, for a generated server to log periodically:POOL-STATS active=1 idle=3 max=4 borrowed=1201 created=4 destroyed=0 createfailed=0
Fields are only ever APPENDED. The reader matches
name=rather than position, so an older Runtime page ignores a field it does not know instead of failing to parse the line.The log is the only channel available to a reader: a generated MCP server runs as its own process, so nothing outside it can call the getters above. Logging is also why the format is flat and fixed rather than JSON — it has to survive being read back out of a text log tail.
-
close
public void close()Close every factory, connected or idle, and stop the evictor. Borrowed factories are closed as and when they are returned. Idempotent, so it is safe in a shutdown hook.- Specified by:
closein interfaceAutoCloseable
-
isConnectionFatal
Could this failure have broken the connection the work ran on?False is the important answer. A constraint violation, a PL/SQL exception, a bad bind count — these leave the session perfectly healthy, and discarding a warm factory over one is pure cost. Only a genuine transport or session failure justifies it.
The message has to be read, not just the exception type.
CSExceptionhas no cause-carrying constructor, and generated DAO code wraps a driver failure asthrow new CSException(e.getMessage())— so a dropped connection arrives here as a plainCSExceptionwhose text is all that survives. Classifying on type alone would call every real Oracle failure harmless.Our own pre-flight failures (a bad bind count, say) carry no ORA code at all, which is what separates them. A factory wrongly kept is not dangerous either way: the pool validates while idle and, when configured, on borrow, and
activateObjectreconnects a session that died. A factory wrongly destroyed costs a logon, which is the expensive mistake.- Parameters:
theFailure- what the work threw; null counts as not fatal- Returns:
- true only if the connection itself is suspect
-