Spring RestTemplate - Log on request sometimes hangs awaiting response - java

I use a rest template in a Java client to log onto a server and receive required headers to upgrade a connection to a secure websocket.
here is my code:
private static void loginAndSaveJsessionIdCookie(final String user, final String password, final HttpHeaders headersToUpdate) {
String url = "http://localhost:" + port + "/websocket-services/login.html";
new RestTemplate().execute(url, HttpMethod.POST,
new RequestCallback() {
#Override
public void doWithRequest(ClientHttpRequest request) throws IOException {
System.out.println("start login attempt");
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("username", user);
map.add("password", password);
new FormHttpMessageConverter().write(map, MediaType.APPLICATION_FORM_URLENCODED, request);
}
},
new ResponseExtractor<Object>() {
#Override
public Object extractData(ClientHttpResponse response) throws IOException {
System.out.println("got login repsonse");
headersToUpdate.add("Cookie", response.getHeaders().getFirst("Set-Cookie"));
return null;
}
});
}
This usually works, but occasionally (especially after the websocket connection has timed out) there is no response from the server and my client stops responding while the method hangs awaiting the response.
Could anyone suggest a fix or work around for this ? As it causes the client to freeze completely, requiring a force close.

To async any code threading is the best way and you can use the ExecutorService
to specify any timeout you wish to have. Following two options are available as per your need (pls chk API to know difference between them) :-
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
OR
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;

Related

O365 Modern authentication with Java Spring Context

I have to use oAuth2 "modern authentication" in Java Spring Context and found here. Java code I could use.
The getAccessToken() method has however a parameter of type ManagedExecutorService and I don't have any idea, what kind of value it should be.
Here is the code:
#Override
public String getAccessToken(final String clientId, final String clientSecret, final ManagedExecutorService service)
throws MalformedURLException, ExecutionException, InterruptedException, TimeoutException {
final long now = System.currentTimeMillis();
if (accessToken != null && now < expiryTimeMs - REFRESH_BEFORE_EXPIRY_MS) {
final AuthenticationContext context = new AuthenticationContext(AUTHORITY, false, service);
final AuthenticationCallback<AuthenticationResult> callback = new AuthenticationCallback<>() {
#Override
public void onSuccess(final AuthenticationResult result) {
log.info("received token");
}
#Override
public void onFailure(final Throwable exc) {
throw new RuntimeException(exc);
}
};
log.info("requesting token");
final Future<AuthenticationResult> future = context.acquireToken(RESOUCE,
new ClientCredential(clientId, clientSecret), callback);
// wait for access token
final AuthenticationResult result = future.get(30, TimeUnit.SECONDS);
// cache token and expiration
accessToken = result.getAccessToken();
expiryTimeMs = result.getExpiresAfter();
}
return accessToken;
}
I put already all necessary dependencies into the project but can't test the method until I know what ManagedExecutorServiceis and where do I get the parameter from.
Why not using one of Spring REST clients which can handle OAuth2 out of the box?
All of #FeignClient, RestTemplate and WebClient expose configuration properties for acquiring access-token from OAuth2 authorization-server using client-credentials (or directly set authorization header if you are processing an authorized request and want to forward incoming access-token).
Choose one of existing clients and read its manual.

Synchronize the asynchronous callback method call

