Apache-camel - custom error handling - java

Is possible to implement some switch before error handler of apache-camel?
Something like: If it is MyException.class, then use default error handler otherwise use dead letter channel for handling an error.
I have try to use but seems this cannot be set globaly so easy as it has to be in method configure() of each route.

Yes you can have a generic error handler.
In the configure method I have done like this:
public void configure() throws Exception {
ExceptionBuilder.setup(this);
...
}
The ExceptionBuilder class look like this:
public class ExceptionBuilder {
public static void setup(RouteBuilder routeBuilder) {
routeBuilder.onException(Exception.class).useOriginalMessage().handled(true).to("direct:errorHandler");
}
}
Finally in the error handler configure it to your requirements. That means, save the body and headers to log file or send them to a jms queue or stop the processing or anything else. That is up to you. You simply configure it once and refer to it from all your routeBuilder classes.

Global scope for the errorHandler is only per RouteBuilder instance. You will need to create a base RouteBuilder class that contains the error handling logic in its configure() method and then extend all of your other routes from it (not forgetting to call super.configure()).
You can use a combination of errorHandler as a catch-all for exceptions, with specific exceptions handled by onException()
errorHandler(deadLetterChannel("mock:generalException"));
onException(NullPointerException.class)
.handled(true)
.to("mock:specificException");
Any routes with these handlers will send exchanges that throw a NullPointerException to the endpoint "mock:specificException". Any other exceptions thrown will be handled by the errorHandler, and the exchange will be sent to "mock:generalException".
http://camel.apache.org/error-handler.html
http://camel.apache.org/exception-clause.html

Use try-catch in camel route
.doTry()
.to("bean:<beanName>?method=<method>")
.endDoTry()
.doCatch(MyException.class)
.to("bean:<beanName>?method=<method1>")
.doCatch(Exception.class)
.to("bean:<beanName>?method=<method2>")

Solution:
I have used DeadLetterChannelBuilder as error handler with failureProcessor and deadLetterHandleNewException as false, that did the check what I needed (rethrowing exception/hide exception).
Thanks for advice anyway, it led me to the right way.

Related

Custom #ControllerAdvice in Spring for exception handling

I am trying to map exceptions from my rest controllers to responses which have a body, and to do it in a central place.
I have tried this:
#Order(Ordered.HIGHEST_PRECEDENCE)
#ControllerAdvice
public class RestErrorResponseExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleExceptionInternal(
Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
super.handleExceptionInternal(ex, body, headers, status, request);
return ResponseEntity.status(status).body(Error.from(status));
}
}
The problem is that the handler is never triggered.
If I define a custom method with #ExceptionHandler in my rest controllers, or extend something that has #ExceptionHandler, then all works well, but that introduces some bad design.
It is my understanding that Spring will first try to look in controller for exception handling methods, then it will check for registered handlers.
I am trying to verify the behaviour via WebMvcTest, and responses I'm getting are not the Error objects that I'm expecting.
Is there something I'm missing?
The ControllerAdvice is a configuration that have to be registered by Spring. You have to move your class in the config package or you can register it by annotation.
In my case, I work with a controllerAdvice like this one :
#ControllerAdvice
public class GlobalControllerExceptionHandler {
#ExceptionHandler(MyException.class)
public ResponseEntity<String> reponseMyException(Exception e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("my message");
}
}
Spring Framework provides following ways to help us achieving robust exception handling.
Controller Based – We can define exception handler methods in our controller classes. All we need is to annotate these methods with #ExceptionHandler annotation. This annotation takes Exception class as argument. So if we have defined one of these for Exception class, then all the exceptions thrown by our request handler method will have handled.
These exception handler methods are just like other request handler methods and we can build error response and respond with different error page. We can also send JSON error response, that we will look later on in our example.
If there are multiple exception handler methods defined, then handler method that is closest to the Exception class is used. For example, if we have two handler methods defined for IOException and Exception and our request handler method throws IOException, then handler method for IOException will get executed.
Global Exception Handler – Exception Handling is a cross-cutting concern, it should be done for all the pointcuts in our application. We have already looked into Spring AOP and that’s why Spring provides #ControllerAdvice annotation that we can use with any class to define our global exception handler.
The handler methods in Global Controller Advice is same as Controller based exception handler methods and used when controller class is not able to handle the exception.
HandlerExceptionResolver – For generic exceptions, most of the times we serve static pages. Spring Framework provides HandlerExceptionResolver interface that we can implement to create global exception handler. The reason behind this additional way to define global exception handler is that Spring framework also provides default implementation classes that we can define in our spring bean configuration file to get spring framework exception handling benefits.
SimpleMappingExceptionResolver is the default implementation class, it allows us to configure exceptionMappings where we can specify which resource to use for a particular exception. We can also override it to create our own global handler with our application specific changes, such as logging of exception messages.
Make sure of 2 things and your code will work.
Your #ControllerAdvice class is available in component-scan path.
Make sure the methods in your #ControllerAdvice have structure somewhat like this-
#ExceptionHandler(value = { RequestProcessingException.class })
public #ResponseBody ResponseEntity<ErrorMessageBO> hotelConfigServiceExceptionHandler(HttpServletRequest request, RequestProcessingException e) {
logger.error("Exception with tracking Id: {}, dev message: {} and Message:", RequestContextKeeper.getContext().getRequestId(), e.getDeveloperMessage(),e);
return new ResponseEntity<ErrorMessageBO>(new ErrorMessageBO(e.getErrorCode(), e.getMessage(),RequestContextKeeper.getContext().getRequestId(),e.getDeveloperMessage()), HttpStatus.OK);
}

