Resilience4j Retry is not working as expected - java

I have two services "product-service" and "rating-service". I am making a rest call from product-service to rating-service to get the data. I have written the Retry configuration in product-service and expecting whenever an exception raised from rating-service, product-service retry the rest call as per configuration. But it is not happening. Whenever an exception thrown from rating-service, product-service also throws the exception without retrying and also fallback. Please find below code of both the services.
Check the code of the both services here.
product-service >> ProductServiceImpl.java
#Retry(name = "rating-service", fallbackMethod = "getDefaultProductRating")
public List<ProductRatingDTO> getProductRating(String id) {
String reqRatingServiceUrl = ratingServiceUrl + "/" + id;
log.info("Making a request to " + reqRatingServiceUrl + " at :" + LocalDateTime.now());
ResponseEntity<List<ProductRatingDTO>> productRatingDTOListRE = restTemplate.exchange(reqRatingServiceUrl,
HttpMethod.GET, null, new ParameterizedTypeReference<List<ProductRatingDTO>>() {
});
List<ProductRatingDTO> productRatingDTOList = productRatingDTOListRE.getBody();
log.info("Retrieved rating for id {} are: {}", id, productRatingDTOList);
return productRatingDTOList;
}
public List<ProductRatingDTO> getDefaultProductRating(String id, Exception ex) {
log.warn("fallback method: " + ex.getMessage());
return new ArrayList<>();
}
product-service >> application.yml
resilience4j.retry:
instances:
rating-service:
maxAttempts: 3
waitDuration: 10s
retryExceptions:
- org.springframework.web.client.HttpServerErrorException
ignoreExceptions:
- java.lang.ArrayIndexOutOfBoundsException
rating-service >> RatingsServiceImpl.java
#Override
public List<RatingsDTO> getRatings(String productId) {
log.info("Ratings required for product id: "+productId);
List<RatingsDTO> ratingsDTOList = ratingsRepository.getRatingsByProductId(productId);
log.info("Ratings fetched for product id {} are : {}",productId,ratingsDTOList);
if (ThreadLocalRandom.current().nextInt(0,5) == 0){ // Erratic block
log.error("Erratic");
throw new org.springframework.web.client.HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR);
}
return ratingsDTOList;
}
Please let me know where I am doing the mistake?

Related

Unable to return Internal Server Error in java unit test case

I am writing negative test case in which I am expecting internal server error but somehow I am not able to get it. The code i have tried and the output is posted below:
delete method:
/**
* delete pricing rule by id
* #param ruleId
* #return
*/
#DeleteMapping(path = "/deleteById/{ruleId}")
public ResponseEntity<String> deletePricingRule(#PathVariable String ruleId) {
logMethodStart(RulesCrudController.class, "deletePricingRule");
try {
log.debug( "deleting pricing rule with id: " + ruleId );
String deletedId = pricingRuleServiceImpl.deletePricingRule(ruleId);
if( deletedId != null ) {
log.debug("deleted pricing rule with id: " + ruleId);
return new ResponseEntity<>("The rule id : " + deletedId + " is deleted successfully", HttpStatus.OK);
} else {
return new ResponseEntity<>("No pricing rule with rule id: "+ ruleId +" is Found",HttpStatus.INTERNAL_SERVER_ERROR);
}
}
catch(Exception e){
log.error("deletePricingRule() got an exception: " + e.toString());
return new ResponseEntity<>("No pricing rule with rule id: "+ ruleId +" is Found",HttpStatus.INTERNAL_SERVER_ERROR);
}
}
delete test case
#Test
public void DeletePricingRule_withDeleteMethod_InternalServerError() throws Exception {
when(mockService.deletePricingRule(dynamo.getId())).thenReturn( null );
mockMVC.perform(MockMvcRequestBuilders.delete("/pricingRule/deleteById/{ruleId}", dynamo.getId()))
.andExpect(status().isInternalServerError());
}
Object that I am using:
#Mock
private PricingRule dynamo, dynamo1;
#Before
public void setUp() {
dynamo = new PricingRule();
dynamo.setId("0653cf02-6b39-40ab-a008-6efea487eb8f");
dynamo.setRuleDesc("dynamo test");
dynamo1 = new PricingRule();
dynamo1.setId("0653cf02-6b39-40ab-a008-6efea487eb8f");
dynamo1.setRuleDesc("dynamo test");
}
Output:
java.lang.AssertionError: Status
Expected :500
Actual :200

