bschandramohan
bschandramohan

Reputation: 2006

How to specify custom JMS Message Listener in Spring Xml

I am new to JMS and had a requirement to batch requests for a listener to handle. Link: http://sleeplessinslc.blogspot.in/2010/04/batchmessagelistenercontainer-using.html gives a good solution. I am stuck in implementing the same. Instead of using the default container, I overwrote the container-class to use this new class:

<jms:listener-container container-class="org.bsnyder.spring.jms.listener.BatchMessageListenerContainer"
       acknowledge="transacted">
    <jms:listener destination="CHANDRA.BATCHTEST" ref="messageListener" />
</jms:listener-container>

<bean id="messageListener" class="org.bsnyder.spring.jms.listener.BatchMessageListener" />

Here the BatchMessageListener is extending SessionAwareBatchMessageListener as mentioned in the blog.

Error I get is:

[ERROR] PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'messageListener' threw exception; 
nested exception is java.lang.IllegalArgumentException: Message listener needs to be of type [org.bsnyder.spring.jms.listener.SessionAwareBatchMessageListener]: 
Failed properties: Property 'messageListener' threw exception; nested exception is java.lang.IllegalArgumentException: Message listener needs to be of type [org.bsnyder.spring.jms.listener.SessionAwareBatchMessageListener]

Should I change the container-type (which is "default" by default)? http://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/jms.html doesn't contain details on how to set this.

Upvotes: 3

Views: 3329

Answers (2)

Jey Santhosh
Jey Santhosh

Reputation: 11

Why do you need to read messages in batch from a queue? In such a case I strongly recommend you to use spring batch instead of writing a custom batch listener. Spring batch is easily configurable.

Upvotes: 1

MattSenter
MattSenter

Reputation: 3120

The problem is that the jms namespace parser is creating an instance of org.springframework.jms.listener.adapter.MessageListenerAdapter with a delegate property that references your org.bsnyder.spring.jms.listener.BatchMessageListener instance. Therefore, in your checkMessageListener() method of BatchMessageListenerContainer, you need to change this:

protected void checkMessageListener(Object messageListener) {
    if (!(messageListener instanceof SessionAwareBatchMessageListener)) {
        throw new IllegalArgumentException("Message listener needs to be of type ["
                + SessionAwareBatchMessageListener.class.getName() + "]");
    }
}

...to this:

protected void checkMessageListener(Object messageListener) {
    if (!(messageListener instanceof MessageListenerAdapter)) {
        throw new IllegalArgumentException("Message listener needs to be of type ["
                + MessageListenerAdapter.class.getName() + "]");
    }
}

Although, this really doesn't buy you much in terms of validation of your loaded classes. It's also unfortunate that the getDelegate() method of MessageListenerAdapter is protected; otherwise, you could do your check against the delegate's type, which should be an instance of your custom SessionAwareBatchMessageListener. I suppose you could use reflection. Alternatively, you could avoid using the jms namespace and create your own custom BatchMessageListenerAdapter that implements SessionAwareBatchMesageListener and extends MessageListenerAdapter and use that custom adapter instead of the jms namespace's default implementation. (There is no way to override this default using the namespace btw.) Ultimately, it boils down to if you REALLY need that load-time validation or not.

If you wanted to skip validation, just do this instead:

protected void checkMessageListener(Object messageListener) {
    // Do nothing...
}

That will allow your app to load without issue.

Upvotes: 1

Related Questions