CamelContextStartedEvent called twice - spring

The CamelContextStartedEvent is called twice for the same camel context (camel-1). The issue might be the way I register the EventNotifier. You can reproduce the issue with Spring Initializr with Spring Boot 1.5.14, Spring Boot Camel Starter 2.21.1 and Spring Boot Web Starter.
See the logs:
2018-07-06 11:04:41.104 INFO 19092 --- [ main] o.a.camel.spring.SpringCamelContext : Apache Camel 2.21.1 (CamelContext: camel-1) is starting
2018-07-06 11:04:41.106 INFO 19092 --- [ main] o.a.c.m.ManagedManagementStrategy : JMX is enabled
2018-07-06 11:04:41.191 INFO 19092 --- [ main] o.a.camel.spring.SpringCamelContext : StreamCaching is not in use. If using streams then its recommended to enable stream caching. See more details at http://camel.apache.org/stream-caching.html
2018-07-06 11:04:41.193 INFO 19092 --- [ main] o.a.camel.spring.boot.RoutesCollector : Starting CamelMainRunController to ensure the main thread keeps running
2018-07-06 11:04:41.193 INFO 19092 --- [ main] o.a.camel.spring.SpringCamelContext : Total 0 routes, of which 0 are started
2018-07-06 11:04:41.194 INFO 19092 --- [ main] o.a.camel.spring.SpringCamelContext : Apache Camel 2.21.1 (CamelContext: camel-1) started in 0.090 seconds
2018-07-06 11:04:41.195 INFO 19092 --- [ main] c.e.bug.service.StartupEventNotifier : CamelContextStartedEvent for SpringCamelContext(camel-1) with spring id application:11223
2018-07-06 11:04:41.195 INFO 19092 --- [ main] c.e.bug.service.StartupEventNotifier : CamelContextStartedEvent for SpringCamelContext(camel-1) with spring id application:11223
2018-07-06 11:04:41.216 INFO 19092 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 11223 (http)
2018-07-06 11:04:41.221 INFO 19092 --- [ main] com.example.bug.BugApplication : Started BugApplication in 4.684 seconds (JVM running for 6.773)
The service that initializes the EventNotifier:
#Service
public class SchedulerService {
private final CamelContext camelContext;
private final StartupEventNotifier startupEventNotifier;
public SchedulerService(CamelContext camelContext, StartupEventNotifier startupEventNotifier) {
this.camelContext = camelContext;
this.startupEventNotifier = startupEventNotifier;
}
#PostConstruct
public void init() {
camelContext.getManagementStrategy().addEventNotifier(startupEventNotifier);
}
}
The EventNotifier:
#Component
public class StartupEventNotifier extends EventNotifierSupport {
private static final Logger logger = LoggerFactory.getLogger(StartupEventNotifier.class);
#Override
public void notify(EventObject event) throws Exception {
if (event instanceof CamelContextStartedEvent) {
logger.info("CamelContextStartedEvent for {}", event.getSource());
}
}
#Override
public boolean isEnabled(EventObject event) {
if (event instanceof CamelContextStartedEvent) {
return true;
}
return false;
}
}
application.yml:
camel:
springboot:
main-run-controller: true
server:
port: 11223

It is called twice, because it is registered twice. Once by you and once by Apache Camel. EventNotifier is registered automatically, if is found in Registry. Since your StartupEventNotifier is annotated as Component, it is part of Registry and Apache Camel registered it during CamelContext startup (You can see it in CamelAutoConfiguration line 424).
You have four options:
Remove your custom registration from SchedulerService.
Remove #Component annotation from StartupEventNotifier and register it with with camelContext.getManagementStrategy().addEventNotifier(new StartupEventNotifier())
Add duplicity check to your SchedulerService. Something like:
if (!context.getManagementStrategy().getEventNotifiers().contains(startupEventNotifier)){
context.getManagementStrategy().addEventNotifier(startupEventNotifier);
}
Register EventNotifier in #PostConstruct of RouteBuilder. It will be registered before automatic discovery is started and then it will be skipped in CamelAutoConfiguration (See line 422)

Related

JobParametersValidator.validate() method parameters returns null in spring batch

