Java tutorial
/* * ptest-server and client provides you with a performance test utility * Copyright (C) 2012 Christian Kreutzfeldt <mnxfst@googlemail.com> * * This program 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. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. * */ package com.mnxfst.testing.activities.http; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.http.ConnectionReuseStrategy; import org.apache.http.HttpHost; import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpVersion; import org.apache.http.NameValuePair; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.utils.URIUtils; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.DefaultConnectionReuseStrategy; import org.apache.http.impl.DefaultHttpClientConnection; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.apache.http.message.BasicNameValuePair; import org.apache.http.params.HttpParams; import org.apache.http.params.HttpProtocolParams; import org.apache.http.params.SyncBasicHttpParams; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.ExecutionContext; import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpProcessor; import org.apache.http.protocol.HttpRequestExecutor; import org.apache.http.protocol.ImmutableHttpProcessor; import org.apache.log4j.Logger; import com.mnxfst.testing.activities.AbstractTSPlanActivity; import com.mnxfst.testing.exception.TSPlanActivityExecutionException; import com.mnxfst.testing.plan.config.TSPlanConfigOption; /** * Common base for http request activities * @author ckreutzfeldt * */ public abstract class AbstractHTTPRequestActivity extends AbstractTSPlanActivity { private static final Logger logger = Logger.getLogger(AbstractHTTPRequestActivity.class); private static final HttpRequestInterceptor[] EMPTY_HTTP_REQUEST_INTERCEPTOR_ARRAY = new HttpRequestInterceptor[0]; private static final String CFG_OPT_SCHEME = "scheme"; private static final String CFG_OPT_HOST = "host"; private static final String CFG_OPT_PORT = "port"; private static final String CFG_OPT_PATH = "path"; private static final String CFG_OPT_MAX_CONNECTIONS = "maxConnections"; private static final String CFG_OPT_PROXY_URL = "proxyUrl"; private static final String CFG_OPT_PROXY_PORT = "proxyPort"; private static final String CFG_OPT_PROXY_USER = "proxyUser"; private static final String CFG_OPT_PROXY_PASSWORD = "proxyPassword"; private static final String CFG_OPT_BASIC_AUTH_USERNAME = "basicAuthUsername"; private static final String CFG_OPT_BASIC_AUTH_PASSWORD = "basicAuthPassword"; private static final String CFG_OPT_BASIC_AUTH_HOST_SCOPE = "basicAuthHostScope"; private static final String CFG_OPT_BASIC_AUTH_PORT_SCOPE = "basicAuthPortScope"; private static final String CFG_OPT_USER_AGENT = "userAgent"; private static final String CFG_OPT_HTTP_PROTOCOL_VERSION = "httpProtocolVersion"; private static final String CFG_OPT_CONTENT_CHARSET = "contentCharset"; private static final String CFG_OPT_EXPECT_CONTINUE = "expectContinue"; private static final String CFG_OPT_HTTP_REQUEST_PROCESSORS = "httpRequestProcessors"; private static final String CFG_OPT_METHOD = "method"; private static final String REQUEST_PARAM_OPTION_PREFIX = "request.param."; ///////////////////////////////////////////////////////////////////////////////////////// // scheme, host, port and path /** holds the url scheme, eg. http, ftp or https */ private String scheme = null; /** holds the host, eg. www.apache.org */ private String host = null; /** holds the port, eg. 8080 */ private int port = -1; /** holds the path, eg. /cms/content/index.html */ protected String path = null; ///////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// // protocol settings /** holds the user agent name */ private String userAgent = null; /** http version to be used - default: 1.1*/ private HttpVersion httpVersion = HttpVersion.HTTP_1_1; /** content charset - default: UTF-8 */ private String contentChartset = "UTF-8"; /** expect continue setting - default: false */ private boolean expectContinue = false; /** method - default: GET */ protected String method = "GET"; ///////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// // proxy settings /** holds the proxy url */ private String proxyUrl = null; /** holds the proxy port */ private int proxyPort = -1; /** holds the proxy user */ private String proxyUser = null; /** holds the proxy password */ private String proxyPassword = null; ///////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// // request parameter /** holds the request parameters as key-value pairs of strings */ protected HttpParams httpParameters = new SyncBasicHttpParams(); ///////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// // variables used for pre-computing required information /** http client used for establishing connection. using concrete implementation in order to have required signature available */ protected DefaultHttpClient httpClient = null; /** URI */ protected URI destinationURI = null; /** max connections allowed per client */ private int maxConnections = 200; /** http request / response processors */ protected HttpProcessor httpRequestResponseProcessor = null; /** has http processors */ protected boolean hasRequestResponseProcessors = false; /** request executor */ protected HttpRequestExecutor httpRequestExecutor = new HttpRequestExecutor(); /** request context */ protected HttpContext httpRequestContext = new BasicHttpContext(null); /** http host information */ protected HttpHost httpHost = null; /** http proxy information */ protected HttpHost proxyHost = null; /** client connection */ protected DefaultHttpClientConnection clientConnection = new DefaultHttpClientConnection(); /** connection strategy */ protected ConnectionReuseStrategy httpConnectionStrategy = new DefaultConnectionReuseStrategy(); /** additional header information */ protected Map<String, String> header = new HashMap<String, String>(); /** http basic authentication - username */ private String basicAuthUsername = null; /** http basic authentication - password */ private String basicAuthPassword = null; /** http basic authentication - host scope */ private String basicAuthHostScope = null; /** http basic authentication - port scope */ private int basicAuthPortScope = -1; ///////////////////////////////////////////////////////////////////////////////////////// /** * @see com.mnxfst.testing.activities.TSPlanActivity#initialize(com.mnxfst.testing.plan.config.TSPlanConfigOption) */ public void initialize(TSPlanConfigOption cfg) throws TSPlanActivityExecutionException { if (cfg == null) throw new TSPlanActivityExecutionException("Failed to initialize activity '" + this.getClass().getName() + "' due to missing configuration options"); ///////////////////////////////////////////////////////////////////////////////////////// // fetch scheme, host, port and path this.scheme = (String) cfg.getOption(CFG_OPT_SCHEME); if (this.scheme == null || this.scheme.isEmpty()) throw new TSPlanActivityExecutionException( "Required config option '" + CFG_OPT_SCHEME + "' missing for activity '" + getName() + "'"); this.host = (String) cfg.getOption(CFG_OPT_HOST); if (this.host == null || this.host.isEmpty()) throw new TSPlanActivityExecutionException( "Requied config option '" + CFG_OPT_HOST + "' missing for activity '" + getName() + "'"); String portStr = (String) cfg.getOption(CFG_OPT_PORT); if (portStr != null && !portStr.isEmpty()) { try { this.port = Integer.parseInt(portStr.trim()); } catch (NumberFormatException e) { throw new TSPlanActivityExecutionException( "Failed to parse expected numerical value for config option '" + CFG_OPT_PORT + "' for activity '" + getName() + "'"); } } // fetch username and password this.basicAuthUsername = (String) cfg.getOption(CFG_OPT_BASIC_AUTH_USERNAME); this.basicAuthPassword = (String) cfg.getOption(CFG_OPT_BASIC_AUTH_PASSWORD); this.basicAuthHostScope = (String) cfg.getOption(CFG_OPT_BASIC_AUTH_HOST_SCOPE); String authPortScopeStr = (String) cfg.getOption(CFG_OPT_BASIC_AUTH_PORT_SCOPE); if (authPortScopeStr != null && !authPortScopeStr.trim().isEmpty()) { try { this.basicAuthPortScope = Integer.parseInt(authPortScopeStr.trim()); } catch (NumberFormatException e) { throw new TSPlanActivityExecutionException( "Failed to parse expected numerical value for config option '" + CFG_OPT_BASIC_AUTH_PORT_SCOPE + "' for activity '" + getName() + "'"); } } if (this.port <= 0) this.port = 80; this.path = (String) cfg.getOption(CFG_OPT_PATH); if (this.path == null || this.path.isEmpty()) this.path = "/"; String maxConnStr = (String) cfg.getOption(CFG_OPT_MAX_CONNECTIONS); if (maxConnStr != null && !maxConnStr.isEmpty()) { try { this.maxConnections = Integer.parseInt(maxConnStr); } catch (NumberFormatException e) { throw new TSPlanActivityExecutionException( "Failed to parse expected numerical value for config option '" + CFG_OPT_MAX_CONNECTIONS + "' for activity '" + getName() + "'"); } } // initialize http host and context this.httpHost = new HttpHost(this.host, this.port); // URI builder try { // query parameters List<NameValuePair> qParams = new ArrayList<NameValuePair>(); for (String key : cfg.getOptions().keySet()) { if (key.startsWith(REQUEST_PARAM_OPTION_PREFIX)) { String value = (String) cfg.getOption(key); String requestParameterName = key.substring(REQUEST_PARAM_OPTION_PREFIX.length(), key.length()); qParams.add(new BasicNameValuePair(requestParameterName, value)); if (logger.isDebugEnabled()) logger.debug("activity[name=" + getName() + ", id=" + getId() + ", requestParameter=" + requestParameterName + ", value=" + value + "]"); } } this.destinationURI = URIUtils.createURI(this.scheme, this.host, this.port, this.path, URLEncodedUtils.format(qParams, this.contentChartset), null); // TODO handle post values } catch (URISyntaxException e) { throw new TSPlanActivityExecutionException("Failed to initialize uri for [scheme=" + this.scheme + ", host=" + this.host + ", port=" + this.port + ", path=" + this.path + "]"); } httpRequestContext.setAttribute(ExecutionContext.HTTP_CONNECTION, clientConnection); httpRequestContext.setAttribute(ExecutionContext.HTTP_TARGET_HOST, httpHost); if (logger.isDebugEnabled()) logger.debug("activity[name=" + getName() + ", id=" + getId() + ", maxConnections=" + maxConnections + ", scheme=" + scheme + ", host=" + host + ", port=" + port + ", path=" + path + "]"); ///////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// // protocol settings this.userAgent = (String) cfg.getOption(CFG_OPT_USER_AGENT); if (this.userAgent == null || this.userAgent.isEmpty()) this.userAgent = "ptest-server"; String protocolVersion = (String) cfg.getOption(CFG_OPT_HTTP_PROTOCOL_VERSION); if (protocolVersion != null && !protocolVersion.isEmpty()) { if (protocolVersion.equalsIgnoreCase("0.9")) this.httpVersion = HttpVersion.HTTP_0_9; else if (protocolVersion.equalsIgnoreCase("1.0")) this.httpVersion = HttpVersion.HTTP_1_0; else if (protocolVersion.equalsIgnoreCase("1.1")) this.httpVersion = HttpVersion.HTTP_1_1; else throw new TSPlanActivityExecutionException("Failed to parse http protocol version '" + protocolVersion + "'. Valid value: 0.9, 1.0 and 1.1"); } this.contentChartset = (String) cfg.getOption(CFG_OPT_CONTENT_CHARSET); if (this.contentChartset == null || this.contentChartset.isEmpty()) this.contentChartset = "UTF-8"; String expectContStr = (String) cfg.getOption(CFG_OPT_EXPECT_CONTINUE); if (expectContStr != null && !expectContStr.isEmpty()) { this.expectContinue = Boolean.parseBoolean(expectContStr.trim()); } HttpProtocolParams.setUserAgent(httpParameters, userAgent); HttpProtocolParams.setVersion(httpParameters, httpVersion); HttpProtocolParams.setContentCharset(httpParameters, contentChartset); HttpProtocolParams.setUseExpectContinue(httpParameters, expectContinue); String httpProcStr = (String) cfg.getOption(CFG_OPT_HTTP_REQUEST_PROCESSORS); if (httpProcStr != null && !httpProcStr.isEmpty()) { List<HttpRequestInterceptor> interceptors = new ArrayList<HttpRequestInterceptor>(); String[] procClasses = httpProcStr.split(","); if (procClasses != null && procClasses.length > 0) { for (int i = 0; i < procClasses.length; i++) { try { Class<?> clazz = Class.forName(procClasses[i]); interceptors.add((HttpRequestInterceptor) clazz.newInstance()); if (logger.isDebugEnabled()) logger.debug("activity[name=" + getName() + ", id=" + getId() + ", httpRequestInterceptor=" + procClasses[i] + "]"); } catch (Exception e) { throw new TSPlanActivityExecutionException("Failed to instantiate http interceptor '" + procClasses[i] + "' for activity '" + getName() + "'. Error: " + e.getMessage()); } } } this.httpRequestResponseProcessor = new ImmutableHttpProcessor( (HttpRequestInterceptor[]) interceptors.toArray(EMPTY_HTTP_REQUEST_INTERCEPTOR_ARRAY)); this.hasRequestResponseProcessors = true; } this.method = (String) cfg.getOption(CFG_OPT_METHOD); if (method == null || method.isEmpty()) this.method = "GET"; if (!method.equalsIgnoreCase("get") && !method.equalsIgnoreCase("post")) throw new TSPlanActivityExecutionException( "Invalid method '" + method + "' found for activity '" + getName() + "'"); if (logger.isDebugEnabled()) logger.debug("activity[name=" + getName() + ", id=" + getId() + ", method=" + method + ", user-agent=" + userAgent + ", httpVersion=" + httpVersion + ", contentCharset=" + contentChartset + ", expectContinue=" + expectContinue + "]"); ///////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// // fetch proxy settings this.proxyUrl = (String) cfg.getOption(CFG_OPT_PROXY_URL); String proxyPortStr = (String) cfg.getOption(CFG_OPT_PROXY_PORT); if (proxyPortStr != null && !proxyPortStr.isEmpty()) { try { this.proxyPort = Integer.parseInt(proxyPortStr.trim()); } catch (NumberFormatException e) { throw new TSPlanActivityExecutionException( "Failed to parse expected numerical value for config option '" + CFG_OPT_PROXY_PORT + "' for activity '" + getName() + "'"); } } this.proxyUser = (String) cfg.getOption(CFG_OPT_PROXY_USER); this.proxyPassword = (String) cfg.getOption(CFG_OPT_PROXY_PASSWORD); if (proxyUrl != null && !proxyUrl.isEmpty()) { if (proxyPort > 0) this.proxyHost = new HttpHost(proxyUrl, proxyPort); else this.proxyHost = new HttpHost(proxyUrl); } if (logger.isDebugEnabled()) logger.debug("activity[name=" + getName() + ", id=" + getId() + ", proxyUrl=" + proxyUrl + ", proxyPort=" + proxyPort + ", proxyUser=" + proxyUser + "]"); ///////////////////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////////////////// // // fetch request parameters // // // unfortunately we must step through the whole set of keys ... // for(String key : cfg.getOptions().keySet()) { // if(key.startsWith(REQUEST_PARAM_OPTION_PREFIX)) { // String value = (String)cfg.getOption(key); // String requestParameterName = key.substring(REQUEST_PARAM_OPTION_PREFIX.length(), key.length()); // httpParameters.setParameter(requestParameterName, value); // if(logger.isDebugEnabled()) // logger.debug("activity[name="+getName()+", id="+getId()+", requestParameter="+requestParameterName+", value="+value+"]"); // } // } // // ///////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////// // configure scheme registry and initialize http client SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory())); schemeRegistry.register(new Scheme("https", 443, SSLSocketFactory.getSocketFactory())); if (logger.isDebugEnabled()) logger.debug("activity[name=" + getName() + ", id=" + getId() + ", registeredSchemes={http, https}]"); ThreadSafeClientConnManager threadSafeClientConnectionManager = new ThreadSafeClientConnManager( schemeRegistry); threadSafeClientConnectionManager.setMaxTotal(maxConnections); threadSafeClientConnectionManager.setDefaultMaxPerRoute(maxConnections); this.httpClient = new DefaultHttpClient(threadSafeClientConnectionManager); if (this.basicAuthUsername != null && !this.basicAuthUsername.trim().isEmpty() && this.basicAuthPassword != null && !this.basicAuthPassword.trim().isEmpty()) { UsernamePasswordCredentials creds = new UsernamePasswordCredentials(this.basicAuthUsername, this.basicAuthPassword); AuthScope authScope = null; if (basicAuthHostScope != null && !basicAuthHostScope.isEmpty() && basicAuthPortScope > 0) { authScope = new AuthScope(basicAuthHostScope, basicAuthPortScope); } else { authScope = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT); } this.httpClient.getCredentialsProvider().setCredentials(authScope, creds); if (logger.isDebugEnabled()) logger.debug("activity[name=" + getName() + ", id=" + getId() + ", credentials=(" + creds.getUserName() + ", " + creds.getPassword() + "), authScope=(" + authScope.getHost() + ":" + authScope.getPort() + ")]"); } if (logger.isDebugEnabled()) logger.debug("activity[name=" + getName() + ", id=" + getId() + ", threadSafeClientConnectionManager=initialized]"); } }