/** Copyright 2008, 2009 Mark Hooijkaas This file is part of the RelayConnector framework. The RelayConnector framework is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The RelayConnector framework is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the RelayConnector framework. If not, see <http://www.gnu.org/licenses/>. */ package org.kisst.gft.action; import java.io.IOException; import java.util.concurrent.TimeUnit; import org.apache.http.auth.AuthScope; import org.apache.http.auth.NTCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.config.AuthSchemes; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.util.EntityUtils; import org.kisst.gft.GftContainer; import org.kisst.http4j.HttpHost; import org.kisst.http4j.IdleConnectionMonitorThread; import org.kisst.props4j.Props; import org.kisst.util.SoapUtil; import org.kisst.util.XmlNode; public class HttpCaller { private static final PoolingHttpClientConnectionManager connmngr = new PoolingHttpClientConnectionManager(); private static final IdleConnectionMonitorThread idleThread = new IdleConnectionMonitorThread(connmngr); private static final CredentialsProvider credsProvider = new BasicCredentialsProvider(); private static final CloseableHttpClient client = HttpClients.custom() .setDefaultCredentialsProvider(credsProvider) .setConnectionManager(connmngr) .build(); static { idleThread.setDaemon(true); idleThread.start(); } protected final Props props; private final long closeIdleConnections; private final HttpHost[] hosts; private final int timeout; private final String urlPostfix; protected final GftContainer gft; protected HttpCaller(GftContainer gft, Props props) { this(gft, props, 30000, null); } protected HttpCaller(GftContainer gft, Props props, int defaultTimeout, String defaultPostfix) { this.gft = gft; this.props = props; closeIdleConnections = props.getLong("closeIdleConnections", -1); String[] hostnames = props.getString("hosts").split(","); hosts = new HttpHost[hostnames.length]; int i = 0; for (String hostname : hostnames) { HttpHost host = gft.getHttpHost(hostname.trim()); hosts[i++] = host; if (host.getCredentials() instanceof NTCredentials) { credsProvider.setCredentials( new AuthScope(getHostFromUrl(host.url), host.port, AuthScope.ANY_REALM, AuthSchemes.NTLM), host.getCredentials()); } else { credsProvider.setCredentials( new AuthScope(getHostFromUrl(host.url), host.port, AuthScope.ANY_REALM, AuthSchemes.BASIC), host.getCredentials()); } } timeout = props.getInt("timeout", defaultTimeout); urlPostfix = props.getString("urlPostfix", defaultPostfix); } public XmlNode httpCall(XmlNode soap) { String response = httpCall(soap.toString()); XmlNode result = new XmlNode(response); String fault = SoapUtil.getSoapFaultMessage(result); if (fault != null) throw new RuntimeException("SOAP:Fault: " + fault); return result; } public String httpCall(String body) { for (int i = 0; i < hosts.length; i++) { HttpHost host = hosts[i]; HttpPost method = createPostMethod(host, body); try { String result = httpCall(method); return result; } catch (RuntimeException e) { if (i < hosts.length - 1) { } else throw e; } } return null; } private HttpPost createPostMethod(HttpHost host, String body) { String url = host.url; if (urlPostfix != null) url += urlPostfix; HttpPost method = new HttpPost(url); method.setConfig(RequestConfig.custom().setSocketTimeout(timeout).build());// setStaleConnectionCheckEnabled()? method.setEntity(new StringEntity(body, ContentType.create("text/xml", "UTF-8"))); return method; } private String httpCall(final HttpRequestBase method) { method.setConfig(RequestConfig.custom().setSocketTimeout(timeout).build());// setStaleConnectionCheckEnabled()? try { if (closeIdleConnections >= 0) { // Hack because often some idle connections were closed which resulted in 401 errors connmngr.closeIdleConnections(closeIdleConnections, TimeUnit.SECONDS); } CloseableHttpResponse response = client.execute(method); // Note: we used to hardcode UTF-8 as character set. // The toString below should be better (gets the charset from the HTTP Header), // but defaults tot ISO-8859-1 (as the HTTP standard prescribes). // This should be better, but long ago I have seen problems when the embedded XML wants to use UTF-8. String result = EntityUtils.toString(response.getEntity()); if (response.getStatusLine().getStatusCode() >= 300) { result = result.replace(" ++++++++++++++++++++++++++ IE filler+++++++++++++++++++++++++++++++++++++++", ""); result = result.replace(" +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", ""); result = result.replace(" ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", ""); throw new RuntimeException("HTTP call returned " + response.getStatusLine().getStatusCode() + "\n" + result); } return result; } catch (IOException e) { throw new RuntimeException(e); } finally { method.releaseConnection(); // TODO: what if connection not yet borrowed? } } private String getHostFromUrl(String url) { String result = url; if (url.contains("http://")) { result = result.replaceAll("http://", ""); } if (result.contains("/")) { result = result.substring(0, result.indexOf("/")); } return result; } }