Using a patched version of Spring Security OAuth

32 views
Skip to first unread message

Bill W

unread,
Nov 22, 2010, 11:00:34 AM11/22/10
to Eureka Streams Development
Eureka Streams has recently secured the restlet feeds with 2-legged
OAuth via the Spring Security OAuth library. We are using a patched
version of the latest from the Codehaus baseline (3.19). The problem
we encountered appears to be fixed in the SpringSource baseline,
however the SpringSource version requires Spring Security 3.0 which in
turn requires Spring 3.0. Eureka Streams currently uses Spring 2.5.6,
and although we would like to upgrade, it does not fit our development
schedule right now. As a temporary measure, we made a fix to Spring
Security OAuth 3.19 and put it in our public Maven dependencies
repository as version "3.19-ES".

The Symptom

Eureka Streams has data access classes (referred to as "mappers") to
retrieve data from the database. Most of these derive from
BaseDomainMapper and are injected with a
javax.persistence.EntityManager via a setter annotated with
@PersistenceContext (javax.persistence.PersistenceContext). As soon
as I added the provider for Spring Security OAuth to our Spring
configuration, SOME (not all) of our mappers stopped being injected
with an EntityManager, which of course caused null reference
exceptions as soon as those mappers were used. I added and removed
the provider from the configuration and the behavior was very
consistent, so it wasn't just a bad build fluke. Very strange indeed!

The Problem

Some explanation of Spring internals is in order here. Spring has an
"application context" which acts as the world in which the beans are
created, and a bean factory which is used by the application context
to create beans. Two other categories of collaborators are bean
factory post-processors and bean post-processors. I believe the
purpose of the former is to make adjustments to the bean factory
before the beans start being created (such as wiring up bean post-
processors). The latter perform any special initialization when beans
are created - such as injecting an EntityManager on beans with
PersistenceContext annotations.

The relevant parts of the lifecycle when setting up the context (known
as "refreshing" it) are: run the bean factory post-processors,
register the bean post-processors, create the singleton beans, and
finally publish a context refreshed event. Based on the lifecycle, it
is important that beans not be created before the bean post-processors
are registered, otherwise there will be beans that the post-processors
will not have an opportunity to work on. In fact, there are two
places in the source code that there are specific comments about
making sure not to initialize regular beans there else the post-
processors will not apply to them; one of which is in the method to
invoke bean factory post-processors. It turns out, that was the flaw
in Spring Security OAuth.

Spring Security OAuth needed a single point in the lifecycle where it
could wire up some of its beans. It fetched a list of all the token
listener beans and a list of all the token registry beans and
registered every listener with every registry (an MxN relationship).
Because of the relationships involved, a bean post-processor would not
be appropriate, since it would be invoked after each bean was created
(vs. just once). Unfortunately, the authors chose to use a bean
factory post-processor, which is invoked only once, but too early in
the lifecycle. The Spring Security OAuth bean factory post-processor
(OAuthTokenLifecycleRegistryPostProcessor) calls
BeanFactoryUtils.beansOfTypeIncludingAncestors to get a list of beans
matching a given type. One issue with this is the beans the call
returns may miss initialization from not-yet-registered bean post-
processors. But a bigger issue is that the call may create other
beans besides the ones it is returning (for some beans, it has to
create them before it can tell whether they're of the correct type),
and those beans will miss the bean post-processor initialization.

That is exactly what was happening in our scenario. Spring had to
create some of our mapper beans to determine that they didn't match
OAuthTokenLifecycleRegistryPostProcessor's query. Since the bean post-
processor that handles PersistenceContext had not yet been registered,
those particular mappers did not get injected with an EntityManager.
So the mappers that were created early due to the query were not
injected and the rest were.

The fix

Spring supports "application listeners" which receive notification of
events on the application context. One such event is when the context
has been refreshed, which is the last step of the lifecycle I
described above, and thus occurs after the singletons have been
created. So we changed OAuthTokenLifecycleRegistryPostProcessor to be
an ApplicationListener which responds to the ContextRefreshedEvent.

This is a temporary solution, since
OAuthTokenLifecycleRegistryPostProcessor no longer exists in the
latest version of Spring Security OAuth (which we could not upgrade to
currently due to the need to upgrade to Spring 3.0).

Perhaps other developers using similar older versions may encounter
this same issue (and find this post). If so, their options are to
upgrade or perform the same patch.
Reply all
Reply to author
Forward
0 new messages