Is there any way to retry different in method when a particular method failing (instead of retrying that failed method) - resttemplate

I have a requirement in which I have 2 methods (in different class) , in which one method makes Rest GET call to fetch token(which is like session token and valid for few seconds) and another method which append this retrieved token in headers and make a Rest POST call, so whenever POST call fails because of token expiration I need to retry GET call method(instead of retrying POST call alone) to fetch new token and use token in POST request headers and retry post call(with new token). I could see retryable retries only failed method, in my case it retries POST call alone so it endup in retrying post call method with expired token. I want to retry Get method and POST method when post method failed for expired token. So I want to know is there any way to retry different method when particular method fails. Appreciate your help.
Retryable class(getCSRFToken- get call, sapPost-post call)
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Component;
#Component
public class RestTemplateRetry extends AbstractRestTemplate{
#Retryable(value= {Exception.class}, maxAttempts=5, backoff = #Backoff(delay = 2000))
public ResponseEntity<String> getCSRFToken (HttpEntity<String> entity, String getUrl) throws Exception{
ResponseEntity<String> csrfToken= getRestTempalte().exchange(getUrl, HttpMethod.GET, entity, String.class);
return csrfToken;
}
#Retryable(value= {Exception.class}, maxAttempts=5, backoff = #Backoff(delay = 2000))
public ResponseEntity<String> sapPost (HttpEntity<String> entity, String postUrl)throws Exception{
ResponseEntity<String> response= getRestTempalte().exchange(postUrl, HttpMethod.POST, entity, String.class);
return response;
}
}
Listener class
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.listener.RetryListenerSupport;
import org.springframework.stereotype.Component;
#Component
public class RetryListener extends RetryListenerSupport {
Logger log = LoggerFactory.getLogger(getClass());
#Override
public <T, E extends Throwable> void onError(
RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
log.warn("Retryable method {} threw {}th, attempted retries = {} with exception {}",
context.getAttribute("context.name"),
context.getRetryCount(), context.getRetryCount() - 1, throwable.toString());
}
}

No; that is not supported; you need to write custom code for these more complex scenarios.

Related

Handling Ratpack Context.parse execptions

I'm writing an API in Kotlin with the Ratpack framework, using Jackson to deserialize JSON request bodies. When I send an invalid request body, my application throws a 500 internal server error exception:
import com.google.inject.Inject
import com.mycompany.mynamespace.MyComponent
import ratpack.func.Action
import ratpack.handling.Chain
import java.util.UUID
class MyTestEndpoint #Inject constructor(
private val myComponent: MyComponent) : Action<Chain> {
override fun execute(chain: Chain) {
chain
.post { ctx ->
ctx.parse(MyParams::class.java)
.map { parsedObject -> myComponent.process(parsedObject) }
.then { ctx.response.send() }
}
}
}
data class MyParams(val borrowingId: UUID)
The exception when this endpoint is hit with an invalid request body is:
com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException: Instantiation of [simple type, class com.mycompany.mynamespace.MyParams] value failed for JSON property borrowingId due to missing (therefore NULL) value for creator parameter borrowingId which is a non-nullable type
I have a generic error handler which checks the type of Exception thrown, and returns an appropriate status. But in this case, checking for a MissingKotlinParameterException and returning 400 bad request does not make sense, because this exception may be thrown in other circumstances.
Additionally, I could add onError after the ctx.parse line, but this is going to be a large API, and implementing that in every handler doesn't follow the pattern of having a generic error handler to keep the API consistent. Is there a way to get Ratpack to throw a specific exception (something like ParseFailedException) when the parse fails, so that I can catch it and return a 400 bad request?
As a workaround I've written an extension method:
fun <T: Any> Context.tryParse(type: Class<T>): Promise<T> {
return parse(type)
.onError { ex -> throw BadRequestException(ex) }
}
Where my generic error handler catches the BadRequestException, and sets the response status to 400

Flink Retrofit not Serializable Exception

