Skip to content
shingjay edited this page Apr 9, 2013 · 1 revision

TransactionMonitorTemplate

The TransactionMonitorTemplate is the opposite of the cancer pattern mentioned elsewhere. The problem with the cancer pattern is that mistakes are too easy. Using the template pattern may look a littler weirder due to the use of the Anonymous class, but it is checked by the compiler, you really can't screw it up.

The limitation here is that you cannot throw checked Exceptions from inside the monitor. The upside is that any RuntimeException you throw will be caught, marked on the monitor as the failedDueTo reason, and rethrown. You never have to worry about remembering to do that sort of work.

Example 1

Simplest possible, uses the singleton template instance, and just a monitor name.

TransactionMonitorTemplate.INSTANCE.doInMonitor("MonitorName",
                new TransactionMonitorCallback() {
                    public Object doInMonitor(TransactionMonitor monitor) {
                        //Hi Everything I do in here is monitored
                        return null;
                    }
                }
        );

Example 2

A little different this one includes the class when creating the TransactionMonitor. Note that whatever you return from within the callback is returned from doInMonitor.

String message = (String) TransactionMonitorTemplate.INSTANCE.doInMonitor(this.getClass(), "MonitorName",
                new TransactionMonitorCallback() {
                    public Object doInMonitor(TransactionMonitor monitor) {
                        //Hi Everything I do in here is monitored
                        return "Woohoo";
                    }
                }
        );

Example 3

There is no harm in creating a template every time, the Singleton is merely a shortcut, so again a simple example with just the monitor name.

TransactionMonitorTemplate template = new TransactionMonitorTemplate();
        template.doInMonitor("monitorName", new TransactionMonitorCallback() {
            public Object doInMonitor(TransactionMonitor monitor) {
                return null;
            }
        });

Example 4

And again, this example creates a new template, but includes the class name when creating the TransactionMonitor.

TransactionMonitorTemplate template = new TransactionMonitorTemplate();
 
        String message = (String) template.doInMonitor(this.getClass(), "monitorName", new TransactionMonitorCallback() {
            public Object doInMonitor(TransactionMonitor monitor) {
                return "Hi there";
            }
        });

How it works

Ok, now what happens in the template? What does the template do in this picture? Well, let's have a look at the main snippet of code that answers that question.

try {
    monitor.succeeded();
    return callback.doInMonitor(monitor);
} catch (RuntimeException e) {
    monitor.failedDueTo(e);
    throw e;
} catch (Error e) {
    monitor.failedDueTo(e);
    throw e;
} finally {
    monitor.done();
}

The TransactionMonitorCallback you pass into the Template is always executed in this block of code. The reason I stress always, is that if you're manually putting in try/catch/finally blocks all over your code, you're guaranteed to screw it up eventually. You'll notice here one of the differences with using the Template, as opposed to using the TransactionMonitor directly is that the template declares the monitor a success by default. This is because the TransactionMonitor declares itself as failed by default. So unless you remember to call monitor.succeeded() every time, you'll end up declaring false failures. The template cannot safely assume the monitor has succeeded after allowing the callback to execute because the monitor was failed going in, therefore the logic here is positive-first.

Summary

The TransactionMonitorTemplate is there to make things more concrete and a little simpler. You don't have to use it, but you can if you choose to.

TransactionMonitorInterceptor

Another new feature in the monitoring api is the TransactionMonitorInterceptor. The TMI is built in the style of Springs existing TransactionInterceptor. It is designed to be flexible enough to use autoproxy with annotations, or declared proxies with or without annotations. Let's see some examples…

The @Monitored annotation

The @Monitored annotation can be declared at the type (class or interface) level, or it can be declared at the method level.

Type level declaration

@Monitored
public interface ExampleService {
 
    void makeMillions();
 
    void sellStuff();
 
    void crushTheCompetition();
}

Here, every method of every implementation of the ExampleService interface will be intercepted, and subsequently monitored. The monitor would be created using the impl class and method name as the class and monitor name.

Method level declaration

public interface ExampleService {
 
    void makeMillions();
 
    void sellStuff();
 
    @Monitored
    void crushTheCompetition();
}

Here, only the crushTheCompetition method will be monitored. Again, the monitor-name will be the method name; "crushTheCompetition".

@Monitored annotation arguments

public interface ExampleService {
 
    @Monitored("MAKE_MILLIONS")
    void makeMillions();
 
    @Monitored(value = "SELL_STUFF", includeResult = true, includeArguments = true)
    Long spreadMarketingPropaganda(String message);
 
}