Apache Camel - Exception handling with multiple RouteBuilders

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 the Validator (seeContractAdvice). ContractAdvice is a CamelInternalProcessorAdvice which applies Transformer (if intype != outtype) and Validator.
Second, moving to single RouterBuilder will need me to move Avro-to-Entity logic to a Camel Transformer, and that approach would differ greatly with the way we're doing currently. But yes, single RouterBuilder + Transformer + Validator should work.
Have a look at this example from Camel In Action which demonstrates how to reuse the error-handling across route builders defined in Java DSL.
BaseRouteBuilder
and InboxRouteBuilder and OrderRouteBuilder
You can create a base class where you setup the context-scoped error configuration.Then your RouteBuilder classes are extending this base class and calling calling super.configure to get the common configuration.
See if it works when you have all the routes in a single RouteBuilder. "Global" exception handlers such as yours are not really global as they are applied to all routes built by that specific builder, so I wouldn't expect your onException to be applied to the REST route.
Alternatively move the onException in to the REST builder. The handler sets HTTP status codes, so on the surface looks like it would be better packaged with REST routes.

Spring #Controller exception handler and global exception handler. How to invoke both

I have a global exception handler to share across REST #Controllers. For this I use a #ControllerAdvice with some #ExceptionHandler methods. This works fine. Now, if I add an #ExceptionHandler in a particular Rest Controller then that new handler takes precedence over the global exception handler and the global one is just never called.
What I need is actually to have both called. The order doesn't matter. The point is that there is some global, controller-agnostic error handling code and also some controller-specific error handling and I need both to execute. Is this possible? e.g. Can I somehow in the controller-specific handler (which is called first) mark the exception handling as not handled so the next handler in line is invoked?
I know I could inject the #ControllerAdvice in the #Controller and invoke the global handler from the specific one myself, but I rather keep the controller decoupled from the global exception handler
I don't think you can do this with out-of-the-box Spring. If you look under the hood at this method ExceptionHandlerExceptionResolver#doResolveHandlerMethodException, you can see that at first Spring looking for single method that will handle occurred exception:
...
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
}
...
You can also look at the implementation of getExceptionHandlerMethod method. First its trying to find appropriate handler within you controller methods, if nothing found - then within controller advisors.
After that it invokes it:
try {
if (logger.isDebugEnabled()) {
logger.debug("Invoking #ExceptionHandler method: " + exceptionHandlerMethod);
}
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
}
catch (Exception invocationEx) {
if (logger.isErrorEnabled()) {
logger.error("Failed to invoke #ExceptionHandler method: " + exceptionHandlerMethod, invocationEx);
}
return null;
}
You should also note that Spring swallows any exception that might occur during original exception handling, so you can't even throw new exception from your first handler or rethrow original exception so it can be catched somewhere else (You can actually, but this is pointless).
So, if you really want to do this - I guess the only way is to write you own ExceptionHandlerExceptionResolver (maybe extend Springs ExceptionHandlerExceptionResolver) and modify doResolveHandlerMethodException method, so it looks for multiply exceptionHandlerMethod (one within controllers and one within advisors) and invokes it in a chain. This might be tricky :)
Also, you can look at this Jira ticket.
Hope it helps.

Make #ExceptionHandler handle every exception except AccessDeniedException?

I wrote an #ExceptionHandler to send the exception in JSON for REST requests. However, due to the way Spring Security works, AccessDeniedExceptions must not be handled.
If I simply rethrow an AccessDeniedException in the handler, I get an ERROR level logging saying:
Failed to invoke #ExceptionHandler method: SomeMethod() throws java.lang.Exception
org.springframework.security.access.AccessDeniedException: Access is denied
...Stack...
I cannot safely disable ERROR level logging for the class logging it (ExceptionHandlerExceptionResolver), but having these stacktrace is quite confusing for operations people.
Is there anyway to make #ExceptionHandler not handle this specific exception?
I think you could just use global exception handler instead, so you don't manually handle this AccessDeniedExceptions,
this global exception will be thrown for every kinds of exceptions (except for the exception handlers that you handle manually):
#ControllerAdvice
public class GlobalHandler {
#ExceptionHandler(Exception.class)
public ModelAndView handleException() {
return new ModelAndView("errorGlobal");
}
}

Apache Camel- Message Redelivery happens before onexception block executes

Have the following camel route.
#Override
public void configure() throws Exception {
onException(java.lang.Exception.class).useOriginalMessage()
.beanRef("discoveryService", "updateConnection")
.redeliveryPolicyRef("redeliverMessagePolicy");
from(ENDPOINT_URI).to(queueName);
}
with Redelivery policy defined as following in xml-
<redeliveryPolicyProfile id="redeliverMessagePolicy"
retryAttemptedLogLevel="WARN" maximumRedeliveries="8"
redeliveryDelay="${redeliveryDelay}" />
However when an exception is thrown the redelivery attempts are made before the OnException block is executed(Some configuration properties get updated in the onException block. Have a debug point in DiscoveryService inside Onexception, it gets called after the redelivery attempts are made). Thus the current message gets lost without being redelivered. Not sure why this happens.
Using activemq-camel version 5.8.0
Thnks
Yes this is intended, the onException block is only executed when the exchange is exhausted (eg after all redelivery attempts have failed).
Read more about how error handling in Camel works in the docs
http://camel.apache.org/error-handling-in-camel.html
And if you have a copy of the Camel in Action book it has an entire chapter devoted to cover all about error handling (most complete documentation there is)
If you want to do some custom logic before each redelivery, then use the onRedelivery processor: http://camel.apache.org/exception-clause.html

Categories