All entries for December 2005
December 16, 2005
Interesting article on threadLocals
Writing about web page http://blog.arendsen.net/index.php/2005/02/22/threadlocals-and-memory-leaks-revisited/
Very good.
December 12, 2005
How to post process a Spring Bean?
So I have a FactoryBean which produces a FreeMarker configuration and it all works well.
Now I wanted to precompile all the freeMarker templates and started looking for the appropriate Springs infrastructure to tie into.
I came up with the following options:
[extend the existing BeanFactory
This just seemed hacky. I wasn't changing the strategy for loading the object so the factoryness hadn't changed.
[implement a BeanPostProcessor
This looked like promising; it executed after the factory had done it's thing and had a very convenient "postProcessAfterInitialization" method which passed in the name of the bean, and the bean itself. Cool. The problem is that the bean is the original beanFactory. Sure, I could call getObject() but it all started to feel a bit fragile and icky
[implement BeanFactoryPostProcessor
This went out of the window for two reasons; it suffers the same "problem" as BPP (not a "problem" with Spring, just the way I want to use it) and the single "postProcessBeanFactory" is called before the beans are realised (or instantiated as Spring prefers).
at this point I was thinking, surely there must be something more :)
and there is
[implement ApplicationListener
This little gem (thanks Dave Hewitt :)) listens to all the various events that hook in very nicely with the lifecycle of a BeanFactory, so there is a ContextClosed, ContextRefreshed and RequestHandled event. Implementing the single method "onApplicationEvent" which passed in the ApplicationEvent meant I could write the following bean:
public final class FreeMarkerCompilerEventListener implements ApplicationListener {
private static final Logger LOGGER = Logger.getLogger(FreeMarkerCompilerEventListener.class);
private final File templateDir;
private final Configuration configuration;
public FreeMarkerCompilerEventListener(final Configuration theConfiguration, final Resource theResource) {
this.configuration = theConfiguration;
this.templateDir = verifyResource(theResource);
}
public void onApplicationEvent(final ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
compile();
}
}
private File verifyResource(final Resource resource) {
File file;
try {
file = resource.getFile();
} catch (final IOException e) {
throw new IllegalStateException("Resource " + resource + " must be a directory!", e);
}
if (!file.isDirectory()) {
throw new IllegalStateException("Resource " + resource + " must be a directory!");
}
return file;
}
private void compile() {
String[] templateNames = getTemplateNames();
for (String name: templateNames) {
try {
LOGGER.info("Precompiling freemarker template [" + name + "]");
configuration.getTemplate(name);
} catch (final IOException e) {
throw new IllegalStateException("Cannot get template for " + name, e);
}
}
}
private String[] getTemplateNames() {
List files = new ArrayList();
for (File file: templateDir.listFiles()) {
findFreeMarkerTemplates(file, "", files);
}
return files.toArray(new String[] {});
}
private void findFreeMarkerTemplates(final File root, final String parentPath, final List filesFound) {
if (root.isDirectory()) {
for (File file: root.listFiles()) {
String newParentPath = parentPath;
if (StringUtils.hasLength(newParentPath)) {
newParentPath += "/";
}
newParentPath += root.getName();
findFreeMarkerTemplates(file, newParentPath, filesFound);
}
} else {
if (root.getName().toLowerCase().endsWith(".ftl")) {
filesFound.add(parentPath + "/" + root.getName());
}
}
}
}
No messing around with BeanFactories, or fragile bean lookups by name, I simply wire this :
<bean id="freeMarkerConfig" class="org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean">
<property name="templateLoaderPath"><value>/WEB-INF/freemarker/</value></property>
</bean>
<bean id="freeMarkerCompiler" class="uk.ac.warwick.sbr.freemarker.FreeMarkerCompilerEventListener">
<constructor-arg index="0" ref="freeMarkerConfig"/>
<constructor-arg index="1" value="/WEB-INF/freemarker/"/>
</bean>
Spring will realise (via the proper use of it's BeanFactoryPostProcessor) that I have defined an ApplicationListener and will automatically call it when an ApplicationEvent is fired.
Cool.
December 02, 2005
Hibernate + Spring DI
Writing about web page http://www.springframework.org/docs/api/org/springframework/beans/factory/config/AutowireCapableBeanFactory.html
People keep asking how to inject service dependencies into Hibernate backed objects. I briefly responded here but I have recently revisited this code.
I stumbled across AutowireCapableBeanFactory which has a really useful method: autowireBeanProperties) which does the DI on an existing bean.
After reading the javadocs I created this simple class:
package uk.mycompany;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import uk.mycompany.ClassFilteringLoadEventListener.LoadedObjectListener;
public final class AutoWiringLoadedObjectListener implements LoadedObjectListener, BeanFactoryAware {
private AutowireCapableBeanFactory beanFactory;
public void loaded(final Object loadedObject) {
beanFactory.autowireBeanProperties(loadedObject, AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, false);
}
public void setBeanFactory(final BeanFactory beanFactory) throws BeansException {
this.beanFactory = (AutowireCapableBeanFactory) beanFactory;
}
}
and replaced theuk.mycompany.ClassThatDoesTheAdvisingLoadedListener with the uk.mycompany.AutoWiringLoadedObjectListener and it works beatifully. Is it slow? Not at all. After load testing there was no discernable difference.
Now I don't need to write a class to do per class introspection. Just leave it up to Spring.
I love Spring.