I´m using:
SpringBoot 2.0.4
ActiveMQ 5.15.5
Apache Camel 2.22.0
Java 1.8
Groovy
Maven
Basically, I have a SpringBoot application with an Apache Camel route which consumes messages from ActiveMQ with transactions. I need to set a RedeliveryPolicy on ActiveMQ, so when an error in processing happens, the message is retried a number of times.
I have made a configuration class with beans for ActiveMQ, the transactions work as intended but the RedeliveryPolicy does not work. Can anybody please help me understand what's wrong with this?
Here's the log output for a message that produces an error:
2018-10-23 10:35:28.005 DEBUG 10524 --- [mer[entryQueue]] o.a.c.s.spi.TransactionErrorHandler : Transaction begin (0x35d60381) redelivered(false) for (MessageId: ID:EPIC-LAP-25-50304-1540306817804-4:3:1:1:2 on ExchangeId: ID-EPIC-LAP-25-1540312510586-0-1))
2018-10-23 10:35:28.020 DEBUG 10524 --- [mer[entryQueue]] o.apache.camel.processor.SendProcessor : >>>> direct://middle Exchange[ID-EPIC-LAP-25-1540312510586-0-1]
2018-10-23 10:35:28.375 DEBUG 10524 --- [mer[entryQueue]] o.a.camel.processor.DefaultErrorHandler : Failed delivery for (MessageId: ID:EPIC-LAP-25-50304-1540306817804-4:3:1:1:2 on ExchangeId: ID-EPIC-LAP-25-1540312510586-0-1). On delivery attempt: 0 caught: java.lang.RuntimeException: ExceptionTest: Order Failed
2018-10-23 10:35:28.390 ERROR 10524 --- [mer[entryQueue]] o.a.camel.processor.DefaultErrorHandler : Failed delivery for (MessageId: ID:EPIC-LAP-25-50304-1540306817804-4:3:1:1:2 on ExchangeId: ID-EPIC-LAP-25-1540312510586-0-1). Exhausted after delivery attempt: 1 caught: java.lang.RuntimeException: ExceptionTest: Order Failed
Here's my config class for ActiveMQ:
import org.apache.activemq.ActiveMQConnectionFactory
import org.apache.activemq.RedeliveryPolicy
import org.apache.activemq.camel.component.ActiveMQComponent
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.jms.connection.JmsTransactionManager
import javax.jms.DeliveryMode
#Configuration
class ActiveMQConfiguration {
#Bean
ActiveMQConnectionFactory activeMQConnectionFactory() {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory()
activeMQConnectionFactory.brokerURL = 'tcp://localhost:61616'
activeMQConnectionFactory.userName = 'admin'
activeMQConnectionFactory.password = 'admin'
RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy()
redeliveryPolicy.maximumRedeliveries = 3
redeliveryPolicy.redeliveryDelay = 150L
redeliveryPolicy.useExponentialBackOff = true
redeliveryPolicy.backOffMultiplier = 1.5
activeMQConnectionFactory.setRedeliveryPolicy(redeliveryPolicy)
activeMQConnectionFactory
}
#Bean
ActiveMQComponent activeMQComponent(#Qualifier('activeMQConnectionFactory')ActiveMQConnectionFactory activeMQConnectionFactory) {
ActiveMQComponent activeMQComponent = new ActiveMQComponent()
activeMQComponent.connectionFactory = activeMQConnectionFactory
activeMQComponent.transacted = true
activeMQComponent.transactionManager = txManager()
activeMQComponent.cacheLevelName = 'CACHE_CONSUMER'
activeMQComponent.lazyCreateTransactionManager = false
activeMQComponent.deliveryMode = DeliveryMode.PERSISTENT
activeMQComponent
}
#Bean
JmsTransactionManager txManager(#Qualifier('activeMQConnectionFactory') ActiveMQConnectionFactory activeMQConnectionFactory) {
JmsTransactionManager txManager = new JmsTransactionManager()
txManager.connectionFactory = activeMQConnectionFactory
txManager.rollbackOnCommitFailure = true
txManager
}
}
There are two issues here
1. You have two transaction managers
Due to the following two lines in your configuration of the Camel ActiveMQ component, you configure two transaction managers. That is a source of problems.
activeMQComponent.transacted = true // activates local JMS transactions
activeMQComponent.transactionManager = txManager() // additional tx manager
if you just want to consume transactional from ActiveMQ, you don't need to configure a Spring transaction manager.
These two lines of your config are enough to get local transactions with your ActiveMQ broker.
activeMQComponent.transacted = true
activeMQComponent.lazyCreateTransactionManager = false
So you should remove this line as well as the whole txManager bean
activeMQComponent.transactionManager = txManager()
If you currently set the transacted flag in your Camel routes, you have to remove this too. And as I wrote, your routes consuming from ActiveMQ are still transacted even if you remove all this.
2. Redelivery not working
You have not published your Camel routes, but according to the error output, I assume that the broker does not redeliver because the error is handled by Camel.
It is the Camel error handler o.a.camel.processor.DefaultErrorHandler that kicks in when the error occurs and because it handles the error, the message is commited against the broker and therefore no redelivery takes place.
Try to disable Camel error handling to see if the broker redelivers messages on errors.
errorHandler(noErrorHandler());
Not very long ago I had problems with dlq queues - not all parameters set in the code worked. I had to add settings to acitvemq configs. Yes, it is not a good decision to divide configs but I didn't find another.
Below is my config class for jms and an example queue configuration via activemq.xml:
#Configuration
#EnableJms
public class JmsConfig {
private Environment env;
#Autowired
public void setEnv(Environment env) {
this.env = env;
}
#Bean(name = "activemq")
public ActiveMQComponent activemq(#Qualifier("activemqTransactionManager") JmsTransactionManager jmsTransactionManager,
#Qualifier("activemqConnectionFactory") ConnectionFactory connectionFactory) {
ActiveMQComponent activeMQComponent = new ActiveMQComponent();
activeMQComponent.setTransactionManager(jmsTransactionManager);
activeMQComponent.setConnectionFactory(connectionFactory);
return activeMQComponent;
}
#Bean(name = "activemqJmsTemplate")
public JmsTemplate jmsTemplate(#Qualifier("activemqConnectionFactory") ConnectionFactory connectionFactory) {
JmsTemplate template = new JmsTemplate();
template.setConnectionFactory(connectionFactory);
return template;
}
#Bean(name = "activemqTransactionPolicy")
public SpringTransactionPolicy activemqTransactionPolicy(
#Qualifier("activemqTransactionManager") JmsTransactionManager jmsTransactionManager) {
SpringTransactionPolicy springTransactionPolicy = new SpringTransactionPolicy(jmsTransactionManager);
springTransactionPolicy.setPropagationBehaviorName("PROPAGATION_REQUIRED");
return springTransactionPolicy;
}
#Bean(name = "activemqTransactionManager")
public JmsTransactionManager activemqTransactionManager(
#Qualifier("activemqConnectionFactory") ConnectionFactory connectionFactory) {
return new JmsTransactionManager(connectionFactory);
}
#Bean(name = "activemqConnectionFactory")
public ConnectionFactory connectionFactory(#Qualifier("activemqRedeliveryPolicy") RedeliveryPolicy redeliveryPolicy) {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
connectionFactory.setBrokerURL("tcp://" + env.getProperty("queue.url"));
connectionFactory.setTrustAllPackages(true);
RedeliveryPolicyMap map = connectionFactory.getRedeliveryPolicyMap();
map.put(new ActiveMQQueue("queueName.DLQ"), redeliveryPolicy);
return connectionFactory;
}
#Bean(name = "activemqRedeliveryPolicy")
public RedeliveryPolicy redeliveryPolicy() {
RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
redeliveryPolicy.setMaximumRedeliveries(0);
return redeliveryPolicy;
}
}
Changes in activevq.xml:
<destinationPolicy>
<policyMap>
<policyEntries>
<!--set dead letter queue for our queue. It name will be "myQueueName.DLQ"-->
<policyEntry queue="myQueueName">
<deadLetterStrategy>
<individualDeadLetterStrategy queuePrefix="" queueSuffix=".DLQ"/>
</deadLetterStrategy>
</policyEntry>
<policyEntry topic=">">
<pendingMessageLimitStrategy>
<constantPendingMessageLimitStrategy limit="1000"/>
</pendingMessageLimitStrategy>
</policyEntry>
</policyEntries>
</policyMap>
</destinationPolicy>
<plugins>
<redeliveryPlugin fallbackToDeadLetter="true" sendToDlqIfMaxRetriesExceeded="true">
<redeliveryPolicyMap>
<redeliveryPolicyMap>
<redeliveryPolicyEntries>
<!--Set the redelivery delay to one hour-->
<redeliveryPolicy queue="myQueueName.DLQ" maximumRedeliveries="-1" redeliveryDelay="3600000"/>
</redeliveryPolicyEntries>
</redeliveryPolicyMap>
</redeliveryPolicyMap>
</redeliveryPlugin>
</plugins>
Related
Error which I am actually getting:
org.springframework.jms.UncategorizedJmsException: Uncategorized exception occurred during JMS processing; nested exception is javax.jms.JMSException: Failed to create session factory; nested exception is ActiveMQNotConnectedException[errorType=NOT_CONNECTED message=AMQ219007: Cannot connect to server(s). Tried with all available servers.]
at org.springframework.jms.support.JmsUtils.convertJmsAccessException(JmsUtils.java:311)
at org.springframework.jms.support.JmsAccessor.convertJmsAccessException(JmsAccessor.java:185)
at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:507)
at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:584)
at org.springframework.jms.core.JmsTemplate.convertAndSend(JmsTemplate.java:661)
at com.cox.fst.wfm.processor.TicketProcessor.processInboundTicket(TicketProcessor.java:68)...
.
.
.
Caused by: org.springframework.jms.UncategorizedJmsException: Uncategorized exception occurred during JMS processing; nested exception is javax.jms.JMSException: Failed to create session factory; nested exception is ActiveMQNotConnectedException[errorType=NOT_CONNECTED message=AMQ219007: Cannot connect to server(s). Tried with all available servers.]
at org.springframework.jms.support.JmsUtils.convertJmsAccessException(JmsUtils.java:311)
at org.springframework.jms.support.JmsAccessor.convertJmsAccessException(JmsAccessor.java:185)
at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:507)
at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:584)
at org.springframework.jms.core.JmsTemplate.convertAndSend(JmsTemplate.java:661)
at com.cox.fst.wfm.processor.TicketProcessor.processInboundTicket(TicketProcessor.java:68)
... 108 common frames omitted
Caused by: javax.jms.JMSException: Failed to create session factory
at org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory.createConnectionInternal(ActiveMQConnectionFactory.java:882)
at org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory.createConnection(ActiveMQConnectionFactory.java:299)
at org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory.createConnection(ActiveMQConnectionFactory.java:294)
at org.springframework.jms.support.JmsAccessor.createConnection(JmsAccessor.java:196)
at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:494)
... 111 common frames omitted
Caused by: org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException: AMQ219007: Cannot connect to server(s). Tried with all available servers.
at org.apache.activemq.artemis.core.client.impl.ServerLocatorImpl.createSessionFactory(ServerLocatorImpl.java:708)
at org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory.createConnectionInternal(ActiveMQConnectionFactory.java:880)
... 115 common frames omitted
Beans in Artemis config class for sender:
#Bean
public ActiveMQConnectionFactory connectionFactory() {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory();
JmsDefaultPrefetchPolicy prefetchPolicy = new JmsDefaultPrefetchPolicy();
prefetchPolicy.setQueuePrefetch(prefetch);
return factory;
}
#Bean
public JmsTemplate jmsTemplate(final ConnectionFactory connectionFactory) {
JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory());
jmsTemplate.setMessageConverter(producerJackson2MessageConverter());
jmsTemplate.setDefaultDestinationName(queue);
return jmsTemplate;
}
#Bean
public SimpleMessageConverter producerJackson2MessageConverter() {
return new SimpleMessageConverter();
}
Beans in Artemis config class for receiver:
#Bean
public ActiveMQConnectionFactory connectionFactory() {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory();
JmsDefaultPrefetchPolicy prefetchPolicy = new JmsDefaultPrefetchPolicy();
prefetchPolicy.setQueuePrefetch(prefetch);
return factory;
}
#Bean
public DefaultJmsListenerContainerFactory amqpListnerFactory(final DefaultJmsListenerContainerFactoryConfigurer configurer, final ConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
configurer.configure(factory, connectionFactory);
factory.setSessionTransacted(false);
factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
factory.setConcurrency(concurrency);
return factory;
}
prefetch ,queue, concurrency are constants.
My application.properties has
spring.artemis.embedded.enabled=false
spring.artemis.mode=native
spring.artemis.host=amqp://(service name).(namespace)
spring.artemis.port=61616
spring.artemis.user=****
spring.artemis.password=****
In sender:
jmsTemplate.convertAndSend(destination,message);
In receiver:
#JmsListener(destination = "${****}", containerFactory = "amqpListnerFactory")
Please help me solve the issue are please guide me in connecting remote ActiveMQ Artemis broker.
This line in your application.properties is problematic:
spring.artemis.host=amqp://(service name).(namespace)
This is a URL, not a hostname. You need to specify just a hostname (or an IP address) of your remote instance of ActiveMQ Artemis here. This is noted in the Spring Boot documentation, e.g.:
spring.artemis.mode=native
spring.artemis.host=192.168.1.210
spring.artemis.port=9876
spring.artemis.user=admin
spring.artemis.password=secret
Better yet, you should move to Spring Boot 3.x and use the broker-url property instead, e.g.:
spring.artemis.mode=native
spring.artemis.broker-url=tcp://192.168.1.210:9876
spring.artemis.user=admin
spring.artemis.password=secret
See more details in the Spring documentation.
I am playing with Camel 3.13.0 but it looks like the transaction is not rolled back when I use transacted. Any idea why?
The code snippet is below:
DataSource dataSource = InOutTest.setupDataSource();
//Initiate registry, transaction manager, policy
SimpleRegistry registry = new SimpleRegistry();
DataSourceTransactionManager transactionManager =
new DataSourceTransactionManager(dataSource);
registry.bind("transactionManager", transactionManager);
SpringTransactionPolicy propagationRequired =
new SpringTransactionPolicy();
propagationRequired.setTransactionManager(
transactionManager);
propagationRequired.setPropagationBehaviorName(
"PROPAGATION_REQUIRED");
registry.bind("PROPAGATION_REQUIRED", propagationRequired);
//Create context, add sql component
SqlComponent sqlComponent = new SqlComponent();
sqlComponent.setDataSource(dataSource);
CamelContext context = new DefaultCamelContext(registry);
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
context.addComponent("jmsComponent", JmsComponent.jmsComponentAutoAcknowledge(connectionFactory));
context.getComponent("sql", SqlComponent.class).setDataSource(InOutTest.setupDataSource());
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:sqlParam")
.transacted("PROPAGATION_REQUIRED")
.to("sql:insert into articles(name, category) values ('ActiveMQ', 'activemq')")
.process((exchange -> {throw new RuntimeException("Mock Ex");}));
}
});
Apache Camel has long have had the Transaction EIP pattern supported. One way to configure this pattern, is as you have already did through your Endpoint DSL configuration using transacted.
Meanwhile, you are attaching the PlatformTransationManager to a DataSource:
DataSourceTransactionManager transactionManager =
new DataSourceTransactionManager(dataSource);
Then you are configuring your SqlComponent to use a different DataSource:
context.getComponent("sql", SqlComponent.class).setDataSource(InOutTest.setupDataSource());
You should use the same managed DataSource used to configure your PlatformTransationManager by removing the aforementioned line and you should be good to go.
I'm having a hard time setting the Client ID on my consumers in ActiveMQ Artemis.
I'm using Spring to create the ConnectionFactory and ContainerFactory and then using it to set the ClientID. For some reason it does not appear on the Artemis Console.
Here is the code defining the factory beans on my configuration class:
#Bean
public ConnectionFactory myConnectionFactory() {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(host);
connectionFactory.setUser(user);
connectionFactory.setPassword(password);
// I have tried setting it here
connectionFactory.setClientID("myClient");
connectionFactory.setGroupID("myClient");
return connectionFactory;
}
#Bean
public DefaultJmsListenerContainerFactory myContainerFactory(#Qualifier("myConnectionFactory") ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setRecoveryInterval(10000);
// I have tried setting it here
factory.setClientId("myClient");
configurer.configure(factory, connectionFactory);
return factory;
}
And here is the code where I create a bunch of listeners according to a ListerEnum:
for (ListersEnum listener : ListenersEnum.values()) {
IListener handler = BeanUtil.getBean(listener.getHandler());
DefaultJmsListenerContainerFactory containerFactory = BeanUtil.getBean<DefaultJmsListenerContainerFactory>(listener.getContainerFactory());
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
endpoint.setId(listener.getId());
endpoint.destination = listener.getQueue();
endpoint.setMessageListener(message -> handler.onReceive(message));
DefaultMessageListenerContainer messageListenerContainer = containerFactory.createListenerContainer(endpoint);
// I have tried setting it here also
messageListenerContainer.setClientId("myClient");
endpoint.setupListenerContainer(messageListenerContainer);
jmsListenerEndpointRegistry.registerListenerContainer(endpoint, containerFactory);
messageListenerContainer.start();
}
And then on the Artemis Console, there is no Client ID set:
Besides that, I have noticed the Creation Time on the console is updated each second. Like the connection is being refresh every time. Is this normal behavior? Does it have anything to do with the client ID?
My dependencias are:
springBootVersion = '1.5.15.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-artemis'
compile 'org.apache.activemq:artemis-commons:1.4.0'
compile 'org.apache.activemq:artemis-core-client:1.4.0'
compile 'org.apache.activemq:artemis-jms-client:1.4.0'
compile 'org.apache.activemq:artemis-selector:1.4.0'
Any help is appreciated
EDIT
I have changed my myContainerFactory to:
public DefaultJmsListenerContainerFactory myContainerFactory(#Qualifier("myConnectionFactory") ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setRecoveryInterval(10000);
// I have tried setting it here
factory.setClientId("myClient");
// Commented this so Spring wont set its default values
// configurer.configure(factory, connectionFactory);
factory.setConnectionFactory(connectionFactory);
return factory;
}
Then I started getting
2019-01-24 17:50:28.612 ERROR 25504 --- [enerContainer-1] o.s.j.l.DefaultMessageListenerContainer : Could not refresh JMS Connection for destination 'TEST_QUEUE' - retrying using FixedBackOff{interval=5000, currentAttempts=9, maxAttempts=unlimited}. Cause: Client id has already been set
I tried hacking it by making my ClientID like "myClient" + new Date().getTime() but got the same result. The strange part is that if I remove the clientId altogether, on the artemis console I only see one connection... So how it is trying to create another ones with the same client id? Any ideas?
Edit 2
I made an example outside of Spring to see if this would be a Spring problem... But it doesn't seem so. I got the same results: The client ID does not show on the console and if I set it on the Connection I get the Client ID already set error. Can it be something about my artemis console...? I'm using Apache ActiveMQ Artemis 2.6.2. Please halp :s
Heres the code in case anyone want to try:
import javax.jms.*;
import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
public class Consumer {
public static void main(String[] args) throws JMSException {
Connection connection = null;
Session session = null;
MessageConsumer consumer = null;
ConnectionFactory factory = null;
ActiveMQConnectionFactory artemisCf = null;
try {
artemisCf = new ActiveMQConnectionFactory("tcp://localhost:61616?retryInterval=1000reconnectAttempts=-1",
"user", "pass");
artemisCf.setRetryInterval(1000);
artemisCf.setReconnectAttempts(-1);
artemisCf.setClientID("TESTE");
factory = (ConnectionFactory) artemisCf;
connection = factory.createConnection();
// If I uncomment this I get the "Client id has already been set" error
// connection.setClientID("TESTE");
session = connection.createSession(true, 2);
Queue queue = session.createQueue("QUEUE.TESTE.CONNCETION");
consumer = session.createConsumer(queue);
consumer.setMessageListener(new Listener("Me"));
connection.start();
Thread.sleep(2000000);
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
if (consumer != null && session != null && connection != null) {
consumer.close();
session.close();
connection.close();
}
}
}
public static class Listener implements MessageListener {
private String listenerName;
public Listener(String consumerName) {
this.listenerName = consumerName;
}
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println(listenerName + " received " + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
I am having 2 applications exchanging data using RabbitMQ. I have implemented this using Spring AMQP. I have scenario once the message has been consumed from the consumer might encounter an exception while processing.
If any exception comes i am planning to log into the database. I have to remove message from the queue explicitly once the message reaches the consumer whether it is successful processing or error encountered.
How to forcefully remove the message from queue otherwise it will be
there if my application fails to process it?
Below is my Listener code
#RabbitListener(containerFactory="rabbitListenerContainerFactory",queues=Constants.JOB_QUEUE)
public void handleMessage(JobListenerDTO jobListenerDTO) {
//System.out.println("Received summary: " + jobListenerDTO.getProcessXML());
//amqpAdmin.purgeQueue(Constants.JOB_QUEUE, true);
try{
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("initiator", "cmy5kor");
Deployment deploy = repositoryService.createDeployment().addString(jobListenerDTO.getProcessId()+".bpmn20.xml",jobListenerDTO.getProcessXML()).deploy();
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(jobListenerDTO.getProcessId(), variables);
System.out.println("Process Instance is:::::::::::::"+processInstance);
}catch(Exception e){
e.printStackTrace();
}
Configuration Code
#Configuration
#EnableRabbit
public class RabbitMQJobConfiguration extends AbstractBipRabbitConfiguration {
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setQueue(Constants.JOB_QUEUE);
template.setMessageConverter(jsonMessageConverter());
return template;
}
#Bean
public Queue jobQueue() {
return new Queue(Constants.JOB_QUEUE);
}
#Bean(name="rabbitListenerContainerFactory")
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter();
DefaultClassMapper classMapper = new DefaultClassMapper();
Map<String, Class<?>> idClassMapping = new HashMap<String, Class<?>>();
idClassMapping.put("com.bosch.diff.approach.TaskMessage", JobListenerDTO.class);
classMapper.setIdClassMapping(idClassMapping);
messageConverter.setClassMapper(classMapper);
factory.setMessageConverter(messageConverter);
factory.setReceiveTimeout(10L);
return factory;
}
}
I don't know about spring api or configuration for rmq but this
I have to remove message from the queue explicitly once the message reaches the consumer whether it is successful processing or error encountered.
is exactly what is happening when you set the auto-acknowledge flag. In that way, the message is acknowledged as soon as it's consumed - so gone from the queue.
As long as your listener catches the exception the message will be removed from the queue.
If your listener throws an exception, it will be requeued by default; that behavior can be modified by throwing a AmqpRejectAndDontRequeueException or setting the defaultRequeueRejected property - see the documentation for details.
I'm creating two springboot server & client applications communicating using JMS, and everything is working fine with the release 5.12.1 for activemq, but as soon as I update to the 5.12.3 version, I'm getting the following error :
org.springframework.jms.support.converter.MessageConversionException: Could not convert JMS message; nested exception is javax.jms.JMSException: Failed to build body from content. Serializable class not available to broker. Reason: java.lang.ClassNotFoundException: Forbidden class MyClass! This class is not trusted to be serialized as ObjectMessage payload. Please take a look at http://activemq.apache.org/objectmessage.html for more information on how to configure trusted classes.
I went on the URL that is provided and I figured out that my issue is related to the new security implemented in the 5.12.2 release of ActiveMQ, and I understand that I could fix it by defining the trusted packages, but I have no idea on where to put such a configuration in my SpringBoot project.
The only reference I'm making to the JMS queue in my client and my server is setting up it's URI in application.properties and enabling JMS on my "main" class with #EnableJms, and here's my configuration on the separate broker :
#Configuration
#ConfigurationProperties(prefix = "activemq")
public class BrokerConfiguration {
/**
* Defaults to TCP 10000
*/
private String connectorURI = "tcp://0.0.0.0:10000";
private String kahaDBDataDir = "../../data/activemq";
public String getConnectorURI() {
return connectorURI;
}
public void setConnectorURI(String connectorURI) {
this.connectorURI = connectorURI;
}
public String getKahaDBDataDir() {
return kahaDBDataDir;
}
public void setKahaDBDataDir(String kahaDBDataDir) {
this.kahaDBDataDir = kahaDBDataDir;
}
#Bean(initMethod = "start", destroyMethod = "stop")
public BrokerService broker() throws Exception {
KahaDBPersistenceAdapter persistenceAdapter = new KahaDBPersistenceAdapter();
persistenceAdapter.setDirectory(new File(kahaDBDataDir));
final BrokerService broker = new BrokerService();
broker.addConnector(getConnectorURI());
broker.setPersistent(true);
broker.setPersistenceAdapter(persistenceAdapter);
broker.setShutdownHooks(Collections.<Runnable> singletonList(new SpringContextHook()));
broker.setUseJmx(false);
final ManagementContext managementContext = new ManagementContext();
managementContext.setCreateConnector(true);
broker.setManagementContext(managementContext);
return broker;
}
}
So I'd like to know where I'm supposed to specify the trusted packages.
Thanks :)
You can just set one of the below spring boot properties in application.properties to set trusted packages.
spring.activemq.packages.trust-all=true
or
spring.activemq.packages.trusted=<package1>,<package2>,<package3>
Add the following bean:
#Bean
public ActiveMQConnectionFactory activeMQConnectionFactory() {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("your broker URL");
factory.setTrustedPackages(Arrays.asList("com.my.package"));
return factory;
}
The ability to do this via a configuration property has been added for the next release:
https://github.com/spring-projects/spring-boot/issues/5631
Method: public void setTrustedPackages(List<String> trustedPackages)
Description: add all packages which is used in send and receive Message object.
Code : connectionFactory.setTrustedPackages(Arrays.asList("org.api","java.util"))
Implementated Code:
private static final String DEFAULT_BROKER_URL = "tcp://localhost:61616";
private static final String RESPONSE_QUEUE = "api-response";
#Bean
public ActiveMQConnectionFactory connectionFactory(){
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
connectionFactory.setBrokerURL(DEFAULT_BROKER_URL);
connectionFactory.setTrustedPackages(Arrays.asList("org.api","java.util"));
return connectionFactory;
}
#Bean
public JmsTemplate jmsTemplate(){
JmsTemplate template = new JmsTemplate();
template.setConnectionFactory(connectionFactory());
template.setDefaultDestinationName(RESPONSE_QUEUE);
return template;
}
If any one still looking for an answer, below snippet worked for me
#Bean
public ActiveMQConnectionFactory connectionFactory() {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
connectionFactory.setBrokerURL(BROKER_URL);
connectionFactory.setPassword(BROKER_USERNAME);
connectionFactory.setUserName(BROKER_PASSWORD);
connectionFactory.setTrustAllPackages(true); // all packages are considered as trusted
//connectionFactory.setTrustedPackages(Arrays.asList("com.my.package")); // selected packages
return connectionFactory;
}
I am setting Java_opts something like below and passing to java command and its working for me.
JAVA_OPTS=-Xmx256M -Xms16M -Dorg.apache.activemq.SERIALIZABLE_PACKAGES=*
java $JAVA_OPTS -Dapp.config.location=/data/config -jar <your_jar>.jar --spring.config.location=file:/data/config/<your config file path>.yml
Yes I found it's config in the new version
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
</parent>
spring:
profiles:
active: #profileActive#
cache:
ehcache:
config: ehcache.xml
activemq:
packages:
trusted: com.stylrplus.api.model