Cannot find the JBoss ConnectionFactory with a JNDI lookup in Spring annotation configuration - spring

Despite what seems to be a successful binding of the JBoss (AS 7.1.1.Final) connection factory:
[org.jboss.as.messaging] (MSC service thread 1-9) JBAS011601: Bound messaging object to jndi name java:/ConnectionFactory
The ConnectionFactory in the lookup is always null. Can anyone see what the problem is?
#Configuration
#ComponentScan(basePackages = "reservation")
public class AppConfiguration extends WebMvcConfigurerAdapter {
// ***********************//
// ******** JMS **********//
// ***********************//
#Bean
public ConnectionFactory jmsConnectionFactory() {
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("java:/ConnectionFactory");
return (ConnectionFactory) jndiObjectFactoryBean.getObject();
}
#Bean
public Queue requestsQueue() {
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("java:/queue/test");
return (Queue) jndiObjectFactoryBean.getObject();
}
#Bean
public JmsOperations jmsOperations() {
final JmsTemplate jmsTemplate = new JmsTemplate(jmsConnectionFactory());
jmsTemplate.setDefaultDestination(requestsQueue());
return jmsTemplate;
}
}

Unfortunately you must call afterPropertiesSet() manually:
#Bean
public ConnectionFactory jmsConnectionFactory() {
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("java:/ConnectionFactory");
jndiObjectFactoryBean.afterPropertiesSet(); //HERE
return (ConnectionFactory) jndiObjectFactoryBean.getObject();
}
An alternative I particularly like is as follows:
#Bean
public JndiObjectFactoryBean jmsConnectionFactoryFactoryBean() {
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("java:/ConnectionFactory");
return jndiObjectFactoryBean;
}
public ConnectionFactory jmsConnectionFactory() {
return (ConnectionFactory) jmsConnectionFactoryFactoryBean().getObject();
}
Notice that jmsConnectionFactory() is not annotated with #Bean (it's important). In that case Spring will call appropriate callback method for you.

Related

Redelivery Policy Is Not Working in Activemq Spring Boot

I have used below configuration
#SpringBootApplication
#EnableScheduling
public class NotificationApplication {
#Value("${jms.broker.endpoint}")
private String brokerUrl;
#Autowired
private Environment env;
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(NotificationApplication.class, args);
RecordReader abc = ctx.getBean(RecordReader.class);
abc.readNotifications();
}
#Bean
public ActiveMQConnectionFactory activeMQConnectionFactory() {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(brokerUrl);
RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
redeliveryPolicy.setMaximumRedeliveries(env.getProperty("jms.redelivery.maximum", Integer.class));
factory.setRedeliveryPolicy(redeliveryPolicy);
factory.setTrustedPackages(Arrays.asList("com.lms.notification"));
return factory;
}
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() throws Throwable {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(activeMQConnectionFactory());
factory.setMessageConverter(jacksonJmsMessageConverter());
factory.setConcurrency(env.getProperty("jms.connections.concurrent"));
return factory;
}
#Bean
public JmsTemplate jmsTemplate() {
JmsTemplate template = new JmsTemplate();
template.setConnectionFactory(activeMQConnectionFactory());
template.setMessageConverter(jacksonJmsMessageConverter());
return template;
}
#Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
}
The issue is the Redelivery policy is not working as I have defined in activeMQConnectionFactory bean. Means I have set maximum redelivery 1, but is not being redelivered in case of exception in listener. Also in case of exception in listener it should go to the default DLQ, which is also not happening. But if I comment the jmsListenerContainerFactory bean all works fine.
I am not able to identify why this is happening. Can any one look into this what wrong I am doing?
I am using Activemq 5.16.1
Thanks

Spring Boot RabbitMQ Null Pointer Exception Error

