OkHttpClient interceptors not getting set - java

I am using an injected OkHttpClient object in my class. I am using method injection to set some interceptors on my client object like following:
#Inject
private OkHttpClient httpClient;
#Inject
void onPostInject() {
httpClient
.newBuilder()
.addInterceptor(loggingInterceptor)
.addInterceptor(httpClientInterceptor);
}
Now if I put a breakpoint in onPostInject method and reach the last statement, I see the size of interceptors collection within the httpClient object is 0. My Integration test is also failing because of the same reason.

I have removed injection from the httpClient field and use the build a bit different to get it worked:
private OkHttpClient httpClient;
#Inject
void onPostInject() {
httpClient =
new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.addInterceptor(httpClientInterceptor)
.build();
}

Related

Unable to get response from API using Micronaut

I am trying to make a POST request using the Micronaut framework with Java. I have created a 'client' class that is called and makes the request. Here is the code:
#Singleton
public class ApiClient {
private final HttpClient httpClient;
private final URI uri;
public ApiClient(#Client(URL) HttpClient httpClient) {
this.httpClient = httpClient;
uri = UriBuilder.of(API_URL).build();
}
Mono<List<ApiResponse>> fetchResponse() {
HttpRequest<?> request = HttpRequest.POST(uri, BODY)
.header("Authorization", API_KEY);
return Mono.from(httpClient.retrieve(request, Argument.listOf(ApiResponse.class)));
}
}
My problem is that I have no idea what the response from API is. As far as I can tell, the call is made. But because the data returns in a Flux object, I can't interrogate the object to find the response. My suspicion is that the POJO I'm trying to store the response in isn't working. I can't tell if this is the case though.
You need to subscribe to the publisher to actually make the request and get a response. There are several subscribe methods depending on what you want to do

How to write tests on HTTP client apache?

I am trying to write a test for my service that sets up a connection with another service that returns me items from the database. My problem is that I set the connection properties in the test and start the service. How can this be mock or something similar?
My start service method:
public void doStartService() {
super.doStartService();
PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
manager.setDefaultMaxPerRoute(maxConnectionsPerRoute);
manager.setMaxTotal(maxConnections);
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(connectTimeout)
.setSocketTimeout(socketTimeout)
.setRedirectsEnabled(false).build();
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setDefaultRequestConfig(requestConfig);
builder.setConnectionManager(manager);
client = builder.build();
}
My setup test method and one test method:
private ProductCatalogIntegrationService service;
#Before
public void setup() {
service = new Service();
service.setConnectTimeout(10000);
service.setSocketTimeout(10000);
service.setMaxConnections(10);
service.setMaxConnectionsPerRoute(10);
service.setUrl("http://localhost:8888/products");
service.doStartService();
}
#Test
public void testReturnProductById() {
service.setProductById(GET_PRODUCT_BY_ID); // set url from get product by id, by this url my other service goes to the database
jsonItem = service.getProductById("1"); //get product by id 1
assertEquals(jsonItem.getId(), FIRST_PRODUCT_ID); // I compare the id on which I made the request to the database, so that I came and was wrapped in a class wrapper
}
How to do it correctly, so as not to configure the connection in the tests?
Javalin would be an excellent tool for mocking the real service as it allows for state assertions in your tests.
Wiremock can be used as well. But it leads to hard to maintain behavioral tests (verify).

Retrofit 2.0 multiple interceptors

I am working with retrofit and need to be able to use multiple interceptors. Currently I am using one to automatically append an auth token but i need to be able to make calls with no auth token. If i add another interceptor with no auth token in the header how do I use that one instead of the auth token interceptor.
val interceptor: Interceptor = Interceptor { chain ->
val newRequest = chain.request().newBuilder().
addHeader("Auth_Token", pref.getString(PSPreferences.prefAuthKey, "")).
cacheControl(CacheControl.FORCE_NETWORK).
build()
chain.proceed(newRequest)
}
okHttpClient = OkHttpClient.Builder().
readTimeout(1, TimeUnit.MINUTES).
connectTimeout(1, TimeUnit.MINUTES).
addInterceptor(interceptor).build()
val retrofitInstance = Retrofit.Builder()
.baseUrl(APIEndpointInterface.BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
apiInterface = retrofitInstance.create<APIEndpointInterface>(APIEndpointInterface::class.java)
OkHttpClient maintains a list of the interceptors which you can access, however it is an unmodifiable collection.
This leaves us with three options I believe:
Create two OkHttpClient instances, and by deduction two Retrofit
instances, one for the unauthenticated requests, and one for the
authenticated requests.
Check if you should use the interceptor, e.g. in your authentication interceptor, you can first check if there exists a key in your preferences for the token, and if so use it; if not, you simply proceed without modifying anything. You do this for your unauthenticated interceptor too. I think this is the easiest solution for your case.
Create a single interceptor, which will maintain a modifiable list
of interceptors which you can add and remove at will. You would need
to keep a reference to this interceptor, maybe make it a Singleton.
For the third option, I have provided a very simple example:
public class HttpRequestResponseInterceptor implements Interceptor {
public final List<RequestInterceptor> requestInterceptors = new ArrayList<>();
public final List<ResponseInterceptor> responseInterceptors = new ArrayList<>();
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
for (RequestInterceptor interceptor : requestInterceptors) {
request = interceptor.intercept(request);
}
Response response = chain.proceed(request);
for (ResponseInterceptor interceptor : responseInterceptors) {
response = interceptor.intercept(response);
}
return response;
}
public interface RequestInterceptor {
Request intercept(Request request) throws IOException;
}
public interface ResponseInterceptor {
Response intercept(Response response) throws IOException;
}
}
In this case you would need to implement the custom interfaces RequestInterceptor and ResponseInterceptor.
An example of what an implementation of these interfaces would look like:
public class ExampleInterceptor implements HttpRequestResponseInterceptor.RequestInterceptor,
HttpRequestResponseInterceptor.ResponseInterceptor {
#Override
public Request intercept(Request request) throws IOException {
return request.newBuilder().addHeader("REQUEST_HEADER", "EXAMPLE").build();
}
#Override
public Response intercept(Response response) throws IOException {
return response.newBuilder().addHeader("RESPONSE_HEADER", "EXAMPLE").build();
}
}
You would then need to add this interceptor to our main interceptor twice, once to requestInterceptors and once to responseInterceptors (or only to one of these if it intercepts only requests or only responses).
This example is far from complete. The benefit of this solution is that it adds the ability to add and remove interceptors without having to recreate the OkHttpClient instance. It requires extra work if you want to support retrying requests, for example.