We are working on a solution which is like this;
Request: (We receive the request via API call and send to third-party via a library we use)
OUR-Client --> OUR-API --> THIRD-PARTY
Response: (This response we receive from third-party asynchronously through a callback method given in the library we are using)
THIRD-PARTY --> OUR-CODE --> OUR-Client
Here is the below code and want to get rid of Thread.sleep() call and make use of the callback to provide response.
----- API Method -------------
#GetMapping
public ResponseEntity<String> getData(#RequestBody String requestId) throws SessionNotFound, InterruptedException {
dataService.get(requestId);
String msg;
long start = System.currentTimeMillis();
do {
// We want to get rid of this sleep() statement and some way to callback here as soon there is message.
Thread.sleep(30);
msg = clientApp.getRespnse(requestId);
} while(msg == null);
return ResponseEntity.ok(msg);
}
------- Service Class and Methods ---------------
#Service
public class DataService {
#Autowired
private ClientApp clientApp;
public void get(String requestId) throws SessionNotFound {
// This method is from the library we use. This only submits the request, response is received on different method.
send(requestId);
}
------- Component Class and Methods ---------------
#Component
public class ClientFixApp {
private Map<String, String> responseMap = new HashMap<>();
// This method is callback from the third party library, whenever there is response this method will get invoked and this message we need to send as response of the API call.
#Override
public void onResponse(String requestId)
throws FieldNotFound, IncorrectDataFormat, IncorrectTagValue, UnsupportedMessageType {
responseMap.put(msgId, jsonMsg);
}
public String getRespnse(String requestId) {
return responseMap.get(requestId);
}
}
DataService and ClientFixApp are flawed by design (the very fact it is 2 different classes while there must be one, speaks a lot). Truly asynchronous programs must allow to register user procedure as a callack, called when the I/O operation finished (successfully or not). ClientFixApp silently writes the result in a table, leaving for client no other option except polling.
You can use any existing asynchronous http library. For example, below is the code for the library java.net.http included in Java 11 and later. Other libraries have similar functionality.
public static CompletableFuture<HttpResponse<String>> doGet(String uri) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.build();
return client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
}
public static void main(String[] args) {
CompletableFuture<HttpResponse<String>> future = doGet("https://postman-echo.com/get");
future.thenApply(HttpResponse::body)
.thenAccept(System.out::println)
.join();
}

How to rollback a http request to an api endpoint that saves data into db

So am writing some component tests and as far as i know the test itself should not have direct access to db or know anything about the application.
Http calls are easy to mock but what about db access in the implementation how do you mock that?
So what i did is i make a http request to an api endpoint that saves data into db, that data is used for the test purposes, the goal is to rollback that transaction after the test.
#Before
public void setupTripEvents() throws ExecutionException, InterruptedException {
dbIntegrationMock = new DbIntegrationMock();
setupTrips(1L, "CHARGING_STATUS_CHANGE");
setupTrips(2L, "CHARGING_STATUS_CHANGE");
setupTrips(3L, "DRIVER_LOGIN");
setupTrips(4L, "DRIVER_LOGOUT");
setupTrips(5L, "IGNITION_ON");
setupTrips(6L, "IGNITION_OFF");
}
private void setupTrips(long l, String triggerType) throws ExecutionException, InterruptedException {
TripEventEntity tripEventEntity = new TripEventEntity();
<---- data ---->
dbIntegrationMock.saveTripEventEntity(tripEventEntity);
}
public Void saveTripEventEntity(TripEventEntity tripEventEntity) throws ExecutionException, InterruptedException {
return HttpClient.forService(Service.withName("componentname"))
.send(HttpRequest.forPut("/dev/db/save/tripevententity")
.withPayload(tripEventEntity)
.withHeader(HttpHeaders.ACCEPT, "application/json"),
HttpResponseType.of(Void.class, MediaType.of("application/json")))
.whenComplete(HttpStatusToExceptionMapper::statusToException)
.thenApply(response -> response.payload().orElse(null))
.toCompletableFuture()
.get();
}
#PutMapping(value = "/db/save/tripevententity",
produces = "application/json")
public DeferredResult<ResponseEntity<Void>> saveTripEvent(HttpEntity<TripEventEntity> request) {
log.info("Save trip event entity...");
return process("tripevententity", "", o -> o, request, call -> save(request));
}
Ok this is not a transaction so it cannot be rolled back but i can create another api that will remove this data after the test is finished

Springboot TomcatEmbeddedServletContainer KeepAliveTimeout not working