Spring Webflux SSE server: how to send error to the client and close connection

I have a Spring WebFlux application server that sends SSE to the end user infinitely while they are connected. I want to implement some kind of id validation, that relies on requesting the id from a 3rd party service, that returns Mono<Boolean>. I want to close connection with an error if the above Mono contains false.
The problem is that I can't return Mono.error() from the handleSse method (because it has to return Flux<ServerSentEvent<UserUpdateResponse>>).
How to properly send an error to the user and close the connection afterwards?
Here's my code:
#GetMapping("/sse")
public Flux<ServerSentEvent<UserUpdateResponse>> handleSse(String id) {
return usersSink.asFlux()
.filter(update -> id.equals(update.getId()))
.map(this::wrapIntoSse);
}
private ServerSentEvent<UserUpdateResponse> wrapIntoSse(UserUpdate userUpdate) {
return ServerSentEvent.builder(new UserResponse(userUpdate.getUserCode()))
.event("user-update")
.build();
}
So after struggling with this for a while I came to this solution:
#GetMapping("/sse")
public Flux<ServerSentEvent<UserUpdateResponse>> handleSse(#RequestParam(required = false) String id) {
if (StringUtils.isBlank(id)) {
throw new WebServiceRuntimeException(HttpStatus.BAD_REQUEST, "The \"id\" parameter is missing");
}
final Mono<Boolean> cached = cacheService.getgetUser(id);
return Flux.from(cached).flatMap(exists -> {
if (exists) {
return userUpdateSink.asFlux()
.filter(update -> id.equals(update.getgetUser()))
.map(this::wrapIntoSse);
}
return Flux.error(new WebServiceRuntimeException(HttpStatus.NOT_FOUND,
"The specified \"user\" does not exist: " + id + "."));
}
);
}
#GetMapping("/sse")
public Flux<ServerSentEvent> handleSse(#RequestParam(required = false) String id) {
if (StringUtils.isBlank(id)) {
throw new WebServiceRuntimeException(HttpStatus.BAD_REQUEST, "The "id" parameter is missing");
}
final Mono cached = cacheService.getgetUser(id);
return Flux.from(cached).flatMap(exists -> {
if (exists) {
return userUpdateSink.asFlux().filter(update -> id.equals(update.getgetUser())).map(this::wrapIntoSse);
}
return Flux.error(new WebServiceRuntimeException(HttpStatus.NOT_FOUND, "The specified "user" does not exist: " + id + "."));
});
}

Send push notification using Java Springboot server and Expo React native client

