Injecting Spring AmqpTemplate into HibernateEventListener Instance
Asked by Jeffrey Doto:

Hi Folks,

I'm trying to build an EventListener that sends messages to a Rabbit Message Queue. I want to use Spring AMQP to wire up and send messages during build events.

I was able to to successfully wire up a very simple build listener using the following configuration:

```
<atlassian-plugin key="${project.groupId}.${project.artifactId}" name="${project.name}" plugins-version="2">    <plugin-info>        <description>${project.description}</description>        <version>${project.version}</version>        <vendor name="${project.organization.name}" url="${project.organization.url}" />    </plugin-info>    <bambooEventListener key="buildStateListener.event" name="Build State Listener"                         class="com.jeffdoto.sherlock.bamboo.BuildStateListener">        <description>Listens for events and adds them to a message queue if enabled.</description>    </bambooEventListener></atlassian-plugin>
```

I cannot, however, seem to inject my own custom beans into my listener. I get a working listener that receives events, but I get null pointer exceptions because my custom beans are not getting injected.

Here's my implementation of the HibernateEventListener interface.

```
public class BuildStateListener implements HibernateEventListener {    Logger logger = LoggerFactory.getLogger(BuildStateListener.class);    private AmqpTemplate amqpTemplate;    private String completedTopicAddress;        @Override    public void handleEvent(Event event) {        if (event instanceof BuildCompletedEvent){            String planKey = ((BuildCompletedEvent) event).getBuildPlanKey();            logger.info(String.format("BuildCompletedEvent received.  Publishing message to %s", completedTopicAddress));            amqpTemplate.convertAndSend(completedTopicAddress, planKey);        }    }    @Override    public Class[] getHandledEventClasses() {        return new Class[]{                BuildCompletedEvent.class        };    }    @Required    public void setAmqpTemplate(AmqpTemplate amqpTemplate) {        this.amqpTemplate = amqpTemplate;    }    @Required    public void setCompletedTopicAddress(String completedTopicAddress) {        this.completedTopicAddress = completedTopicAddress;    }}
```

Now, my question is, is it possible for me to inject the AmqpTemplate and topic address? If so, what is the correct way to do so? I've tried using OSGi Felix instructions in my pom.xml to no avail (I had a bunch of issues with BundleExceptions and constraint errors), and I've tried using a custom Spring Dynamic Modules config file:

```
<?xml version="1.0" encoding="UTF-8"?><beans:beans xmlns:beans="http://www.springframework.org/schema/beans"             xmlns:osgi="http://www.springframework.org/schema/osgi"             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"             xmlns:rabbit="http://www.springframework.org/schema/rabbit"             xsi:schemaLocation="    http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd    http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd"             default-autowire="autodetect">    <rabbit:connection-factory id="connectionFactory"/>    <rabbit:amqpTemplate id="amqpTemplate" exchange="bamboo-build-events" connection-factory="connectionFactory"/>    <rabbit:admin connection-factory="connectionFactory"/>    <rabbit:queue name="default"/>    <bean id="buildStateListener" class="com.jeffdoto.sherlock.bamboo.BuildStateListener">        <property name="amqpTemplate" ref="amqpTemplate"/>        <property name="completedTopicAddress" ref="bamboo.build.completed"/>    </bean>    <osgi:service id="buildStateListenerOsgi" ref="buildStateListener"                  interface=" com.atlassian.bamboo.event.HibernateEventListener"/></beans:beans>
```

But i'm not making any progress here. Any suggestions? This has to be doable.

I do see that there are separate Notification plugin modules available, but there is no direct support for anything other than email and IM, hence my desire to wire up my own AMQP stuff here. This also seems simpler, just having the logic right in the event listener.

Thanks for your help!

Jeff


Answers:
author: Jeffrey Doto
body: **Update**: I was able to solve my issue with a little help from the guys in the Atlassian IRC chatroom (thanks Swoon!).

There were a few things of issue in my initial configuration. The AmqpTemplate stuff I was trying to use relied on a newer version of Spring which is not included in the 3.3 Bamboo release. I stripped out the AmqpTemplate, and replaced it with RabbitMQ specific classes, not dependent on Spring.

Also, it is very important to make sure that your Spring is in the correct place on your classpath, specifically META-INF/spring/spring-file.xml. Be sure to use the correct 'beans' namespace prefix for your configured beans, as well.

