Okhttp newBuilder per request - java

Trying to implement okhttp the correct way. I understand the OkHttpClient must be shared (Singleton), however I am not clearly understanding .newBuilder();
Sample Code:
// Instantiated once
private static OkHttpClient client = new OkHttpClient.Builder()
.readTimeout(readTime, TimeUnit.MILLISECONDS)
.connectionPool(new ConnectionPool(200, connectTimeout, TimeUnit.MILLISECONDS));
.build();
public static String makeRestCall(String url, String data, Interceptor customInterceptor) {
// Questions on the line below
OkHttpClient newClient = client.newBuilder()
.addInterceptor(customInterceptor)
.build();
....
try (Response response = newClient.newCall(httpRequest).execute()) {
final ResponseBody body = response.body();
return body.string();
}
return "NO_DATA";
}
I have a few questions around .newBuilder()
When we add a new interceptor to newClient, does the original client also get updated by reference?
Classes calling makeRestCall decide on what customInteceptor they need. Is it ok to call .newBuilder() for every request?
I have been searching the documentation and playing with the implementation but haven't had clarity on the above.
Any assistance/pointers are appreciated.

When we add a new interceptor to newClient, does the original client also get updated by reference?
No, the original is unchanged. Its configuration is immutable.
Classes calling makeRestCall decide on what customInteceptor they need. Is it ok to call .newBuilder() for every request?
Absolutely. That operation is cheap because it only duplicates the configuration. Resource-intensive stuff like the connection pool and cache are not duplicated.

Related

The response type after using HttpResponse.BodyHandlers.OfString()

class Main{
//we can use class HttpClient
//https://www.youtube.com/watch?v=5MmlRZZxTqk
public static void main(String args[]) throws IOException, InterruptedException {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
// .GET() this line is optional because it is default
.header("accept","application/json")
.uri(URI.create("https://whatever.com"))
.build();
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
System.out.println(response.getClass().getName());
}
}
So I read the documentation about the BodyHandlers.ofString() method, my understanding is that it will parse the value of the response variable into a String. But if that's the case then why when I ran this line
System.out.println(response.getClass().getName());
the outcome is jdk.internal.net.http.HttpResponseImpl? I read another post saying that you should never use any class in idk.internal.*, so am I doing anything wrong ?
so am I doing anything wrong ?
There is nothing wrong here, HttpResponse is the interface. So response.getClass() is fine to return any implementation that implement HttpResponse(HttpResponseImpl in this case).
I read another post saying that you should never use any class in idk.internal.*
This means that you should not declare/initialise this class directly in your code. For example, you should not write something like:
HttpResponseImpl<String> response;
The principal behind is we should depend on the interface, but not the implementation details, more details can be found in JEP 260

Test of endpoint that sends a request to external api

There is a small method mymethod that gets the response from the external api data in json format, which will be Dto and
then there is a getEnums method that selects which data to leave and returns a list of strings and as a result mymethod itself returns a list of strings.
I tried to write a test, but I get :
Expected :200
Actual :302
As I understand the problem is in the redirection, but how to solve it I do not understand, maybe you have an answer?
controller
#GetMapping(value = "api/mymethod", produces = "application/json")
public ResponseEntity<List<String>> mymethod(#RequestHeader(value = "Enum") String Enum,
#RequestParam(value = "Type") String Type) {
URI uri = UriComponentsBuilder.fromUriString("...url...").build().toUri();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(null, headers);
ResponseEntity<myDto> response =
restTemplate.exchange(uri, HttpMethod.GET, entity, myDto.class);
List<String> currencies =
getEnums(response, Type); // <- custom method
return new ResponseEntity<>(currencies, HttpStatus.OK);
}
// (The test is not written in full, left exactly the problem area)
test
#Test
public void mytest() throws Exception{
ResultActions getResult = mockMvc.perform(get("/api/mymethod")
.header("Enum", "OOL")
.param("Type", "Counter"))
.andExpect(status().isOk());
}
The problem with testing against an external service is that you do not manage it's state. Therefore your test cases may show varying results even if you did not change anything.
Usually you'd create a mock of the REST api your test object would access. Then you can send requests to your test object and check in the mocked api whether the expected requests did come in. You can also fake success or error responses and check how your test object reacts.
To finally answer your question: How do you want your client to treat a redirection? Should it follow or error out? Looking at the meaning of status 302 it means the resource has moved temporarily or at least was found at a new location. This might mean a valid redirect if the server is trying to loadbalance or tries to point out a url that is closer to you network-wise. Therefore I believe the client should follow a 302.

How to set RequestConfiguration per request using RestTemplate?

