Code:
import org.springframework.context.ApplicationListener;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationContext;
import org.springframework.richclient.security.LoginEvent;
import org.springframework.richclient.security.LogoutEvent;
import org.springframework.richclient.security.ClientSecurityEvent;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.BeanIsNotAFactoryException;
import org.springframework.beans.factory.BeanIsAbstractException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import net.sf.acegisecurity.Authentication;
import java.util.Map;
import java.util.Iterator;
import java.util.List;
import java.util.Collections;
import java.util.LinkedList;
import java.lang.ref.WeakReference;
/**
* Whenever a user logs in this component will find all beans that implement
* AuthorizationAware in the Spring ApplicationContext and notify them of
* the new authorization information.
*/
public class AuthorizationAwareConfigurer implements ApplicationListener,
ApplicationContextAware,
BeanPostProcessor
{
private static final Log log = LogFactory.getLog(AuthorizationAwareConfigurer.class);
private ApplicationContext applicationContext;
private String currentPrincipal;
private String currentCredentials;
private boolean currentAttempting = false;
private final List nonSingletonListeners = Collections.synchronizedList(new LinkedList());
public ApplicationContext getApplicationContext()
{
return this.applicationContext;
}
protected void addToNonSingletonListeners(final AuthorizationAware aa)
{
if(log.isDebugEnabled()) log.debug(">>> Adding AuthorizationAware bean '" + aa + "' to list of non singleton listeners.");
this.nonSingletonListeners.add(new WeakReference(aa));
}
protected void updateBean(final AuthorizationAware aa, final String principal, final String credentials, final boolean attempting, final boolean postOp)
{
if(log.isDebugEnabled()) log.debug(">>> Notifying bean '" + aa + "' of new authorization for '" + principal + "'");
if(attempting) {
aa.setUserAttemptedAuthorization(principal, credentials);
} else {
if(!postOp) {
aa.setUserAuthorization(principal, credentials);
} else {
aa.setPostUserAuthorization(principal, credentials);
}
}
}
protected void updateBeans(final String principal, final String credentials,
final boolean attempting, final boolean postOp)
{
if(log.isDebugEnabled()) log.debug(">> ENTERING updateBean(principal='" + principal + "', credentials=PROTECTED, attempting=" + attempting);
this.currentPrincipal = principal;
this.currentCredentials = credentials;
this.currentAttempting = attempting;
final ApplicationContext ac = getApplicationContext();
if(ac != null) {
if(log.isDebugEnabled()) log.debug(">> Notifying non factory beans...");
final Map map = ac.getBeansOfType(AuthorizationAware.class, false, true);
if(log.isDebugEnabled()) log.debug(">> AuthorizationAware BEANS=" + map);
final Iterator iEntries = map.entrySet().iterator();
while(iEntries.hasNext()) {
final Map.Entry entry = (Map.Entry)iEntries.next();
final Object bean = entry.getValue();
if(bean instanceof AuthorizationAware) {
if(log.isDebugEnabled()) log.debug(">> Setting authorization information on bean name='" + entry.getKey() + "', bean='" + bean + "'");
updateBean((AuthorizationAware)bean, principal, credentials, attempting, postOp);
}
}
if(log.isDebugEnabled()) log.debug(">> Notifying factory beans...");
final String[] names = ac.getBeanDefinitionNames();
final StringBuffer factoryBuf = new StringBuffer();
factoryBuf.append("&");
for(final String beanName : names) {
factoryBuf.setLength(1);
factoryBuf.append(beanName);
final String factoryName = factoryBuf.toString();
try {
if(ac.containsBean(factoryName) && ac.isSingleton(factoryName) &&
ac.isSingleton(beanName)) {
final Object factoryBean = ac.getBean(factoryName);
if(factoryBean instanceof AuthorizationAware) {
if(log.isDebugEnabled()) log.debug(">> Bean '" + beanName + "' is a factory bean implementing AuthorizationAware, setting auth info for principal '" + principal + "'");
updateBean((AuthorizationAware)factoryBean, principal, credentials, attempting, postOp);
}
}
} catch(BeanIsNotAFactoryException binafe) {
// Just skip it then...
} catch(BeanIsAbstractException biae) {
}
}
if(log.isDebugEnabled()) log.debug(">> Notifying non-singleton beans...");
updateNonSingletonBeans(principal, credentials, attempting, postOp);
}
}
protected void updateNonSingletonBeans(final String principal, final String credentials,
final boolean attempting, final boolean postOp)
{
synchronized(this.nonSingletonListeners) {
for(Iterator iNonSingletons = this.nonSingletonListeners.iterator(); iNonSingletons.hasNext();) {
final WeakReference weakReference = (WeakReference) iNonSingletons.next();
final AuthorizationAware aa = (AuthorizationAware)weakReference.get();
if(aa == null) {
if(log.isDebugEnabled()) log.debug(">> JUST REMOVED garbage collected AuthorizationAware non-singleton from list.");
iNonSingletons.remove();
} else {
if(log.isDebugEnabled()) log.debug(">> UPDATING non-singleton AuthorizationAware instance '" + aa + "'");
updateBean(aa, principal, credentials, attempting, postOp);
}
}
}
}
//
// METHODS FROM INTERFACE ApplicationListener
//
public void onApplicationEvent(final ApplicationEvent event)
{
if(event instanceof ClientSecurityEvent) {
if(log.isDebugEnabled()) log.debug(">> RECEIVED ClientSecurityEvent: " + event);
if(event instanceof LoginEvent ||
event instanceof AttemptingAuthorizationEvent) {
Authentication authentication = (Authentication)event.getSource();
if(log.isDebugEnabled()) log.debug(">> Authentication = " + authentication);
updateBeans(authentication.getPrincipal().toString(),
authentication.getCredentials().toString(),
event instanceof AttemptingAuthorizationEvent,
false);
} else if(event instanceof LogoutEvent) {
if(log.isDebugEnabled()) log.debug(">> This is a LogoutEvent");
updateBeans(null, null, false, false);
} else if(event instanceof PostLoginEvent) {
Authentication authentication = (Authentication)event.getSource();
updateBeans(authentication.getPrincipal().toString(),
authentication.getCredentials().toString(),
false, true);
} else if(event instanceof PostLogoutEvent) {
updateBeans(null, null, false, true);
}
}
}
//
// METHODS FROM INTERFACE ApplicationContextAware
//
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException
{
this.applicationContext = applicationContext;
}
//
// METHODS FROM INTERFACE BeanPostProcessor
//
public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException
{
if(bean instanceof AuthorizationAware) {
if(beanName == null || !getApplicationContext().containsBean(beanName) ||
!getApplicationContext().isSingleton(beanName)) {
addToNonSingletonListeners((AuthorizationAware)bean);
}
updateBean((AuthorizationAware)bean, this.currentPrincipal, this.currentCredentials, this.currentAttempting, false);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
{
return bean;
}
}
As you can see from this code, we have run into issues with factory beans implementing AuthorizationAware, and have a kind of hacky workaround. At the time this code was written, Spring did not provide methods for finding factory beans that implement a certain interface (as opposed to factory beans whose returned bean implements a certain interface), thus the hackiness. It could be that newer versions of Spring offer more in this area now? You can see that the code goes through a two-step process: first handling non-factory beans, then handling factory beans. Also, this code handles non-singleton beans by keeping them in a collection as they are created using weak references. Finally, it appears that some of the newer code (aka, the factory bean fix code) uses newer Java 1.5 syntax (we recently made the decision to go with Java 1.5, so we've been writing any new code with new syntax).