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.