Class DaoFactoryPool<T extends PooledResourceUser>

java.lang.Object
com.mcpdbwizard.pub.DaoFactoryPool<T>
All Implemented Interfaces:
AutoCloseable

public class DaoFactoryPool<T extends PooledResourceUser> extends Object implements AutoCloseable
A pool of warm DAO factories: grows on demand to 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 Classes
    Modifier and Type
    Class
    Description
    static interface 
    A unit of work to run against a borrowed factory.
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final String
    Marks a line of statsLine() output.
  • Constructor Summary

    Constructors
    Constructor
    Description
    DaoFactoryPool(Supplier<T> theSupplier, DaoFactoryPoolConfig theConfig, LogInterface theLog)
     
  • Method Summary

    Modifier and Type
    Method
    Description
    Take a factory out of the pool, creating one if the pool is below its maximum and none is free.
    void
    Close every factory, connected or idle, and stop the evictor.
    long
    Total borrows since startup.
    The settings this pool is running with.
    long
    Factories created since startup — how often the pool had to grow.
    long
    Creations that threw since start-up — a refused or failed logon, not a destroyed factory.
    long
    Factories closed since startup: evicted when idle, or discarded when broken.
    int
    The configured ceiling, so a status page can show "3 of 10".
    int
    Factories currently checked out.
    int
    Factories currently sitting in the pool, connected and warm.
    void
    invalidate(T theFactory)
    Discard a borrowed factory instead of returning it, freeing its slot.
    static boolean
    Could this failure have broken the connection the work ran on?
    void
    release(T theFactory)
    Give a factory back after successful work.
    A one-line, machine-readable snapshot of this pool, for a generated server to log periodically:
    <R> R
    Borrow a factory, run the work against it, and return it — the safe shape, because the return happens in a finally.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • STATS_PREFIX

      public static final String STATS_PREFIX
      Marks a line of statsLine() 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

      public DaoFactoryPool(Supplier<T> theSupplier, DaoFactoryPoolConfig theConfig, LogInterface theLog)
      Parameters:
      theSupplier - creates a fresh, unconnected factory — typically () -> new DaoFactory(theLog)
      theConfig - sizing and lifetime settings; validated here
      theLog - where the pool reports evictions and discarded factories. May be null for silence.
  • Method Details

    • withFactory

      public <R> R withFactory(DaoFactoryPool.PoolTask<T,R> theTask) throws Exception
      Borrow a factory, run the work against it, and return it — the safe shape, because the return happens in a finally.

      Exceptions from the work propagate unchanged, so callers keep whatever CSException handling they already had.

      Parameters:
      theTask - the work to run
      Returns:
      whatever the work returned
      Throws:
      CSPoolExhaustedException - if no factory became free within DAO_POOL_MAX_WAIT_MS
      Exception - whatever the work threw
    • borrow

      public T borrow() throws CSException
      Take a factory out of the pool, creating one if the pool is below its maximum and none is free. Must be paired with release(T) in a finally.
      Returns:
      a connected, validated factory owned exclusively by the caller until it is returned
      Throws:
      CSPoolExhaustedException - if no factory became free within DAO_POOL_MAX_WAIT_MS
      CSException - if a new factory could not be created or connected
    • release

      public void release(T theFactory)
      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 by borrow(); null is ignored so callers can return from a finally without a null check
    • invalidate

      public void invalidate(T theFactory)
      Discard a borrowed factory instead of returning it, freeing its slot. For callers using borrow() directly who know the factory is no longer sound.
      Parameters:
      theFactory - a factory previously returned by borrow()
    • 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 while getCreatedCount() 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

      public DaoFactoryPoolConfig getConfig()
      The settings this pool is running with.
    • statsLine

      public String 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:
      close in interface AutoCloseable
    • isConnectionFatal

      public static boolean isConnectionFatal(Throwable theFailure)
      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. CSException has no cause-carrying constructor, and generated DAO code wraps a driver failure as throw new CSException(e.getMessage()) — so a dropped connection arrives here as a plain CSException whose 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 activateObject reconnects 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