CloseableHttpClient: connection hangs forever - httpclient

I have a problem with a connection managed by CloseableHttpClient.
A Spring service manages ny connection:
#Service
public class MyService {
...
private CloseableHttpClient closeableHttpClient;
public String setPayment() {
...
try {
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader(ACCEPT, APP_JSON);
httpPost.setHeader(CONTENT_TYPE, APP_JSON);
StringEntity entity = new StringEntity(request, CHARSET);
httpPost.setEntity(entity);
CloseableHttpResponse response = closeableHttpClient.execute(httpPost);
logger.info("Execution");
} catch (IOException e) {
logger.error("Error");
}
}
}
My setPayment method is called max 3 times when execution is not successful. Sometimes after the first execution my method hangs with no response.
Any suggestion is welcomed.

I suggest you to do the following:
1) set timeout in a constructor:
public MyService() {
int timeout = 180;
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(timeout * 1000)
.setConnectionRequestTimeout(timeout * 1000)
.setSocketTimeout(timeout * 1000).build();
closeableHttpClient = HttpClientBuilder.create().setDefaultRequestConfig(config).build();
}
2) use try-with-resources to manage CloseableHttpResponse
public String setPayment() {
...
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader(ACCEPT, APP_JSON);
httpPost.setHeader(CONTENT_TYPE, APP_JSON);
StringEntity entity = new StringEntity(request, CHARSET);
httpPost.setEntity(entity);
try (CloseableHttpResponse response = closeableHttpClient.execute(httpPost)){
logger.info("Execution");
} catch (IOException e) {
logger.error("Error");
}
}

Related

What does PoolingHttpClientConnectionManager do when there is a temporary network outage?

I am adding PoolingHttpClientConnectionManager to our Spring Boot service, which is currently using RestTemplate and CloseableHttpClient to make requests. The service is using Apache httpcomponents 4.5.
What happens if there is a temporary network outage? Do the bad connections get discarded from the pool? Or does the pool fill up with bad connections during the outage? Once the outage is over, does the pool recover on its own? Or do I need to write code to detect them and remove them from the pool?
Here is the code I have so far:
#Bean
public CloseableHttpClient closeableHttpClient() {
CloseableHttpClient client = HttpClients.custom()
.setDefaultRequestConfig(getDefaultRequestConfig())
.setConnectionManager(poolingHttpClientConnectionManager())
.setKeepAliveStrategy(getConnectionKeepAliveStrategy())
.setRetryHandler(getHttpRequestRetryHandler())
.build();
return client;
}
#Bean
public PoolingHttpClientConnectionManager poolingHttpClientConnectionManager() {
Registry<ConnectionSocketFactory> socketFactoryRegistry = getSocketFactoryRegistry();
PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
poolingConnectionManager.setMaxTotal(maxTotalConnections);
poolingConnectionManager.setDefaultMaxPerRoute(maxRouteConnections);
return poolingConnectionManager;
}
#Bean
public Runnable idleConnectionMonitor(PoolingHttpClientConnectionManager pool) {
return new Runnable() {
#Override
#Scheduled(fixedDelay = 20000)
public void run() {
if (pool != null) {
pool.closeExpiredConnections();
pool.closeIdleConnections(idleConnectionCloseTime, TimeUnit.MILLISECONDS);
}
}
};
}
#Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setThreadNamePrefix("idleMonitor");
scheduler.setPoolSize(idleMonitorPoolSize);
return scheduler;
}
private RequestConfig getDefaultRequestConfig() {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(establishConnectionTimeout)
.setConnectionRequestTimeout(fetchConnectionTimeout)
.setSocketTimeout(socketInactivityTimeout)
.build();
return requestConfig;
}
private Registry<ConnectionSocketFactory> getSocketFactoryRegistry() {
SSLConnectionSocketFactory socketFactory;
socketFactory = new SSLConnectionSocketFactory(getSSLContext(), new String[] { sslProtocol }, null, new DefaultHostnameVerifier());
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
.<ConnectionSocketFactory> create().register("https", socketFactory)
.build();
return socketFactoryRegistry;
}
private SSLContext getSSLContext() {
TrustStrategy acceptingTrustStrategy = null;
try {
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(new FileInputStream(new File(keyStoreFile)), keyStorePwd.toCharArray());
return new SSLContextBuilder()
.loadTrustMaterial(keyStore, acceptingTrustStrategy)
.loadKeyMaterial(keyStore, keyStorePwd.toCharArray())
.build();
} catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | KeyManagementException | UnrecoverableKeyException e) {
throw new RuntimeException(e);
}
}
private ConnectionKeepAliveStrategy getConnectionKeepAliveStrategy() {
return (httpResponse, httpContext) -> {
HeaderIterator headerIterator = httpResponse.headerIterator(HTTP.CONN_KEEP_ALIVE);
HeaderElementIterator elementIterator = new BasicHeaderElementIterator(headerIterator);
while (elementIterator.hasNext()) {
HeaderElement element = elementIterator.nextElement();
String param = element.getName();
String value = element.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
return Long.parseLong(value) * 1000; // convert to ms
}
}
return defaultKeepAliveTime;
};
}
private DefaultHttpRequestRetryHandler getHttpRequestRetryHandler() {
return new DefaultHttpRequestRetryHandler(httprequestRetryCount, false);
}
Those connections become invalid or "stale". If HttpClient has been configured to validate connections prior to using them to execute requests, stale connections will be detected and discarded.

