I have a camel 3 with springboot application that contains this route:
public class SendForRatingRoute extends RouteBuilder
{
public void configure() throws Exception
{
onException(Exception.class)
.handled(true)
.process(exProcessor);
// Queue
String qn = "ratingmq:queue:MY_OUT_QUEUE");
from("direct:sendRate")
.routeId("sendRate")
.bean(MessagingBean.class, "createMessage" )//creates input object
.marshal().xstream()
.log(LoggingLevel.INFO,logger, "~~~ sending to MQ: ${body} " )
.to(qn)
.log(LoggingLevel.INFO,logger, "~~~ Message was sent to MQ successfully ");
} `
}
this route takes an Order object as input, converts it to a java class that matches the Queue message schema (in the MessagingBean) and then mashall that class to XML and send it to an IBM MQ endpoint.
(for simplicity the configuration of the queue is skipped)
I need to write a test case that passes a sample order to this route and see if it actually sends it to the queue correctly. (I want the message to get sent and not mock the sending)
I created a test class like this:
class SendForRatingTest extends CamelTestSupport
{
public void setUp() throws Exception
{
super.setUp();
}
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new SendForRatingRoute();
}
#Test
void testSendingForRating() throws Exception
{
template.sendBody("direct:sendRate", getSampleOrder());
assertTrue(true); //??
}
}
The method GetSampleOrder() creates the order object with sample data to pass to the route.
I am not sure if I am doing this right (I have never written test case for camel before)
When I try to run the test case using maven:
mvn test -Dtest=SendForRatingTest -X
I am getting this error:
<<< ERROR!
org.junit.platform.commons.PreconditionViolationException: You must register at least one TestTemplateInvocationContextProvider that supports #TestTemplate method [void SendForRatingTest.testSendingForRating() throws java.lang.Exception]
What am I doing wrong?
Also I am not sure how to test that the route was executed successfully. Ultimately I am going to check the queue for a message but also I want the route test to validate it worked without errors. Since I don't return anything in the route, I am not sure what to put in the assertTrue()
Thank you in advance.
Related
I've been trying to figure out how route errors to my own error handler with the following, seemingly simple configuration, but Camel is swallowing the exception without routing it to any error handler I configure. I've run out of ideas. Any help would be much appreciated.
I've got a seda route that supports multiple consumers:
#Component
public class MessageGenerator {
public static final String ERROR_GENERATOR_CHANNEL = "seda:my-error-generator?multipleConsumers=true&concurrentConsumers=3";
private final FluentProducerTemplate producerTemplate;
public MessageGenerator(FluentProducerTemplate producerTemplate) {
this.producerTemplate = producerTemplate;
}
public void generateMessage() {
producerTemplate
.to(ERROR_GENERATOR_CHANNEL)
.withBody("Hello World")
.asyncSend();
}
}
I've got two separate POJO consumers:
#Configuration
public class MessageConsumer1 {
#Consume(ERROR_GENERATOR_CHANNEL)
void receiveMessage(String message) {
System.out.println("Received message 1: " + message);
throw new NullPointerException("Error generated");
}
}
#Configuration
public class MessageConsumer2 {
#Consume(ERROR_GENERATOR_CHANNEL)
void receiveMessage(String message) {
System.out.println("Received message 2: " + message);
}
}
When I run the following example, the NullPointerException gets swallowed by the underlying Camel MulticastProcessor as we can see in the logs:
Received message 2: Hello World
Received message 1: Hello World
2022-01-15 13:40:23.711 DEBUG 32945 --- [error-generator] o.a.camel.processor.MulticastProcessor : Message exchange has failed: Multicast processing failed for number 0 for exchange: Exchange[] Exception: java.lang.NullPointerException: Error generated
2022-01-15 13:40:23.711 DEBUG 32945 --- [error-generator] o.a.camel.processor.MulticastProcessor : Message exchange has failed: Multicast processing failed for number 0 for exchange: Exchange[] Exception: java.lang.NullPointerException: Error generated
The exception only gets logged as debug and never gets propagated to any error handler I set up.
Any thoughts on how I could receive the error in my own error handler rather than Camel swallowing the exception as a debug statement?
Note1: I've attempted many variations on both default error handling and default dead letter handling to no avail. I could just be doing it wrong...
Note2: that I'm using Spring[Boot] here too, hence the #Configuration annotation.
Note1: I've attempted many variations on both default error handling and default dead letter handling to no avail. I could just be doing it wrong...
Haven't used #Consume annotations but generally if you want Camel route not to handle any errors you can use .errorHandler(noErrorHandler()). This can be used to pass the error back to parent route or all the way to the code calling ProducerTemplate.sendBody.
Example:
public class ExampleTest extends CamelTestSupport {
#Test
public void noErrorHandlerTest() {
try {
template.sendBody("direct:noErrorHandler", null);
fail();
} catch (Exception e) {
System.out.println("Caught Exception: " + e.getMessage());
}
}
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder(){
#Override
public void configure() throws Exception {
from("direct:noErrorHandler")
.errorHandler(noErrorHandler())
.log("Throwing error")
.throwException(Exception.class, "Test Exception");
}
};
}
}
I have the following RESTful Services method :
#PostMapping("/ajouterNewField")
public String ajouterField(#Valid #ModelAttribute("field") Fields field, Model model) throws IOException {
fieldDao.save(field);
// SOME CODE
return displayListeChamps( model);
}
The method is working fine and my question is how to handle any error (database not connected ...) or every issue that can happen durring the execution of this RESTful Services method.
You can use #ControllerAdvice
Refer to the code below
#ControllerAdvice
public String NyExceptionHandlerAdvice {
private final Logger logger = ...;
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler({MyRunTimeException.class})
public void handleMyRunTimeException(Exception e) {
logger.error("Exception : ", e);
}
return MY_ERROR_STRING;
}
Best Practice is:
You can have your code throw RunTimeExceptions and handle all of them together or separately in handler methods similar to handleMyRunTimeException above.
You can decide what status code your request should return upon exception.
Basically you'll have to a sort of exception handler for any kind of exception your method might throw:
public class FooController{
// ...
#ExceptionHandler({ CustomException1.class, CustomException2.class })
public void handleException() {
//
}
}
Here's a nice article about that: https://www.baeldung.com/exception-handling-for-rest-with-spring
Is there a way to get the reason a HystrixCommand failed when using the #HystrixCommand annotation within a Spring Boot application? It looks like if you implement your own HystrixCommand, you have access to the getFailedExecutionException but how can you get access to this when using the annotation? I would like to be able to do different things in the fallback method based on the type of exception that occurred. Is this possible?
I saw a note about HystrixRequestContext.initializeContext() but the HystrixRequestContext doesn't give you access to anything, is there a different way to use that context to get access to the exceptions?
Simply add a Throwable parameter to the fallback method and it will receive the exception which the original command produced.
From https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-javanica
#HystrixCommand(fallbackMethod = "fallback1")
User getUserById(String id) {
throw new RuntimeException("getUserById command failed");
}
#HystrixCommand(fallbackMethod = "fallback2")
User fallback1(String id, Throwable e) {
assert "getUserById command failed".equals(e.getMessage());
throw new RuntimeException("fallback1 failed");
}
I haven't found a way to get the exception with Annotations either, but creating my own Command worked for me like so:
public static class DemoCommand extends HystrixCommand<String> {
protected DemoCommand() {
super(HystrixCommandGroupKey.Factory.asKey("Demo"));
}
#Override
protected String run() throws Exception {
throw new RuntimeException("failed!");
}
#Override
protected String getFallback() {
System.out.println("Events (so far) in Fallback: " + getExecutionEvents());
return getFailedExecutionException().getMessage();
}
}
Hopefully this helps someone else as well.
As said in the documentation Hystrix-documentation getFallback() method will be thrown when:
Whenever a command execution fails: when an exception is thrown by construct() or run()
When the command is short-circuited because the circuit is open
When the command’s thread pool and queue or semaphore are at capacity
When the command has exceeded its timeout length.
So you can easily get what raised your fallback method called by assigning the the execution exception to a Throwable object.
Assuming your HystrixCommand returns a String
public class ExampleTask extends HystrixCommand<String> {
//Your class body
}
do as follows:
#Override
protected ErrorCodes getFallback() {
Throwable t = getExecutionException();
if (circuitBreaker.isOpen()) {
// Log or something
} else if (t instanceof RejectedExecutionException) {
// Log and get the threadpool name, could be useful
} else {
// Maybe something else happened
}
return "A default String"; // Avoid using any HTTP request or ypu will need to wrap it also in HystrixCommand
}
More info here
I couldn't find a way to obtain the exception with the annotations, but i found HystrixPlugins , with that you can register a HystrixCommandExecutionHook and you can get the exact exception in that like this :
HystrixPlugins.getInstance().registerCommandExecutionHook(new HystrixCommandExecutionHook() {
#Override
public <T> void onFallbackStart(final HystrixInvokable<T> commandInstance) {
}
});
The command instance is a GenericCommand.
Most of the time just using getFailedExecutionException().getMessage() gave me null values.
Exception errorFromThrowable = getExceptionFromThrowable(getExecutionException());
String errMessage = (errorFromThrowable != null) ? errorFromThrowable.getMessage()
this gives me better results all the time.
I want to intercept a bad JSON input and return custom error messages using Dropwizard application. I followed the approach of defining a custom exception mapper as mentioned here : http://gary-rowe.com/agilestack/2012/10/23/how-to-implement-a-runtimeexceptionmapper-for-dropwizard/ . But it did not work for me. This same question has been asked here https://groups.google.com/forum/#!topic/dropwizard-user/r76Ny-pCveA but unanswered.
Any help would be highly appreciated.
My code below and I am registering it in dropwizard as environment.jersey().register(RuntimeExceptionMapper.class);
#Provider
public class RuntimeExceptionMapper implements ExceptionMapper<RuntimeException> {
private static Logger logger = LoggerFactory.getLogger(RuntimeExceptionMapper.class);
#Override
public Response toResponse(RuntimeException runtime) {
logger.error("API invocation failed. Runtime : {}, Message : {}", runtime, runtime.getMessage());
return Response.serverError().type(MediaType.APPLICATION_JSON).entity(new Error()).build();
}
}
Problem 1:
The exception being thrown by Jackson doesn't extends RuntimeException, but it does extend Exception. This doesn't matter though. (See Problem 2)
Problem 2:
DropwizardResourceConfig, registers it's own JsonProcessingExceptionMapper. So you should already see results similar to
{
"message":"Unrecognized field \"field\" (class d.s.h.c.MyClass),..."
}
Now if you want to override this, then you should create a more specific exception mapper. When working with exception mappers the most specific one will be chosen. JsonProcessingException is subclassed by JsonMappingException and JsonProcessingException, so you will want to create an exception mapper for each of these. Then register them. I am not sure how to unregister the Dropwizard JsonProcessingExceptionMapper, otherwise we could just create a mapper for JsonProcessingException, which will save us the hassle of create both.
Update
So you can remove the Dropwizard mapper, if you want, with the following
Set<Object> providers = environment.jersey().getResourceConfig().getSingletons();
Iterator it = providers.iterator();
while (it.hasNext()) {
Object val = it.next();
if (val instanceof JsonProcessingExceptionMapper) {
it.remove();
break;
}
}
Then you are free to use your own mapper, JsonProcessingException
I am connection through SockJS over STOMP to my Spring backend. Everything work fine, the configuration works well for all browsers etc. However, I cannot find a way to send an initial message. The scenario would be as follows:
The client connects to the topic
function connect() {
var socket = new SockJS('http://localhost:8080/myEndpoint');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/notify', function(message){
showMessage(JSON.parse(message.body).content);
});
});
}
and the backend config looks more or less like this:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketAppConfig extends AbstractWebSocketMessageBrokerConfigurer {
...
#Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
registry.addEndpoint("/myEndpoint").withSockJS();
}
I want to send to the client an automatic reply from the backend (on the connection event) so that I can already provide him with some dataset (e.g. read sth from the db) without the need for him (the client) to send a GET request (or any other). So to sum up, I just want to send him a message on the topic with the SimMessagingTemplate object just after he connected.
Usually I do it the following way, e.g. in a REST controller, when the template is already autowired:
#Autowired
private SimpMessagingTemplate template;
...
template.convertAndSend(TOPIC, new Message("it works!"));
How to achieve this on connect event?
UPDATE
I have managed to make it work. However, I am still a bit confused with the configuration. I will show here 2 configurations how the initial message can be sent:
1) First solution
JS part
stompClient.subscribe('/app/pending', function(message){
showMessage(JSON.parse(message.body).content);
});
stompClient.subscribe('/topic/incoming', function(message){
showMessage(JSON.parse(message.body).content);
});
Java part
#Controller
public class WebSocketBusController {
#SubscribeMapping("/pending")
Configuration
#Override
public void configureMessageBroker(final MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
... and other calls
template.convertAndSend("/topic/incoming", outgoingMessage);
2) Second solution
JS part
stompClient.subscribe('/topic/incoming', function(message){
showMessage(JSON.parse(message.body).content);
})
Java part
#Controller
public class WebSocketBusController {
#SubscribeMapping("/topic/incoming")
Configuration
#Override
public void configureMessageBroker(final MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
// NO APPLICATION PREFIX HERE
}
... and other calls
template.convertAndSend("/topic/incoming", outgoingMessage);
SUMMARY:
The first case uses two subscriptions - this I wanted to avoid and thought this can be managed with one only.
The second one however has no prefix for application. But at least I can have a single subscription to listen on the provided topic as well as send initial message.
If you just want to send a message to the client upon connection, use an appropriate ApplicationListener:
#Component
public class StompConnectedEvent implements ApplicationListener<SessionConnectedEvent> {
private static final Logger log = Logger.getLogger(StompConnectedEvent.class);
#Autowired
private Controller controller;
#Override
public void onApplicationEvent(SessionConnectedEvent event) {
log.debug("Client connected.");
// you can use a controller to send your msg here
}
}
You can't do that on connect, however the #SubscribeMapping does the stuff in that case.
You just need to mark the service method with that annotation and it returns a result to the subscribe function.
From Spring Reference Manual:
An #SubscribeMapping annotation can also be used to map subscription requests to #Controller methods. It is supported on the method level, but can also be combined with a type level #MessageMapping annotation that expresses shared mappings across all message handling methods within the same controller.
By default the return value from an #SubscribeMapping method is sent as a message directly back to the connected client and does not pass through the broker. This is useful for implementing request-reply message interactions; for example, to fetch application data when the application UI is being initialized. Or alternatively an #SubscribeMapping method can be annotated with #SendTo in which case the resulting message is sent to the "brokerChannel" using the specified target destination.
UPDATE
Referring to this example: https://github.com/revelfire/spring4Test how would that be possible to send anything when the line 24 of the index.html is invoked: stompClient.subscribe('/user/queue/socket/responses' ... from the spring controllers?
Well, look like this:
#SubscribeMapping("/queue/socket/responses")
public List<Employee> list() {
return getEmployees();
}
The Stomp client part remains the same.