I have a library which is being used by customer and they are passing DataRequest object which has userid, timeout and some other fields in it. Now I use this DataRequest object to make a URL and then I make an HTTP call using RestTemplate and my service returns back a JSON response which I use it to make a DataResponse object and return this DataResponse object back to them.
Below is my DataClient class used by customer by passing DataRequest object to it. I am using timeout value passed by customer in DataRequest to timeout the request if it is taking too much time in getSyncData method.
public class DataClient implements Client {
private final RestTemplate restTemplate = new RestTemplate();
private final ExecutorService service = Executors.newFixedThreadPool(10);
// this constructor will be called only once through my factory
// so initializing here
public DataClient() {
try {
restTemplate.setRequestFactory(clientHttpRequestFactory());
} catch (Exception ex) {
// log exception
}
}
#Override
public DataResponse getSyncData(DataRequest key) {
DataResponse response = null;
Future<DataResponse> responseFuture = null;
try {
responseFuture = getAsyncData(key);
response = responseFuture.get(key.getTimeout(), key.getTimeoutUnit());
} catch (TimeoutException ex) {
response = new DataResponse(DataErrorEnum.CLIENT_TIMEOUT, DataStatusEnum.ERROR);
responseFuture.cancel(true);
// logging exception here
}
return response;
}
#Override
public Future<DataResponse> getAsyncData(DataRequest key) {
DataFetcherTask task = new DataFetcherTask(key, restTemplate);
Future<DataResponse> future = service.submit(task);
return future;
}
// how to set socket timeout value by using `key.getSocketTimeout()` instead of using hard coded 400
private ClientHttpRequestFactory clientHttpRequestFactory() {
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
RequestConfig requestConfig =
RequestConfig.custom().setConnectionRequestTimeout(400).setConnectTimeout(400)
.setSocketTimeout(400).setStaleConnectionCheckEnabled(false).build();
SocketConfig socketConfig =
SocketConfig.custom().setSoKeepAlive(true).setTcpNoDelay(true).build();
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager =
new PoolingHttpClientConnectionManager();
poolingHttpClientConnectionManager.setMaxTotal(300);
poolingHttpClientConnectionManager.setDefaultMaxPerRoute(200);
CloseableHttpClient httpClientBuilder =
HttpClientBuilder.create().setConnectionManager(poolingHttpClientConnectionManager)
.setDefaultRequestConfig(requestConfig).setDefaultSocketConfig(socketConfig).build();
requestFactory.setHttpClient(httpClientBuilder);
return requestFactory;
}
}
DataFetcherTask class:
public class DataFetcherTask implements Callable<DataResponse> {
private final DataRequest key;
private final RestTemplate restTemplate;
public DataFetcherTask(DataRequest key, RestTemplate restTemplate) {
this.key = key;
this.restTemplate = restTemplate;
}
#Override
public DataResponse call() throws Exception {
// In a nutshell below is what I am doing here.
// 1. Make an url using DataRequest key.
// 2. And then execute the url RestTemplate.
// 3. Make a DataResponse object and return it.
}
}
Customer within our company will use my library like this as shown below by using my factory in their code base -
// if they are calling `getSyncData()` method
DataResponse response = DataClientFactory.getInstance().getSyncData(key);
// and if they want to call `getAsyncData()` method
Future<DataResponse> response = DataClientFactory.getInstance().getAsyncData(key);
I am implementing sync call as async + waiting since I want to throttle them with the number of threads otherwise they can bombard our service without any control.
Problem Statement:-
I am going to add another timeout variable called socket timeout in my DataRequest class and I want to use that variable value (key.getSocketTimeout()) in my clientHttpRequestFactory() method instead of using hard coded 400 value. What is the best and efficient way to do that?
Right now I am using Inversion of Control and passing RestTemplate in a constructor to share the RestTemplate between all my Task objects. I am confuse now how to use key.getSocketTimeout() value in my clientHttpRequestFactory() method. I think this is mostly design question of how to use RestTemplate efficiently here so that I can use key.getSocketTimeout() value in my clientHttpRequestFactory() method.
I have simplified the code so that idea gets clear what I am trying to do and I am on Java 7. Using ThreadLocal is the only option I have here or there is any better and optimized way?
As Peter explains, using ThreadLocal is not a good idea here.
But I also could not find a way to "pass the value up the chain of method calls".
If you use plain "Apache HttpClient", you can create an HttpGet/Put/etc. and simply call
httpRequest.setConfig(myRequestConfig). In other words: set a request configuration per request
(if nothing is set in the request, the request configuration from the HttpClient which executes the request is used).
In contrast, the RestTemplate
calls createRequest(URI, HttpMethod) (defined in HttpAccessor)
which uses the ClientHttpRequestFactory. In other words: there is no option to set a request configuration per request.
I'm not sure why Spring left this option out, it seems a reasonable functional requirement (or maybe I'm still missing something).
Some notes about the "they can bombard our service without any control":
This is one of the reasons to use the PoolingHttpClientConnectionManager:
by setting the appropriate maximum values, there can never be more than the specified maximum connections in use (and thus requests running) at the same time. The assumption here is that you re-use the same RestTemplate instance (and thus connection manager) for each request.
To catch a flood earlier, specify a maximum amount of waiting tasks in the threadpool and set a proper error-handler
(use the workQueue and handler in this constructor).
ThreadLocal is a way to pass dynamic value which normally you would pass via method properties, but you are using an API you can't/don't want to change.
You set the ThreadLocal (possible a data structure containing multiple values) at some level in the thread stack and you can use it further up the stack.
Is this the best approach? NO, you should really pass the value up the chain of method calls, but sometimes this is not practical.
Can you provide an example of how my code will look like with ThreadLocal
You might start with
static final ThreadLocal<Long> SOCKET_TIMEOUT = new ThreadLocal<>();
To set it you can do
SOCKET_TIMEOUT .set(key.getSocketTimeout());
and to get the value you can do
long socketTimeout = SOCKET_TIMEOUT.get();