Unable to access REST API’s in Camunda

In our project , we are trying to use camunda BPMN. using camunda standalone distro and deployed and running in Tomcat.
login as a admin user and able to access cockpit and task lists.But,when we try access the APIs using a Java client . we are getting an unauthorized (401) error. Though we are sending JSESSIONID as a “Cookie”
Tried both DefaultHttpClient and HttpURLConnection - It didn’t work out
Note : JSESSIONID is retrieved by calling the login api with admin username and password.
Help me to solve the issue
Attached below is the java client code
public static void main(String[] args) {
CamundaBMPNClient bpmnClient = new CamundaBMPNClient();
Map<Integer, String> cookieHeader = bpmnClient.getCookieHeader();
bpmnClient.getListofTasks(cookieHeader);
}
public Map<Integer, String> getCookieHeader() {
String jSessionID = null;
Map<Integer, String> headerValues = new HashMap<Integer, String>();
HttpClient httpClient = HttpClientBuilder.create().build();
HttpPost request = new HttpPost(
"http://localhost:8090/camunda-webapp-tomcat-standalone-7.2.0/"
+ "api/admin/auth/user/default/login/cockpit");
request.addHeader("content-type", "application/x-www-form-urlencoded");
request.addHeader("Accept", "application/json");
String jsonString = new Gson()
.toJson("username=admin&password=admin#123");
StringEntity params;
try {
params = new StringEntity(jsonString);
request.setEntity(params);
HttpResponse response = httpClient.execute(request);
Header[] cookieheader = response.getHeaders("Set-Cookie");
for (Header s : cookieheader) {
// Do your stuff here
System.out.println(s.getValue());
String[] str = s.getValue().split(";");
int i = 1;
for (String s1 : str) {
headerValues.put(i, s1.trim());
i++;
}
}
System.out.println("jSessionID::" + jSessionID);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return headerValues;
}
public void getListofTasks(Map<Integer, String> cookieHeader) {
int id = 0;
// DefaultHttpClient httpclient = new DefaultHttpClient();
HttpPost request = new HttpPost(
"http://localhost:8090/camunda-webapp-tomcat-standalone-7.2.0/api/engine/engine/default/task");
request.addHeader("Content-type", "application/json");
String[] arrJSessionID = cookieHeader.get(1).split("=");
System.out.println("" + arrJSessionID[1]);
CookieStore cookieStore = new BasicCookieStore();
BasicClientCookie cookie = new BasicClientCookie("JSESSIONID=",
arrJSessionID[1]);
cookie.setDomain("http://localhost:8090");
cookie.setPath("/camunda-webapp-tomcat-standalone-7.2.0/");
// cookie.setAttribute(ClientCookie.DOMAIN_ATTR, "true");
cookieStore.addCookie(cookie);
// httpclient.setCookieStore(cookieStore);
HttpClient httpclient = HttpClientBuilder.create()
.setDefaultCookieStore(cookieStore).build();
String jsonString = new Gson().toJson("{}");
StringEntity jsonStr;
try {
jsonStr = new StringEntity(jsonString);
request.setEntity(jsonStr);
HttpResponse response = httpclient.execute(request);
int statusCode = response.getStatusLine().getStatusCode();
Header[] header = response.getHeaders("Set-Cookie");
for (Header h : header) {
System.out.println(h.getValue());
}
System.out.println("statusCode::" + statusCode);
} catch (Exception e) {
e.printStackTrace();
}
}

Issue when getting response from Apache HttpClient

I'm trying to work with the response body of a HTTP POST action. I'm implementing the following method:
public class post2 {
public final static void main(String[] args) throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
List<NameValuePair> formparams = new ArrayList <NameValuePair>();
formparams.add(new BasicNameValuePair("login_id", myID));
formparams.add(new BasicNameValuePair("api_key", myKey));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
HttpPost httppost = new HttpPost(myURL);
httppost.setEntity(entity);
CloseableHttpResponse response2 = httpclient.execute(httppost);
try {
System.out.println(response2.getStatusLine());
HttpEntity entity2 = response2.getEntity();
EntityUtils.consume(entity2);
String responseBody = EntityUtils.toString(entity2);
System.out.println("finalResult"+responseBody.toString());
}
finally {
httpclient.close();
}
}
}
I'm just receiving a "HTTP/1.1 200 OK", followed by:
Exception in thread "main" java.lang.ClassCastException: org.apache.http.impl.execchain.HttpResponseProxy cannot be cast to org.apache.http.HttpEntity
at post2.main(post2.java:62)
How should I recover the body information from the WebService?
Thanks,
Leo
Had to move EntityUtils.consume(entity2); to the end of the block:
public final static void main(String[] args) throws Exception {
CloseableHttpClient httpclient = HttpClients.createDefault();
List<NameValuePair> formparams = new ArrayList <NameValuePair>();
formparams.add(new BasicNameValuePair("login_id", myID));
formparams.add(new BasicNameValuePair("api_key", myKey));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
HttpPost httppost = new HttpPost(myURL);
httppost.setEntity(entity);
CloseableHttpResponse response2 = httpclient.execute(httppost);
try{
System.out.println(response2.getStatusLine());
HttpEntity entity2 = response2.getEntity();
String responseBody = EntityUtils.toString(entity2);
System.out.println("finalResult"+responseBody.toString());
EntityUtils.consume(entity2);
}
finally {
httpclient.close();
}
}

