简体   繁体   中英

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM