I am trying to build an central error handling using Camel.
I want that all my routes exposing REST endpoints would have transparent error handling translating Exceptions into HTTP error codes in one piece of code.
Currently I achieve this with an Abstract RouteBuilder defining an onException route in its Contructor. When I inherit it in a concrete RouteBuilder it works fine.
However this is not transparent since I have to know that I have to inherit from the abstract class to have it working.
Is there any mechanism in Camel similar to Jersey ExceptionMapper?
What I need is a place in the Camel framework where I can configure the error handling without the need to ensure that any team member has to remember to inherit the abstract error handling class.
what I currently have
public abstract class ErrorHandlerRoute extends RouteBuilder {
public ErrorHandlerRoute () {
super();
onException().handled(true)
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
Exception cause = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
//handle exceptions here
exchange.getOut().setHeader(Exchange.HTTP_RESPONSE_CODE, HttpStatus.SC_INTERNAL_SERVER_ERROR);
exchange.getOut().setFault(false);
});
}
}
public class MyBusinessRoute extends ErrorHandlerRoute {
#Override
public void configure() throws Exception {
//OK, I know that my errors will be handled
rest()
.get("/{param}")
to("http4://backend");
}
}
Related
Is there a way to intercept DataAccessException that is thrown by data-layer (#Repository) with knowing which method is causing this exception?
Writing custom SQLExceptionTranslator does not fit my need as I cannot determine which method caused the exception.
I have a repository like this:
public interface UserRepository extends JpaRepository<UserEntity, Integer> {
#ErrorCode("E1000")
User findById(int id);
#ErrorCode("E1001")
User findByUsername(String username);
}
ErrorCode is a custom annotation holds an error code that I need to send to client whenever DataAccessException occurs.
If there is a way to intercept the call to findById with catching DataAccessException, then it is easy to extract error code from annotation and re-throw a custom exception that can be catched by exception handler.
If Spring AOP is allowed, you could build your own aspect, for example:
#Aspect
public class ErrorCodeAspect {
#Around("#annotation(errorCode)")
public Object aroundErrorCode(ProceedingJoinPoint joinPoint, ErrorCode errorCode) throws Throwable {
try {
return joinPoint.proceed();
} catch (DataAccessException dae) {
throw new YourCustomException(errorCode.value(), dae);
}
}
}
Note that annotations on interface methods are not inherited by implementing class methods (even with #Inherited which applies to parent classes only), so you will probably need to annotate your concrete service classes instead for the aspect to plug in (unless Spring does some additional black magic with the repository proxies I wouldn't be aware of).
You can define the custom ExceptionHandler.
#RestControllerAdvice
public class RestExceptionResolver {
#ExceptionHandler(DataAccessException.class)
public ResponseEntity<String> handleNoSuchElementException(DataAccessException ex) {
return ResponseEntity.status(yourErrorCode);
}
}
I have my Spring error controller, and I need to get the actual exception class so I can print stack trace and other things like that.
This is my error controller
#Controller
public class ErrorController implements org.springframework.boot.web.servlet.error.ErrorController {
#RequestMapping("/error")
public String handleError() {
return "somethingwentwrong";
}
#Override
public String getErrorPath() {
return null;
}
}
I know its not much, but I need the exception object to be able to do some extra handling.
If you wish to execute different code based on the TYPE of the exception thrown, you should look at using #ControllerAdvice along with #ExceptionHandler. Any exceptions that you do not handle with an #ExceptionHandler will then bubble up to the default ErrorController (though you could handle Exception in a handler and then all exceptions will be handled via your custom handler). Something like:
#ControllerAdvice
#RestController
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(MyException.class)
protected ResponseEntity<String> handleMyException(MyException ex) {
// Your code here
}
#ExceptionHandler(Exception.class)
protected ResponseEntity<String> handleException(Exception ex){
// Your code here
}
}
https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler
No worries guys, I solved the problem, I just had to do some digging into springs built in error controller to get the stacktrace.
I have a camel route that sends to loadbalancer and processes the response. Is it possible to mock that response somehow in unit test? I tried to use velocity but it doesn't seem to work in unit tests.
Apache already takes care of such testing requirements. There is adviceWith construct which will solve this problem.
Quoting the example directly with few modifications from the link mentioned above:
#Test
public void testAdvised() throws Exception {
context.addRoutes(new RouteBuilder() {
#Override public void configure() throws Exception {
from("direct:start").id("my-route").to("mock:foo");
}
});
context.getRouteDefinition("my-route").adviceWith(context, new RouteBuilder() {
#Override
public void configure() throws Exception {
// intercept sending to mock:foo and do something else
interceptSendToEndpoint("mock:foo")
.skipSendToOriginalEndpoint()
.to("log:foo")
.to("mock:result");
}
});
getMockEndpoint("mock:foo").expectedMessageCount(0);
getMockEndpoint("mock:result").expectedMessageCount(1);
template.sendBody("direct:start", "Hello World");
assertMockEndpointsSatisfied();
}
Now here a route is defined as:
from("direct:start").id("my-route").to("mock:foo");
And let's say we want to mock the to part here.
This is precisely doing it for me:
context.getRouteDefinition("my-route").adviceWith(context, new RouteBuilder() {
#Override
public void configure() throws Exception {
// intercept sending to mock:foo and do something else
interceptSendToEndpoint("mock:foo")
.skipSendToOriginalEndpoint()
.to("log:foo")
.to("mock:result");
}
});
We get the reference of the route definition we want to modify from CamelContext and using the adviceWith method we can define what all action needs to be done. As here, I advised not to send to actual destination i.e. mock:foo, rather send to two other routes log:foo and mock:result.
Hope it answers your query.
New to Camel, so maybe I'm misunderstanding how processors and beans should interact. We have some logging to a database that we want to do throughout a camel route. The idea was to do this in a processor. However, we'd also like to do this logging from w/in the beans. Is that possible? I know I could pass it back as a return field from the bean...but it is already passing back a return.
A related question is how to pass that status, thinking it would be an exchange property or header.
Basically I want something along the lines of
processor
class EventStatusProcessor implements Processor {
#Override
void process(Exchange exchange) throws Exception {
// do some stuff, thinking this will be a header
}
}
route
from("direct:route1")
.bean(doSomething, 'doSomething')
.process(new EventStatusProcessor())
bean
#Component
#Slf4j
class DoSomething{
String doSomething()
//doing stuff
new EventStatusProcessor().process()
You can pass Exchange to method invoked with bean component too and set there headers/properties/body/whatever depending on your needs.
class DoSomething {
#SuppressWarnings("unused") //called via Camel bean invocation
public void doSomething(Exchange exchange){
exchange.setProperty("propertyFromDoSomething", "Hello, I am property");
exchange.getIn().setHeader("headerFromDoSomething", "Hi, I am header");
exchange.getIn().setBody("It's me, body!");
}
}
class EventStatusProcessor implements Processor {
#Override
public void process(Exchange exchange) throws Exception {
System.out.println(exchange.getIn().getHeader("headerFromDoSomething", String.class));
System.out.println(exchange.getProperty("propertyFromDoSomething", String.class));
System.out.println(exchange.getIn().getBody(String.class));
}
}
If you really need to call processor inside bean, as you are writing in title, extract processor to direct route and then invoke it with ProducerTemplate.
RouteBuilder
from("direct:log")
.process(new EventStatusProcessor());
DoSomething class
public class DoSomething {
#SuppressWarnings("unused") //called via Camel bean invocation
public void doSomething(Exchange exchange){
exchange.getContext().createProducerTemplate().sendBody("direct:log", "I am body and I will be passed to EventStatusProcessor");
}
}
I have dao, service and action classes in my spring mvc application.
I am throwing Exception in Dao and Service classes. Now in Action, normally I have to write try catch block and in case exception occurs in dao and service, it will be thrown from there and it will go in catch block in action.
I have a error jsp which will be displayed.
Problem is I need to write same catch block in all action methods.
Is it possible to throw it again in action methods too and handle it from a single point rather than writing same code everywhere.
Please suggest.
You can also have a look at Spring Integration. It provides the use of gateways, filters and channels. Each can have a Request, Response and Error channel assigned. Or there is even a default error handler. In case all data flows through a specific channel, having a custom error handler is as simple as follows:
#MessageEndpoint
public class MyErrorHandler {
#ServiceActivator(inputChannel = "errorChannel")
public String handle(String messsage) {
// do whatever you like
}
}
The Integration framework offers lots of usefull stuff for general handling.
I think you are looking for cross-cutting exception handling and good news, you are working with Spring MVC yes you can use this feature.
All you need to do, is throw your CustomExcptions or whatever other Exceptions that are from your services to your action methods.
Let's say here is your service:
#Service
public class MyService {
public void someMethod throws RuntimeException {
...
}
}
In your controller method:
#Controller
public class MyController {
#Autowired
MyService service;
#RequestMapping("/someuri"){
try {
service.someMethod();
} catch {
throw new RuntimeException();
}
}
#ExceptionHandler(RuntimeException.class)
public ModelAndView handleException(RuntimeException ex) {
ModelAndView model = new ModelAndView("errorpage");
return model;
}
}
The handleException method annotated with ExceptionHandler is your advice method for exception handling and it will be called anytime a RuntimeException is throw inside your controller and you can keep up like this for all other exceptions.