In AWS sqs i need to consider message as failed and retry it only on specific custom exception rather than on all runtime exception
#SqsListener(value = "/MyQueueURL", deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS )
public void getMessageFromSqs(MyMessage message) {
log.info("message: {}", message);
// Ignore other exceptions
if(somecondition) {
throw new MyCustomException("Retry it"); //<--- Fail only on this exception
}
log.info("Success");
}
Then you need to use java try catch block, to hide the other errors. Although this concept does seem risky as ANY coding error you could lose data.
Since the deletion policy is on success it will automatically remove message from SQS.
#SqsListener(value = "/MyQueueURL", deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS )
public void getMessageFromSqs(MyMessage message) {
try {
log.info("message: {}", message);
// Ignore other exceptions
if(somecondition) {
throw new MyCustomException("Retry it"); //<--- Fail only on this exception
}
log.info("Success");
} catch(Exception e) {
log.info("error")
if (e instanceOf MyCustomException) {
throw e
}
}
}
Related
I am trying to create a unit test for my Kafka Producer which is integrated into a file. Here's my Kafka Producer:
FileName: MessageProducer.java
public boolean sendMessage(ReceivedMessage message) {
private String topicName = "output-flow";
try{
logger.info("Sending message: {} to topic: {}", message, topicName);
kafkaProducer.send(topicName, message).get();
return true;
} catch (Exception e){
logger.error("Error sending message: {} to topic: {}", message, topicName, e);
return false;
}
}
And here is what I have done so far for my unit test, obviously, with not success at all:
#Mock
private KafkaTemplate<String, ReceivedMessage > kafkaProducer;
private static final String TRANSACTION_TOPIC = "test";
// Function for parameterized values
#ParameterizedTest
#MethodSource("getTransactionProvider")
public void sendMessageTest(ReceivedMessage message) {
MessageProducer mockProducer = new MessageProducer(kafkaProducer);
when(kafkaProducer.send(TRANSACTION_TOPIC, message)).thenReturn({no idea what to put here});
when(mockProducer.sendMessage(message)).thenReturn(true);
assertTrue(mockProducer.sendMessage(message));
}
// Test for exception
// Fails too
#ParameterizedTest
#MethodSource("getTransactionProvider")
public void sendMessageTest_ThrowsException(ReceivedMessage message) {
MessageProducer mockProducer = new MessageProducer(kafkaProducer);
when(kafkaProducer.send(TRANSACTION_TOPIC, message)).thenThrow(new RuntimeException());
assertThrows(RuntimeException.class, () -> mockProducer.sendMessage(null));
}
I get Exception: org.opentest4j.AssertionFailedError: Expected java.lang.RuntimeException to be thrown, but nothing was thrown. for the latter unit test.
If I understood your question, you should return a new SendResult that would have the methods implemented with the data you expect
https://docs.spring.io/spring-kafka/api/org/springframework/kafka/support/SendResult.html
And wrap it in a Future
ListenableFuture<SendResult<K,V>>
Alternatively, make sendMessage void method (or return a Future itself), and pass in a producer callback parameter that's carried through to send, rather than making it block. Then you can assert the response of the callback
Welcome to SO...
Why does your test case fail?
Because your logic will not throw an error.
Your function will not throw an exception since you catch the exception inside the function as follow and you return boolean value.
catch (Exception e){
logger.error("Error sending message: {} to topic: {}", message, topicName, e);
return false;
}
In that case, you need to test whether the function returns false or not.
As I commented earlier, don't block the main thread by calling the get method in the future object. You can simply implement the future callbacks which can be invoked once you get the result as following
public void sendMessage(ReceivedMessage message) {
private String topicName = "output-flow";
try{
logger.info("Sending message: {} to topic: {}", message, topicName);
ListenableFuture<SendResult<String, String>> future = kafkaProducer.send(topicName, message);
future.addCallback(new ListenableFutureCallback<SendResult<String, String>>() {
#Override
public void onSuccess(SendResult<String, String> result) {
System.out.println("Message Sent " + result.getRecordMetadata().timestamp());
//your logic for the success scenario
}
#Override
public void onFailure(Throwable ex) {
System.out.println(" sending failed ");
// your logic if failed
throw new RuntimeException("Kafka Failed");
}
});
} catch (Exception e){
logger.error("Error sending message: {} to topic: {}", message, topicName, e);
throw new RuntimeException("Exception occurred");
}
}
UPDATE: Here's the full test:
#Test(expected = NullPointerException.class)
public void testMissingData() throws Exception{
Resource<ObjectDataModel, Content, Status> resource = builder.build(content, argA1Response,
argA2Response, objFilterParam, argA3Response);}
And here's the build method:
public Resource<ObjectDataModel, Content, Status> build(Content argContent,
ResponseA1 argA1Response,
ResponseA2 argA2Response, String argObjectTypeFilter,
ResponseA3 argA3Response) {
try {
viewDataModel.setObjectType(this.buildObjectType(filteredObjectType,
argA1Response.getData().getDataObject().getCategories().get(0).getObjectTypes().get(0)));
}
catch (Exception e) {
String msg = "Exception occoured while buildng the Object Data Model";
LOG.error(msg, e);
}
// we have the required information gathered to return
return Resource.okFromDataAndContent(viewDataModel, argContent);
}
And here's the buildObjectType() method:
private ObjectType buildObjectType(ObjectTypes argA1ProductType,
PendingObjectTypes argA2ProductType) {
ProductType objectType = new ObjectType();
List<Plan> plans = argA1ObjectType.getPlan();
List<PendingObjectSummary> objPlans = argA1ObjectType.getData();
if (objectType.getData() == null) {
objectType.setData(new ArrayList<>());
}
PendingObjectSummary tempPlan = null;
for (Plan currPlan : plans) {
tempPlan = plans.stream()
.filter(plan -> plan.getObjId().equals(currPlan.getObjId()))
.findFirst()
.orElseThrow(NullPointerException::new);
}
return objectType;
}
I'm using an Optional to test for null and I can confirm that the exception is being thrown -- but JUnit isn't catching it. Here's the test case:
#Test(expected = NullPointerException.class)
public void testMissingData() throws Exception{
Object<> response = fixture.create();
assertNotNull(response);
assertNotNull(response.getData());
assertNull(resource.getData().getObjectType());
}
In my create method I'm simply iterating over a bunch of objects to try and find one that matches my ID; if not found then throw a NullPointerException:
for (Object currObj : objects) {
tempObj = myOtherCollection.stream()
.filter(obj -> obj.getId().equals(currObj.getId()))
.findFirst()
.orElseThrow(NullPointerException::new);
}
The JUnit output clearly isn't catching the exception - here's the output:
java.lang.AssertionError: Expected exception: java.lang.NullPointerException
And my tomcat logs are definitely throwing the exception here:
18:48:30.015 [main] ERROR com.myCompany.src.ModelBuilder - Exception occoured while buildng the Data Model
java.lang.NullPointerException: null
at java.util.Optional.orElseThrow(Optional.java:290)
The only issue I can see is that maybe where I assign tempObj that the code is wrong. Am I missing anything obvious? Thanks for any helpful tips.
You are catching the nullpointer exception so the exception is not propagated to your test.
see
try {
viewDataModel.setObjectType(this.buildObjectType(filteredObjectType,
argA1Response.getData().getDataObject().getCategories().get(0).getObjectTypes().get(0)));
}
catch (Exception e) {
String msg = "Exception occoured while buildng the Object Data Model";
LOG.error(msg, e);
}
If you want to test for an exception you could throw an exception in your error handling (for example a custom ObjectCreationExcepion) and assert that that one is thrown, like
try {
viewDataModel.setObjectType(this.buildObjectType(filteredObjectType,
argA1Response.getData().getDataObject().getCategories().get(0).getObjectTypes().get(0)));
}
catch (Exception e) {
String msg = "Exception occoured while buildng the Object Data Model";
LOG.error(msg, e);
throw new ObjectCreationException(msg);
}
and in your test
#Test(expected = ObjectCreationException.class)
public void testMissingData() throws Exception{
Object<> response = fixture.create();
}
#Test(expected = ObjectCreationException.class) only handles exceptions that are not handled within the tested code OR the test itself.
So what you could do is
public Resource<ObjectDataModel, Content, Status> build(Content argContent,
ResponseA1 argA1Response,
ResponseA2 argA2Response, String argObjectTypeFilter,
ResponseA3 argA3Response) throws NullPointerExceptions // << notice thrwoing declatration
{ // do some stuf}
and then in test you can handle it like you where trying by
public void testMissingData() throws Exception{
Resource<ObjectDataModel, Content, Status> resource = builder.build(content, argA1Response,
argA2Response, objFilterParam, argA3Response);
}
I use a Timer.schedule() to periodically call the run() method of the TimerTask class to poll devices. Sometimes a MalformedJsonException or a IllegalStateException are thrown, which is processed in the catch block. The thread should continue to poll devices after handling the exception, but it stops.
When there are no errors, the run method is periodically called as expected.
I also tried calling the runModulesPoll() method from the catch block, but that didn't help.
private static void runModulesPoll(Boiler boiler) {
new Timer("Modules Poll Flow").schedule(new TimerTask() {
#Override
public void run() {
try {
Module[] modules = boiler.getCore().getModules();
for (Module module : modules) {
String response = ControllersService.sendMessage(MessageBuilder.buildDataRequest(module.getAlias(), boiler.getBoilerMode()));
if (AppUtils.isStringInvalid(response)) {
module.setOnline(false);
ModulesResetService.reset();
continue;
}
module.setOnline(true);
module.fromShortJson(response);
}
MqttService.publishMessage(MqttMessageFactory.createDataMessage(boiler.getCore().getModulesDataAsJson()));
} catch (Throwable e) {
LoggerLocal.error("Exception in Modules Poll Flow: " + e.getLocalizedMessage());
e.printStackTrace();
}
}
}, 0, 1);
}
According to the logs, the exception is handled as expected, but the thread does not continue polling.
14-11-2019 18:10:47 -- Exception in Modules Poll Flow: Not a JSON Object: "hgjhgjhg"
java.lang.IllegalStateException: Not a JSON Object: "hgjhgjhg"
at com.google.gson.JsonElement.getAsJsonObject(JsonElement.java:90)
at eezo.AppUtils.getJsonObjectFromString(AppUtils.java:101)
at eezo.services.ControllersService.handleIfErrorMessage(ControllersService.java:148)
at eezo.services.ControllersService.sendMessage(ControllersService.java:53)
at eezo.ApplicationRunner$1.run(ApplicationRunner.java:107)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
UPDATE:
Exception throws inside try block and handles in catch but thread stops without any other exceptions.
LoggerLocal doesn't produce exceptions at all.
I simulated the situation with a simpler example and everything works as expected, the thread does not fall and handles exceptions constantly.
private static void run(String[] args) {
final int[] i = {0};
new Timer("Modules Poll Flow").schedule(new TimerTask() {
#Override
public void run() {
try {
System.out.println("run() " + args);
if (i[0] == 5) throw new IllegalStateException("ssss");
i[0]++;
} catch (Exception e) {
System.out.println("Exception in Modules Poll Flow: " + e.getLocalizedMessage());
e.printStackTrace();
}
}
}, 0, 1);
}
I have the following code:
// How to throw the ServerException?
public void myFunc() throws ServerException{
// Some code
CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> {
try {
return someObj.someFunc();
} catch(ServerException ex) {
// throw ex; gives an error here.
}
}));
// Some code
}
someFunc() throws a ServerException. I don't want to handle this here but throw the exception from someFunc() to caller of myFunc().
Your code suggests that you are using the result of the asynchronous operation later in the same method, so you’ll have to deal with CompletionException anyway, so one way to deal with it, is
public void myFunc() throws ServerException {
// Some code
CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> {
try { return someObj.someFunc(); }
catch(ServerException ex) { throw new CompletionException(ex); }
});
// Some code running in parallel to someFunc()
A resultOfA;
try {
resultOfA = a.join();
}
catch(CompletionException ex) {
try {
throw ex.getCause();
}
catch(Error|RuntimeException|ServerException possible) {
throw possible;
}
catch(Throwable impossible) {
throw new AssertionError(impossible);
}
}
// some code using resultOfA
}
All exceptions thrown inside the asynchronous processing of the Supplier will get wrapped into a CompletionException when calling join, except the ServerException we have already wrapped in a CompletionException.
When we re-throw the cause of the CompletionException, we may face unchecked exceptions, i.e. subclasses of Error or RuntimeException, or our custom checked exception ServerException. The code above handles all of them with a multi-catch which will re-throw them. Since the declared return type of getCause() is Throwable, the compiler requires us to handle that type despite we already handled all possible types. The straight-forward solution is to throw this actually impossible throwable wrapped in an AssertionError.
Alternatively, we could use an alternative result future for our custom exception:
public void myFunc() throws ServerException {
// Some code
CompletableFuture<ServerException> exception = new CompletableFuture<>();
CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> {
try { return someObj.someFunc(); }
catch(ServerException ex) {
exception.complete(ex);
throw new CompletionException(ex);
}
});
// Some code running in parallel to someFunc()
A resultOfA;
try {
resultOfA = a.join();
}
catch(CompletionException ex) {
if(exception.isDone()) throw exception.join();
throw ex;
}
// some code using resultOfA
}
This solution will re-throw all “unexpected” throwables in their wrapped form, but only throw the custom ServerException in its original form passed via the exception future. Note that we have to ensure that a has been completed (like calling join() first), before we query the exception future, to avoid race conditions.
For those looking for other ways on exception handling with completableFuture
Below are several ways for example handling Parsing Error to Integer:
1. Using handle method - which enables you to provide a default value on exception
CompletableFuture correctHandler = CompletableFuture.supplyAsync(() -> "A")
.thenApply(Integer::parseInt)
.handle((result, ex) -> {
if (null != ex) {
ex.printStackTrace();
return 0;
} else {
System.out.println("HANDLING " + result);
return result;
}
})
.thenAcceptAsync(s -> {
System.out.println("CORRECT: " + s);
});
2. Using exceptionally Method - similar to handle but less verbose
CompletableFuture parser = CompletableFuture.supplyAsync(() -> "1")
.thenApply(Integer::parseInt)
.exceptionally(t -> {
t.printStackTrace();
return 0;
}).thenAcceptAsync(s -> System.out.println("CORRECT value: " + s));
3. Using whenComplete Method - using this will stop the method on its tracks and not execute the next thenAcceptAsync
CompletableFuture correctHandler2 = CompletableFuture.supplyAsync(() -> "A")
.thenApply(Integer::parseInt)
.whenComplete((result, ex) -> {
if (null != ex) {
ex.printStackTrace();
}
})
.thenAcceptAsync(s -> {
System.out.println("When Complete: " + s);
});
4. Propagating the exception via completeExceptionally
public static CompletableFuture<Integer> converter(String convertMe) {
CompletableFuture<Integer> future = new CompletableFuture<>();
try {
future.complete(Integer.parseInt(convertMe));
} catch (Exception ex) {
future.completeExceptionally(ex);
}
return future;
}
Even if other's answer is very nice. but I give you another way to throw a checked exception in CompletableFuture.
IF you don't want to invoke a CompletableFuture in another thread, you can use an anonymous class to handle it like this:
CompletableFuture<A> a = new CompletableFuture<A>() {{
try {
complete(someObj.someFunc());
} catch (ServerException ex) {
completeExceptionally(ex);
}
}};
IF you want to invoke a CompletableFuture in another thread, you also can use an anonymous class to handle it, but run method by runAsync:
CompletableFuture<A> a = new CompletableFuture<A>() {{
CompletableFuture.runAsync(() -> {
try {
complete(someObj.someFunc());
} catch (ServerException ex) {
completeExceptionally(ex);
}
});
}};
I think that you should wrap that into a RuntimeException and throw that:
throw new RuntimeException(ex);
Or many be a small utility would help:
static class Wrapper extends RuntimeException {
private Wrapper(Throwable throwable) {
super(throwable);
}
public static Wrapper wrap(Throwable throwable) {
return new Wrapper(throwable);
}
public Throwable unwrap() {
return getCause();
}
}
public static void go() {
CompletableFuture<String> a = CompletableFuture.supplyAsync(() -> {
try {
throw new Exception("Just because");
} catch (Exception ex) {
throw Wrapper.wrap(ex);
}
});
a.join();
}
And then you could unwrap that..
try {
go();
} catch (Wrapper w) {
throw w.unwrap();
}
I am new to JUnit and I have to test a method using JUnit api. One method internall calls another. My test case goes inside the method but while catchign the exception it fails.
Method under test is
public void checkANDCondition( Map<String, Message> messagesMap ) throws EISClientException
{
List<String> codes = getMessageCodes();
if(isAllReturnedMessagesContainCodes(codes, messagesMap))
{
StringBuffer buff = new StringBuffer("All of the specified message codes matched returned errors.");
for(String code: codes )
{
Message message = messagesMap.get(code);
buff.append(message.getMessageCode() + ": " + message.getMessageType() + ": " + message.getMessageText() + " ");
}
throw new EISClientException(buff.toString());
}
}
public boolean isAllReturnedMessagesContainCodes(List<String> codes, Map<String, Message> messagesMap)
{
if(codes!=null)
{
for(String code: codes)
{
if(!messagesMap.containsKey(code))
{
return false;
}
}
}
return true;
}
What I have done so far is
#Test
public void testPostProcess() throws Exception {
clientResponse = mock(ClientResponse.class);
MessageToExceptionPostProcessFilter postProcessFilter = new MessageToExceptionPostProcessFilter();
RetrieveBillingServiceResponse serviceResponse = new RetrieveBillingServiceResponse();caughtException = false;
try {
postProcessFilter.setCondition(ConditionOperator.AND);
List<String> messagesCodes = new ArrayList<String>();
messagesCodes.add("200");
messagesCodes.add("400");
Message message = new Message();
message.setMessageCode("200");
message.setMessageType(MessageTypeEnum.MESSAGE_TYPE_INFO);
message.setMessageText("Service completed successfully");
serviceResponse.setMessages(Arrays.asList(message));
postProcessFilter.setMessageCodes(messagesCodes);
serviceResponse = postProcessFilter.postProcess(serviceResponse, clientResponse);
assertNotNull(serviceResponse.getMessages());
} catch (EISClientException ex) {
caughtException = true;
assertEquals("All of the specified message codes matched returned errors.", ex.getMessage());
}
assertTrue(caughtException);
}
How can I make it pass?
Thanks
#Test(expected = EISCLientException.class)
public void testPostProcess() throws Exception {
...
serviceResponse.getMessages();
fail("Shouldn't reach this point");
}
That way you don't need to catch, with expected if it does not get throw a EISClientException it will fail.
edit: There are two times I can think of where you wouldn't want to use this.
1) You are mocking exceptions that are thrown mock(exception.class);
this i believe then throws some Mockito excpetion and it will not match the expected exception.
2) You are wrapping caught exceptions in your code, and throwing a generic exception. Example of code:
try {
} catch (FileParseException e){
throw new (ProjectFailingException(e, "file is bad");
}
if you have multiple catches and are wrapping them as ProjectFailingExceptions then you may want to catch in the test like this...
#Test ( expected = FileParseException.class)
public void testProcess() {
try {
...
} catch (ProjectFailingException e){
throw e.getCause();
}
Then the proper exception is thrown and you can make sure that process isn't throwing an exception from a a different catch.