Apache HttpClient: how to get redirect locations when using Fluent API?

I am using Apache HttpClient 4.5's Fluent API, the following way:
CloseableHttpClient client = HttpClientBuilder.create().build();
Executor executor = Executor.newInstance(client);
Response resp = executor.execute(Request.Get(url));
Unfortunately, I can't find a proper way of getting redirect locations (the RedirectLocation class).
They are normally stored in a HttpContext object; but when using the Fluent API, its instance is created locally in Executor.execute(...) and never exposed:
public Response execute(final Request request) {
final HttpClientContext localContext = HttpClientContext.create();
/* ... */
return new Response(request.internalExecute(this.httpclient, localContext));
}
I've tried to override Executor.execute(...) method by creating a decorator/proxy class; by creating a child class; even by copy-pasting its source into my own package.
None of these solutions were feasible (for one, Executor invokes package-local methods of other classes).
The only workaround I've managed to find so far was to implement my own RedirectStrategy and pass it to HttpClient:
public class MyRedirectStrategy extends DefaultRedirectStrategy {
private HttpContext context;
public RedirectLocations getRedirectLocations() {
return (RedirectLocations) context.getAttribute(REDIRECT_LOCATIONS);
}
#Override
public URI getLocationURI(final HttpRequest request, final HttpResponse response, final HttpContext context) {
this.context = context; // to keep the HttpContext!
return super.getLocationURI(request, response, context);
}
}
/* ... */
RedirectStrategy stra = new MyRedirectStrategy();
CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(stra).build();
Executor executor = Executor.newInstance(client);
Response resp = executor.execute(Request.Get(url));
for (final String redirectedUri : stra.getRedirectLocations()) {
/* process redirectedUri's */
}
However, I don't think it is a proper solution. To my knowledge, RedirectStrategies were intended to be immutable, stateless classes, as they are passed to the HttpClient, which can be shared by multiple threads/connections.
In other words: logically, the HttpContext is not a property of a RedirectStrategy.
Any ideas?
Thanks!
You cannot. HC fluent API hides HttpContext instance way from the consumer. Consider using HttpClient APIs directly.

How to unit test the rest call using easy mock

How to unit test the below method with the EasyMock. I tried to mock WebResource but it is returning me a NullPointerException.
public void connect()
{
Client client = setUpClient();
WebResource jobBuilder = client.resource("URL");
String jobXml = jobBuilder.accept(MediaType.APPLICATION_XML)
.type(MediaType.APPLICATION_JSON)
.entity(request)
.post(String.class);
}
public Client setUpClient()
{
ClientConfig cc = new DefaultClientConfig();
cc.getClasses().add(JacksonJsonProvider.class);
Client client = Client.create(cc);
return client;
}
You clearly have to read up on the Inversion of Control Principle (http://en.wikipedia.org/wiki/Inversion_of_control), if not for the sake of the better designed code, then for the sake of unit testing. The client object in the method above is created inside the method itself, using the static factory method Client.create(). There is no good way to inject a mock collaborator with that approach. You should either allow injection of the client via a setter or a constructor, or delegate its creation to some sort of a factory.
If you use the 1st approach, you can inject the mock client directly via the setter or constructor during the unit test setup.
If you use the 2nd approach, you can provide a factory that would return a mock client when called.
EDIT 5/03:
Here's example of making your code testable by providing an object factory for a 3rd party library object:
public class ClassToTest {
private ClientFactory factory;
public ClassTotest() {
this(new ClientFactory());
}
public ClassToTest(ClientFactory factory) {
this.factory = factory;
}
public void connect() {
Client client = factory.getClient();
WebResource jobBuilder = client.resource("URL");
String jobXml = jobBuilder.accept(MediaType.APPLICATION_XML)
.type(MediaType.APPLICATION_JSON)
.entity(request)
.post(String.class);
}
}
public class ClientFactory() {
public Client getClient() {
ClientConfig cc = new DefaultClientConfig();
cc.getClasses().add(JacksonJsonProvider.class);
Client client = Client.create(cc);
return client;
}
}
Now, in your application code you can instantiate your class using the no-argument constructor. In the unit test you would use the other constructor. This way you would be able to inject a mock Client that you would script for the purpose of testing WebResource.
Hope this helps.

Categories