I have this method:
public void updateService(JSONObject json, String url) throws IOException {
PrintStream log = this.getLogger();
CloseableHttpClient httpClient = null;
httpClient = this.getCloseableHttpClient();
log.println("Sending data to " + url);
HttpPut request = new HttpPut(url);
StringEntity params = new StringEntity(json.toString());
request.addHeader("content-type", "application/json");
request.setEntity(params);
httpClient.execute(request);
log.println("Sending report succeeded");
httpClient.close();
}
My test then does this:
#Test
public void updateServiceCloseException() {
RegistryTask task = new RegistryTask(false, null);
RegistryTask spy = spy(task);
CloseableHttpClient client = HttpClientBuilder.create().build();
CloseableHttpClient clientSpy = spy(client);
String url = "http://www.example.com/api/service/testo";
String message = "Execute failure";
JSONObject json = new JSONObject();
json.put("name", "Bob");
json.put("key", "testo");
try {
doReturn(clientSpy).when(spy).getCloseableHttpClient();
// Make sure no http request is actually sent
CloseableHttpResponse response = mock(CloseableHttpResponse.class);
IOException exception = new IOException(message);
doReturn(response).when(clientSpy).execute(any(HttpPut.class));
doThrow(exception).when(clientSpy).close();
spy.updateService(json, url);
} catch (IOException e) {
failWithTrace(e);
// assertEquals(message, e.getMessage());
return;
} catch (Exception e) {
failWithTrace(e);
// assertEquals(message, e.getMessage());
return;
}
fail("Exception not thrown");
}
For some reason doThrow(exception).when(clientSpy).close(); is saying Checked exception is invalid for this method!. But considering my method has throws IOException and close itself throws IOException, I am completely confused about getting this JUnit exception.
Update
I tried updating the doReturn to when(clientSpy.execute(any(HttpPut.class))).thenReturn(response);. The new exception is java.lang.AssertionError: java.lang.IllegalArgumentException: HTTP request may not be null. Not sure why using any would count as null in this case. This question actually shows I should use doReturn since it always work vs thenReturn not working in all cases... Mockito - difference between doReturn() and when()
I changed from spy to full mock with CloseableHttpClient clientSpy = mock(CloseableHttpClient.class);. I know this removes all methods from running, but I was already using spy to override the two methods I was hitting. While this was the solution, I'd love a proper explanation as the answer for future readers.
Related
Could you please help me with the following:
I have the following method:
public static CloseableHttpResponse getRequest (String url) {
try (CloseableHttpClient httpClient = HttpClients.createDefault();){
HttpGet httpget = new HttpGet(url); //http get request (create get connection with particular url)
return httpClient.execute(httpget);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
Where I use CloseableHttpClient with try-with-resources
I invoke method in some simple test:
CloseableHttpResponse closeableHttpResponse = RestClient.getRequest("https://reqres.in/api/users?page=2");
String responseString = EntityUtils.toString(closeableHttpResponse.getEntity(), "UTF-8");
JSONObject responseJson = new JSONObject(responseString);
System.out.println(responseJson);
And I am getting error: org.apache.http.TruncatedChunkException: Truncated chunk (expected size: 379; actual size: 358)
When I am not using try-with-resources like that:
public static CloseableHttpResponse getRequest (String url) throws IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpget = new HttpGet(url); //http get request (create get connection with particular url)
return httpClient.execute(httpget);
}
I have no error at all! Could you please explain - what the wrong? I am newbie and have no clue - some examples from internet are working good...
The try-with-resources block will automatically call close() on the object, so the return from one of those getRequest calls is a closed CloseableHttpClient instance.
The call without try-with-resources will return a working (not closed) CloseableHttpClient.
I have a problem with my unit test. In my unit test I am getting 401 Unauthorised as response status and I don't know how to solve this problem. This is not a Spring project.
My Test class
#RunWith(MockitoJUnitRunner.class)
public class LTest {
#Test
public void test_retrieve() throws Exceptions{
CloseableHttpClient mockHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockHttpResponse = mock(CloseableHttpResponse.class);
HttpEntity mockEntity = mock(HttpEntity.class);
StatusLine mockStatusLine = mock(StatusLine.class);
when(mockHttpClient.execute(new HttpGet(new URIBuilder(anyString()).build()))).thenReturn(mockHttpResponse);
when(mockHttpResponse.getEntity()).thenReturn(mockEntity);
when(mockHttpResponse.getStatusLine()).thenReturn(mockStatusLine);
when(mockStatusLine.getStatusCode()).thenReturn(HttpStatus.SC_OK);
Map<String, Employee> map = sample.retrieve();
assertNotNull(map);
assertEquals(1,map.size());
}
source code for the above test case
CloseableHttpClient httpClient = HttpClientUtils.setupClient(HttpClientBuilder.create()).build();
String url = "http://someexample.com";
UriBuilder builder = new URIBuilder(url)
.setParameter("limit",5)
.setParameter("centre",centre);
CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(builder.build()));
if(httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
try{
String entity = EntityUtils.toString(httpResponse.getEntity());
ObjectNode node = new ObjectMapper().readValue(entity,ObjectNode.class);
} catch (IOException e){
e.printStackTrace();
}
}
While running the test case it's showing assertion error because it's going through catch block due below line is throwing 401 instead of 200
CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(builder.build()));
Can anyone please help me with the above error I am getting?
In the test, HTTPClient is not mocked and that is the reason for the failure.
To mock the HTTPClient we can follow the below strategy
Extract getHttpClient() in the ClassToBeTested as
public class HttpClientToBeTested {
public Map retrieve() throws URISyntaxException, IOException {
CloseableHttpClient httpClient = getHttpClient();
String url = "http://someexample.com";
URIBuilder builder = new URIBuilder(url)
.setParameter("limit","5")
.setParameter("centre","centre");
CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(builder.build()));
if(httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
try{
String entity = EntityUtils.toString(httpResponse.getEntity());
//ObjectNode node = new ObjectMapper().readValue(entity,ObjectNode.class);
Map node = new ObjectMapper().readValue(entity, Map.class); // Assume ObjectNode is a custom class, so for demo using Map.
return node;
} catch (IOException e){
e.printStackTrace();
throw e;
}
}
return null;
}
// New extracted method that will be mocked in the test case
protected CloseableHttpClient getHttpClient() {
return HttpClientBuilder.create().build();
}
}
Next in the test class, we can inject the mock by subclassing the ClassToBeTested in an anonymous class as follows.
#Test
public void test_retrieve() throws Exception {
CloseableHttpClient mockHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockHttpResponse = mock(CloseableHttpResponse.class);
//HttpEntity mockEntity = mock(HttpEntity.class); Not required since we will pass actual entity
StatusLine mockStatusLine = mock(StatusLine.class);
when(mockHttpClient.execute(new HttpGet(new URIBuilder(anyString()).build()))).thenReturn(mockHttpResponse);
when(mockHttpResponse.getEntity()).thenReturn(new StringEntity("{\"key\":\"value\"}")); // Important: Pass your actual response as string here.
when(mockHttpResponse.getStatusLine()).thenReturn(mockStatusLine);
when(mockStatusLine.getStatusCode()).thenReturn(HttpStatus.SC_OK);
// Code to mock the http client
HttpClientToBeTested sample = new HttpClientToBeTested() {
#Override
protected CloseableHttpClient getHttpClient() {
return mockHttpClient;
}
};
Map map = sample.retrieve();
assertNotNull(map);
assertEquals(1,map.size());
}
UPDATE: After mocking the httpClient, httpClient.execute() should return SC_OK. However, post that deserializing the response will fail since the mock HttpEntity will return null. To avoid it, we will rather send a StringEntity. Updated the actual and test class with the details.
I need to send a GET request with a json body in java/spring boot. I'm aware of the advice against it, however I have to do it this was for a couple of reasons:
1. The 3rd party API I'm using only allows GET requests, so POST is not an option.
2. I need to pass an extremely large parameter in the body (a comma separated list of about 8-10k characters) so tacking query params onto the url is not an option either.
I've tried a few different things:
apache HttpClient from here: Send content body with HTTP GET Request in Java. This gave some error straight from the API itself about a bad key.
URIComponentsBuilder from here: Spring RestTemplate GET with parameters. This just tacked the params onto the url, which as I explained before is not an option.
restTemplate.exchange. This seemed the most straightforward, but the object wouldn't pass: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#exchange-java.lang.String-org.springframework.http.HttpMethod-org.springframework.http.HttpEntity-java.lang.Class-java.util.Map-
as well as probably another thing or two that I've forgotten about.
Here is what I'm talking about in Postman. I need to be able to pass both of the parameters given here. It works fine if run through Postman, but I can't figure it out in Java/Spring Boot.
Here is a code snippet from the restTemplate.exchange attempt:
public String makeMMSICall(String uri, List<String> MMSIBatchList, HashMap<String, String> headersList) {
ResponseEntity<String> result = null;
try {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
for (String key : headersList.keySet()) {
headers.add(key, headersList.get(key));
}
Map<String, String> params = new HashMap<String, String>();
params.put("mmsi", String.join(",", MMSIBatchList));
params.put("limit", mmsiBatchSize);
HttpEntity<?> entity = new HttpEntity<>(headers);
result = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class, params);
System.out.println(result.getBody());
} catch (RestClientException e) {
LOGGER.error("Exception in makeGetHTTPCall :" + e.getMessage());
throw e;
} catch (Exception e) {
LOGGER.error("Exception in makeGetHTTPCall :" + e.getMessage());
throw e;
}
return result.getBody();
}
Thanks for helping!
You can try java.net.HttpUrlConnection, it works for me but indeed I normally use a POST
HttpURLConnection connection = null;
BufferedReader reader = null;
String payload = "body";
try {
URL url = new URL("url endpoint");
if (url.getProtocol().equalsIgnoreCase("https")) {
connection = (HttpsURLConnection) url.openConnection();
} else {
connection = (HttpURLConnection) url.openConnection();
}
// Set connection properties
connection.setRequestMethod(method); // get or post
connection.setReadTimeout(3 * 1000);
connection.setDoOutput(true);
connection.setUseCaches(false);
if (payload != null) {
OutputStream os = connection.getOutputStream();
os.write(payload.getBytes(StandardCharsets.UTF_8));
os.flush();
os.close();
}
int responseCode = connection.getResponseCode();
}
There's no way of implementing it via RestTemplate, even with .exchange method. It'll simply not send the request body for GET calls even if we pass the entity within the function parameters.(Tested via interceptor logs)
You can use the Apache client to solve this issue/request (whatever you'd like to call it). The code you need is something along following lines.
private static class HttpGetWithBody extends HttpEntityEnclosingRequestBase {
JSONObject requestBody;
public HttpGetWithBody(URI uri, JSONObject requestBody) throws UnsupportedEncodingException {
this.setURI(uri);
StringEntity stringEntity = new StringEntity(requestBody.toString());
super.setEntity(stringEntity);
this.requestBody = requestBody;
}
#Override
public String getMethod() {
return "GET";
}
}
private JSONObject executeGetRequestWithBody(String host, Object entity) throws ClientProtocolException, IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
try{
JSONObject requestBody = new JSONObject(entity);
URL url = new URL(host);
HttpRequest request = new HttpGetWithBody(url.toURI(), requestBody);
request.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
request.addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
HttpResponse response;
if(url.getPort() != 0) response = httpClient.execute(new HttpHost(url.getHost(), url.getPort()), request);
else response = httpClient.execute(new HttpHost(url.getHost()), request);
if(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
JSONObject res = new JSONObject(EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
httpClient.close();
return res;
}
}catch (Exception e){
log.error("Error occurred in executeGetRequestWithBody. Error: ", e.getStackTrace());
}
httpClient.close();
return null;
}
If you inspect even Apache client library doesn't support passing the body natively(checked via code implementation of HttpGet method), since contextually request body for a GET request is not a good and obvious practice.
Try creating a new custom RequestFactory.
Similar to
get request with body
This question already has answers here:
What is the difference between CloseableHttpClient and HttpClient in Apache HttpClient API?
(8 answers)
Closed 2 years ago.
I'm using CloseableHttpResponse (from apache-httpclient-4.5.3) and I'm not sure I'm using it right, I saw an answer with no votes to use EntityUtils.consume on finally:
CloseableHttpResponse response1 = httpclient.execute(httpGet);
try {
System.out.println(response1.getStatusLine());
} finally {
EntityUtils.consume(response1.getEntity());
CloseableHttpClient is abstract and has no close method to call although in this answer it's used:
CloseableHttpResponse response = httpclient.execute(httpget);
try {
//do something
} finally {
response.close();
}
Currently I'm using try with resources for CloseableHttpClient and CloseableHttpResponse inside of send method.
Am I not missing any resource open or using it in a wrong way?
private CloseableHttpResponse send()
throws URISyntaxException, UnsupportedEncodingException, IOException, ClientProtocolException {
URIBuilder uriBuilder = new URIBuilder(BASE_URL);
HttpHost target = new HttpHost(uriBuilder.getHost(), uriBuilder.getPort(), uriBuilder.getScheme());
HttpPost post = new HttpPost(uriBuilder.build());
try (CloseableHttpClient httpClient = HttpClients.custom().build(); CloseableHttpResponse response = httpClient.execute(target, post)) {
return response;
}
It has been explained in detail in the docs here.
Quoting the pseudo code from the docs here's a typical way to allocate/deallocate an instance of CloseableHttpClient:
try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
<...>
}
The same applies to CloseableHttpResponse :
try (CloseableHttpResponse response = httpclient.execute(httpget)) {
<...>
}
Now, about the close method in CloseableHttpClient. CloseableHttpClient is an abstract class that implements Closeable interface. That is, although it doesn't have a close method itself the classes that extend it are required to implement the close method. One class is InternalHttpClient. You can check the source code for the details.
Before Java7, explicit close would be required:
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
<...>
} finally {
httpclient.close();
}
CloseableHttpResponse response = httpclient.execute(httpget);
try {
<...>
} finally {
response.close();
}
You can avoid the finally by using the try(resource)
try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
... }
I am using the apache library. I have created a class which sends a post request to a servlet. I have set up the parameters for the client and i have created a HTTP post object to be sent but for some reason when i excute the request i get a reposnse that says the get method is not supported(which is true cause i have only made a dopost method in my servlet). It seems that a get request is being sent but i dont know why. The post method worked before but i started gettng http error 417 "Expectation Failed" due to me not setting the protocal version but i fixed this by adding paramenters.
below is my class where you see a HTTPpost object being created and exectued. I have a response handler method but i took it out of my code below because it has nothing to do with my problem.
I know a HTTP GET is being sent because of the reponse mesage that is returned says. The specified HTTP method is not allowed for the requested resource (HTTP method GET is not supported by this URL).
Thanks in advance.
P.s i am developing for android.
public class HTTPrequestHelper {
private final ResponseHandler<String> responseHandler;
private static final String CLASSTAG = HTTPrequestHelper.class.getSimpleName();
private static final DefaultHttpClient client;
static{
HttpParams params = new BasicHttpParams();
params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
params.setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, HTTP.UTF_8);
///params.setParameter(CoreProtocolPNames.USER_AGENT, "Android-x");
params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 15000);
params.setParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(
new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(
new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
client = new DefaultHttpClient(cm,params);
}
public HTTPrequestHelper(ResponseHandler<String> responseHandler) {
this.responseHandler = responseHandler;
}
public void performrequest(String url, String para)
{
HttpPost post = new HttpPost(url);
StringEntity parameters;
try {
parameters = new StringEntity(para);
post.setEntity(parameters);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
BasicHttpResponse errorResponse =
new BasicHttpResponse(
new ProtocolVersion("HTTP_ERROR", 1, 1),
500, "ERROR");
try {
client.execute(post, this.responseHandler);
}
catch (Exception e) {
errorResponse.setReasonPhrase(e.getMessage());
try {
this.responseHandler.handleResponse(errorResponse);
}
catch (Exception ex) {
Log.e( "ouch", "!!! IOException " + ex.getMessage() );
}
}
}
I tried added the allow header to the request but that did not work as well but im not sure if i was doing right. below is the code.
client.addRequestInterceptor(new HttpRequestInterceptor() {
#Override
public void process(HttpRequest request, HttpContext context)
throws HttpException, IOException {
//request.addHeader("Allow", "POST");
}
});
How do you know that a HTTP GET is actually being sent? Examining the http packets sent by your client or received by the server would be helpful here
You catch and swallow UnsupportedEncodingException when constructing the Post's parameters - what happens if you encounter this exception while setting parameters? Your code as is today will still attempt to execute the POST.