Here's the atlassian-plugin.xml (pretty much the same as my first cut):

```
<atlassian-plugin key="${project.groupId}.${project.artifactId}" name="${project.name}" plugins-version="2">    <plugin-info>        <description>${project.description}</description>        <version>${project.version}</version>        <vendor name="${project.organization.name}" url="${project.organization.url}"/>    </plugin-info>    <bambooEventListener key="buildStateListener.event" name="Build State Listener"                         class="com.jeffdoto.sherlock.bamboo.BuildStateListener">        <description>Listens for BuildComplete events and sends a message to a configured destination.</description>    </bambooEventListener></atlassian-plugin>
```

And here is the resulting spring-config.xml, located at src/main/resources/META-INF/spring/spring-context.xml in my project. Note the addition of the 'beans' prefix to each bean.

```
<?xml version="1.0" encoding="UTF-8"?><beans:beans xmlns:beans="http://www.springframework.org/schema/beans"             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"             xsi:schemaLocation="http://www.springframework.org/schema/beans    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"             default-autowire="autodetect">    <beans:bean id="messager" class="com.jeffdoto.sherlock.bamboo.messaging.RabbitMessager">        <beans:property name="messagerConfig" ref="messagerConfig"/>    </beans:bean>        <beans:bean id="connectionFactory" class="com.rabbitmq.client.ConnectionFactory">        <beans:property name="host" value="localhost"/>        <beans:property name="port" value="5672"/>    </beans:bean>        <beans:bean id="messagerConfig" class="com.jeffdoto.sherlock.bamboo.messaging.MessagerConfig">        <beans:property name="connectionFactory" ref="connectionFactory"/>        <beans:property name="exchangeName" value="bamboo-build-events"/>        <beans:property name="completedTopicName" value="bamboo.build.completed"/>    </beans:bean></beans:beans>
```

And the final HibernateEventListener instance, which depends on a simple Messager class I wrapped around the RabbitMQ specific classes:

```
package com.jeffdoto.sherlock.bamboo;import com.atlassian.bamboo.event.BuildCompletedEvent;import com.atlassian.bamboo.event.HibernateEventListener;import com.atlassian.event.Event;import com.jeffdoto.sherlock.bamboo.messaging.Messager;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Required;/** * Listens for Bamboo Build Lifecycle events. * * Sends a message to a configured messaging entity announcing the received event Plan Information, Build Number, * and State. * User: jdoto200 * Date: 1/29/12 * Time: 10:55 PM */public class BuildStateListener implements HibernateEventListener {    Logger logger = LoggerFactory.getLogger(BuildStateListener.class);    private Messager messager;    @Override    public void handleEvent(Event event) {        if (event instanceof BuildCompletedEvent) {            logger.info(String.format("BuildCompletedEvent received.  Publishing message to %s", messager.getName()));            messager.sendMessage(                    String.format(((BuildCompletedEvent) event).getBuildPlanKey()                            + " Build #" + ((BuildCompletedEvent) event).getBuildNumber()                            + " completed with a result of " + ((BuildCompletedEvent) event).getBuildState()).toUpperCase());        }    }    @Override    public Class[] getHandledEventClasses() {        return new Class[]{                BuildCompletedEvent.class        };    }    @Required    public void setMessager(Messager messager) {        this.messager = messager;    }}
```

Check out <https://github.com/jbdoto/sherlock-bamboo> to see the full project.


author: Przemek Bruski
body: Does your custom spring-components.xml work when you use it in a standalone application on Spring 2.0 ?

comments: author: Jeffrey Doto
body: After a little trial here, no...turns out the Rabbit AMQP stuff depends on Spring Core 3.0.x.

Can I get around this by using some clever OSGi tricks?

Thanks for your help.


author: Przemek Bruski
body: As far as I understand, you need a simple "put" operation on that queue, nothing fancy. I think your best bet is to write it in plain Java using Rabbit's classes, without Spring - it shouldn't take much more than 20-50 lines.


author: Jeffrey Doto
body: Actually...I'm not totally tied to the Spring AMQP thing. I just got the standalone to work trashing the Spring AMQP stuff and using the Rabbit-provided libraries.

I'll be all set if I can perform injection on the BuildStateListener class, if possible.


Tags: app

hibernate

injection

osgi

spring