I have a Flink Job reading events from a Kafka queue then calling another service if certain conditions are met.
I wanted to use Retrofit2 to call the REST endpoint of that service but I get a is not Serializable Exception. I have several Flat Maps connected to each other (in series) then calling the service happens in the last FlatMap. The exception I get:
Exception in thread "main"
org.apache.flink.api.common.InvalidProgramException: The
implementation of the RichFlatMapFunction is not serializable. The
object probably contains or references non serializable fields.
...
Caused by: java.io.NotSerializableException: retrofit2.Retrofit$1
...
The way I am initializing retrofit:
RetrofitClient.getClient(BASE_URL).create(NotificationService.class);
And the NotificationService interface
public interface NotificationService {
#PUT("/test")
Call<String> putNotification(#Body Notification notification);
}
The RetrofitClient class
public class RetrofitClient {
private static Retrofit retrofit = null;
public static Retrofit getClient(String baseUrl) {
if (retrofit == null) {
retrofit = new Retrofit.Builder().baseUrl(baseUrl).addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
Put your Notification class code for more details, but looks like this answer helps
java.io.NotSerializableException with "$1" after class

Spring boot how to handle throwable

I made a project like this sample. So the controllers are like this
package mypackagename.controller;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
#RestController
#RequestMapping("/")
public class StoresRestController {
#RequestMapping(method = RequestMethod.GET)
public String stores() {
return ...
}
}
I like to handle all throwables and make my customized unified response. The problem is I cannot find a guide or a sample to do this correctly.
First of all, I tried ExceptionHandler, with Throwable, but it didn't work, so I decided to move on. Then, the most close approach that I found is this, so I tried jersey, by adding something like this. But it's not working for all throwables. Also, it's ignoring my controllers, by complaining
o.g.jersey.internal.inject.Providers : A provider mypackagename.controller.StoresRestController registered in SERVER runtime does not implement any provider interfaces applicable in the SERVER runtime. Due to constraint configuration problems the provider mypackagename.controller.StoresRestController will be ignored.
I searched for this error and found this, which I'm not using ContainerResponseFilter in my project as I provided the sample above. So I'm clueless. The main problem is how to handle all throwables, but if you can give me some suggestions about how to solve Providers problem, I'll be so appreciated.
In my project I use #ControllerAdvice to handle my exceptions. Here's an example. Hope this helps. Just make sure this class is on your component scan so it gets picked up.
#RestController
#ControllerAdvice
public class StoresExceptionHandler {
#ExceptionHandler(Throwable.class)
public ResponseEntity<Object> handleThrowable(final Throwable ex) {
return new ResponseEntity<Object>("Unable to process request.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Finally this post helped me to handle all Throwables, except authentication exceptions. The important part was to use #EnableWebMvc and ResponseEntityExceptionHandler. To handle authentication exceptions I used this answer. Hope it's helping someone.
as #carlos-cook said, you could use a #ControllerAdvice and, ProblemDetail defined in RFC 7807 which could look like:
import org.springframework.http.ProblemDetail;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(RuntimeException.class)
public ProblemDetail handleUnexpectedException(RuntimeException rte, WebRequest wr) {
ProblemDetail pd = this.createProblemDetail(HttpStatus.INTERNAL_SERVER_ERROR, rte);
pd.setType(URI.create("http://your-site.com/internal-server-error"));
pd.setTitle("Internal server error");
return pd;
}
#ExceptionHandler(YourCustomeException.class)
public ProblemDetail handleUnexpectedException(YourCustomException rte, WebRequest wr) {
ProblemDetail pd = this.createProblemDetail(HttpStatus.INTERNAL_SERVER_ERROR, rte);
pd.setType(URI.create("http://your-site.com/custom-error-page"));
pd.setTitle("Internal server error");
return pd;
}
}
Then in your controller you could simply throw YourCustomException
This controller advice will handle every exception and YourCustomException separately.

AEM Servlet response writter removing links

In AEM, I'm trying to write a JSON object that contains a string object via a get servlet, like this:
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(jsonObject.toString());
Response being of type SlingHttpServletResponse
When the servlet is accessed in a browser the is stripped with a warning coming out of the aem log:
03.08.2015 16:55:27.359 *WARN* [127.0.0.1 [1438617327343] GET /bin/integration.json HTTP/1.1] com.day.cq.rewriter.linkchecker.impl.LinkCheckerImpl Ignoring malformed URI: java.net.URISyntaxException: Illegal character in path at index 0: \
Link checker is bypassed for a lot of patterns including the link above.
For example the string object inside the json:
pageIntro:'this link doesn't work'
becomes:
pageIntro:'this link</a> doesn't work'
Any help would be much appreciated.
Cheers,
Alex
By doing a quick fiddle around AEM 6.0 , I am not able to reproduce this issue .
Following is what I did in the servlet. Attaching the snippet below. Is there anything else you are doing to achieve this ?
import java.io.IOException;
import javax.servlet.ServletException;
import org.apache.felix.scr.annotations.sling.SlingServlet;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.commons.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#SlingServlet( label = "Stack Overflow - Sabya Test Servlet",
description = "Used for quick fiddle",
paths="/bin/sabya-servlet.json",
metatype = true
)
public class SabyaTestServlet extends SlingAllMethodsServlet {
private static final long serialVersionUID = 1335893917596088016L;
private static final Logger log = LoggerFactory
.getLogger(SabyaTestServlet.class);
#Override
protected void doGet(SlingHttpServletRequest request,
SlingHttpServletResponse response) throws ServletException,
IOException {
log.trace("Sabya Test Servlet : doGet() starts .. ");
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("pageIntro", "this <a href='http://www.domain.com/my-section/page.html'>link</a> doesn't work");
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(jsonObject.toString());
} catch (JSONException e) {
log.error("Something ridiculous has happened !! {}" , e);
}
log.trace("Sabya Test Servlet : doGet() ends .. ");
}
}
Request URL : http://localhost:4502/bin/sabya-servlet.json
Response :
{
pageIntro: "this <a href='http://www.domain.com/my-section/page.html'>link</a> doesn't work"
}
Note : I believe you are using org.apache.sling.commons.json.JSONObject .

Spring 3.1 MVC: GET & POST on the same mapping, can it work?

I'm learning Spring 3.1.
My webapp name is "acme".
The url is roughly https://blah.blah.blah/acme
That URL is set up to display the login.jsp
I have a "/login" mapping in my controller that my login.jsp submits to
If something goes wrong it return the user to the login.jsp with this url in the browser:
https://blah.blah.blah/acme/login
The "/login" mapping is set up to handle POST requests, so I am concerned about users bookmarking
https://blah.blah.blah/acme/login, and getting the error message of "GET request not supported"
So, I thought I would put in a function to handle GET requests to /login to reroute through my general mapping handler for "/" and "/home":
Login.Controller.java
package gov.noaa.acme.controller;
import java.security.Principal;
import javax.servlet.http.*;
import org.springframework.stereotype.Controller;
import org.springframework.validation.*;
import org.springframework.ui.ModelMap;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.apache.log4j.Logger;
#Controller
public class LoginController {
private static final Logger logger = Logger.getLogger(LoginController.class);
#RequestMapping({"/","home"})
public String home(ModelMap model,HttpSession session,HttpServletRequest request) {
// Do some stuff
return "login";
}
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String login_get(){
logger.debug("started...");
return "forward:home";
}
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(#ModelAttribute("laph") LAPH laph,
BindingResult bindingResult,
ModelMap model,
HttpSession session,
HttpServletRequest request,
HttpServletResponse response,
#RequestParam(required=true) String last_usertype) {
if (bindingResult.hasErrors()) {
logger.debug("Error returning home");
return "home";
}
logger.debug("Started ....");
// Do authentication
if (!isAuthenticated) {
model.put("status_message", error_message);
return "login";
}
// success, send newly authenticated user to a search page
nextView = "search";
return "redirect:" + nextView;
}
}// end class LoginController
My logs show that I am not even reaching the controller method for handling GET requests for /login, I'm still getting the error messages that GET is not supported for /login.
Any ideas on how I can fix this?
Thanks
Steve
I am concerned about users bookmarking https:// blah.blah.blah/acme/login, and getting the error message of "GET request not supported".
Your method signatures are correct; with the annotations you have placed on login_get and login, Spring will not be confused and will invoke the correct methods for GET and POST requests.
Your method home is wrong; it returns the string "login", but I guess you do not have a view named login and that you would like it to invoke one of the login methods. In that case you should have returned "forward:login", but this solution is not much better.
My advice is:
/home should render the home view, by using file home.jsp or whatever view technology you're using.
Use a HandlerInterceptor to check whether a user is logged in, and if not, only then you redirect him to the login url.

Resources