Apache Camel error Because of ref must be specified on: process?

What I am trying to do is read an mail inbox and looking for emails where the Subject line contains the word "ERROR".

If it find an email which does not hold the word "ERROR" in the subject line, then it must go to the onException process.

It sort of work up until a point when, I think it tries to process, and then I get an error.

public class MailRouteBuilder extends RouteBuilder {

   @Autowired
   ExceptionMailProcessor exceptionMailProcessor;

   @Override
   public void configure() throws Exception {
      String mailRouteStr = "imap://host.......";

      List<String> tmpKeywords = new ArrayList<>();
      tmpKeywords.add("ERROR"):

      onException(MailRouteException.class, RuntimeException.class).log(LoggingLevel.ERROR, "EXCEPTION in MAIL")
                .process(exceptionMailProcessor)
                .end();

      from(mailRouteStr).routeId("mymail")
             .validate(header("subject").in(tmpKeywords))
             .log("Sending to Mail Queue")
             .to("{{jmsMailIn}}")
             .log("Done with Mail Queue")
             .end();
    }
.
.
.

My ExceptionMailProcessor class.

@Component
public class ExceptionMailProcessor implements Processor {

    @Override
    public void process(Exchange exchange) throws Exception {

        String messageBody;
        Exception e = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);

    }
}

The error:

org.apache.camel.FailedToCreateRouteException: Failed to create route mymail at: >>> OnException[[class MailRouteException, class java.lang.RuntimeException] -> [Log[EXCEPTION in MAIL], process[Processor@0x0]]] <<< in route: Route(mymail)[[From[imap://mai... because of ref must be specified on: process[Processor@0x0]
    at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:1352)
    at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:212)
    at org.apache.camel.impl.DefaultCamelContext.startRoute(DefaultCamelContext.java:1140)
    at org.apache.camel.impl.DefaultCamelContext.startRouteDefinitions(DefaultCamelContext.java:3735)
    at org.apache.camel.impl.DefaultCamelContext.addRouteDefinitions(DefaultCamelContext.java:1072)
    at org.apache.camel.builder.RouteBuilder.populateRoutes(RouteBuilder.java:478)
    at org.apache.camel.builder.RouteBuilder.addRoutesToCamelContext(RouteBuilder.java:391)
    at org.apache.camel.impl.DefaultCamelContext$3.call(DefaultCamelContext.java:1024)
    at org.apache.camel.impl.DefaultCamelContext$3.call(DefaultCamelContext.java:1021)
    at org.apache.camel.impl.DefaultCamelContext.doWithDefinedClassLoader(DefaultCamelContext.java:3267)
    at org.apache.camel.impl.DefaultCamelContext.addRoutes(DefaultCamelContext.java:1021)
    at com.agile.net.international.mc360.routing.routebuilder.process.mail.MailRouteProcess.lambda$checkMailDataSourceRoutes$0(MailRouteProcess.java:135)
    at java.util.HashMap.forEach(HashMap.java:1289)
    at com.agile.net.international.mc360.routing.routebuilder.process.mail.MailRouteProcess.checkMailDataSourceRoutes(MailRouteProcess.java:131)
    at com.agile.net.international.mc360.routing.routebuilder.process.mail.MailRouteProcess.process(MailRouteProcess.java:60)
    at org.apache.camel.processor.DelegateSyncProcessor.process(DelegateSyncProcessor.java:63)
    at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:548)
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:201)
    at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:201)
    at org.apache.camel.component.timer.TimerConsumer.sendTimerExchange(TimerConsumer.java:197)
    at org.apache.camel.component.timer.TimerConsumer$1.run(TimerConsumer.java:79)
    at java.util.TimerThread.mainLoop(Timer.java:555)
    at java.util.TimerThread.run(Timer.java:505)
Caused by: java.lang.IllegalArgumentException: ref must be specified on: process[Processor@0x0]
    at org.apache.camel.util.ObjectHelper.notNull(ObjectHelper.java:340)
    at org.apache.camel.model.ProcessDefinition.createProcessor(ProcessDefinition.java:99)
    at org.apache.camel.model.ProcessorDefinition.createProcessor(ProcessorDefinition.java:518)
    at org.apache.camel.model.ProcessorDefinition.createOutputsProcessorImpl(ProcessorDefinition.java:481)
    at org.apache.camel.model.ProcessorDefinition.createOutputsProcessor(ProcessorDefinition.java:448)
    at org.apache.camel.model.ProcessorDefinition.createOutputsProcessor(ProcessorDefinition.java:186)
    at org.apache.camel.model.OnExceptionDefinition.addRoutes(OnExceptionDefinition.java:210)
    at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:1349)
    ... 22 more

The class MailRouteProcess in the error is just where I build my imap string and adding it to the camel context.

All I want is for when the Route fails that it goes to the ExceptionMailProcessor. There I will do some stuff and the actual mail message must be marked as READ and not processed again.


Rather than trying to Autowire in an instance of your ExceptionMailProcessor, try referencing it as a bean in the DSL. Normally for my processors in Camel, I use a Spring Configuration class to register the processors as beans and then reference them in the DSL:

@Configuration
public class AppConfig {

  @Bean(name = "ExceptionMailProcessor ")
  public ExceptionMailProcessor exceptionMailProcessor() {
    return new ExceptionMailProcessor();
  }
}

Then in your Camel DSL you can reference it like this:

onException(MailRouteException.class, RuntimeException.class).log(LoggingLevel.ERROR, "EXCEPTION in MAIL")
                .to("bean:ExceptionMailProcessor")
                .end();

What works for me and needs less code, is to use Constructor injection (its also better practice):

public class MailRouteBuilder extends RouteBuilder {

   
   private final ExceptionMailProcessor exceptionMailProcessor;

   public MailRouteBuilder(ExceptionMailProcessor exceptionMailProcessor){
       this.exceptionMailProcessor = exceptionMailProcessor;
   } 

   @Override
   public void configure() throws Exception {
      String mailRouteStr = "imap://host.......";

      List<String> tmpKeywords = new ArrayList<>();
      tmpKeywords.add("ERROR"):

      onException(MailRouteException.class, RuntimeException.class).log(LoggingLevel.ERROR, "EXCEPTION in MAIL")
                .process(exceptionMailProcessor)
                .end();

      from(mailRouteStr).routeId("mymail")
             .validate(header("subject").in(tmpKeywords))
             .log("Sending to Mail Queue")
             .to("{{jmsMailIn}}")
             .log("Done with Mail Queue")
             .end();
    }
 ...
}

Spring will imply the Autowire here (no need to add). Because of this and if you use Lombok you can omit the ctor and just add @RequiredArgsConstructor at class. this will add a ctor for every final field (what is good practice for all injected stuff).

Actually i came here because i stumbled across this error because i forgot to add "final" at processor field and thus Lombok didn't add it to ctor.