AEPortal uses a pooling infrastructure that allows various QOS, e.g. aging by time or request count. This infrastructure should be used for all pooling needs.
Note: Pooling is not the same as caching because cached objects need a unique name or id so that clients can address them. This is not necessary for pooling. Still, both could probably be implemented using the same base classes. This would be a topic for the re-design.
AEPortal uses a generic object pool called "Minerva". Minerva is now part of the jboss open source project (http://javatree.web.cern.ch/javatree) and does also advanced JDBC connection pooling (JDBC SE, 2 phase commit support) but we only use the generic object pool. It is in the package org.jboss.Minerva.pools. Infrastructure now has a package comaepinfrastructure.pooling which includes standard factories and a main pool factory. This package assumes right now a couple of default pools, e.g. DOMParserPool but new pools can be created programmatically. The process is similar to the creation of new caches: You need a new factory with a method to create a specific object and – optionally – have that object implement the PooledObject interface.
What is missing:
a xml description of default pools so that the system can configure the pool factory at boot time (like we do with reference data)
a way to do the same at runtime from within applications or services (to make AEPortal dynamically extensible)
more quality of service features: aging by time and request.
Documentation: twiki, search for "ObjectPool".
We pool things because we want to save either time – if it takes a lot of time to build an object – or memory – if an object has a large footprint.
The "what" part is harder to answer: Typically heavyweight objects that are not re-entrant because they keep some state e.g. XML Parsers. Right now it is necessary to give two threads two XML Parser instances because they might each install a different entity manager etc. There is only one requirement: the pooled objects need to be "resettable", i.e. a new client thread (or the pool itself when the object is returned) can return the object to its initial state.
Typically objects that get pooled are:
Unlike caching pooling follows an allocate/free pattern. Objects that are not "freed" after use are no longer available for other threads.
Sometimes it is hard to retrofit a piece of code with pooling because the moment where the pooled resource needs to be returned to the pool cannot be determined.
Example 5.2. A pooling example:
// before pooling Class ResourceUser { Public ResourceUser () { Resource mResource = new Resource(); // get a new instance mResource.setSomeMode(x); // initialize it } Public useResource() { String result = mResource.parse(foo); } } Given this class, when would you return a pooled resource to the pool? // with pooling Class ResourceUser { Public ResouceUser () { } private Resource getResource() { Resource mResouce = (Resource) pool.getResource(); // get a pooled instance mResource.setSomeMode(x); // initialize it } private void freeResource(Resource res) { pool.releaseObject(res); } Public useResource() { Resource res = getResource(); // get resource String result = res.parse(foo); // use it FreeResource(res); // return it immediately } }
You can no longer let objects of class PoolUser be collected by the Garbage Collector without returning the pooled resource beforehand. The code is designed for short time usage of a resource. In the non-pooled version the resource is private to class PoolUser. This is expensive but the good side of it is that this cannot create a bottleneck. In the pooled version – if class PoolUser does not call freeResource() after every use – we have created a bottleneck in the system. Of course, if class PoolUser would use the resource in a tight and non-blocking loop, it would probably hold on to the one and avoid the pool management overhead.