I have a list of objects and for each object, I need to hit external API using the RestTemplate. To do that I am using the for loop with try-catch block. In case external API does respond with 4xx, 5xx status code I need to create the list of errors and throw the custom exception which is handled using the exception handler to send client-friendly messages which also send the email notifications. The requirement is to remove the try-catch block and hit the external API in a loop and create the list of errors and check if the error list is not empty throw the exception and send all the error messages at once in the email notification using the exception handler method handleApplicationErrors. But I believe when any exception will occur for loop will be a break and I will not be able to create a list of error messages without a try-catch block, is there any possible way to do it?
public void method() {
List<Objects> objects = fetchObjects();
List<String> errorList = new ArrayList();
for(int i=0;i<objects.size();i++) {
try{
hitExternalApi(object)
}
catch(Exception e){
errorList.add("Error Message")
}
}
if(!errorList.isEmpty()) {
throw new ErrorDTO(Status.BAD_REQUEST, errorList);
}
}
#Override
public void hitExternalApi(Object object) {
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Object> request = new HttpEntity<>(object, httpHeaders);
restTemplate.exchange(url, HttpMethod.POST, request, Void.class);
}
#ExceptionHandler(ErrorDTO.class)
public ResponseEntity<Problem> handleApplicationErrors(NativeWebRequest request, ErrorDTO error) {
notificationService.sendNotification(error.getErrors());
Problem problem =
Problem.builder()
.withStatus(error.getStatus())
.withTitle(error.getMessage())
.withDetail(error.getErrors().toString())
.build();
return create(error, problem, request);
}
you can put the for loop inside a function and and call the exception after calling that function.
refer the following example for a better understanding:
BEFORE
public class ExceptionInLoop{
public static void sampleMethod(){
String str[] = {"Mango", "Apple", "Banana", "Grapes", "Oranges"};
try {
for(int i=0; i<=10; i++) {
System.out.println(str[i]);
System.out.println(i);
}
}catch (ArrayIndexOutOfBoundsException ex){
System.out.println("Exception occurred");
}
System.out.println("hello");
}
public static void main(String args[]) {
sampleMethod();
}}
AFTER
public class ExceptionInLoop{
public static void print(String str) {
System.out.println(str);
}
public static void sampleMethod()throws ArrayIndexOutOfBoundsException {
String str[] = {"Mango", "Apple", "Banana", "Grapes", "Oranges"};
for(int i=0; i<=10; i++) {
try {
print(str[i]);
System.out.println(i);
} catch(Exception e){
System.out.println(i);
}
}
}
public static void main(String args[]) {
try{
sampleMethod();
}catch(ArrayIndexOutOfBoundsException e) {
System.out.println("");
}
}}
You should be able to solve what you are asking for by moving the try-catch block down to the hitExternalApi method and let this return a boolean or a DTO if you need to pass any information. The for-loop can then just check the return value of hitExternalApi and populate the errorList if needed.
Modified code example:
public void method() {
List<Objects> objects = fetchObjects();
List<String> errorList = new ArrayList();
for (int i = 0; i < objects.size(); i++) {
if (!hitExternalApi(object)) {
errorList.add("Error Message");
}
}
if (!errorList.isEmpty()) {
throw new ErrorDTO(Status.BAD_REQUEST, errorList);
}
}
public boolean hitExternalApi(Object object) {
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Object> request = new HttpEntity<>(object, httpHeaders);
try {
restTemplate.setErrorHandler();
restTemplate.exchange(url, HttpMethod.POST, request, Void.class);
return true;
} catch (Exception exception) {
return false;
}
}
Per default, (so when using the DefaultResponseErrorHandler), the invocation of restTemplate.exchange(...) will throw some kind of RestClientException for 4xx and 5xx responses, so you will either need to handle this exception somewhere or provide your own ResponseErrorHandler and build logic around that.
You can just create an extra object for controlling the flow
public class ApiResponsePojo {
private HttpStatus status;
private String message;
private String data;
}
You can use a class like this and modify it to the way you want to store the messages. you can check if there's any error through status and populate a message in the status
Related
I've created a Flowable (RxJava v3) that parses a file. I'd like it to support backpressure. This is important because the files can be quite large, and I don't want them to be loaded into memory at once. Here is my first attempt:
public Flowable<Byte> asFlowable(InputStream is) {
return Flowable.create(new FlowableOnSubscribe<Byte>() {
#Override
public void subscribe(FlowableEmitter<Byte> emitter) throws Exception {
try (DataInputStream inputStream = new DataInputStream(is)){
if (inputStream.readInt() != SOME_CONSTANT) {
throw new IllegalArgumentException("illegal file format");
}
if (inputStream.readInt() != SOME_OTHER_CONSTANT) {
throw new IllegalArgumentException("illegal file format");
}
final int numItems = inputStream.readInt();
for(int i = 0; i < numItems; i++) {
if(emitter.isCancelled()) {
return;
}
emitter.onNext(inputStream.readByte());
}
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
}
}, BackpressureStrategy.BUFFER);
}
The reason I used Flowable.create instead of Flowable.generateis because I need to validate the file, and throw errors if some magic numbers at the beginning of the file are wrong or not found. This didn't fit well with the Flowable.generate lambdas (but if you know of a better way please post it).
Ok let's assume the cold Flowable supported backpressure. Now I'd like to process it in a console-like application.
Question:
I want to request a new Byte from the Flowable and print it to console each time the user presses space (similar to what more or less do in Linux). What would the best way of doing it? I intend to observe the flowable directly in the public static void main method, since I need to read and write using the console.
I've been reading the Backpressure section in RxJAva's Wiki and found this snippet:
someObservable.subscribe(new Subscriber<t>() {
#Override
public void onStart() {
request(1);
}
#Override
public void onCompleted() {
// gracefully handle sequence-complete
}
#Override
public void onError(Throwable e) {
// gracefully handle error
}
#Override
public void onNext(t n) {
// do something with the emitted item "n"
// request another item:
request(1);
}
});
But this confused me even more as the request method doesn't seem to exist in RxJava 3.
Use generate, blockingSubscribe and read a line from the console:
class State {
DataInputStream inputStream;
int count;
int i;
}
BufferedReader bin = new BufferedReader(new InputStreamReader(System.in));
Flowable.generate(() -> {
State s = new State();
s.inputStream = new DataInputStream(is);
try {
if (s.inputStream.readInt() != SOME_CONSTANT) {
throw new IllegalArgumentException("illegal file format");
}
if (s.inputStream.readInt() != SOME_OTHER_CONSTANT) {
throw new IllegalArgumentException("illegal file format");
}
s.count = s.inputStream.readInt();
} catch (IOException ex) {
s.inputStream.close();
throw ex;
}
return s;
}, (state, emitter) -> {
if (state.i < s.count) {
emitter.onNext(state.inputStream.readByte());
s.i++;
}
if (state.i >= s.count) {
emitter.onComplete();
}
}, state -> {
state.inputStream.close();
})
.subscribeOn(Schedulers.io())
.blockingSubscribe(b -> {
System.out.println(b);
bin.readLine();
}, Flowable.bufferSize());
It's more of a conceptual thing. My method is supposed to return a list of Conferences. But if there is an error, I just want it to send a String response or maybe a JSON response like {err: 'Some error'}.Offcourse following method throws compiler error for this line - return e.getMessage(); . How to achieve this?
#RequestMapping(value = "/api/allconf", method = RequestMethod.GET)
public List<Conferences> getAllConf(#RequestBody Conferences conf) {
List<Conferences> allConf = new ArrayList<Conferences>();
try {
allConf.addAll(confRepository.findAll());
} catch(Exception e){
return e.getMessage();
}
return allConf;
}
e.getMessage() returns a String and you method is a Conferences List, use a new generic response class like
public class Response {
private Object content;
private String error;
// getters and setters
}
and change your method
#RequestMapping(value = "/api/allconf", method = RequestMethod.GET)
public Response getAllConf(#RequestBody Conferences conf) {
Response resp = new Response();
List<Conferences> allConf = new ArrayList<Conferences>();
try{
allConf.addAll(confRepository.findAll());
resp.setContent(allConf);
}catch(Exception e){
resp.setError(e.getMessage());
}
return resp;
}
Have one option:
Best solution it is throw an exception:
#RequestMapping(value = "/api/allconf", method = RequestMethod.GET)
public List<Conferences> getAllConf(#RequestBody Conferences conf) {
List<Conferences> allConf = new ArrayList<Conferences>();
try {
allConf.addAll(confRepository.findAll());
} catch(Exception e){
throw new IllegalArgumentException(e.getMessage());
}
return allConf;
}
And create an error handler to handle exception and how you wanna display it:
#ControllerAdvice
public class CustomErrorHandler {
#ExceptionHandler(IllegalArgumentException.class)
public void handlerIllegalArgumentException(IllegalArgumentException exception, ServletWebRequest webRequest) throws IOException {
webRequest.getResponse().sendError(HttpStatus.BAD_REQUEST.value(), exception.getMessage());
}
}
I have this bit of code:
public static void main(String[] args) throws UnirestException {
ArrayList<Stock> listStock
= getAllAvailableStocks("https://api.iextrading.com/1.0/ref-data/symbols");
//doing more actions after the one before, using the data from the listStock etc.
}
private static ArrayList<Stock> getAllAvailableStocks(String url) {
ArrayList<Stock> stocks = new ArrayList<Stock>();
Future<HttpResponse<JsonNode>> future = Unirest.get(url)
.header("accept", "application/json")
.asJsonAsync(new Callback<JsonNode>() {
public void failed(UnirestException e) {
System.out.println("The request has failed");
}
public void completed(HttpResponse<JsonNode> response) {
ObjectMapper objectMapper = new ObjectMapper();
try {
listStock = objectMapper.readValue(response.getRawBody(), new TypeReference<List<Stock>>(){});
} catch (Exception e) {
System.out.println("all is fucked");
}
return listStock;
}
public void cancelled() {
System.out.println("The request has been cancelled");
}
});
}
I am a newbie in java, i want to do the following:
1) I want to do async call to get and extract a list of stocks, only after the request completed i want to do the next things in the main method.
2) How do i extract data from the method i built so i can use the data outside of the method?
3) If i need to do the following:
getAllAvailableStocks("https://api.iextrading.com/1.0/ref-data/symbols",new Callback<JsonNode>() {
public void failed(UnirestException e) {
System.out.println("The request has failed");
}
public void completed(HttpResponse<JsonNode> response) {
ArrayList<Stock> listStock = new ArrayList<Stock>();
ObjectMapper objectMapper = new ObjectMapper();
int code = response.getStatus();
System.out.println(code);
try {
listStock = objectMapper.readValue(response.getRawBody(), new TypeReference<List<Stock>>(){});
} catch (Exception e) {
}
System.out.println(listStock);
}
public void cancelled() {
System.out.println("The request has been cancelled");
}
});
}
private static Future<HttpResponse<JsonNode>> getAllAvailableStocks(String url,Callback<JsonNode> cb) {
return Unirest.get(url)
.header("accept", "application/json")
.asJsonAsync(cb);
}
Or something of that sort, it makes the code horrible, and when i want to do much more async requests after, i have a callback hell here, is there any way to avoid it? what are my options here?
I think your are mixing up asnychronous and synchronous.
If you
want to do async call to get and extract a list of stocks, only after the request completed I want to do the next things in the main method
then you actually want to perform a synchronous call.
An asynchronous call would be to perform the request, then doing other things (not related to the request) and at some point in the future you get the result of the request and handle it.
To perform a synchronous call, which is probably what you want, try to adapt your code like this:
private static ArrayList<Stock> getAllAvailableStocks(String url) {
ArrayList<Stock> stocks = new ArrayList<Stock>();
Future<HttpResponse<JsonNode>> future = Unirest.get(url)
.header("accept", "application/json")
.asJsonAsync(new Callback<JsonNode>() {
public void failed(UnirestException e) {
System.out.println("The request has failed");
}
public void completed(HttpResponse<JsonNode> response) {
System.out.println("The request succeeded");
}
public void cancelled() {
System.out.println("The request has been cancelled");
}
});
HttpResponse<JsonNode> response = future.get(); // NOTE: This call is blocking until the request is finished
if (response != null && response.getStatus() == 200) {
JsonNode body = response.getBody();
// TODO Parse body and add items to `stocks`
}
return stocks;
}
This method can be used like this:
ArrayList<Stock> stocks = getAllAvailableStocks(...);
stocks.forEach(x -> System.out.println(x));
Edit
If you want to handle the result asynchronously without providing callbacks, you could use a CompletableFuture. Consider the following snippet as a starting point which does not handle unsuccessful calls.
private static CompletableFuture<ArrayList<Stock>> getAllAvailableStocks(String url) {
CompletableFuture<ArrayList<Stock>> result = new CompletableFuture<>();
Future<HttpResponse<JsonNode>> future = Unirest.get(url)
.header("accept", "application/json")
.asJsonAsync(new Callback<JsonNode>() {
public void failed(UnirestException e) {
System.out.println("The request has failed");
}
public void completed(HttpResponse<JsonNode> response) {
System.out.println("The request succeeded");
ArrayList<Stock> stocks = new ArrayList<Stock>();
if (response != null && response.getStatus() == 200) {
JsonNode body = response.getBody();
// TODO Parse body and add items to `stocks`
}
result.complete(stocks);
}
public void cancelled() {
System.out.println("The request has been cancelled");
}
});
return result;
}
The method can be used as follows:
CompletableFuture<ArrayList<Stock>> stocksFuture = getAllAvailableStocks(...);
stocksFuture.thenAccept((stocks) -> {
// NOTE: This will be called after and only if the request succeeded
stocks.forEach(x -> System.out.println(x));
});
System.out.println("This is probably executed before the above request finished.");
Thread.sleep(10000); // If you are calling from your `main` method: Prevent JVM exit
I have a Controller like this example:
#RestController
#RequestMapping(value = "/risk", produces = {MediaType.APPLICATION_JSON_VALUE})
public class CalculationController {
#RequestMapping(value = "/calculate", method = RequestMethod.POST)
public CalculationResult calculate(InputFields i) {
try {
return calcService.calculate(i);
} catch (CustomException custEx) {
throw new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR,
null,
null,
getReportLogAsBytes(custEx), //for some reason not working when serialized in Json
Charset.defaultCharset());
}
}
private byte[] getReportLogAsBytes(CustomException e) {
try {
return objectMapper.writeValueAsBytes(e.getReportLog()); //
} catch (JsonProcessingException e1) {
throw new RuntimeException("Unable to serialize Simulation report log to bytes ", e1);
}
}
class CustomException extends Exception {
private List<String> reportLog;
public CustomException(List<String> reportLog) {
super();
this.setReportLog(reportLog);
}
public List<String> getReportLog() {
return reportLog;
}
public void setReportLog(List<String> reportLog) {
this.reportLog = reportLog;
}
}
}
When posting the inputs to the controller and a CustomException occurs, I instantiate HttpServerErrorException using the constructor that accepts a byte array for responseBody. I basically give it a list of String error messages converted to byte array.
The problem is the response body still does not show the list of errors messages I passed to it. I tried looking for examples on using HttpServerErrorException with response body but cannot seem to find any... Any help would be greatly appreciated.
You throw your HttpServerErrorException but don't handle it in the proper way.
Read this: https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
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.