I am using Apache Camel to implement Rest APIs. I've 2 RouteBuilder types defining all the Camel routes, my application needs. All REST endpoints reside in RestRouter, and it frames the execution using CustomRouter. For example, I've RestRouter to hold my REST routes
public class RestRouter extends RouteBuilder
{
@Override
public void configure() throws Exception
{
rest("/sample")
.post()
.route()
.routeId("postSample")
.to("direct:validate")
.to("direct:save")
.endRest();
}
}
And another RouteBuilder called CustomRouter to bundle non-REST routes.
public class CustomRouter extends RouteBuilder
{
@Override
public void configure() throws Exception
{
onException(ValidationException.class)
.handled(true)
.setBody(simple("${exchangeProperty[CamelExceptionCaught]}"))
.to("bean:exceptionHandler?method=constraintViolationHandler")
.setHeader(Exchange.CONTENT_TYPE, constant(ErrorResponse.class.getName()))
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(HttpStatus.SC_BAD_REQUEST));
validator()
.type(Sample.class)
.withBean("sampleValidator");
from("direct:validate")
.to("bean-validator://x"); // Runs javax/Hibernate annotated validations
from("direct:save")
.routeId("saveSample")
.inputTypeWithValidate(Sample.class)
.to("bean:sampleRepository?method=save");
}
}
Validation bean SampleValidator is a Camel org.apache.camel.spi.Validator which throws org.apache.camel.ValidationException for any violations.
Problem with setup is that Camel doesn't invoke my custom exception handler for ValidationException. Validation exception occurs for route saveSample. Here's my finding on how it goes further inside Camel processor types.
- Control goes to
RedeliveryErrorHandler's handleException()where it looks for the exception policy. Root of failing exchange (i.e.RestRouter-> postSample) is expected here to define the exception handler. - Later, Camel goes to failing
UnitOfWork(i.e. to saveSample) to identify the exception handler. That means, for below expression, routeId is from CustomRouter and exceptionPolicy is from the RestRouter. Combination never exists and Camel fails to find the exception processor.
processor = exceptionPolicy.getErrorHandler(routeId)
In above context, I've following questions
- Is it a good practice to divide a functionality within multiple
RouterBuildertypes? - Shouldn't Camel use current
UnitOfWorkto resolve the exception policy? - Is there some way Camel can invoke my custom handler, provided different
RouteBuildertypes?
Edit
I can't move to have a single RouterBuilder.
- One, because I've an Apache Avro object coming in payload for post from another orchestration service, and then transformation to my JPA entities is done via the bean methods, not using Camel's
Transformer. This arrangement doesn't fit with how Camel invokes theValidator(seeContractAdvice). ContractAdvice is aCamelInternalProcessorAdvicewhich appliesTransformer(if intype != outtype) andValidator. - Second, moving to single
RouterBuilderwill need me to move Avro-to-Entity logic to a CamelTransformer, and that approach would differ greatly with the way we're doing currently. But yes, single RouterBuilder + Transformer + Validator should work.