Nelz' Blog

Ruminations on Development


« Previous month (Sep 2008) | Main | Next month (Nov 2008) »
Saturday Oct 18, 2008

Compile-Time Dependencies

When building out your architecture, I would encourage you to break out your APIs as first-class modules. (Sorry, a 'module' is Maven speak for the parts of a project that create their own artifacts such as JAR or WAR files.)

For an example of a simple suggested structure, look at the following image. (Arrows denote compile-time dependencies.): http://www.nelz.net/roller/nelz/resource/IdealModule.png

In some of my recent projects I received some push-back on breaking out the APIs. The concern was that breaking them out as modules would add overhead to the build process. I do understand that concern, but I think it is a minimized threat if you are using a fairly sophisticated build system.

I acquiesced at the time, and now we have a structure that looks similar to the next image. (Can you see the problem already?): http://www.nelz.net/roller/nelz/resource/PreviousModule.png

The black arrow indicates just one of the weaknesses of this model. Here, you can see that a UI component can (at the least offensive) access the DAO API as well as (at the most offensive) access the concrete DAO implementations.

When you have a structure like this, the question is not if, but when will one of your fellow engineers take advantage of this and circumvent the nice functional striations that you have taken so much time to isolate?

Tuesday Oct 14, 2008

XMLUnit and TestNG

Our entire testing system is based on TestNG, but one of our developers recently invoked XMLUnit to test some of the XML he was producing.

Now, XMLUnit is built to be used with JUnit (old JUnit... like 3.8.1 style JUnit...), which at first glance doesn't seem to play well with TestNG test runners. (We were having some build timeouts and other mysterious behavior when trying to run these tests.)

To give you an idea, here is a snippet from the examples:

package net.nelz.test.xmlunit;

import org.custommonkey.xmlunit.XMLTestCase;
public class MyXMLTestCase extends XMLTestCase {
    public MyXMLTestCase(String name) {
        super(name);
    }
    public void testForEquality() throws Exception {
        String myControlXML = "<msg><uuid>0x00435A8C</uuid></msg>";
        String myTestXML = "<msg><localId>2376</localId></msg>";
        assertXMLEqual("comparing test xml to control xml", myControlXML, myTestXML);
        assertXMLNotEqual("test xml not similar to control xml", myControlXML, myTestXML);
    }
}

If you look at that example, the only methods that seem to add any value are assertXMLEqual(...) and assertXMLNotEqual(...).

If you look at the source code for those methods, you'll realize that they just delegate to parallel static methods in the class XMLAssert:

...
    public void assertXMLEqual(String control, String test)
        throws SAXException, IOException {
        XMLAssert.assertXMLEqual(control, test);
    }
...

What this all means is that XMLUnit already is TestNG friendly. Here's what that snippet from XMLUnit's example code would look like if it were rewritten to target TestNG.:

package net.nelz.test.xmlunit;

import org.custommonkey.xmlunit.XMLAssert;
import org.testng.annotations.Test;

public class MyXMLTestCase {
    @Test
    public void testForEquality() throws Exception {
        String myControlXML = "<msg><uuid>0x00435A8C</uuid></msg>";
        String myTestXML = "<msg><localId>2376</localId></msg>";
        XMLAssert.assertXMLEqual("comparing test xml to control xml", myControlXML, myTestXML);
        XMLAssert.assertXMLNotEqual("test xml not similar to control xml", myControlXML, myTestXML);
    }
}

Friday Oct 10, 2008

Spring JMX Challenges

Yesterday I started to do some clean-up work around how we are using Spring and JMX in some of our webapps.

We started off with a very basic configuration:

...
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
  <property name="beans">
    <map>
      <entry key="OurApp:name=beanName" value-ref="beanReference"/>
      ...
    </map>
  </property>
</bean>
...

By default, this exposes all public methods for management, which is overkill for us. So I wanted to use the Java 5 Annotation capabilities to expose only those methods that are appropriate for management. This brought us to the following configuration:

...
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
  <property name="assembler" ref="assembler"/>
  <property name="beans">
    <map>
      <entry key="OurApp:name=beanName" value-ref="beanReference"/>
      ...
    </map>
  </property>
</bean>

<bean id="jmxAttributeSource"
  class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>

<bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
  <property name="attributeSource" ref="jmxAttributeSource"/>
</bean>
...

This worked for most of the beans, but when I tried to port this one bean to the new Annotation-based interface definition, I kept getting a nasty stack-trace upon startup, the root of which says "MetadataMBeanInfoAssembler does not support JDK dynamic proxies - export the target beans directly or use CGLIB proxies instead".

I couldn't figure out for the life of me what the structural difference was between this one bean, and all the others... I tried changing around the other annotations in the class, substituting other beans from the same layer, everything. I couldn't figure it out.

After a bit, I decided to give up on adding the Annotations to that one bean, but I still wanted to expose the bean. I couldn't find documentation on this, but it turns out that you are completely able to have multiple "exporters" invoked in your Spring configuration files. This gave me a configuration like this:

...
<bean id="exporter1" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
  <property name="assembler" ref="assembler"/>
  <property name="beans">
    <map>
      <entry key="OurApp:name=beanName" value-ref="beanReference"/>
      ...
    </map>
  </property>
</bean>

<bean id="exporter2" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
  <property name="beans">
    <map>
      <entry key="OurApp:name=problemBeanName" value-ref="problemBeanReference"/>
    </map>
  </property>
</bean>

<bean id="jmxAttributeSource"
  class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>

<bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
  <property name="attributeSource" ref="jmxAttributeSource"/>
</bean>
...

Great. This worked. I was able to manage the problem bean, and I was able to define the interface to all the other beans via the Annotations. But, when I went to manage the problem bean via the JMX console, there were all sort of dynamic proxy methods exposed...

This got me thinking back to the stack trace:

~MetadataMBeanInfoAssembler does not support JDK dynamic proxies - export the target beans directly or use CGLIB proxies instead

... I definitely see other traces of dynamic proxying in the JMX interface itself.

Then it hit me: AOP. The one thing that was different between the problem bean and all the other beans is that the problem bean is being AOP'ed.

Doing some research on that stack trace error message, I found this discussion. They seem to have figured out there's a conflict with the MetadataMBeanInfoAssembler and dynamically proxied classes, but I'm still unclear (until further testing) if the suggestion to "set the proxyTargetClass property of your ProxyFactoryBean to true" will work.

In summation, here are the learnings:

  1. MetadataMBeanInfoAssembler is challenged with defining an interface based on Annotations of an AOP'ed class.
  2. It is trivial to define multiple MBeanExporter's with different configurations if you need to.