I am writing simple helloworld application in spring batch, and I'm trying to validate the JobParameters with custom implementation of JobParametersValidator.
public class ParameterValidator implements JobParametersValidator{
#Override
public void validate(JobParameters parameters) throws JobParametersInvalidException {
//String parameter = parameters.getParameters().get("outputText");
System.out.println("jobparameters are ............ " + parameters);
//System.out.println("parameter is " + parameter);
//if(!StringUtils.hasText(parameter)) {
throw new JobParametersInvalidException("Parameter is missing.........");
//}
}
}
But when I tried to Sysout the JobParameters inside the validate method, parameters return null.
Here is my console out.
2021-11-26 10:06:32.457 INFO 11912 --- [ restartedMain] com.pack.SpringBatchDemo2Application : Starting SpringBatchDemo2Application using Java 16.0.2 on DESKTOP-T1L7IA7 with PID 11912 (D:\Workspace\EclipseWorkspaceSpringPractise\SpringBatchDemo2\target\classes started by arepa in D:\Workspace\EclipseWorkspaceSpringPractise\SpringBatchDemo2)
2021-11-26 10:06:32.458 INFO 11912 --- [ restartedMain] com.pack.SpringBatchDemo2Application : No active profile set, falling back to default profiles: default
2021-11-26 10:06:32.536 INFO 11912 --- [ restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2021-11-26 10:06:33.575 INFO 11912 --- [ restartedMain] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2021-11-26 10:06:33.784 INFO 11912 --- [ restartedMain] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2021-11-26 10:06:33.920 INFO 11912 --- [ restartedMain] o.s.b.c.r.s.JobRepositoryFactoryBean : No database type set, using meta data indicating: POSTGRES
2021-11-26 10:06:34.262 INFO 11912 --- [ restartedMain] o.s.b.c.l.support.SimpleJobLauncher : No TaskExecutor has been set, defaulting to synchronous executor.
2021-11-26 10:06:34.484 INFO 11912 --- [ restartedMain] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2021-11-26 10:06:34.539 INFO 11912 --- [ restartedMain] com.pack.SpringBatchDemo2Application : Started SpringBatchDemo2Application in 2.596 seconds (JVM running for 4.056)
2021-11-26 10:06:34.544 INFO 11912 --- [ restartedMain] o.s.b.a.b.JobLauncherApplicationRunner : Running default command line with: []
jobparameters are ............ {}
2021-11-26 10:06:34.713 INFO 11912 --- [ restartedMain] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-11-26 10:06:34.756 ERROR 11912 --- [ restartedMain] o.s.boot.SpringApplication : Application run failed
Here is my Job creation code.
#Component
public class HelloWorldJob {
#Autowired
private JobBuilderFactory jobBuilderFactory;
#Autowired
private StepBuilderFactory stepBuilderFactory;
public Step step() {
Tasklet tasklet = (contribution, chunkContext) -> {
return RepeatStatus.FINISHED;
};
return this.stepBuilderFactory.get("step1")
.tasklet(tasklet).build();
}
#Bean
public Job job() {
return this.jobBuilderFactory.get("job1").start(step()).validator(new ParameterValidator()).build();
}
}
Here is my code for triggering jobs.
#Component
public class TriggerJobLauncher {
#Autowired
private JobLauncher launcher;
#Autowired
private Job job;
public void triggerJob() throws JobExecutionAlreadyRunningException,
JobRestartException,
JobInstanceAlreadyCompleteException,
JobParametersInvalidException {
JobParameters parameters = new JobParametersBuilder().addParameter("aa", new JobParameter("Hello World")).toJobParameters();
launcher.run(job, parameters);
}
}

Multiple DispatcherServlet - Spring-Boot-2.4

I'm trying to create multiple dispatcherservlet ("/rest/", "/jsp/", "mq/*"). Springboot initialises only one dispatcherservlet.
i have two bean creation methods for DispatcherServletRegistrationBean in order to create two dispatcher servlet. I has set the order and precedence level for both beans. When i start the application, only one of the dispatcherservlet is getting Initialised. In this case, it is "restDisparcher" (please look for console output). what should i do in order to setup multiple dispatcher servlet.
#SpringBootConfiguration
public class AppConfiguration {
#Bean
#Primary
public DispatcherServletRegistrationBean dispatcherServletRegistrationBeanRest() {
DispatcherServlet dispatcherServlet = new DispatcherServlet(new AnnotationConfigServletWebApplicationContext("com.michael.springsecurityentitlement.rest"));
DispatcherServletRegistrationBean dispatcher = new DispatcherServletRegistrationBean(dispatcherServlet , "/rest/*");
dispatcher.setName("restDispatcher");
dispatcher.setLoadOnStartup(1);
dispatcher.setOrder(Ordered.HIGHEST_PRECEDENCE);
return dispatcher;
}
#Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBeanJsp() {
DispatcherServlet dispatcherServlet = new DispatcherServlet(new AnnotationConfigServletWebApplicationContext("com.michael.springsecurityentitlement.jsp"));
DispatcherServletRegistrationBean dispatcher = new DispatcherServletRegistrationBean(dispatcherServlet , "/jsp/*");
dispatcher.setName("restDispatcher");
dispatcher.setLoadOnStartup(1);
dispatcher.setOrder(Ordered.HIGHEST_PRECEDENCE);
return dispatcher;
}
#Bean
public TomcatServletWebServerFactory servletWebServerFactory() {
TomcatServletWebServerFactory tomcatServletWebServerFactory = new TomcatServletWebServerFactory("/custom",8081);
return tomcatServletWebServerFactory;
}
public static void main(String[] args) {
SpringApplication app = new SpringApplication(AppConfiguration.class);
app.run(args);
}
}
Console:
2021-03-04 12:09:39.015 INFO 1108 --- [ main] c.m.s.AppConfiguration : Starting AppConfiguration using Java 15.0.2 on ASINTHs-MacBook-Pro.local with PID 1108 (/Users/asinth/git/spring-security-entitlement/target/classes started by asinth in /Users/asinth/git/spring-security-entitlement)
2021-03-04 12:09:39.030 INFO 1108 --- [ main] c.m.s.AppConfiguration : No active profile set, falling back to default profiles: default
2021-03-04 12:09:39.539 INFO 1108 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8081 (http)
2021-03-04 12:09:39.551 INFO 1108 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-03-04 12:09:39.551 INFO 1108 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.43]
2021-03-04 12:09:39.704 INFO 1108 --- [ main] o.a.c.c.C.[.[localhost].[/custom] : Initializing Spring embedded WebApplicationContext
2021-03-04 12:09:39.704 INFO 1108 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 635 ms
2021-03-04 12:09:39.806 INFO 1108 --- [ main] o.s.boot.web.servlet.RegistrationBean : Servlet restDispatcher was not registered (possibly already registered?)
2021-03-04 12:09:39.880 INFO 1108 --- [ main] o.a.c.c.C.[.[localhost].[/custom] : Initializing Spring DispatcherServlet 'restDispatcher'
2021-03-04 12:09:39.881 INFO 1108 --- [ main] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'restDispatcher'
2021-03-04 12:09:40.131 INFO 1108 --- [ main] o.s.web.servlet.DispatcherServlet : Completed initialization in 250 ms
2021-03-04 12:09:40.136 INFO 1108 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8081 (http) with context path '/custom'
2021-03-04 12:09:40.155 INFO 1108 --- [ main] c.m.s.AppConfiguration : Started AppConfiguration in 2.093 seconds (JVM running for 3.252)
I think the problem is your dispatcher name. Set different names for different Dispatcher and it should works. Maybe like this :
#SpringBootApplication
public class AppConfiguration {
#Bean
#Primary
public DispatcherServletRegistrationBean dispatcherServletRegistrationBeanRest() {
DispatcherServlet dispatcherServlet = new DispatcherServlet(new AnnotationConfigServletWebApplicationContext("com.michael.springsecurityentitlement.rest"));
DispatcherServletRegistrationBean dispatcher = new DispatcherServletRegistrationBean(dispatcherServlet , "/rest/*");
dispatcher.setName("restDispatcher");
dispatcher.setLoadOnStartup(1);
dispatcher.setOrder(Ordered.HIGHEST_PRECEDENCE);
return dispatcher;
}
#Bean
public DispatcherServletRegistrationBean dispatcherServletRegistrationBeanJsp() {
DispatcherServlet dispatcherServlet = new DispatcherServlet(new AnnotationConfigServletWebApplicationContext("com.michael.springsecurityentitlement.jsp"));
DispatcherServletRegistrationBean dispatcher = new DispatcherServletRegistrationBean(dispatcherServlet , "/jsp/*");
dispatcher.setName("jspDispatcher");
dispatcher.setLoadOnStartup(1);
dispatcher.setOrder(Ordered.HIGHEST_PRECEDENCE);
return dispatcher;
}
#Bean
public TomcatServletWebServerFactory servletWebServerFactory() {
TomcatServletWebServerFactory tomcatServletWebServerFactory = new TomcatServletWebServerFactory("/custom",8081);
return tomcatServletWebServerFactory;
}
public static void main(String[] args) {
SpringApplication.run(AppConfiguration.class, args);
}
}

Resolving Singletons in Spring Boot Rest Controllers with Kotlin

I am new to Spring and Spring Boot and I played around with different ways how to resolve Beans. In my example I've got a Bean that should always be a singleton. What surprises me is that there seems to be a way where this bean is resolved as, I assume, "prototype".
Could anyone explain to me why it's not a singleton when it is resolved in the signature of the method showSingletonBeans?
#SpringBootApplication
class DemoApplication
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
#Service("stackSingletonBean")
// #Scope("singleton")
class MySingletonBean {
init {
println("Created MySingletonBean " + this.hashCode())
}
}
#RestController
class MyController {
#Autowired
// #Qualifier("singletonBean")
lateinit var memberSingletonBean: MySingletonBean
#Autowired
lateinit var singeltonFactory: ObjectFactory<MySingletonBean>
fun buildSingleton() : MySingletonBean {
return singeltonFactory.`object`
}
#Lookup
fun getSingletonInstance() : MySingletonBean? {
return null
}
#GetMapping("/")
fun showSingletonBeans(#Autowired stackSingletonBean: MySingletonBean) {
println("member " + memberSingletonBean.hashCode() )
println("stack " + stackSingletonBean.hashCode())
println("lookup:" + getSingletonInstance().hashCode())
println("factory: " + buildSingleton().hashCode())
}
}
The log looks like that:
2020-08-13 18:44:32.604 INFO 172175 --- [ main] com.example.demo.DemoApplicationKt : No active profile set, falling back to default profiles: default
2020-08-13 18:44:33.118 INFO 172175 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2020-08-13 18:44:33.124 INFO 172175 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-08-13 18:44:33.124 INFO 172175 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.37]
2020-08-13 18:44:33.164 INFO 172175 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-08-13 18:44:33.164 INFO 172175 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 528 ms
Created MySingletonBean 1747702724
2020-08-13 18:44:33.286 INFO 172175 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-08-13 18:44:33.372 INFO 172175 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-08-13 18:44:33.379 INFO 172175 --- [ main] com.example.demo.DemoApplicationKt : Started DemoApplicationKt in 1.011 seconds (JVM running for 1.24)
2020-08-13 18:44:37.341 INFO 172175 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-08-13 18:44:37.341 INFO 172175 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2020-08-13 18:44:37.344 INFO 172175 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 3 ms
Created MySingletonBean 562566586
member 1747702724
stack 562566586
lookup:1747702724
factory: 1747702724
Created MySingletonBean 389331797
member 1747702724
stack 389331797
lookup:1747702724
factory: 1747702724
Resolving controller method parameters is actually quite different mechanism. It has nothing to do with dependency injection and the #Autowired annotation: the annotation can be removed and it won't change the behavior.
Although #Autowired can technically be declared on individual method or constructor parameters since Spring Framework 5.0, most parts of the framework ignore such declarations. The only part of the core Spring Framework that actively supports autowired parameters is the JUnit Jupiter support in the spring-test module (see the TestContext framework reference documentation for details).
https://docs.spring.io/
In your case, the stackSingletonBean is instantiated by the ModelAttributeMethodArgumentResolver. It's not aware of the #Service annotation nor of its scope: it simply uses the default constructor on each request.
Model attributes are sourced from the model, or created using a default constructor and then added to the model.
Note that use of #ModelAttribute is optional — for example, to set its attributes. By default, any argument that is not a simple value type( as determined by BeanUtils#isSimpleProperty) and is not resolved by any other argument resolver is treated as if it were annotated with #ModelAttribute. Web on Reactive Stack

Simple unit test for Apache Camel SNMP route

I'm having some trouble getting a working Camel Spring-Boot unit test written, that tests a simple SNMP route. Here is what I have so far:
SnmpRoute.kt
open class SnmpRoute(private val snmpProperties: SnmpProperties, private val repository: IPduEventRepository) : RouteBuilder() {
#Throws(Exception::class)
override fun configure() {
logger.debug("Initialising with properties [{}]", snmpProperties)
from("snmp:0.0.0.0:1161?protocol=udp&type=TRAP")
.process { exchange ->
// do stuff
}
.bean(repository, "save")
}
}
SnmpRouteTest.kt
#CamelSpringBootTest
#SpringBootApplication
#EnableAutoConfiguration
open class SnmpRouteTest : CamelTestSupport() {
object SnmpConstants {
const val SNMP_TRAP = "<snmp><entry><oid>...datadatadata...</oid><value>123456</value></entry></snmp>"
const val MOCK_SNMP_ENDPOINT = "mock:snmp"
}
#Mock
lateinit var snmpProperties: SnmpProperties
#Mock
lateinit var repository: IPduEventRepository
#InjectMocks
lateinit var snmpRoute: SnmpRoute
#EndpointInject(SnmpConstants.MOCK_SNMP_ENDPOINT)
lateinit var mock: MockEndpoint
#Before
fun setup() {
initMocks(this)
}
#Throws(Exception::class)
override fun createRouteBuilder(): RouteBuilder {
return snmpRoute
}
#Test
#Throws(Exception::class)
fun `Test SNMP endpoint`() {
mock.expectedBodiesReceived(SnmpConstants.SNMP_TRAP)
template.sendBody(SnmpConstants.MOCK_SNMP_ENDPOINT,
SnmpConstants.SNMP_TRAP)
mock.assertIsSatisfied()
verify(repository).save(PduEvent(1234, PDU.TRAP))
}
}
However, when I run this test, it fails as the repository mock never has any interactions:
Wanted but not invoked:
repository.save(
PduEvent(requestId=1234, type=-89)
);
-> at org.meanwhile.in.hell.camel.snmp.route.SnmpRouteTest.Test SNMP endpoint(SnmpRouteTest.kt:61)
Actually, there were zero interactions with this mock.
Can someone help me understand why this isn't interacting correctly? When run manually, this works and saves as expected.
Now I see what is going on here!
Your RouteBuilder under test has a from("snmp"). If you wish to deliver a mock message there for testing, you need to swap the snmp: component with something like a direct: or seda: component, during test execution.
Your current test is delivering a message to a Mock endpoint and verifying if it was received there. It does not interact with the real route builder. That's why your mock endpoint assertions do passed but Mockito.verify() failed.
TL;DR
Presuming that you are using Apache Camel 3.x, here is how to do it. I'm not fluent in Kotlin so, I'll show how to do that in Java.
AdviceWithRouteBuilder.adviceWith(context, "route-id", routeBuilder -> {
routeBuilder.replaceFromWith("direct:snmp-from"); //Replaces the from part of the route `route-id` with a direct component
});
You need to modify your route builder code to assign an ID to the route (say, route-id)
Replace the SNMP component at the start of the route with a direct component
Deliver test messages to the direct: component instead of SNMP
TL;DR ends.
Full blown sample code below.
PojoRepo.java
#Component
public class PojoRepo {
public void save(String body){
System.out.println(body);
}
}
SNMPDummyRoute.java
#Component
public class SNMPDummyRoute extends RouteBuilder {
PojoRepo pojoRepo;
public SNMPDummyRoute(PojoRepo pojoRepo) {
this.pojoRepo = pojoRepo;
}
#Override
public void configure() throws Exception {
from("snmp:0.0.0.0:1161?protocol=udp&type=TRAP")
.id("snmp-route")
.process(exchange -> {
exchange.getMessage().setBody(String.format("Saw message [%s]", exchange.getIn().getBody()));
})
.to("log:snmp-log")
.bean(pojoRepo, "save");
}
}
SNMPDummyRoteTest.java
Note: This class uses CamelSpringBootRunner instead of extending CamelTestSupport, but the core idea is same.
#RunWith(CamelSpringBootRunner.class)
#SpringBootTest
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
#DisableJmx(false)
#MockEndpoints("log:*")
public class SNMPDummyRouteTest {
#MockBean
PojoRepo repo;
#EndpointInject("mock:log:snmp-log")
MockEndpoint mockEndpoint;
#Produce
ProducerTemplate testTemplate;
#Autowired
CamelContext camelContext;
#Test
public void testRoute() throws Exception {
AdviceWithRouteBuilder.adviceWith(camelContext,"snmp-route",routeBuilder -> {
routeBuilder.replaceFromWith("direct:snmp-from");
});
testTemplate.sendBody("direct:snmp-from","One");
testTemplate.sendBody("direct:snmp-from","Two");
mockEndpoint.expectedMinimumMessageCount(2);
mockEndpoint.setAssertPeriod(2_000L);
mockEndpoint.assertIsSatisfied();
Mockito.verify(repo, Mockito.atLeast(2)).save(anyString());
}
}
Logs from test run below. Take a closer look at the XML piece where the SNMP endpoint gets swapped in with a direct component.
2019-11-12 20:52:57.126 INFO 32560 --- [ main] o.a.c.component.snmp.SnmpTrapConsumer : Starting trap consumer on udp:0.0.0.0/1161
2019-11-12 20:52:58.363 INFO 32560 --- [ main] o.a.c.component.snmp.SnmpTrapConsumer : Started trap consumer on udp:0.0.0.0/1161 using udp protocol
2019-11-12 20:52:58.364 INFO 32560 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Route: snmp-route started and consuming from: snmp://udp:0.0.0.0/1161
2019-11-12 20:52:58.368 INFO 32560 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Total 1 routes, of which 1 are started
2019-11-12 20:52:58.370 INFO 32560 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Apache Camel 3.0.0-M4 (CamelContext: MyCamel) started in 2.645 seconds
2019-11-12 20:52:59.670 INFO 32560 --- [ main] o.a.c.i.engine.DefaultShutdownStrategy : Starting to graceful shutdown 1 routes (timeout 10 seconds)
2019-11-12 20:52:59.680 INFO 32560 --- [ - ShutdownTask] o.a.c.component.snmp.SnmpTrapConsumer : Stopped trap consumer on udp:0.0.0.0/1161
2019-11-12 20:52:59.683 INFO 32560 --- [ - ShutdownTask] o.a.c.i.engine.DefaultShutdownStrategy : Route: snmp-route shutdown complete, was consuming from: snmp://udp:0.0.0.0/1161
2019-11-12 20:52:59.684 INFO 32560 --- [ main] o.a.c.i.engine.DefaultShutdownStrategy : Graceful shutdown of 1 routes completed in 0 seconds
2019-11-12 20:52:59.687 INFO 32560 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Route: snmp-route is stopped, was consuming from: snmp://udp:0.0.0.0/1161
2019-11-12 20:52:59.689 INFO 32560 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Route: snmp-route is shutdown and removed, was consuming from: snmp://udp:0.0.0.0/1161
2019-11-12 20:52:59.691 INFO 32560 --- [ main] o.apache.camel.builder.AdviceWithTasks : AdviceWith replace input from [snmp:0.0.0.0:1161?protocol=udp&type=TRAP] --> [direct:snmp-from]
2019-11-12 20:52:59.692 INFO 32560 --- [ main] org.apache.camel.reifier.RouteReifier : AdviceWith route after: Route(snmp-route)[From[direct:snmp-from] -> [process[Processor#0x589dfa6f], To[log:snmp-log], Bean[org.foo.bar.POJORepo$MockitoMock$868728200]]]
2019-11-12 20:52:59.700 INFO 32560 --- [ main] org.apache.camel.reifier.RouteReifier : Adviced route before/after as XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<route xmlns="http://camel.apache.org/schema/spring" customId="true" id="snmp-route">
<from uri="snmp:0.0.0.0:1161?protocol=udp&type=TRAP"/>
<process id="process1"/>
<to id="to1" uri="log:snmp-log"/>
<bean id="bean1" method="save"/>
</route>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<route xmlns="http://camel.apache.org/schema/spring" customId="true" id="snmp-route">
<from uri="direct:snmp-from"/>
<process id="process1"/>
<to id="to1" uri="log:snmp-log"/>
<bean id="bean1" method="save"/>
</route>
2019-11-12 20:52:59.734 INFO 32560 --- [ main] .i.e.InterceptSendToMockEndpointStrategy : Adviced endpoint [log://snmp-log] with mock endpoint [mock:log:snmp-log]
2019-11-12 20:52:59.755 INFO 32560 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Route: snmp-route started and consuming from: direct://snmp-from
2019-11-12 20:52:59.834 INFO 32560 --- [ main] snmp-log : Exchange[ExchangePattern: InOnly, BodyType: String, Body: Saw message [One]]
2019-11-12 20:52:59.899 INFO 32560 --- [ main] snmp-log : Exchange[ExchangePattern: InOnly, BodyType: String, Body: Saw message [Two]]
2019-11-12 20:52:59.900 INFO 32560 --- [ main] o.a.camel.component.mock.MockEndpoint : Asserting: mock://log:snmp-log is satisfied
2019-11-12 20:53:01.903 INFO 32560 --- [ main] o.a.camel.component.mock.MockEndpoint : Re-asserting: mock://log:snmp-log is satisfied after 2000 millis
2019-11-12 20:53:01.992 INFO 32560 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Apache Camel 3.0.0-M4 (CamelContext: MyCamel) is shutting down
2019-11-12 20:53:01.993 INFO 32560 --- [ main] o.a.c.i.engine.DefaultShutdownStrategy : Starting to graceful shutdown 1 routes (timeout 10 seconds)
2019-11-12 20:53:01.996 INFO 32560 --- [ - ShutdownTask] o.a.c.i.engine.DefaultShutdownStrategy : Route: snmp-route shutdown complete, was consuming from: direct://snmp-from
2019-11-12 20:53:01.996 INFO 32560 --- [ main] o.a.c.i.engine.DefaultShutdownStrategy : Graceful shutdown of 1 routes completed in 0 seconds

Why Spring Boot Application logs that it started twice after adding spring-cloud-bus dependency

This is simple code in my Spring boot application:
package com.maxxton.SpringBootHelloWorld;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class SpringBootHelloWorldApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootHelloWorldApplication.class, args);
}
}
And a ApplicationListener class to listen to ApplicationEvent:
package com.maxxton.SpringBootHelloWorld;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
#Component
public class Test implements ApplicationListener {
#Override
public void onApplicationEvent(ApplicationEvent event) {
if (event.getClass().getSimpleName().equals("ApplicationReadyEvent")) {
System.out.println("-------------------------------------");
System.out.println(event.getClass().getSimpleName());
System.out.println("-------------------------------------");
}
}
}
build.gradle contains these dependencies:
dependencies {
compile("org.springframework.boot:spring-boot-starter-amqp")
compile("org.springframework.cloud:spring-cloud-starter-bus-amqp")
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter')
compile("org.springframework.cloud:spring-cloud-starter")
compile("org.springframework.cloud:spring-cloud-starter-security")
compile("org.springframework.cloud:spring-cloud-starter-eureka")
testCompile('org.springframework.boot:spring-boot-starter-test')
}
Now, when I run this spring boot application, I see this log printed twice:
[main] c.m.S.SpringBootHelloWorldApplication : Started SpringBootHelloWorldApplication in ... seconds (JVM running for ...)
Usually, this log get printed only once, but it get printed twice if I add these dependencies:
compile("org.springframework.boot:spring-boot-starter-amqp")
compile("org.springframework.cloud:spring-cloud-starter-bus-amqp")
This is complete log:
2017-11-17 15:44:07.372 INFO 5976 --- [ main] o.s.c.support.GenericApplicationContext : Refreshing org.springframework.context.support.GenericApplicationContext#31c7c281: startup date [Fri Nov 17 15:44:07 IST 2017]; root of context hierarchy
-------------------------------------
ApplicationReadyEvent
-------------------------------------
2017-11-17 15:44:07.403 INFO 5976 --- [ main] c.m.S.SpringBootHelloWorldApplication : Started SpringBootHelloWorldApplication in 1.19 seconds (JVM running for 10.231)
2017-11-17 15:44:09.483 WARN 5976 --- [ main] o.s.amqp.rabbit.core.RabbitAdmin : Failed to declare exchange: Exchange [name=springCloudBus, type=topic, durable=true, autoDelete=false, internal=false, arguments={}], continuing... org.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused: connect
2017-11-17 15:44:09.492 INFO 5976 --- [ main] o.s.integration.channel.DirectChannel : Channel 'a-bootiful-client.springCloudBusOutput' has 1 subscriber(s).
2017-11-17 15:44:09.493 INFO 5976 --- [ main] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 0
2017-11-17 15:44:09.530 INFO 5976 --- [ main] o.s.i.endpoint.EventDrivenConsumer : Adding {logging-channel-adapter:_org.springframework.integration.errorLogger} as a subscriber to the 'errorChannel' channel
2017-11-17 15:44:09.530 INFO 5976 --- [ main] o.s.i.channel.PublishSubscribeChannel : Channel 'a-bootiful-client.errorChannel' has 1 subscriber(s).
2017-11-17 15:44:09.530 INFO 5976 --- [ main] o.s.i.endpoint.EventDrivenConsumer : started _org.springframework.integration.errorLogger
2017-11-17 15:44:09.530 INFO 5976 --- [ main] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 2147482647
2017-11-17 15:44:09.539 INFO 5976 --- [ main] c.s.b.r.p.RabbitExchangeQueueProvisioner : declaring queue for inbound: springCloudBus.anonymous.kZ1vvxHaRfChKe1TncH-MQ, bound to: springCloudBus
2017-11-17 15:44:11.562 WARN 5976 --- [ main] o.s.amqp.rabbit.core.RabbitAdmin : Failed to declare exchange: Exchange [name=springCloudBus, type=topic, durable=true, autoDelete=false, internal=false, arguments={}], continuing... org.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused: connect
2017-11-17 15:44:13.587 WARN 5976 --- [ main] o.s.amqp.rabbit.core.RabbitAdmin : Failed to declare queue: Queue [name=springCloudBus.anonymous.kZ1vvxHaRfChKe1TncH-MQ, durable=false, autoDelete=true, exclusive=true, arguments={}], continuing... org.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused: connect
2017-11-17 15:44:15.611 WARN 5976 --- [ main] o.s.amqp.rabbit.core.RabbitAdmin : Failed to declare binding: Binding [destination=springCloudBus.anonymous.kZ1vvxHaRfChKe1TncH-MQ, exchange=springCloudBus, routingKey=#], continuing... org.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused: connect
2017-11-17 15:44:17.662 INFO 5976 --- [ main] o.s.i.a.i.AmqpInboundChannelAdapter : started inbound.springCloudBus.anonymous.kZ1vvxHaRfChKe1TncH-MQ
2017-11-17 15:44:17.662 INFO 5976 --- [ main] o.s.i.endpoint.EventDrivenConsumer : Adding {message-handler:inbound.springCloudBus.default} as a subscriber to the 'bridge.springCloudBus' channel
2017-11-17 15:44:17.662 INFO 5976 --- [ main] o.s.i.endpoint.EventDrivenConsumer : started inbound.springCloudBus.default
2017-11-17 15:44:17.663 INFO 5976 --- [ main] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 2147483647
2017-11-17 15:44:17.714 INFO 5976 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
-------------------------------------
ApplicationReadyEvent
-------------------------------------
2017-11-17 15:44:17.717 INFO 5976 --- [ main] c.m.S.SpringBootHelloWorldApplication : Started SpringBootHelloWorldApplication in 20.131 seconds (JVM running for 20.545)
As you can see, ApplicationReadyEvent is happening twice.
Why is this happening?
Is there any way to avoid this?
spring-cloud-bus uses spring-cloud-stream which puts the binder in a separate boot child application context.
You should make your event listener aware of the application context it is running in. You can also use generics to select the event type you are interested in...
#Component
public class Test implements ApplicationListener<ApplicationReadyEvent>,
ApplicationContextAware {
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
if (event.getApplicationContext().equals(this.applicationContext)) {
System.out.println("-------------------------------------");
System.out.println(event.getClass().getSimpleName());
System.out.println("-------------------------------------");
}
}
}
Are u using multiple binders rabbitmq configuration in your application.yml/.xml ?
If it's a yes, then u can try to exclude RabbitAutoConfiguration.
#EnableDiscoveryClient
#EnableAutoConfiguration(exclude = {RabbitAutoConfiguration.class})
#SpringBootApplication
public class SpringBootHelloWorldApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootHelloWorldApplication.class, args);
}
}

Resources