tag:blogger.com,1999:blog-10292249981458043062024-03-08T14:44:00.485-08:00Robert Berger's BlogRobert Bergerhttp://www.blogger.com/profile/15698968421968413565noreply@blogger.comBlogger1125tag:blogger.com,1999:blog-1029224998145804306.post-74181721493043274592011-02-17T18:33:00.000-08:002011-02-18T13:16:32.477-08:00Dependency Injection and Logging<h3>Dependency Injection under Java EE 6 (CDI)</H3><p>I was recently working on a project that runs under Java EE 6. One of the new features of Java EE 6 is CDI, also known as "Contexts and Dependency Injection for the Java EE platform", or JSR 299. CDI allows fine grained dependency injection for initializing container managed objects ("beans"). CDI allows any Java object to be injected into a bean. This is a big improvement over the dependency injection introduced in Java EE 5, which allows only certain objects such as EJB's or JNDI registered resources to be injected.<br />
</p><p>The reference implemntation of CDI is called Weld, and comes from the JBoss division of Red Hat.<br />
</p><h3>What's in a name? My logging woes</H3>I use logging in almost every Java class I write. I prefer the Log4J logging framework, but write my code that uses logging to use the Apache Commons Logging library, which provides a thin wrapper around Log4J and other logging frameworks. Using Commons Logging allows for flexibility when deploying code in different environments, and fixes an issue with using Log4J directly in serializable classes. (This issue will be discussed in a later post.) In the past I always used code like this to initialize the Log object for each class:<br />
<pre>class MyClass {
private static final Log log = LogFactory.getLog(MyClass.class);
</pre><p>That's a bit of typing, so I often copy the line that initializes the log field from an existing class. If I had a dollar for every time I copied such a line but forgot to change the class name to the name of the class I was pasting into, I would right now be enjoying a nice sushi dinner at my favorite restaurant instead of typing this blog post!<br />
</p><h3>A solution to my logging woes</H3>You may be asking yourself what this discussion about logging has to do with the JSR-299 (CDI) dependency injection framework I mentioned earlier. While reading the documentation for the Weld implementation of JSR-299<sup>1</sup>, I came across a nifty solution to my Log mis-naming problem. I replace the above code with:<br />
<pre>class MyClass {
@Inject private Log log;
</pre><p>I configure CDI to automatically inject a Log instance properly configured for the name of the class being injected into by defining a CDI bean in my application that has an appropriate factory method:<br />
</p><pre>public class LoggerFactory {
@Produces Log createLogger(InjectionPoint injectionPoint) {
String name = injectionPoint.getMember().getDeclaringClass().getName();
return LogFactory.getLog(name);
}
</pre><p>Now whenever the CDI framework creates an object that contains the <span class="code">@Inject</span> line shown above, a <span class="code">Log</span> object is created for the field that is automatically configured with the name of the class that contains the log field.<br />
</p><p><h3>Annotations vs. XML configuration files</H3>The <span class="code">@Inject</span> annotation shown above simplifies the use of dependency injection compared to earlier frameworks which required all dependencies to be described in an XML configuration file. Usin XML files to describe every bean gave early dependency injection frameworks one of the same problems as early versions of EJB. You had to write your code, and then describe your code in some detail in an XML configuration file.<br />
</p><p>Modern dependency injection frameworks usually provide the option of describing dependencies with Java annotations such as the <span class="code">@Inject</span> annotation shown above. This reduces the need to write XML configuration files. However, using framework specific annotations in your business classes causes one of the problems dependency injection was supposed to avoid: a tight coupling between your business classes and a specific framework.<br />
</p><p>Fortunatel the Java community is converging on a solution. The <span class="code">@Inject</span> annotation in the examples above is defined in JSR 330, which defines annotations for describing dependencies of a bean. JSR 330 is now supported by multiple dependency injection frameworks, including CDI, Spring (3.0 and later), and Google Guice (3.0 and later).<br />
</p><h3>It's time for Spring!</H3><p>Or so says a certain groundhog that lives near here. As mentioned above, Spring 3.0 supports the JSR 330 annotations such as the <span class="code">@Inject</span> annotation used in the examples above.<br />
</p><p>I recently worked on a Spring based project that needed to use the classes I had written for a Java EE 6 project using <span class="code">@Inject</span> annotations to configure logging. Getting this to work under Spring was a bit trickier than under CDI.<br />
</p><p>Spring supports defining factory methods to create object instances to satisify dependencies. The dependencies for <span class="code">Log</span> fields can by satisfied by adding the following to the Spring config file:<br />
<pre><bean id="defaultLog"
class="org.apache.commons.logging.LogFactory"
factory-method="getLog"scope="singleton">
<constructor-arg type="java.lang.String" value="defaultLog"/>
</bean>
</pre></p><p>Although this succeeds in injecting a valid <span class="code">Log</span> instance into any bean that uses <span class="code">@Inject</span> to defines a <span class="code">Log</span> field, all of the <span class="code">Log</span> instances inserted have the same name, which is less than ideal.<br />
</p><p>I found a solution to this on the web.<sup>2</sup> A Spring bean is defined that implements the <span class="code">BeanPostProcessor</span> interface. This bean contains a method that replaces the injected <span class="code">Log</span> instance with one that has a name that matches the class name of the bean that contains the log field:<br />
</p><pre>public class LoggerPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
ReflectionUtils.doWithFields(bean.getClass(), new ReflectionUtils.FieldCallback() {
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
if (field.getAnnotation(Inject.class) != null && field.getType().equals(Log.class)) {
ReflectionUtils.makeAccessible(field);
field.set(bean, LogFactory.getLog(bean.getClass()));
}
}
});
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
</pre><p>This is certainly messier than the CDI implementation, but once the <span class="code">LoggerPostProcessor</span> is added to your Spring application you can use <span class="code">@Inject</span> to set up your <span class="code">Log</span> fields and it will work the same way in both CDI and Spring.<br />
</p><p>Note the the factory method shown in the Spring configuration above must still be present, or Spring will get errors at runtime because it cannot satisfy the declared dependency.<br />
</p><p>My implementations of <span class="code">LoggerFactory</span> and <span class="code">LoggerPostProcessor</span> are availabe at:<br />
</p><p> http://www.bberger.net/di/<br />
</p><p>Feel free to use them as is or adapt them to your own needs.<br />
</p><p>In my next post I will show how to configure Google Guice to work with classes that use the same <span class="code">@Inject</span> annotation used in these examples.<br />
</p><h3>References</H3><ol><li>http://docs.jboss.org/weld/reference/1.0.0/en-US/html/injection.html#d0e1540<br />
JBoss Weld documentation example using <span class="code">InjectionPoint</span> to configure logging</li>
<li>http://www.tzavellas.com/techblog/2007/03/31/implementing-seam-style-logger-injection-with-spring<br />
Example using a Spring <span class="code">BeanPostProcessor</span> to configure logging</li>
</ol>Robert Bergerhttp://www.blogger.com/profile/15698968421968413565noreply@blogger.com5