Use a TemporaryQueue on client-side for synchronous Request/Reply JMS with a JBoss server bean

I have a MDB running on JBoss 7.1, and a simple Java application as a client on another machine. The goal is the following:
the client sends a request (ObjectMessage) to the server
the server processes the request and sends back a response to the client (ObjectMessage again)
I thought to use a TemporaryQueue on the client to listen for the response (because I don't know how to do it asynchronously), and the JMSReplyTo Message's property to correctly reply back because I should support multiple independent clients.
This is the client:
public class MessagingService{
private static final String JBOSS_HOST = "localhost";
private static final int JBOSS_PORT = 5455;
private static Map connectionParams = new HashMap();
private Window window;
private Queue remoteQueue;
private TemporaryQueue localQueue;
private ConnectionFactory connectionFactory;
private Connection connection;
private Session session;
public MessagingService(Window myWindow){
this.window = myWindow;
MessagingService.connectionParams.put(TransportConstants.PORT_PROP_NAME, JBOSS_PORT);
MessagingService.connectionParams.put(TransportConstants.HOST_PROP_NAME, JBOSS_HOST);
TransportConfiguration transportConfiguration = new TransportConfiguration(NettyConnectorFactory.class.getName(), connectionParams);
this.connectionFactory = (ConnectionFactory) HornetQJMSClient.createConnectionFactoryWithoutHA(JMSFactoryType.CF, transportConfiguration);
}
public void sendRequest(ClientRequest request) {
try {
connection = connectionFactory.createConnection();
this.session = connection.createSession(false, QueueSession.AUTO_ACKNOWLEDGE);
this.remoteQueue = HornetQJMSClient.createQueue("testQueue");
this.localQueue = session.createTemporaryQueue();
MessageProducer producer = session.createProducer(remoteQueue);
MessageConsumer consumer = session.createConsumer(localQueue);
ObjectMessage message = session.createObjectMessage();
message.setObject(request);
message.setJMSReplyTo(localQueue);
producer.send(message);
ObjectMessage response = (ObjectMessage) consumer.receive();
ServerResponse serverResponse = (ServerResponse) response.getObject();
this.window.dispatchResponse(serverResponse);
this.session.close();
} catch (JMSException e) {
// TODO splittare e differenziare
e.printStackTrace();
}
}
Now I'm having troubles writing the server side, as I cannot figure out how to establish a Connection to a TemporaryQueue...
public void onMessage(Message message) {
try {
if (message instanceof ObjectMessage) {
Destination replyDestination = message.getJMSReplyTo();
ObjectMessage objectMessage = (ObjectMessage) message;
ClientRequest request = (ClientRequest) objectMessage.getObject();
System.out.println("Queue: I received an ObjectMessage at " + new Date());
System.out.println("Client Request Details: ");
System.out.println(request.getDeparture());
System.out.println(request.getArrival());
System.out.println(request.getDate());
System.out.println("Replying...");
// no idea what to do here
Connection connection = ? ? ? ? ? ? ? ?
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer replyProducer = session.createProducer(replyDestination);
ServerResponse serverResponse = new ServerResponse("TEST RESPONSE");
ObjectMessage response = session.createObjectMessage();
response.setObject(serverResponse);
replyProducer.send(response);
} else {
System.out.println("Not a valid message for this Queue MDB");
}
} catch (JMSException e) {
e.printStackTrace();
}
}
I cannot figure out what am I missing
You are asking the wrong question here.. You should look at how to create a Connection inside any Bean.
you need to get the ConnectionFactory, and create the connection accordingly.
For more information, look at the javaee examples on the HornetQ download.
In specific look at javaee/mdb-tx-send/ when you download hornetq.
#MessageDriven(name = "MDBMessageSendTxExample",
activationConfig =
{
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/testQueue")
})
public class MDBMessageSendTxExample implements MessageListener
{
#Resource(mappedName = "java:/JmsXA")
ConnectionFactory connectionFactory;
public void onMessage(Message message)
{
Connection conn = null;
try
{
// your code here...
//Step 11. we create a JMS connection
conn = connectionFactory.createConnection();
//Step 12. We create a JMS session
Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
//Step 13. we create a producer for the reply queue
MessageProducer producer = sess.createProducer(replyDestination);
//Step 14. we create a message and send it
producer.send(sess.createTextMessage("this is a reply"));
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
if(conn != null)
{
try
{
conn.close();
}
catch (JMSException e)
{
}
}
}
}

HTTPClient unable to establish route between https and http

I am testing HttpClient 4.2 by hitting a mixture of http and https links.
HttpClient seems to stick with the protocol from the first call. If the first call is http, then all following https calls fail but http calls are fine. And vice versa.
Here is the test code I used.
#Test
public void testNoRedirectMixed() throws ClientProtocolException, IOException {
HttpClient httpclient = new DefaultHttpClient();
httpclient=WebClientDevWrapper.wrapClient(httpclient);
HttpClientParams.setRedirecting(httpclient.getParams(), false);
{
HttpGet httpget = new HttpGet("http://www.hotmail.com");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
assertTrue(EntityUtils.toString(entity).indexOf("com")>0);
}
try {
HttpGet httpget = new HttpGet("https://www.hotmail.com");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
}catch (Exception e) {
e.printStackTrace();
}
{
HttpGet httpget = new HttpGet("http://www.baidu.com");
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
assertTrue(EntityUtils.toString(entity).indexOf("com")>0);
}
}
The second request (https) will fail, but the baidu request is fine.
Caused by: org.apache.http.HttpException: Unable to establish route: planned = {s}->https://www.hotmail.com; current = {s}->http://www.hotmail.com
at org.apache.http.impl.client.DefaultRequestDirector.establishRoute(DefaultRequestDirector.java:842)
I also have to disable redirection because hotmail redirects request: http://www.hotmail.com -> https://www.hotmail.com or https://www.hotmail.com -> https://www.live.com. A similar error is thrown in either cases.
The wrapper is shown below. It is used to accept all certificates.
public class WebClientDevWrapper {
public static HttpClient wrapClient(HttpClient base) {
try {
SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] xcs,
String string) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] xcs,
String string) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
};
ctx.init(null, new TrustManager[] { tm }, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx);
ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
ClientConnectionManager ccm = base.getConnectionManager();
SchemeRegistry sr = ccm.getSchemeRegistry();
sr.register(new Scheme("https", ssf, 443));
DefaultHttpClient client= new DefaultHttpClient(ccm, base.getParams());
return client;
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
}
HttpClient should be able to manage connections absolutely transparently to the user. This problem is likely to be caused by a regression introduced in the 4.2 release (see HTTPCLIENT-1193).
Use either PoolingConnectionManager or SingleConnectionManager instead of the default one until 4.2.1 version is released.
You are trying to use one connection to communicate to a number of different sites. AFAIR You have to create new connection (== new client) for every unique site.