Reputation: 23
Update: Posted the non-working version on https://github.com/justsomecoder/boot-rabbit-issue
I am trying to implement some basic RabbitMQ sending and receiving functionality but cannot seem to get it working across both my Spring Boot projects. While I have tried multiple examples, as of now I implemented the example at https://spring.io/guides/gs/messaging-rabbitmq/, with the only exception that I defined the beans in the Application.java in a separate RabbitConfig class annotated with @Configuration.
I also tried it the exact way it's done in the example, however this also does not work.
The funny thing is, the implementation works in one (older) Spring Boot project while it's not working in a newer Boot project. In the other project the output during runtime shows me that the connection to RabbitMQ is succesfully setup:
Part of output working project
2017-12-18 10:53:45,205 INFO [restartedMain] o.s.j.e.a.AnnotationMBeanExporter [MBeanExporter.java:431] Registering beans for JMX exposure on startup
2017-12-18 10:53:45,215 INFO [restartedMain] o.s.j.e.a.AnnotationMBeanExporter [MBeanExporter.java:916] Bean with name 'rabbitConnectionFactory' has been autodetected for JMX exposure
2017-12-18 10:53:45,219 INFO [restartedMain] o.s.j.e.a.AnnotationMBeanExporter [MBeanExporter.java:678] Located managed bean 'rabbitConnectionFactory': registering with JMX server as MBean [org.springframework.amqp.rabbit.connection:name=rabbitConnectionFactory,type=CachingConnectionFactory]
2017-12-18 10:53:45,238 INFO [restartedMain] o.s.c.s.DefaultLifecycleProcessor [DefaultLifecycleProcessor.java:343] Starting beans in phase -2147482648
2017-12-18 10:53:45,239 INFO [restartedMain] o.s.c.s.DefaultLifecycleProcessor [DefaultLifecycleProcessor.java:343] Starting beans in phase 2147483647
2017-12-18 10:53:45,268 INFO [container-1] o.s.a.r.c.CachingConnectionFactory [AbstractConnectionFactory.java:359] Created new connection: SimpleConnection@6bb6caa0 [delegate=amqp://[email protected]:32770/, localPort= 51889]
2017-12-18 10:53:45,273 INFO [container-1] o.s.a.r.c.RabbitAdmin [RabbitAdmin.java:491] Auto-declaring a non-durable, auto-delete, or exclusive Queue (floors) durable:false, auto-delete:false, exclusive:false. It will be redeclared if the broker stops and is restarted while the connection factory is alive, but all messages will be lost.
Output non-working project
2017-12-18 18:40:39.452 INFO 4722 --- [ restartedMain] name.nameFootstepProcessor : Starting nameFootstepProcessor on Macbook-Pro-van-Lars.local with PID 4722 (/Users/lars/IdeaProjects/name-footstep-processor/target/classes started by lars in /Users/lars/IdeaProjects/name-footstep-processor)
2017-12-18 18:40:39.453 INFO 4722 --- [ restartedMain] name.nameFootstepProcessor : No active profile set, falling back to default profiles: default
2017-12-18 18:40:39.453 DEBUG 4722 --- [ restartedMain] o.s.boot.SpringApplication : Loading source class name.nameFootstepProcessor
2017-12-18 18:40:39.945 INFO 4722 --- [ restartedMain] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@426b0d00: startup date [Mon Dec 18 18:40:39 CET 2017]; root of context hierarchy
2017-12-18 18:40:40.697 INFO 4722 --- [ restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2017-12-18 18:40:41.389 INFO 4722 --- [ restartedMain] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.amqp.rabbit.annotation.RabbitBootstrapConfiguration' of type [org.springframework.amqp.rabbit.annotation.RabbitBootstrapConfiguration$$EnhancerBySpringCGLIB$$b410db6c] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
It also shows up as a consumer for the queue in the RabbitMQ Admin Management interface.
In my other project, none of this is shown. However, in both projects the Rabbit configuration class is found and the beans are loaded (checked using Print all the Spring beans that are loaded - Spring Boot).
Both projects share the same dependency 'spring-boot-starter-amqp'. They also both share the same application.properties file containing the right information for connecting to my local RabbitMQ server. Is there anything I can do to find out why one project is loading the Rabbit configuration beans correctly while the other is not?
Below I have attached some files which I think are helpful to understand the problem more, but if any other files or output are needed please let me know. I changed some package names for privacy concerns.
Thank you!
Regards, larsl95.
Working project Spring Boot 1.5.3
Non-working project Spring Boot 1.5.9
application.properties (same for both projects, port is due to Docker, mapped to 5276)
spring.rabbitmq.host=localhost
spring.rabbitmq.port=32770
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
RabbitConfig.java (same for both projects)
package name.configuration;
import org.springframework.context.ConfigurableApplicationContext;
import name.queue.Receiver;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
@Configuration
public class RabbitConfig {
public final static String queueName = "floors";
public RabbitConfig(ConfigurableApplicationContext ctx) {
this.printBeans(ctx);
}
@Bean
Queue queue() {
return new Queue(queueName, false);
}
@Bean
TopicExchange exchange() {
return new TopicExchange("test-exchange");
}
@Bean
Binding binding(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(queueName);
}
@Bean
SimpleMessageListenerContainer container(ConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(queueName);
container.setMessageListener(listenerAdapter);
return container;
}
@Bean
MessageListenerAdapter listenerAdapter(Receiver receiver) {
return new MessageListenerAdapter(receiver, "receiveMessage");
}
// just added this to know which beans are loaded, called it from constructor to see
// if RabbitConfig class is found by Spring at all
private void printBeans(ConfigurableApplicationContext ctx) {
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
}
}
Receiver.java (same for both projects)
package name.queue;
import org.springframework.stereotype.Component;
import java.util.concurrent.CountDownLatch;
@Component
public class Receiver {
private CountDownLatch latch = new CountDownLatch(1);
public void receiveMessage(String message) {
System.out.println("Received <" + message + ">");
latch.countDown();
}
public CountDownLatch getLatch() {
return latch;
}
}
Main.java (non-working project)
@SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Main.class);
app.setWebEnvironment(false);
ConfigurableApplicationContext appContext = app.run(args);
name.tcp.TcpServer tcpServer = new name.tcp.TcpServer();
}
}
Main.java (working project)
package name;
import name.tcp.TcpServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import java.io.IOException;
@SpringBootApplication
public class Main extends SpringBootServletInitializer {
public static void main(String[] args) {
ConfigurableApplicationContext app = SpringApplication.run(Main.class, args);
TcpServer tcpServer = new TcpServer();
System.out.println("starting TCP server from main");
}
}
Runner.java (same for both projects)
package name.queue;
import java.util.concurrent.TimeUnit;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
import name.configuration.RabbitConfig;
@Component
public class Runner implements CommandLineRunner {
private final RabbitTemplate rabbitTemplate;
@Autowired
public Runner(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}
@Override
public void run(String... args) throws Exception {
System.out.println("Sending message...");
rabbitTemplate.convertAndSend(RabbitConfig.queueName, "Hello from RabbitMQ!");
}
}
Upvotes: 2
Views: 6711
Reputation: 3364
Seeing the project you linked that reproduced the issue, when the application is started, the TcpServer component gets initialized, which causes the startListen()
method to be executed.
That method has an infinite loop without any separate thread being created, hence blocking the remaining of the startup process (code excerpt below).
public void startListen() throws IOException {
while (true) {
Socket clientSocket = serverSocket.accept();
threadPool.submit(new XClientHandler(clientSocket,eventProducer));
}
}
Removing that component shows that the RabbitMQ listener gets started, so I would just advise to (at least) use a different Thread
for your TcpServer
.
Upvotes: 1