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
RouterBuilder
types? - Shouldn't Camel use current
UnitOfWork
to resolve the exception policy? - Is there some way Camel can invoke my custom handler, provided different
RouteBuilder
types?
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 aCamelInternalProcessorAdvice
which appliesTransformer
(if intype != outtype) andValidator
. - Second, moving to single
RouterBuilder
will 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.