GWT request factory - Fire request inside of success method of another request

I am trying to nest two request factory calls in each other. I retrieve a post object and in the success-method i use the same object again (just for testing purposes, I get the same behavior for other request like for example persisting).
The problem is: Only the first request reaches the server.
I don't get any error message. If I debug the code, everything works until the second request is fired. Nothing happens then. The method on the backend is not called, the frontend shows no error, even if I implement the "onFailure"-method for the receiver of the second request.
public class RequestFactoryFindTest extends GWTTestCase{
/**
* must refer to a valid module that sources this class.
*/
public String getModuleName() {
return "com.Test.MyTest";
}
public void test(){
final ClientFactory clientFactory = GWT.create(ClientFactoryImpl.class);
final MyRequestFactory requestFactory = clientFactory.getRequestFactory();
final PostRequest request = requestFactory.postRequest();
request.findPost(1l).fire(new Receiver<PostProxy>() {
#Override
public void onSuccess(PostProxy response) {
final ClientFactory clientFactory = GWT.create(ClientFactoryImpl.class);
final MyRequestFactory requestFactory = clientFactory.getRequestFactory();
final PostRequest request = requestFactory.postRequest();
System.out.println("outer success");
request.findPost(1l).fire(new Receiver<PostProxy>() {
#Override
public void onSuccess(PostProxy response) {
System.out.println("inner success");
}
});
}
});
}
}
Can someone explain this?
Edit:
I tried a lot of stuff like to fire an event on the event bus, catch the event and do my inner request factory call there. But nothing worked. I think this is some Issue with the GWTTestcase in combination with RequestFactory.
I also changed my code, so i use only one clientFactory.
Try to create an event in the first onSuccess method. When your event is handled, you could send another request to the server.
Check out How to use the GWT EventBus to use the eventbus.
Thomas Broyer statement is also right. You should only use one RequestFactory and one ClientFactory!
This may be a problem when you are constructing your second client factory as per Thomas Broyer. You should probably go into your ClientFactory.java interface and at the top add the the single client factory instance. Also put a GWT.log("ON SUCCESS") at the top of your onSuccess(PostProxy response) to make sure it is getting there.
public interface ClientFactory {
public static final ClientFactory INSTANCE = GWT.create(ClientFactory.class);
...
Then you can simple do somehting like the following
final PostRequest request = ClientFactory.INSTANCE.getRequestFactory().postRequest();

Returning 200 response code instead of 204

This is my method for creating Response with header parameters and body:
public Response sendOKResponse(request req)
{
ResponseBuilderImpl builder = new ResponseBuilderImpl();
// set the header params.
for(int index =0; index<req.headerParameters.size(); index++)
{
builder.header(req.headerParameters.get(index).getName(), req.headerParameters.get(index).getBody());
}
// set the body and response code
builder.status(Response.Status.OK).entity(req.getBody());
Response r = builder.build();
return r;
}
And this is how i return the Response:
Response response;
response = sendBadMesseage();
return response;
This code returns code 204(No content) instead of 200.
Any ideas why?
You shouldn't be instantiating your response builder with new, the whole point of the JAX-RS abstraction layer is to hide implementation details away from calling clients. This is what makes it possible to have various vendor implementations which can be interchanged at will. Also, if you are using JEE6, or hope to migrate to it, this code will almost certainly fail. Most JEE6 vendor implementations utilize CDI, which is concept-incompatible with usage of new. But, closer to the topic, the JAX-RS implementation specifies that a 204 status code be returned if a responses wrapped entity is null. You might want to verify this is not the case in any of your methods. Also, you might want to make some changes to your code:
public Response sendOKResponse(request req) {
ResponseBuilder response = Response.ok();
// set the header params.
for(Header h: req.headerParameters()) {
builder = builder.header(h.getName(), h.getValue());
}
// set the body and response code
builder = builder.entity(req.getBody());
return builder.build();
}
Your sendBadMessage method should also look similar to above. You can log your entity before adding it to the builder, to verify that you only get a 204 when it's null.

Categories