My serser uses Java with SpringBoot and my client is an expo react native app which uses typescript.
I am really blocked by this feature: I want to sens push notifications. I tried a lot of methods, but I didn't succeed.
On the client side I am using the method described in expo documentation: https://docs.expo.dev/push-notifications/overview/.
When I use their tool to send test notifications(https://expo.dev/notifications), I receive them on my device.
I didn't manage to send notifications from my client app. I tried all I found on the Inthernet. When I used FirebaseMessagingService and I used the server key from the firebase project as token, I received the SENDER_ID_MISMATCH error.
#Service
public class FirebaseMessagingService {
private final FirebaseMessaging firebaseMessaging;
public FirebaseMessagingService(FirebaseMessaging firebaseMessaging) {
this.firebaseMessaging = firebaseMessaging;
}
public String sendNotification(Note note, String topic) throws FirebaseMessagingException {
Notification notification = Notification
.builder()
.setTitle(note.getSubject())
.setBody(note.getContent())
.setImage(note.getImage())
.build();
Message message = Message
.builder()
.setNotification(notification)
.putAllData(note.getData())
.setToken(topic)
.build();
return firebaseMessaging.send(message);
}
}
I also found the expo-server-sdk-java but I couldn't manage to integrate it.
Any heeeeelp, pleaseeee?
not sure if it's the best practice but it works fine for me.
My pom
<dependency>
<groupId>io.github.jav</groupId>
<artifactId>expo-server-sdk</artifactId>
<version>1.1.0</version>
</dependency>
Then in the java class
private static void sendPushNotification(String token, String titulo, String mensaje, Map<String, Object> data) throws PushClientException {
if (!PushClient.isExponentPushToken(token)) throw new Error("Token:" + token + " is not a valid token.");
ExpoPushMessage expoPushMessage = new ExpoPushMessage();
expoPushMessage.getTo().add(token);
expoPushMessage.setTitle(titulo);
expoPushMessage.setBody(mensaje);
expoPushMessage.setData(data);
List<ExpoPushMessage> expoPushMessages = new ArrayList<>();
expoPushMessages.add(expoPushMessage);
PushClient client = new PushClient();
List<List<ExpoPushMessage>> chunks = client.chunkPushNotifications(expoPushMessages);
List<CompletableFuture<List<ExpoPushTicket>>> messageRepliesFutures = new ArrayList<>();
for (List<ExpoPushMessage> chunk : chunks) {
messageRepliesFutures.add(client.sendPushNotificationsAsync(chunk));
}
// Wait for each completable future to finish
List<ExpoPushTicket> allTickets = new ArrayList<>();
for (CompletableFuture<List<ExpoPushTicket>> messageReplyFuture : messageRepliesFutures) {
try {
allTickets.addAll(messageReplyFuture.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
List<ExpoPushMessageTicketPair<ExpoPushMessage>> zippedMessagesTickets = client.zipMessagesTickets(expoPushMessages, allTickets);
List<ExpoPushMessageTicketPair<ExpoPushMessage>> okTicketMessages = client.filterAllSuccessfulMessages(zippedMessagesTickets);
String okTicketMessagesString = okTicketMessages.stream().map(p -> "Title: " + p.message.getTitle() + ", Id:" + p.ticket.getId()).collect(Collectors.joining(","));
LOGGER.info("Recieved OK ticket for " + okTicketMessages.size() + " messages: " + okTicketMessagesString);
List<ExpoPushMessageTicketPair<ExpoPushMessage>> errorTicketMessages = client.filterAllMessagesWithError(zippedMessagesTickets);
String errorTicketMessagesString = errorTicketMessages.stream().map(p -> "Title: " + p.message.getTitle() + ", Error: " + p.ticket.getDetails().getError()).collect(Collectors.joining(","));
LOGGER.error("Recieved ERROR ticket for " + errorTicketMessages.size() + " messages: " + errorTicketMessagesString);
/**
// Countdown 30s
int wait = 30;
for (int i = wait; i >= 0; i--) {
System.out.print("Waiting for " + wait + " seconds. " + i + "s\r");
Thread.sleep(1000);
}
System.out.println("Fetching reciepts...");
List<String> ticketIds = (client.getTicketIdsFromPairs(okTicketMessages));
CompletableFuture<List<ExpoPushReceipt>> receiptFutures = client.getPushNotificationReceiptsAsync(ticketIds);
List<ExpoPushReceipt> receipts = new ArrayList<>();
try {
receipts = receiptFutures.get();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
System.out.println("Recieved " + receipts.size() + " receipts:");
for (ExpoPushReceipt reciept : receipts) {
System.out.println("Receipt for id: " + reciept.getId() + " had status: " + reciept.getStatus());
}
*/
}
In the App.js from react native project with EXPO 44 i take expo token in this way
async function registerForPushNotificationsAsync() {
let token;
if (isDevice) {
const { status: existingStatus } =
await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== "granted") {
alert("Failed to get push token for push notification!");
return;
}
token = (await Notifications.getExpoPushTokenAsync()).data;
}
if (Platform.OS === "android") {
Notifications.setNotificationChannelAsync("default", {
name: "default",
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: "#FF231F7C",
});
}
return token;
}
effect from App.js
useEffect(() => {
initFirebaseApp();
registerForPushNotificationsAsync().then(async (token) => {
//store in some place token
});
// This listener is fired whenever a notification is received while the app is foregrounded
notificationListener.current =
Notifications.addNotificationReceivedListener(handleNotification);
// This listener is fired whenever a user taps on or interacts with a notification (works when app is foregrounded, backgrounded, or killed)
responseListener.current =
Notifications.addNotificationResponseReceivedListener(
handleNotificationResponse
);
return () => {
Notifications.removeNotificationSubscription(
notificationListener.current
);
Notifications.removeNotificationSubscription(responseListener.current);
};
}, []);
notification handlers in App.js
const handleNotification = (response) => {
// console.log(response);
};
const handleNotificationResponse = (response) => {
// console.log(response)
};
I hope this helps you
Docs
Expo SDK documentation
Expo docs reference

java.lang.NullPointerException at org.web3j.protocol.core.filters.LogFilter.process(LogFilter.java:46)

I am trying to read blockchain event (in Java) using web3j but getting NPE:
java.lang.NullPointerException: null
at org.web3j.protocol.core.filters.LogFilter.process(LogFilter.java:46)
at org.web3j.protocol.core.filters.Filter.getInitialFilterLogs(Filter.java:119)
at org.web3j.protocol.core.filters.Filter.run(Filter.java:69)
at org.web3j.protocol.rx.JsonRpc2_0Rx.run(JsonRpc2_0Rx.java:89)
at org.web3j.protocol.rx.JsonRpc2_0Rx.lambda$ethLogFlowable$2(JsonRpc2_0Rx.java:79)
at io.reactivex.internal.operators.flowable.FlowableCreate.subscribeActual(FlowableCreate.java:71)
at io.reactivex.Flowable.subscribe(Flowable.java:14935)
at io.reactivex.Flowable.subscribe(Flowable.java:14872)
at io.reactivex.Flowable.subscribe(Flowable.java:14791)
Code in question
for (EthLog.LogResult logResult : logResults) {
https://github.com/web3j/web3j/blob/master/core/src/main/java/org/web3j/protocol/core/filters/LogFilter.java#L46
#Override
protected void process(List<EthLog.LogResult> logResults) {
for (EthLog.LogResult logResult : logResults) {
if (logResult instanceof EthLog.LogObject) {
Log log = ((EthLog.LogObject) logResult).get();
callback.onEvent(log);
} else {
throw new FilterException(
"Unexpected result type: " + logResult.get() + " required LogObject");
}
}
}
Raised issue https://github.com/web3j/web3j/issues/1486
But as not expected that to be fixed, what should I do?
Sample code and/or code snippets
private void createEventMonitor() {
log.info("createEventMonitor() begin...");
// contract from block
EthFilter filter = new EthFilter(DefaultBlockParameter.valueOf(new BigInteger("7605105")),
DefaultBlockParameterName.LATEST, contractAddress);
//Disposable subscription
subscription = web3j
.ethLogFlowable(filter)
.subscribe(
event -> {
log.info("Withdraw event received:");
log.info(" event data >>> {}", event.getData());
log.info(" event topic >>> {}", event.getTopics().stream().collect(Collectors.joining()));
log.info(" event address >>> {}", event.getAddress());
log.info(" event txHash >>> {}", event.getTransactionHash());
}, error -> {
log.error("Event error: {}", error, error); //!
});
log.info("createEventMonitor() end.");
}
This error should go away in the future after https://github.com/web3j/web3j/pull/1495 solving https://github.com/web3j/web3j/issues/1486 (should be next version after 4.8.7)
In short: JSON RPC error was no visible on web3j level.
To analyze issue like that run program with log level debug to see JSON RPC messages.

Unable to persist entity with managed executor in Quarkus

I am unable to persist an entity using ManagedExecutor in Quarkus.
managedExecutor.runAsync(() -> {
User user = User.findById(userId);
if (user == null) {
return;
}
BigDecimal amount;
try {
amount = new BigDecimal(split[1]);
} catch (NumberFormatException e) {
throw new BadRequestException("Invalid amount: " + split[1] + ", " + e.getMessage());
}
managedExecutor.runAsync(threadContext.contextualRunnable(new Runnable() {
#Override
#Transactional
#ActivateRequestContext
public void run() {
user.minimumAmount = amount;
user.persistAndFlush();
}
}));
sendMessage(userId, new NewMinimumAmountMessage(user.minimumAmount));
});
Right now there are no exceptions thrown and nothing after persistAndFlush() gets executed.
I have tried to put the persist in the initial runAsync but that also doesn't work.
The code here is in a websocket function which is annotated with #OnMessage, putting #Transactional here doesn't do anything.
Both the ManagedExecutor and the ThreadContext are injected.
I was able to fix it but I know it's not the proper way.
Response response = given()
.auth().oauth2(token)
.header("Bearer", token)
.header("Content-Type", "application/json")
.when().put("/api/user/amount/" + split[1])
.thenReturn();
I added a path in a resource and used the test function to call it.

Categories