Search code examples
javaspring-bootapache-camel

Allow Camel to start up even if the "from" endpoint is currently not available


I have a camel application running with Spring Boot. I want to improve resilience by letting my application start up even if one or more of the "from" endpoints (MQ or SMPP in my case) are not currently available - I want the application to just keep checking, and pick up messages once the endpoint becomes available.

Current code:

// Application.java
@SpringBootApplication
@ConfigurationPropertiesScan
public class Application 
{   
    @Autowired private CamelContext context;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
   @Bean
    CommandLineRunner init(ProducerTemplate template) 
    {
        return args -> 
        {
            context.addRoutes(new MQRouteBuilder());
            context.addRoutes(new SMPPRouteBuilder());
        };
    }
}

// example route builder
public class SMPPRouteBuilder extends RouteBuilder
{
    public void configure() throws Exception
    {
        from("smpp://{{camel.component.smpp.configuration.host}}").to("direct:processSMS");
        //...
    }
}

Currently, if the SMS-C defined as camel.component.smpp.configuration.host isn't available, the application fails to start:

java.lang.IllegalStateException: Failed to execute CommandLineRunner at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:787) ~[spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE] at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:768) ~[spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:322) ~[spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) ~[spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE] at Application.main(Application.java:28) ~[classes/:na] Caused by: org.apache.camel.RuntimeCamelException: java.io.IOException: localhostInvalid at org.apache.camel.RuntimeCamelException.wrapRuntimeException(RuntimeCamelException.java:68) ~[camel-api-3.1.0.jar:3.1.0] at org.apache.camel.support.service.ServiceSupport.start(ServiceSupport.java:134) ~[camel-api-3.1.0.jar:3.1.0] at org.apache.camel.impl.engine.AbstractCamelContext.startService(AbstractCamelContext.java:2989) ~[camel-base-3.1.0.jar:3.1.0] at org.apache.camel.impl.engine.AbstractCamelContext.doStartOrResumeRouteConsumers(AbstractCamelContext.java:3327) ~[camel-base-3.1.0.jar:3.1.0] at org.apache.camel.impl.engine.AbstractCamelContext.doStartRouteConsumers(AbstractCamelContext.java:3258) ~[camel-base-3.1.0.jar:3.1.0] at org.apache.camel.impl.engine.AbstractCamelContext.safelyStartRouteServices(AbstractCamelContext.java:3163) ~[camel-base-3.1.0.jar:3.1.0] at org.apache.camel.impl.engine.AbstractCamelContext.safelyStartRouteServices(AbstractCamelContext.java:3189) ~[camel-base-3.1.0.jar:3.1.0] at org.apache.camel.impl.engine.AbstractCamelContext.startRouteService(AbstractCamelContext.java:3033) ~[camel-base-3.1.0.jar:3.1.0] at org.apache.camel.impl.DefaultModel.start(DefaultModel.java:358) ~[camel-core-engine-3.1.0.jar:3.1.0] at org.apache.camel.impl.DefaultModel.startRoute(DefaultModel.java:330) ~[camel-core-engine-3.1.0.jar:3.1.0] at org.apache.camel.impl.DefaultModel.startRouteDefinitions(DefaultModel.java:323) ~[camel-core-engine-3.1.0.jar:3.1.0] at org.apache.camel.impl.DefaultModel.addRouteDefinitions(DefaultModel.java:88) ~[camel-core-engine-3.1.0.jar:3.1.0] at org.apache.camel.impl.AbstractModelCamelContext.addRouteDefinitions(AbstractModelCamelContext.java:110) ~[camel-core-engine-3.1.0.jar:3.1.0] at org.apache.camel.builder.RouteBuilder.populateRoutes(RouteBuilder.java:520) ~[camel-core-engine-3.1.0.jar:3.1.0] at org.apache.camel.builder.RouteBuilder.addRoutesToCamelContext(RouteBuilder.java:437) ~[camel-core-engine-3.1.0.jar:3.1.0] at org.apache.camel.impl.engine.AbstractCamelContext.lambda$addRoutes$0(AbstractCamelContext.java:1160) ~[camel-base-3.1.0.jar:3.1.0] at org.apache.camel.impl.engine.AbstractCamelContext.doWithDefinedClassLoader(AbstractCamelContext.java:2544) ~[camel-base-3.1.0.jar:3.1.0] at org.apache.camel.impl.engine.AbstractCamelContext.addRoutes(AbstractCamelContext.java:1160) ~[camel-base-3.1.0.jar:3.1.0] at Application.lambda$0(Application.java:42) ~[classes/:na] at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:784) ~[spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE] ... 5 common frames omitted Caused by: java.io.IOException: localhostInvalid at org.apache.camel.component.smpp.SmppConnectionFactory.createConnection(SmppConnectionFactory.java:112) ~[camel-smpp-3.1.0.jar:3.1.0] at org.jsmpp.session.SMPPSession.connectAndBind(SMPPSession.java:222) ~[jsmpp-2.3.8.jar:2.3.8] at org.jsmpp.session.SMPPSession.connectAndBind(SMPPSession.java:201) ~[jsmpp-2.3.8.jar:2.3.8] at org.apache.camel.component.smpp.SmppConsumer.createSession(SmppConsumer.java:95) ~[camel-smpp-3.1.0.jar:3.1.0] at org.apache.camel.component.smpp.SmppConsumer.doStart(SmppConsumer.java:84) ~[camel-smpp-3.1.0.jar:3.1.0] at org.apache.camel.support.service.ServiceSupport.start(ServiceSupport.java:121) ~[camel-api-3.1.0.jar:3.1.0] ... 23 common frames omitted Caused by: java.net.UnknownHostException: localhostInvalid at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:229) ~[na:na] at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) ~[na:na] at java.base/java.net.Socket.connect(Socket.java:609) ~[na:na] at org.apache.camel.component.smpp.SmppConnectionFactory.createConnection(SmppConnectionFactory.java:99) ~[camel-smpp-3.1.0.jar:3.1.0]

What I want instead, is for the application to start up, start listening on the connections it can connect to (e.g. MQ), and just periodically retry the ones it can't until they become available.


Solution

  • I had the same problem but with JMS component.

    Here is what I did:

    1. Set an id on my route (.routeId("demo"))

    2. Put my route with .autostart(false)

    3. Add to the context an extra system-oriented route with a from("timer:") in order to regularly monitor the health of (in my case) the JMS broker. When the broker is detected "up", then a Camel processor can simply (via Camel API) ask te controller to restart the involved route(s): context.getRouteController().start("demo")

    Hope this helps