/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2004-2011, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotools.data.ows;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
/**
* An Apache commons HTTP client based {@link HTTPClient} backed by a multithreaded connection
* manager that allows to reuse connections to the backing server and to limit the
* {@link #setMaxConnections(int) max number of concurrent connections}.
*
* @author groldan
* @see AbstractOpenWebService#setHttpClient(HTTPClient)
*/
public class MultithreadedHttpClient implements HTTPClient {
private MultiThreadedHttpConnectionManager connectionManager;
private HttpClient client;
private String user;
private String password;
public MultithreadedHttpClient() {
connectionManager = new MultiThreadedHttpConnectionManager();
HttpConnectionManagerParams params = new HttpConnectionManagerParams();
params.setSoTimeout(30000);
params.setConnectionTimeout(30000);
params.setMaxTotalConnections(6);
params.setDefaultMaxConnectionsPerHost(6);
connectionManager.setParams(params);
client = new HttpClient(connectionManager);
}
@Override
public HTTPResponse post(final URL url, final InputStream postContent,
final String postContentType) throws IOException {
PostMethod postMethod = new PostMethod(url.toExternalForm());
postMethod.setDoAuthentication(user != null && password != null);
if (postContentType != null) {
postMethod.setRequestHeader("Content-type", postContentType);
}
RequestEntity requestEntity = new InputStreamRequestEntity(postContent);
postMethod.setRequestEntity(requestEntity);
int responseCode = client.executeMethod(postMethod);
if (200 != responseCode) {
postMethod.releaseConnection();
throw new IOException("Server returned HTTP error code " + responseCode + " for URL "
+ url.toExternalForm());
}
return new HttpMethodResponse(postMethod);
}
@Override
public HTTPResponse get(final URL url) throws IOException {
GetMethod getMethod = new GetMethod(url.toExternalForm());
getMethod.setDoAuthentication(user != null && password != null);
int responseCode = client.executeMethod(getMethod);
if (200 != responseCode) {
getMethod.releaseConnection();
throw new IOException("Server returned HTTP error code " + responseCode + " for URL "
+ url.toExternalForm());
}
return new HttpMethodResponse(getMethod);
}
@Override
public String getUser() {
return user;
}
@Override
public void setUser(String user) {
this.user = user;
resetCredentials();
}
@Override
public String getPassword() {
return password;
}
@Override
public void setPassword(String password) {
this.password = password;
resetCredentials();
}
private void resetCredentials() {
client.getState().clearCredentials();
if (user != null && password != null) {
AuthScope authscope = AuthScope.ANY;
Credentials credentials = new UsernamePasswordCredentials(user, password);
client.getParams().setAuthenticationPreemptive(true);
client.getState().setCredentials(authscope, credentials);
} else {
client.getParams().setAuthenticationPreemptive(false);
}
}
@Override
public int getConnectTimeout() {
return connectionManager.getParams().getConnectionTimeout() / 1000;
}
@Override
public void setConnectTimeout(int connectTimeout) {
connectionManager.getParams().setConnectionTimeout(connectTimeout * 1000);
}
@Override
public int getReadTimeout() {
return connectionManager.getParams().getSoTimeout() / 1000;
}
@Override
public void setReadTimeout(int readTimeout) {
connectionManager.getParams().setSoTimeout(readTimeout * 1000);
}
public int getMaxConnections() {
return connectionManager.getParams().getDefaultMaxConnectionsPerHost();
}
public void setMaxConnections(final int maxConnections) {
connectionManager.getParams().setMaxTotalConnections(maxConnections);
connectionManager.getParams().setDefaultMaxConnectionsPerHost(maxConnections);
}
private static class HttpMethodResponse implements HTTPResponse {
private HttpMethod methodResponse;
private InputStream responseBodyAsStream;
public HttpMethodResponse(final HttpMethod methodResponse) {
this.methodResponse = methodResponse;
}
@Override
public void dispose() {
if (responseBodyAsStream != null) {
try {
responseBodyAsStream.close();
} catch (IOException e) {
// ignore
}
}
if (methodResponse != null) {
methodResponse.releaseConnection();
methodResponse = null;
}
}
@Override
public String getContentType() {
return getResponseHeader("Content-Type");
}
@Override
public String getResponseHeader(final String headerName) {
Header responseHeader = methodResponse.getResponseHeader(headerName);
return responseHeader == null ? null : responseHeader.getValue();
}
@Override
public InputStream getResponseStream() throws IOException {
if (responseBodyAsStream == null) {
responseBodyAsStream = methodResponse.getResponseBodyAsStream();
}
return responseBodyAsStream;
}
}
}