I am using RabbitMQ with Spring Boot to broker messages between two services. I am able to receive the message and format it but when I call a service class in the onMessage method, I get a null pointer exception error. Here is my message listener class which receives the message
public class QueueListener implements MessageListener{
#Autowired
private QueueProcessor queueProcessor;
#Override
public void onMessage(Message message) {
String msg = new String(message.getBody());
String output = msg.replaceAll("\\\\", "");
String jsonified = output.substring(1, output.length()-1);
JSONArray obj = new JSONArray(jsonified);
queueProcessor.processMessage(obj);
}
}
Calling the method processMessage throws null pointer exception
Can someone point to me what I ma doing wrong?
I found out the issue was in the RabbitMqConfig class. Here is the code which was causing the error:
#Configuration
public class RabbitMqConfig {
private static final String QUEUE_NAME = "my.queue.name";
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("<url.to.rabbit>");
connectionFactory.setUsername("<username>");
connectionFactory.setPassword("<password>");
return connectionFactory;;
}
#Bean
public Queue simpleQueue() {
return new Queue(QUEUE_NAME);
}
#Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setRoutingKey(QUEUE_NAME);
template.setMessageConverter(jsonMessageConverter());
return template;
}
#Bean
public SimpleMessageListenerContainer userListenerContainer() {
SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer();
listenerContainer.setConnectionFactory(connectionFactory());
listenerContainer.setQueues(simpleQueue());
listenerContainer.setMessageConverter(jsonMessageConverter());
listenerContainer.setMessageListener(new QueueListener());
listenerContainer.setAcknowledgeMode(AcknowledgeMode.AUTO);
return listenerContainer;
}
}
The line listenerContainer.setMessageListener(new QueueListener()); was the source of the error. I solved it by Autowiring the class instead of using new. Here is the working code
#Configuration
public class RabbitMqConfig {
private static final String QUEUE_NAME = "my.queue.name";
#Autowired
private QueueListener queueListener;
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("<url.to.rabbit>");
connectionFactory.setUsername("<username>");
connectionFactory.setPassword("<password>");
return connectionFactory;
}
#Bean
public Queue simpleQueue() {
return new Queue(QUEUE_NAME);
}
#Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setRoutingKey(QUEUE_NAME);
template.setMessageConverter(jsonMessageConverter());
return template;
}
/*#Bean
public SimpleMessageListenerContainer userListenerContainer() {
SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer();
listenerContainer.setConnectionFactory(connectionFactory());
listenerContainer.setQueues(simpleQueue());
listenerContainer.setMessageConverter(jsonMessageConverter());
listenerContainer.setMessageListener(queueListener);
listenerContainer.setAcknowledgeMode(AcknowledgeMode.AUTO);
return listenerContainer;
}
}
Hope this helps someone else
Make sure the QueueListener is a component class or service class that can be managed by the Spring IoC. Otherwise, the config class cannot make this a bean out of the box, since this is just a normal Java class that need to be in the container #runtime.
So when u write new QueueListener() in yr config class, then the Java class is not in the SpringContext at the time when the config class is instantiated and is therefore null.
Hope this helps clear out some of this issue!

Error using Spring AOP with Dynamic Proxy with JMS Template on Weblogic 12.2.1

I'm getting an error when I try to use in the same project Spring Template JMS to messaging to ActiveMQ and Spring AOP to auditing and handle exceptions on Weblogic 12.2.1. Error occurs on server startup.
If I config aspect to use CGLIB, I got exceptions from Weblogic, and I prefer, if it's possible to maintain using of Dynamic Proxy. Does anyone already had this problem or have any idea what could cause it?
My aspect config class:
#Configuration
#EnableAspectJAutoProxy
#lombok.extern.slf4j.Slf4j
public class AspectConfig {
#Bean
public LoggingErrorAspect loggingErrorAspect(){
return new LoggingErrorAspect();
}
}
Message Listener config class:
#Configuration
#EnableJms
#lombok.extern.slf4j.Slf4j
public class MessagingListenerConfig {
#Autowired
ConnectionFactory connectionFactory;
#Bean
public JmsListenerContainerFactory<?> jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setConcurrency("1-1");
return factory;
}
}
Stack:
weblogic.application.ModuleException:
org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean
named
'org.springframework.jms.config.internalJmsListenerEndpointRegistry'
is expected to be of type
'org.springframework.jms.config.JmsListenerEndpointRegistry' but was
actually of type 'com.sun.proxy.$Proxy213'
You don't show where you are injecting the JmsListenerEndpointRegistry (or RabbitTemplate) but you have to inject by interface when you proxy with JDK proxies.
Why are you advising the registry - what are you trying to achieve with that? It doesn't provide many interfaces so it can't be referenced once proxied, except by DisposableBean and SmartLifecycle.
The RabbitTemplate can be proxied but you need to inject RabbitOperations, not RabbitTemplate.
I fix it the problem. I had to change the broker to AMQP RabbitMQ, and I needed to use interface instead of classes in all my config. After it, startup load works and aspect too.
Here is Aspect Config and Messaging Config.
AspectConfig:
#Configuration
#EnableAspectJAutoProxy
#lombok.extern.slf4j.Slf4j
public class AspectConfig {
#Bean
public LoggingErrorAspect loggingErrorAspect(){
return new LoggingErrorAspect();
}
}
MessagingConfig:
#Configuration
#PropertySources({ #PropertySource("classpath:/config/messaging.properties") })
#ComponentScan("br.com.aegea.scab.notification")
#lombok.extern.slf4j.Slf4j
public class MessagingConfig {
public static final String ERROR_QUEUE = "ERROR_QUEUE";
public static final String EMAIL_QUEUE = "EMAIL_QUEUE";
#Autowired
private Environment environment;
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(environment.getProperty("spring.rabbitMQ.host"));
connectionFactory.setUsername(environment.getProperty("spring.rabbitMQ.user"));
connectionFactory.setPassword(environment.getProperty("spring.rabbitMQ.password"));
return connectionFactory;
}
#Bean
public MessageConverter jsonMessageConverter() {
return new JsonMessageConverter();
}
#Bean
public RabbitOperations emailRabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setRoutingKey(EMAIL_QUEUE);
template.setMessageConverter(jsonMessageConverter());
return template;
}
#Bean
public MessageListenerContainer listenerContainer() {
SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer();
listenerContainer.setConnectionFactory(connectionFactory());
listenerContainer.setQueueNames(EMAIL_QUEUE);
listenerContainer.setMessageConverter(jsonMessageConverter());
listenerContainer.setMessageListener(messageReceiver());
listenerContainer.setAcknowledgeMode(AcknowledgeMode.AUTO);
return listenerContainer;
}
#Bean
public MessageListener messageReceiver() {
return new MessageReceiver();
}
}

Access connectionfactory inside messageListener onMessage

I am creating a rabbitmq messageListener and would like to be able to access the connectionfactory configuration while in the onMessage method, is that possible? It would be useful for logging and other details. Being able to log the vhost from which the message was delivered would be helpful and it is not available in the message itself. Here is my consumer and config
public class Consumer implements MessageListener {
#Override
public void onMessage(Message message) {
//how can I get the connection factory configuration when a message is sent?
}
here is the config
{
#Configuration
#EnableAutoConfiguration
public class RabbitConfig {
private static final String SIMPLE_MESSAGE_QUEUE = "qDLX2.dlq";
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("server");
connectionFactory.setUsername("admin");
connectionFactory.setPassword("ad,om");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("vhost1");
return connectionFactory;
}
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
return template;
}
#Autowired
private Consumer consumer;
#Bean
public SimpleMessageListenerContainer listenerContainer() {
SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer();
listenerContainer.setConnectionFactory(connectionFactory());
listenerContainer.setQueueNames(SIMPLE_MESSAGE_QUEUE);
listenerContainer.setMessageListener(consumer);
listenerContainer.setAcknowledgeMode(AcknowledgeMode.AUTO);
return listenerContainer;
}
}
Thanks
Gregg
There's no standard way of doing that.
You could inject the connection factory into the consumer field in your listenerContainer bean definition...
listenerContainer.setMessageListener(this.consumer);
this.consumer.setConnectionFactory(connectionFactory());
You could probably #Autowire it too.
You can then use getRabbitConnectionFactory().getVirtualHost() to access the vhost.

Change JMS settings with Glassfish and Spring

In our project we use Glassfish v3.1.2.2 with a ConnectionFactory bound as "jms/ConnectionFactory" and a Queue bound as "jms/Queue". Both are created in the glassfish admin console:
The Spring config is implemented this way:
#Bean
public JndiTemplate jndiTemplate() {
JndiTemplate jndiTemplate = new JndiTemplate();
Properties props = new Properties();
props.setProperty("java.naming.factory.initial", "com.sun.enterprise.naming.SerialInitContextFactory");
props.setProperty("java.naming.factory.url.pkgs", "com.sun.enterprise.naming");
props.setProperty("java.naming.factory.state", "com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
props.setProperty("org.omg.CORBA.ORBInitialHost", "localhost");
props.setProperty("org.omg.CORBA.ORBInitialPort", "3700");
jndiTemplate.setEnvironment(props);
return jndiTemplate;
}
#Bean
public JndiObjectFactoryBean connectionFactory() {
JndiObjectFactoryBean connectionFactory = new JndiObjectFactoryBean();
connectionFactory.setJndiTemplate(jndiTemplate());
connectionFactory.setJndiName("jms/ConnectionFactory");
return connectionFactory;
}
#Bean
public TransactionAwareConnectionFactoryProxy connectionFactoryProxy() {
return new TransactionAwareConnectionFactoryProxy((ConnectionFactory) connectionFactory().getObject());
}
#Bean
public JndiObjectFactoryBean destination() {
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiTemplate(jndiTemplate());
jndiObjectFactoryBean.setJndiName("jms/Queue");
return jndiObjectFactoryBean;
}
#Bean
public JmsTemplate jmsTemplate() {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory((ConnectionFactory) connectionFactory().getObject());
jmsTemplate.setDefaultDestination((Destination) destination().getObject());
return jmsTemplate;
}
#Bean
public DefaultMessageListenerContainer simpleMessageListenerContainer() {
DefaultMessageListenerContainer listenerContainer = new DefaultMessageListenerContainer();
listenerContainer.setConnectionFactory(connectionFactoryProxy());
listenerContainer.setDestination((Destination) destination().getObject());
listenerContainer.setMessageListener(messageDispatcher);
listenerContainer.setSessionTransacted(true);
listenerContainer.setTransactionManager(jtaTransactionManager());
return listenerContainer;
}
Everything works fine so far, message will be sent to and consumed from the queue without any problems. Rolling back messages wit a RuntimeException works, too.
The problem is, how to change some fundamental settings, like RedeliveryAttempts or RedeliveryInterval from the Activation Spec. I cant find any solution to change this with Spring only with MDB. Is there a way to do this in Glassfishs admin console or in Spring configuration? Did I have to use some different implementation or is it not possible over all?
Hope anybody can help.
Thanks in advance,
Danny
Through some trial and error I found the following, which works on Glassfish 4.
#Bean
public JmsTemplate jmsTemplate() {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setPubSubDomain(true); //may be necessary if using topic
jmsTemplate.setDefaultDestinationName("topicname");
jmsTemplate.setConnectionFactory(connectionFactory());
return jmsTemplate;
}
#Bean
public ConnectionFactory connectionFactory() {
try {
JndiObjectFactoryBean jndiFactory = new JndiObjectFactoryBean();
jndiFactory.setJndiName("jms/ConnectionFactory");
jndiFactory.setResourceRef(true); //adds java:comp/env/ prefix
jndiFactory.afterPropertiesSet(); //very important, actually causes the object to be loaded
return (ConnectionFactory) jndiFactory.getObject();
} catch (IllegalArgumentException | NamingException e) {
throw new RuntimeException(e);
}
}

Resources