I have set the keep alive timeout in spring boot embeded tomcat server to 30 seconds. So i use below in the Application.java,
#Bean
public EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {
TomcatEmbeddedServletContainerFactory containerFactory = new TomcatEmbeddedServletContainerFactory();
containerFactory
.addConnectorCustomizers(new TomcatConnectorCustomizer() {
#Override
public void customize(Connector connector) {
((AbstractProtocol) connector.getProtocolHandler())
.setKeepAliveTimeout(30000);
}
});
return containerFactory;
}
Then i sleep a request thread for 40 seconds from my rest controller. But when i make a request via postman it successfully return HTTP status code 200 instead it should return gateway timeout error.
I try both setConnectionTimeout and setKeepAliveTimeout and it did not work.
What am i missing here?
Edit question: My initial problem
Let me explain the original question of mine, which lead me to ask above question.
Well i have a long poll process which normally runs about more than 5 minits.
So what happen is when i call the Rest API for longpoll, After 2.2 minits i get a 504 http error in browser.
I am using a AWS environment, where i have a ELB and a HAProxy which is installed in AWS EC2 instance.
As per AWS doc, it says the default Idle Connection Timeout of ELB is 60 seconds. So i have increase it to up to 30 mins.
Moreover it says,
If you use HTTP and HTTPS listeners, we recommend that you enable the
keep-alive option for your EC2 instances. You can enable keep-alive in
your web server settings or in the kernel settings for your EC2
instances.
So have increase the embedded tomcat keep-alive timeout like above code snippet to 30.2 mins
So now i expect my long poll request to be completed, with out getting a 504 error. But still i get 504 error in browser?
Ref: AWS dev guide
It looks like you want to close abandoned HTTP connections which might occur on mobile devices.
#RestController
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
public EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {
TomcatEmbeddedServletContainerFactory containerFactory = new TomcatEmbeddedServletContainerFactory();
containerFactory
.addConnectorCustomizers(new TomcatConnectorCustomizer() {
#Override
public void customize(Connector connector) {
((AbstractProtocol) connector.getProtocolHandler()).setConnectionTimeout(100);
}
});
return containerFactory;
}
#RequestMapping
public String echo(#RequestBody String body) {
return body;
}
}
Connection timeout has been set to 100 millisencods in order to run my tests fast. Data is sent in chunks. Between every chunk the running thread is suspended for x milliseconds.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = DemoApplication.class)
#WebIntegrationTest("server.port:19000")
public class DemoApplicationTests {
private static final int CHUNK_SIZE = 1;
private static final String HOST = "http://localhost:19000/echo";
#Rule
public ExpectedException expectedException = ExpectedException.none();
#Test
public void slowConnection() throws Exception {
final HttpURLConnection connection = openChunkedConnection();
OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
writeAndWait(500, out, "chunk1");
writeAndWait(1, out, "chunk2");
out.close();
expectedException.expect(IOException.class);
expectedException.expectMessage("Server returned HTTP response code: 400 for URL: " + HOST);
assertResponse("chunk1chunk2=", connection);
}
#Test
public void fastConnection() throws Exception {
final HttpURLConnection connection = openChunkedConnection();
OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
writeAndWait(1, out, "chunk1");
writeAndWait(1, out, "chunk2");
out.close();
assertResponse("chunk1chunk2=", connection);
}
private void assertResponse(String expected, HttpURLConnection connection) throws IOException {
Scanner scanner = new Scanner(connection.getInputStream()).useDelimiter("\\A");
Assert.assertEquals(expected, scanner.next());
}
private void writeAndWait(int millis, OutputStreamWriter out, String body) throws IOException, InterruptedException {
out.write(body);
Thread.sleep(millis);
}
private HttpURLConnection openChunkedConnection() throws IOException {
final URL url = new URL(HOST);
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setChunkedStreamingMode(CHUNK_SIZE);
return connection;
}
}
Set log level for package org.apache.catalina.core to DEBUG
logging.level.org.apache.catalina.core=DEBUG
and you can see a SocketTimeoutException for slowConnection test.
I don't know why you want HTTP status code 502 as error response status. HTTP 502 says:
The 502 (Bad Gateway) status code indicates that the server, while
acting as a gateway or proxy, received an invalid response from an
inbound server it accessed while attempting to fulfill the request.
The client Postman calls your server application. I don't see any gateway or proxy in between.
If you just condensed your question to a bare minimum and in reality you want to build a proxy on your own, you might consider using Netflix Zuul.
Update 23.03.2016:
That is the root cause for OP's question on Stackoverflow:
What i did with longpolling was, from service api, i sleep the thread for some time and wake it and do it again and again untill some db status is completed.
That implementation actually prevents the Tomcat worker thread from processing new HTTP requests. As a result your request throughput reduces with every additional long running operation.
I propose to offload the long running operation into a separate thread. The client (browser) initiates a new request to fetch the result.
Depending on the processing status, server returns either the result or a notification/error/warning/.
Here's a very simple example :
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.OK;
#RestController
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
private ExecutorService executorService = Executors.newFixedThreadPool(10);
private Map<String, String> results = new ConcurrentHashMap<>();
#RequestMapping(path = "put/{key}", method = RequestMethod.POST)
public ResponseEntity<Void> put(#PathVariable String key) {
executorService.submit(() -> {
try {
//simulate a long running process
Thread.sleep(10000);
results.put(key, "success");
} catch (InterruptedException e) {
results.put(key, "error " + e.getMessage());
Thread.currentThread().interrupt();
}
});
return new ResponseEntity<>(CREATED);
}
#RequestMapping(path = "get/{key}", method = RequestMethod.GET)
public ResponseEntity<String> get(#PathVariable String key) {
final String result = results.get(key);
return new ResponseEntity<>(result, result == null ? NOT_FOUND : OK);
}
}

