简体   繁体   中英

Jms queue redelivery issues with Spring boot, Atomikos and ActiveMQ

I'm trying to setup a jms queue and have redelivery behaviour in case the transaction fails. What is happenning is that a message is redelivered (and processed) multiple times because after the message processing code, the session is closed twice. The second close attempt throws an error because it is already closed, so transaction is rollbacked and message redelivered.

Here is my configuration:

@Configuration
public class MyJtaConfiguration {

private static final Logger LOGGER = Logger.getLogger(MyJtaConfiguration.class);

@Bean(name = "atomikosUserTransaction")
public UserTransaction atomikosUserTransaction() throws Throwable {
    UserTransactionImp userTransactionImp = new UserTransactionImp();
    userTransactionImp.setTransactionTimeout(10000);

    AtomikosJtaPlatform.transaction = userTransactionImp;

    return userTransactionImp;
}

@Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
public TransactionManager atomikosTransactionManager() throws Throwable {
    UserTransactionManager userTransactionManager = new UserTransactionManager();
    userTransactionManager.setForceShutdown(false);

    AtomikosJtaPlatform.transactionManager = userTransactionManager;

    return userTransactionManager;
}

@Bean(name = "transactionManager")
@DependsOn({ "atomikosUserTransaction", "atomikosTransactionManager" })
public PlatformTransactionManager transactionManager() throws Throwable {
    UserTransaction userTransaction = atomikosUserTransaction();
    TransactionManager atomikosTransactionManager = atomikosTransactionManager();
    return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
}

@Bean
public QueueConnectionFactory connectionFactory() {     
    Map<String, Object> parameters = new HashMap<>();
    parameters.put("dataDirectory", "activeMqDataDirectory");
    String brokerUrl = Interpolator.getString("vm://MyBroker?broker.persistent=true&broker.dataDirectory=${dataDirectory}&broker.useJmx=true", parameters );


    ActiveMQXAConnectionFactory activeMQXAConnectionFactory = new ActiveMQXAConnectionFactory(brokerUrl);
    activeMQXAConnectionFactory.setTrustedPackages(
            new ArrayList<>(Arrays.asList("java.lang,javax.security,java.util,org.apache.activemq,org.fusesource.hawtbuf,com.thoughtworks.xstream.mapper,com.brabo".split(","))));

    RedeliveryPolicy redeliveryPolicy = activeMQXAConnectionFactory.getRedeliveryPolicy();
    redeliveryPolicy.setInitialRedeliveryDelay(10 * 1000);
    redeliveryPolicy.setMaximumRedeliveryDelay(10 * 1000);
    redeliveryPolicy.setMaximumRedeliveries(2);

    AtomikosConnectionFactoryBean atomikosConnectionFactoryBean = new AtomikosConnectionFactoryBean();
    atomikosConnectionFactoryBean.setUniqueResourceName("xamq");
    atomikosConnectionFactoryBean.setLocalTransactionMode(false);
    atomikosConnectionFactoryBean.setXaConnectionFactory(activeMQXAConnectionFactory);

    try {
        atomikosConnectionFactoryBean.init();
    } catch (JMSException e) {
        throw new RuntimeException(e);
    }
    CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
    cachingConnectionFactory.setTargetConnectionFactory(atomikosConnectionFactoryBean);
    cachingConnectionFactory.setSessionCacheSize(50);
    cachingConnectionFactory.setExceptionListener(new ExceptionListener() {

        @Override
        public void onException(JMSException exception) {
            LOGGER.error(exception);
        }
    });
    return cachingConnectionFactory;
}


@Bean(name = "myTestQueue")
public Queue backgroundTaskQueue() {
    ActiveMQQueue queue = new ActiveMQQueue("myTestQueue");
    return queue;
}


@Bean
public DefaultMessageListenerContainer backgroundTaskQueueListenerContainer(@Autowired ConnectionFactory connectionFactory,
        @Autowired @Qualifier("myServiceProcessorBean") MessageListener messageListener, @Autowired PlatformTransactionManager txManager) {
    return createListenerContainer(connectionFactory, messageListener, txManager, QueueManager.BACKGROUND_TASK_QUEUE_JNDI);
}

private DefaultMessageListenerContainer createListenerContainer(ConnectionFactory connectionFactory, MessageListener messageListener, PlatformTransactionManager txManager,
        String destinationName) {       
    DefaultMessageListenerContainer listenerContainer = new DefaultMessageListenerContainer();
    listenerContainer.setConnectionFactory(connectionFactory);
    listenerContainer.setDestinationName(destinationName);
    listenerContainer.setMessageListener(messageListener);
    listenerContainer.setTransactionManager(txManager);
    listenerContainer.setSessionTransacted(true); 

    listenerContainer.setConcurrentConsumers(1);
    listenerContainer.setReceiveTimeout(3000);
    return listenerContainer;
}

}

And

@Configuration
@DependsOn("transactionManager")
@EnableJpaRepositories(basePackages = {"com.myapp.common"}, 
    entityManagerFactoryRef = "myEntityManager", transactionManagerRef = "transactionManager")
@ConfigurationProperties("myapp.ds")
@Validated
public class MyDataSourceConfiguration {

@Bean
public DataSource dataSource() throws SQLException {
    OracleXADataSource mysqlXaDataSource = new OracleXADataSource();
    mysqlXaDataSource.setURL(url);
    mysqlXaDataSource.setPassword(password);
    mysqlXaDataSource.setUser(username);

    AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
    xaDataSource.setXaDataSource(mysqlXaDataSource);
    xaDataSource.setUniqueResourceName("xads");
    xaDataSource.setPoolSize(10);
    xaDataSource.setMaxPoolSize(70);

    xaDataSource.init();
    return xaDataSource;
}

@Bean(name = "myEntityManager")
@DependsOn("transactionManager")
public LocalContainerEntityManagerFactoryBean customerEntityManager() throws Throwable {

    HashMap<String, Object> properties = new HashMap<String, Object>();     
    properties.put("javax.persistence.transactionType",     "JTA");
    properties.put("hibernate.archive.autodetection",       "hbm");
    properties.put("hibernate.dialect",                     "org.hibernate.dialect.Oracle10gDialect");
    properties.put("hibernate.transaction.jta.platform",    AtomikosJtaPlatform.class.getName());
    properties.put("hibernate.generate_statistics",         "false");
    properties.put("hibernate.jdbc_fetch_size",             "2");
    properties.put("hibernate.jdbc.batch_size",             "20");
    properties.put("hibernate.show_sql",                    "false");
    properties.put("hibernate.format_sql",                  "false");
    properties.put("hibernate.hbm2ddl.auto",                "none");
    properties.put("hibernate.id.new_generator_mappings",   "false"); 

    LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
    entityManager.setJtaDataSource(dataSource());
    entityManager.setPackagesToScan("com.myapp.common");
    entityManager.setPersistenceUnitName("myPersistenceUnit");
    entityManager.setJpaPropertyMap(properties);
    entityManager.setPersistenceProvider(new HibernatePersistenceProvider());
    return entityManager;
}

}

What could be wrong? The error mentioned above is: ExceptionMapperStandardImpl : HHH000346: Error during managed flush [Session/EntityManager is closed]

I would try to make sure that the datasource + connectionFactory are init-ed before the transaction manager and closed afterwards. That would already be an improvement by itself, regardless of whether it solves the issue or not.

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