Better J2EEing with Spring

Pages: 1, 2, 3

Although this templating mechanism is only available for a few specific circumstances, we can generalize this concept and apply it in a wider context. It would be helpful, for example, to have a mechanism that turns checked exceptions into unchecked exceptions and additionally may provide some intermediate or unified exception handling strategies to exception handling. We could also use this facility to "soften" low-level, nuts-and-bolts exceptions to something more palatable. We could soften something like a PacketFrameParityFaultException to a CommunicationsUnreliableException. This remapped and softer exception suggests a situation that may not be quite so serious and suggests that a retry of the request may be appropriate.

Spring already has a mechanism similar to this that wraps EJB invocation (we saw this in the last section), but unfortunately it doesn't have anything that is considered "general purpose," at least in the exception softening sense. But Spring does have a very robust AOP (Aspect Oriented Programming) framework that we can use to approximate this behavior. What follows is (admittedly) a somewhat contrived example of where this softening might come into play.

Let's go back to some of the concepts that we started to develop earlier in this article. Recall in the first section we had a small application based on remote sensors. Let's continue to develop that example. We'll start off with a simple sensor interface that looks something like this:

public interface ProtocolAdapterIfc
{
  public Integer getRemoteSensorValue()
    throws CommChecksumFault,
           CommConnectFailure,
           CommPacketSequenceFault;
}

There's nothing special here. It is clear that anyone who implements this interface gets a remote value and returns it to the caller. During this time our intrepid caller risks some horrible disaster, as exemplified by the checked exceptions this interface could possibly throw.

Next we have an implementer of this interface. This class that implements the ProtocolAdapter is CarrierPigeon, which looks like this:

public class CarrierPigeon
      implements ProtocolAdapterIfc
{
  private boolean isTired = true;
  private boolean canFlapWings = false;

  public Integer getRemoteSensorValue()
    throws CommChecksumFault,
           CommConnectFailure,
           CommPacketSequenceFault
    {
        if(isTired && !canFlapWings )
        {
          throw new
               CommConnectFailure("I'm Tired!");
        }
        return new Integer(42);
    }
}

For the sake of brevity, I've omitted the getters and setters for the properties. When the getRemoteSensorValue() is called, the CarrierPigeon method checks if it is tired and if it can flap its wings. If it is tired and it can't flap its wings, we get no value and have to throw a CommConnectionFailure which (as I'm sure you'll recall) is a checked exception. So far so good. But wait! I've decided that I really don't want my application programmers dealing with tired carrier pigeons. I could wrap this in some sort of object and catch exceptions before they get there, but I have 20 different types of sensors that I have to deal with. Also, I would prefer to deal with this stuff somewhat transparently and perhaps vary the logic inside the exception handlers as we gain more knowledge of the system. What I want is a consistent API that my application programmers can deal with that can be varied or enhanced over time. I want to templatize my exception handling and let my application programmers deal with a soft, unchecked exception as opposed to a hard exception. Spring fills the bill very nicely in this scenario. Here's an outline of the strategy that we will use to do this:

  • Define a softer interface that eliminates the checked exceptions and reveals a minimal interface. This is the interface that application programmers will deal with.
  • Using Spring AOP constructs, develop an interceptor that will be shimmed in between the client invocation and the invocation on the target object, in our case the "CarrierPigeon."
  • Wire this up using Spring and run the application.

First, here's our softer programmer's interface:

public interface SensorIfc
{
  public Integer getSensorValue();
}

Notice that within limitations, you have the opportunity to rename the methods to something perhaps a bit more meaningful. You also have the chance to strip off the checked exceptions as I have done here. Next and possibly more interesting is the shim that we're going to get Spring to inject in the call stack for us:

import org.aopalliance.intercept.MethodInterceptor;