How to use RestTemplate efficiently with RequestFactory?

I am working on a project in which I need to make a HTTP URL call to my server which is running Restful Service which returns back the response as a JSON String. I am using RestTemplate here along with HttpComponentsClientHttpRequestFactory to execute an url.
I have setup a http request timeout (READ and CONNECTION time out) on my RestTemplate by using HttpComponentsClientHttpRequestFactory.
Below is my Interface:
public interface Client {
// for synchronous
public String getSyncData(String key, long timeout);
// for asynchronous
public String getAsyncData(String key, long timeout);
}
Below is my implementation of Client interface -
public class DataClient implements Client {
private final RestTemplate restTemplate = new RestTemplate();
private ExecutorService executor = Executors.newFixedThreadPool(10);
// for synchronous call
#Override
public String getSyncData(String key, long timeout) {
String response = null;
try {
Task task = new Task(key, restTemplate, timeout);
// direct call, implementing sync call as async + waiting is bad idea.
// It is meaningless and consumes one thread from the thread pool per a call.
response = task.call();
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
}
return response;
}
// for asynchronous call
#Override
public Future<String> getAsyncData(String key, long timeout) {
Future<String> future = null;
try {
Task task = new Task(key, restTemplate, timeout);
future = executor.submit(task);
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
}
return future;
}
}
And below is my simple Task class
class Task implements Callable<String> {
private RestTemplate restTemplate;
private String key;
private long timeout; // in milliseconds
public Task(String key, RestTemplate restTemplate, long timeout) {
this.key = key;
this.restTemplate = restTemplate;
this.timeout = timeout;
}
public String call() throws Exception {
String url = "some_url_created_by_using_key";
// does this looks right the way I am setting request factory?
// or is there any other effficient way to do this?
restTemplate.setRequestFactory(clientHttpRequestFactory());
String response = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
return response;
}
private static ClientHttpRequestFactory clientHttpRequestFactory() {
// is it ok to create a new instance of HttpComponentsClientHttpRequestFactory everytime?
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(timeout); // setting timeout as read timeout
factory.setConnectTimeout(timeout); // setting timeout as connect timeout
return factory;
}
}
Now my question is - Does the way I am using RestTemplate along with setRequestFactory in the call method of Task class everytime is efficient? Since RestTemplate is very heavy to be created so not sure whether I got it right.
And is it ok to create a new instance of HttpComponentsClientHttpRequestFactory everytime? Will it be expensive?
What is the right and efficient way to use RestTemplate if we need to setup Read and Connection timeout on it.
This library will be used like this -
String response = DataClientFactory.getInstance().getSyncData(keyData, 100);
From what I can tell, you're reusing the same RestTemplate object repeatedly, but each Task is performing this line: restTemplate.setRequestFactory(clientHttpRequestFactory());. This seems like it can have race conditions, e.g. one Task can set the RequestFactory that another Task will then accidentally use.
Otherwise, it seems like you're using RestTemplate correctly.
How often do your timeouts change? If you mostly use one or two timeouts, you can create one or two RestTemplates using the RequestFactory constructor with the pre-loaded timeout. If you're a stickler for efficiency, create a HashMap<Integer, RestTemplate> that caches a RestTemplate with a particular timeout each time a new timeout is requested.
Otherwise, looking at the code for RestTemplate's constructor, and for HttpComponentsClientHttpRequestFactory's constructor, they don't look exceptionally heavy, so calling them repeatedly probably won't be much of a bottleneck.

Categories

Resources