Craig,
I've used this approach myself, and found it to generally improve
productivity in web application development.
However, I would question whether enshrining it in the API is really
that valuable. There are definite limitations to the usefulness of a
ThreadLocal PM, from the obvious one of not being able to easily share
it in threaded subtasks (producer-consumer, for example, or other
scenarios where a web request initiates an asynchronous action), to the
lack of ability to control PM lifecycle (from a user perspective) and
the lack of ability to optimize PM reuse and caching (from a provider
perspective). ThreadLocals are also notoriously difficult to debug,
particularly in the area of memory leaks. Undoubtedly there are
techniques to get around all of those, but let's just say it's not a
silver bullet.
Apart from the J2EE integration scenario, it's trivially easy for the
user (or a web framework component) to create a ThreadLocal PM helper
method; the API is public (unlike most JDOHelper methods, which rely on
"hidden methods" and an SPI layer).
Now, with that all said, I support anything that helps get us closer to
truly transparent persistence, which I define as "not having to worry
about the sodding PMF or PM". What I'd really like to see is a
configuration/injection method (per environment -- SE and EE) that makes
it as trivial as possible to get a PM and start doing some real work.
At the most (and overly) basic level, this becomes
PersistenceManager.getInstance(), and the environment is responsible for
determining exactly where the instance comes from. In this context,
ThreadLocal is a particular configuration strategy that may suit some
applications.
The persistence unit concept -- or for our purposes, named PMFs -- is a
useful bridge between configuring the environment (runtime) and
configuring the application (design time). For example, I might want to
configure the environment such that PM.getInstance(), when called from
any Tomcat threadpool thread (I think this could be done by
ClassLoader), uses, as its secret sauce, a ThreadLocal
PersistenceManager that comes from the "persistence-unit1" PMF
configuration.
You could take the approach of adding, e.g., javax.jdo.ThreadLocal=true
to the PMF properties, but I think that unnecessarily mixes the data
access configuration with the application environment's usage of that
configuration.
So --
I like the ability to have a ThreadLocal PM setup. But it seems like we
could do more here than just enshrine that one pattern.
I like the ability to use PersistenceUnitNames to look up PMFs. I think
this should be available regardless of the proxy scenario, via
JDOHelper.getNamedPersistenceManagerFactory(String name) (or possibly
overriding the semantics of getPersistenceManagerFactory(String) -- have
to think about that). The requirement is that when loading a PMF, it is
interrogated for its name and registered in a hash on JDOHelper.
It's possible I've completely jumped the shark here, and if so I
apologize and await the hail of bullets, but I hope I may provoke some
interesting discussion.
Thoughts?
Wes
Craig L Russell wrote:
Javadogs,
I'd like to propose a solution to a problem that we have with
usability in the web tier. When using a servlet, each method that
needs access to a PersistenceManager needs to figure out where the
current PersistenceManager is, and if it is even active. There are
many ways around this issue, but they are not general. Among the
workarounds are putting the active PersistenceManager into the servlet
context as a request or session attribute, passing the
PersistenceManager explicitly as a parameter, and putting the
PersistenceManager into a ThreadLocal field.
Of these workarounds, the one with the most appeal is the ThreadLocal
solution. So I'd like to propose that we formalize this by adding a
method to return a thread-safe PersistenceManager proxy associated
with a PersistenceManagerFactory that can be implemented as a
singleton, stored in a component's static field, and that dynamically
binds to the PersistenceManager that is currently active on the thread.
Multiple PersistenceManager proxies can be active, each with its own
binding to a (possibly different) PersistenceManagerFactory.
The benefit of this proposal is ease of use for web and ejb components
that currently have to manage their own PersistenceManager resources.
Instead of having to look up and store a PersistenceManager in each
method of each component that needs one, looking up the
PersistenceManagerFactory can be done once during initialization of
the component.
The limitations of the proposal is that in environments that do not
support Java EE 5 TransactionSynchronizationRegistry (i.e. J2SE
servers and Java SE), only one PersistenceManager per
PersistenceManagerFactory can be used per thread (which maps to a
single web request), and only one transaction can be used per
PersistenceManager.
In environments that support Java EE 5
TransactionSynchronizationRegistry, container-managed transactions can
be used, including the ability to suspend transactions.
If used in an environment that does not support Java EE 5
TransactionSynchronizationRegistry, the behavior is as follows:
The first component to use a PersistenceManager method on a thread
would get a PersistenceManager from the factory. Subsequent callers
would use the same PersistenceManager delegate until the transaction
completed, at which time the PersistenceManager is cleared and the
first subsequent request would create a new one.
Implementation: The proxy would delegate most methods to the current
PersistenceManager, as determined by the ThreadLocal field being
non-null. Calling close() would have no effect. If the ThreadLocal
field is null, then getPersistenceManager() would be called on the
PersistenceManagerFactory. A synchronization instance would be created
and registered with the currentTransaction of the newly acquired
PersistenceManager, the PersistenceManager would be set into the
ThreadLocal, and then the request wold be delegated to the new
PersistenceManager. At afterCompletion, the ThreadLocal would be
nullified.
If used in an environment that supports Java EE
5 TransactionSynchronizationRegistry, the behavior is as follows:
The first component to use a PersistenceManager method in a managed
transaction would get a PersistenceManager from the factory.
Subsequent callers in the same transaction would use the same
PersistenceManager delegate until the transaction completed, at which
time the PersistenceManager is cleared and the first request in a new
transaction would create a new one.
Implementation: The proxy would delegate most methods to the current
PersistenceManager, as determined by
the TransactionSynchronizationRegistry entry for the
PersistenceManagerFactory being non-null. Calling close() would have
no effect. If the TransactionSynchronizationRegistry entry for the
PersistenceManagerFactory is null, then getPersistenceManager() would
be called on the PersistenceManagerFactory. An interposed
synchronization instance would be created and registered with
the TransactionSynchronizationRegistry, the PersistenceManager would
be set into the TransactionSynchronizationRegistry entry for the
PersistenceManagerFactory, and then the request wold be delegated to
the new PersistenceManager. At afterCompletion,
the TransactionSynchronizationRegistry entry for the
PersistenceManagerFactory would be nullified.
If we agree on the semantics of this behavior, the method can be
implemented as a helper method of JDOHelper, PersistenceManager
getPersistenceManagerProxy or something like it (suggestions welcome).
To support container-managed transactions in J2EE servers (1.4 and
earlier), we would have to put the method on PersistenceManagerFactory
and allow the implementation to use its own secret sauce to provide
the proper semantics. If the jdo implementation chose, it could simply
delegate to the JDOHelper version of the method, with the limitations
therein.
Craig
Craig Russell
Architect, Sun Java Enterprise System http://java.sun.com/products/jdo
408 276-5638 mailto:[EMAIL PROTECTED]
P.S. A good JDO? O, Gasp!