public class SensorInvocationInterceptor
  implements MethodInterceptor
{
  public Object invoke(MethodInvocation
     invocationTarget) throws Throwable
  {
    // Return object reference
    Object o = null;

    // Convert it to the protocol interface type
    ProtocolAdapterIfc pai =
      (ProtocolAdapterIfc) invocationTarget.getThis();

    try
    {
      o = pai.getRemoteSensorValue();
    }
    catch (CommChecksumFault csf)
    {
      throw new SoftenedProtocolException(
       "protocol error [checksum error]: "
        + csf.getMessage());
    }
    catch (CommConnectFailure cf)
    {
      throw new SoftenedProtocolException(
        "protocol error [comm failure]: "
        + cf.getMessage());
    }
    catch (CommPacketSequenceFault psf)
    {
      throw new SoftenedProtocolException(
        "protocol error [message sequence error]"
        + psf.getMessage());
    }

    return o;
  }
}

By implementing the Spring MethodInterceptor interface and plugging this class into the Spring system (we'll see that in a minute), we will have handed to us the actual target method via the MethodInvocation parameter. From this we can extract the real object we want to call. Remember, we changed the interface that the caller sees to a SensorIfc whereby the target object implements ProtocolAdapterIfc. We did this to simplify the invocation but more importantly to strip off all the checked exceptions. This interceptor simply invokes the target method catching any checked exceptions that may be thrown and rewrapping them with the SoftenedProtocolException. Well, actually, we don't rewrap anything but the message, but you get the idea. SoftenedProtocolException extends RuntimeException which of course is an unchecked exception. The net result of this action is that application developers do not have to deal with any checked exceptions whatsoever. If they do decide to handle an exception, they only (optionally) have to deal with one: SoftenedProtocolException. Pretty nice!

So, what magic needs to occur to get Spring to do all this stuff? About all that needs to happen is to get the correct configuration file. Let's take a look at that and see how it works:

<!-- TARGET OBJECT -->
<bean id="protocolAdapter"
  class="yourco.project.comm.CarrierPigeon">
  <property name="isTired">
    <value>true</value>
  </property&gl;
  <property name="canFlapWings">
    <value>true</value>
  </property&gl;
</bean&gl;

<!-- INTERCEPTOR -->
<bean id="sensorInterceptor"
  class="yourco.project.springsupport.
  SensorInvocationInterceptor"/>

<!--WIRE EVERYTHING UP, HAND BACK TO THE USER-->
<bean id="temperatureSensorOne"
  class="org.springframework.aop.framework.
  ProxyFactoryBean">
  <property name="proxyInterfaces">
    <value>
      yourco.project.interfaces.SensorIfc
    </value>
  </property>
  <property name="target">
    <ref local="protocolAdapter"/>
  </property>
  <property name="interceptorNames">
    <list>
      <value>sensorInterceptor</value>
    </list>
  </property>
</bean>

When we look at this stanza by stanza, it's a little more complicated than the previous Spring configuration files we have seen. The first stanza under the "TARGET OBJECT" comment specifies the class that is to be the target object for the invocation. Remember that the interceptor had a parameter passed into it representing the target object? This is how that happens. Spring now knows what object to pass in on that parameter. The stanza under "INTERCEPTOR" is the class that gets called before our target method. This is the place where we get to fool around with our exceptions and soften in the event of a checked exception being thrown by the target class. Spring will wire this together with our target class for us. The last stanza brings everything together. The bean that the user will request is under the bean key temperatureSensorOne. The ProxyFactoryBean will manufacture and hand back a proxy class that implements the interface yourco.project.interfaces.SensorIfc. The target object is of course the protocolAdapter bean, which is backed by an instance of yourco.project.comm.CarrierPigeon. The interceptor that will be plugged in and called whenever the user calls a method on the proxy is under the bean key sensorInterceptor and is backed up by a yourco.project.springsupport.SensorInvocationInterceptor. Notice that there is provision for any number of interceptors because the property is a list, therefore you can have any number of interceptor objects wired into your method invocation, which is a very powerful concept.

When we run this application, we can see some interesting behavior based on the values we plug in for our CarrierPigeon. If our CarrierPigeon is not tired and can flap its wings, we'll see output like this:

The sensor says the temp is: 42

So apparently the carrier pigeon was alive and well and figured out the temperature was 42. Now if the carrier pigeon is tired or can't flap its wings adjusted by changing the values in our CarrierPigeon Spring stanza, we'll get something like this:

yourco.project.exceptions.comm.SoftenedProtocolException:
  protocol error [comm failure]: I'm Tired!
  at yourco.project.springsupport.
    SensorInvocationInterceptor.invoke 
          (SensorInvocationInterceptor.java:57)
  at org.springframework.aop.framework.
     ReflectiveMethodInvocation.proceed
         (ReflectiveMethodInvocation.java:144)
  at org.springframework.aop.framework.
     JdkDynamicAopProxy.invoke
        (JdkDynamicAopProxy.java:174)
  at $Proxy0.getSensorValue(Unknown Source)
  at yourco.project.main.Main.main(Main.java:32)

In this scenario, the carrier pigeon was either tired or couldn't flap its wings, therefore we got a SoftProtocolException with a message that describes what happened: "I'm tired!" Pretty cool!

I'm hoping the power and versatility of Spring is starting to sink in. The Spring Framework has a tendency to creep in and become a veritable "Swiss Army Knife" in your programming toolkit. Spring delivers big on that fabled promise of letting you concentrate on the nuts and bolts of your application, that being the business logic that makes it go. Spring liberates you from some of the trickier aspects of the J2EE landscape. The power of Spring lies in its ability to make most things look like very simple Java objects regardless of their nature or origin. Since Spring shoulders the burden of creating, wiring, and configuration internally, the developer is left only to deal with the mechanics of using that object, not constructing it. Spring is akin to getting a precooked meal at the grocery store. All you have to do is decide what you want for dinner, take it home, heat, and eat!

General Observations and Comments

Spring is a wonderfully robust yet lightweight framework that very nicely complements the J2EE/EJB environment. One of the truly great aspects of Spring is that it is not an all-or-nothing proposition. You can begin the use of Spring in a very simple fashion as I did, just wiring up common associations and as an implementation of the Locator Pattern. After a while, you'll discover its compelling capabilities and you'll soon be asking more and more of it.

One thing that I find amazing about Spring is the flexibility it offers in terms of testing. The growing emphasis on unit testing via JUnit encourages more testing, not less. The use of J2EE containers has complicated testing to the point where it is often unwieldy. The reason for this difficulty stems from the coupling that develops between our business logic and the container framework services. Spring helps liberate business objects from the container by allowing implementations to be switched around dynamically via Spring's configuration mechanism. As we've seen, it is relatively trivial to swap live EJBs for their delegates or for a stubbed business service in the event we're only interested in testing a Web component. With JDBC or Hibernate data access objects, we test these components with everyday plain JDBC, non-XA data source connections and swap these out for robust JTA, JNDI-based counterparts seamlessly. The bottom line is this: If code is easier to test and therefore more tested, the quality inevitably increases.

Some Conclusions and Final Thoughts

I've given a bit of a whirlwind tour of IoC in general and Spring in particular. There is a wide world of Spring capabilities in the Spring framework, many of which I've only hinted at in this article. From basic dependency injection to sophisticated AOP manipulation, Spring offers tremendous capability, which is one of its main strengths. The idea that you can use as little or as much IoC capability as your problem dictates is a compelling concept and, in my opinion, a welcome guest in the often complicated universe of J2EE programming. Now that you've read this article, my sincerest hope is that you've found it helpful and applicable to your work. As always feedback is welcome and encouraged!

Resources

  • Spring - the official Spring Web site has good documentation!

Peter Braswell is Chief Scientist at ALTERThought. Peter possesses over fifteen years of software engineering expertise. He has led the implementation of leading edge, mission critical systems and applications in a variety of industries including high technology, finance, retail, and telecommunications.