The default 'value' for the @Monitored annotation is it's monitor name. Above, the makeMillions method will be monitored but it's monitor name will be "MAKE_MILLIONS". The larger example is the spreadMarketingPropaganda method. Here, the monitor name is going to be "SELL_STUFF" as before, but also the monitor will have two attributes set. An attribute called "result" that holds whatever value the method invocation returned, and an attribute called "arguments" that holds the method argument values as an Object array.

Annotation based AutoProxy

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <!-- #1 -->
    <bean id="autoproxy" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
 
    <!-- #2 -->
    <bean id="monitoredAttributeSourceAdvisor" class="com.orbitz.monitoring.lib.interceptor.MonitoredAttributeSourceAdvisor">
        <constructor-arg index="0" ref="monitoredAttributeSource"/>
    </bean>
 
    <!-- #3 -->
    <bean id="monitoredAttributeSource" class="com.orbitz.monitoring.lib.interceptor.annotation.AnnotationMonitoredAttributeSource"/>
 
    <!-- Declare your annotated services at will-->
 
</beans>

The sample xml above configures spring to automatically generate interceptor proxies for all beans that have the @Monitored annotation.

Here is an explanation of each of the beans…

  1. Enable auto-proxying of beans in the application context.
  2. The DefaultAutoProxyCreator looks for Advisors to consult when deciding to proxy something, here we are declaring our MonitoredAttributeSourceAdvisor
  3. The MonitoredAttributeSourceAdvisor uses an abstraction layer to mask it's logic from Annotations and keep it jdk 1.4 compliant. The MonitoredAttributeSourceAdvisor needs an instance of MonitoredAttributeSource, here we are using the AnnotationMonitoredAttributeSource.

TransactionMonitorProxyFactoryBean with @Monitored Annotation

The TransactionMonitorProxyFactoryBean provides a mean of explicitly defining an interceptor around a specified target bean.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean name="exampleService" class="com.orbitz.monitoring.lib.interceptor.TransactionMonitorProxyFactoryBean">
        <property name="proxyInterfaces">
            <list>
                <value>com.orbitz.....ExampleService</value>
            </list>
        </property>
        <property name="target">
            <bean class="com.orbitz....ExampleServiceImpl"/>
        </property>
        <property name="monitoredAttributeSource">
            <bean class="com.orbitz.monitoring.lib.interceptor.annotation.AnnotationMonitoredAttributeSource"/>
        </property>
    </bean>
</beans>

Here we are explicitly defining an interceptor over an instance of ExampleServiceImpl. Note that an interceptor will be created, but may not act on all method invocations. It will only act on methods that are annotated with the @Monitored annotation (or all methods if the @Monitored annotation is applied at the Type level).

TransactionMonitorProxyFactoryBean without annotations

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
 
    <bean name="simpleService" class="com.orbitz.monitoring.lib.interceptor.TransactionMonitorProxyFactoryBean">
        <property name="target">
            <bean class="com.orbitz.monitoring.lib.interceptor.SimpleServiceImpl"/>
        </property>
    </bean>
 
</beans>

Well, it doesn't get much simpler than this does it? Here every method of the target bean will be monitored using it's method name as the monitor name. Simple.

TransactionMonitorProxyFactoryBean using declarative properties

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
 
    <bean name="simpleService" class="com.orbitz.monitoring.lib.interceptor.TransactionMonitorProxyFactoryBean">
        <property name="transactionMonitorAttributes">
            <props>
                <prop key="sayHello">MONITOR_NAME=HelloMonitor</prop>
                <prop key="sayGoodbye">MONITOR_NAME=GoodbyeMonitor, INCLUDE_RESULT, INCLUDE_ARGUMENTS</prop>
            </props>
        </property>
        <property name="target" ref="simpleServiceImpl"/>
    </bean>
 
    <bean id="simpleServiceImpl" class="com.orbitz.monitoring.lib.interceptor.SimpleServiceImpl"/>
 
</beans>

Look closely and you'll see that this is exactly like the previous example, except for the transactionMonitorAttributes property. Here a Properties object is being constructed using the method name as the key, and a funky syntax to define the MonitoredAttribute. We are defining a bean called simpleService that is an interception of the simpleServiceImpl bean. Note that we are ONLY going to monitor the sayHello, and sayGoodbye methods. The monitor for the sayGoodBye method will be named "GoodbyeMonitor", and it will have the "result" and "arguments" attribute set on it.

Summary

These are just some of the options available that I have time to type up. For more information take a look in the ./test/src/java/com/orbitz/monitoring/lib/interceptor/container_tests package. There are several Spring ApplicationContext examples in there.