1
votes

For some context I am doing a JMS reader and writer in Spring batch using a jsr compliant implementation. I am using the JMSReader and JMS writer classes provided by Spring batch but I am wrapping them in my own reader and writer. I do not have the typical application context available since I am using the JSR approach. The classes are initialized via the job spec or via the batch.xml as per the jsr spec.

The issue I have run into is I have a stand alone batch application that when I define a JMS reader and/or jms writer that creates a connection factory for active mq and sets that as the target factory for spring's JMSTemplate class, the application when done processing does not shut down properly. The alternative using a connection factory for IBM MQ works fine.

Let me provide some code that I have done.

This is where i create my connection factory. There are a couple commented lines of changes I made trying to get the threads that are left alive to die when the app does.

private ConnectionFactory openAMQ() throws IllegalArgumentException{
    ActiveMQConnectionFactory targetConnectionFactory = new ActiveMQConnectionFactory();

    if(protocol == null){
        throw new IllegalArgumentException("Active MQ protocol can not be empty");
    }

    AMQProtocols proto = AMQProtocols.valueOf(protocol.toUpperCase());

    StringBuilder sb = new StringBuilder();

    sb.append(proto.getProtocol()).append("://").append(this.host).append(":").append(this.port);

    if(amqParams != null && amqParams.trim().length() > 0){
        sb.append("?").append(amqParams);
    }

    targetConnectionFactory.setBrokerURL(sb.toString());
    //targetConnectionFactory.setAlwaysSessionAsync(false);
    //targetConnectionFactory.setUseAsyncSend(false);
    return targetConnectionFactory;
}

Here is where I create the JMSTemplate object

protected JmsTemplate getJMSTemplate(ConnectionFactory targetConnectionFactory){
    CachingConnectionFactory ccf = new CachingConnectionFactory();

    ccf.setTargetConnectionFactory(targetConnectionFactory);
    JmsTemplate template = new JmsTemplate(ccf);

    template.setDefaultDestinationName(jmsDefaultDestinationName);

    template.setReceiveTimeout(Long.parseLong(jmsReceiveTimeoutValue));
    template.setSessionTransacted(Boolean.parseBoolean(jmsSessionTransacted));
    //template.setMessageConverter(messageConverter);


    return template;
}

And lastly here is the open method of the JMSReader, the writer is nearly identical

public void open(Serializable checkpoint) throws Exception {

    //First we need to get our broker specific connection factory
    ConnectionFactory targetConnectionFactory = getTargetConnectionFactory();

    this.template = getJMSTemplate(targetConnectionFactory);

    reader = new JmsItemReader();
    reader.setItemType(Class.forName(jmsItemTypeFullyQualifiedName));
    reader.setJmsTemplate(template);

}

The threads that I am seeing alive seem to be related to the connection as well as some inactivity monitoring threads. With these staying alive like they are, it prevents a standalone application from shutting down.

Does anyone have any idea how i can configure the connection factory or jms template to stop this from happening, or possibly manage it once the reader is complete to get it to properly shut down.

1

1 Answers

0
votes

I think I answered my own question. On the CachingConnectionFactory I added a call to the destroy method in the close method of both the jms read and writer. I do have some concern that calling that does not allow proper clean up, but it is allowing the app to shut down rather than hang.