Java tutorial
/* Copyright 2009 Fabrizio Cannizzo * * This file is part of JMeterRestSampler. * * JMeterRestSampler (http://code.google.com/p/rest-fixture/) is free software: * you can redistribute it and/or modify it under the terms of the * BSD License * * You should have received a copy of the BSD License * along with JMeterRestSampler. If not, see <http://opensource.org/licenses/bsd-license.php>. * * If you want to contact the author please see http://smartrics.blogspot.com */ package smartrics.jmeter.sampler; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.zip.GZIPInputStream; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethodBase; import org.apache.commons.httpclient.methods.DeleteMethod; import org.apache.commons.httpclient.methods.EntityEnclosingMethod; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.HeadMethod; import org.apache.commons.httpclient.methods.OptionsMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.PutMethod; import org.apache.commons.httpclient.methods.TraceMethod; import org.apache.jmeter.protocol.http.control.CacheManager; import org.apache.jmeter.protocol.http.sampler.HTTPSampleResult; import org.apache.jmeter.protocol.http.sampler.HTTPSampler2; import org.apache.jmeter.protocol.http.util.EncoderCache; import org.apache.jorphan.logging.LoggingManager; import org.apache.jorphan.util.JOrphanUtils; import org.apache.log.Logger; /** * Models a REST request for sampling purposes. */ public class RestSampler extends HTTPSampler2 { private static final long serialVersionUID = -5877623539165274730L; private static final Logger log = LoggingManager.getLoggerForClass(); public static final String REQUEST_BODY = "RestSampler.request_body"; public static final String QUERY_STRING = "RestSampler.query_string"; public static final String RESOURCE = "RestSampler.resource"; public static final String BASE_HOST = "RestSampler.base_host"; public static final String REQUEST_HEADERS = "RestSampler.request_headers"; public RestSampler() { log.debug("initialised new RestSampler"); } public void setRequestBody(String data) { setProperty(REQUEST_BODY, data); } public void setRequestHeaders(String headers) { setProperty(REQUEST_HEADERS, headers); } public String getRequestBody() { return getPropertyAsString(REQUEST_BODY); } public String getRequestHeaders() { return getPropertyAsString(REQUEST_HEADERS); } public void setResource(String data) { setProperty(RESOURCE, data); } public String getResource() { return getPropertyAsString(RESOURCE); } public void setQueryString(String data) { setProperty(QUERY_STRING, data); getArguments().clear(); parseArguments(data, EncoderCache.URL_ARGUMENT_ENCODING); } public String getQueryString() { return getPropertyAsString(QUERY_STRING); } public void setHostBaseUrl(final String data) { setProperty(BASE_HOST, data); } public String getHostBaseUrl() { return getPropertyAsString(BASE_HOST); } /** * Returns the full resource URI concatenating the base url and the resource * id. If either components are missing or invalid, it returns a canned * value of <code>"http://undefined.com"</code> */ public URL getUrl() { String validHost = toValidUrl(getHostBaseUrl()); URL u = toURL("http://undefined.com"); if (validHost != null && getResource() != null) { String fullUrl = validHost + getResource(); u = toURL(fullUrl); } return u; } public String toString() { return "Base host url: " + getHostBaseUrl() + ", resource: " + getResource() + ", Method: " + getMethod(); } private String toValidUrl(String u) { try { URL url = new URL(u); String urlStr = url.toString(); if (urlStr.endsWith("/")) { url = toURL(urlStr.substring(0, urlStr.length() - 1)); urlStr = url.toString(); } return urlStr; } catch (MalformedURLException e) { return null; } } private URL toURL(String u) { try { return new URL(u); } catch (MalformedURLException e) { return null; } } private void overrideHeaders(HttpMethodBase httpMethod) { String headers = getRequestHeaders(); String[] header = headers.split(System.getProperty("line.separator")); for (String kvp : header) { int pos = kvp.indexOf(':'); if (pos < 0) pos = kvp.indexOf('='); if (pos > 0) { String k = kvp.substring(0, pos).trim(); String v = ""; if (kvp.length() > pos + 1) v = kvp.substring(pos + 1).trim(); httpMethod.addRequestHeader(k, v); } } } /** * Method invoked by JMeter when a sample needs to happen. It's actually an * indirect call from the main sampler interface. it's resolved in the base * class. * * This is a copy and paste from the HTTPSampler2 - quick and dirty hack as * that class is not very extensible. The reason to extend and slightly * modify is that I needed to get the body content from a text field in the * GUI rather than a file. */ protected HTTPSampleResult sample(URL url, String method, boolean areFollowingRedirect, int frameDepth) { String urlStr = url.toString(); log.debug("Start : sample " + urlStr); log.debug("method " + method); HttpMethodBase httpMethod = null; HTTPSampleResult res = new HTTPSampleResult(); res.setMonitor(isMonitor()); res.setSampleLabel(urlStr); // May be replaced later res.setHTTPMethod(method); res.setURL(url); res.sampleStart(); // Count the retries as well in the time HttpClient client = null; InputStream instream = null; try { httpMethod = createHttpMethod(method, urlStr); // Set any default request headers res.setRequestHeaders(""); // Setup connection client = setupConnection(url, httpMethod, res); // Handle the various methods if (httpMethod instanceof EntityEnclosingMethod) { String postBody = sendData((EntityEnclosingMethod) httpMethod); res.setResponseData(postBody.getBytes()); } overrideHeaders(httpMethod); res.setRequestHeaders(getConnectionHeaders(httpMethod)); int statusCode = -1; try { statusCode = client.executeMethod(httpMethod); } catch (RuntimeException e) { log.error("Exception when executing '" + httpMethod + "'", e); throw e; } // Request sent. Now get the response: instream = httpMethod.getResponseBodyAsStream(); if (instream != null) {// will be null for HEAD Header responseHeader = httpMethod.getResponseHeader(HEADER_CONTENT_ENCODING); if (responseHeader != null && ENCODING_GZIP.equals(responseHeader.getValue())) { instream = new GZIPInputStream(instream); } res.setResponseData(readResponse(res, instream, (int) httpMethod.getResponseContentLength())); } res.sampleEnd(); // Done with the sampling proper. // Now collect the results into the HTTPSampleResult: res.setSampleLabel(httpMethod.getURI().toString()); // Pick up Actual path (after redirects) res.setResponseCode(Integer.toString(statusCode)); res.setSuccessful(isSuccessCode(statusCode)); res.setResponseMessage(httpMethod.getStatusText()); String ct = null; org.apache.commons.httpclient.Header h = httpMethod.getResponseHeader(HEADER_CONTENT_TYPE); if (h != null)// Can be missing, e.g. on redirect { ct = h.getValue(); res.setContentType(ct);// e.g. text/html; charset=ISO-8859-1 res.setEncodingAndType(ct); } String responseHeaders = getResponseHeaders(httpMethod); res.setResponseHeaders(responseHeaders); if (res.isRedirect()) { final Header headerLocation = httpMethod.getResponseHeader(HEADER_LOCATION); if (headerLocation == null) { // HTTP protocol violation, but // avoids NPE throw new IllegalArgumentException("Missing location header"); } res.setRedirectLocation(headerLocation.getValue()); } // If we redirected automatically, the URL may have changed if (getAutoRedirects()) { res.setURL(new URL(httpMethod.getURI().toString())); } // Store any cookies received in the cookie manager: saveConnectionCookies(httpMethod, res.getURL(), getCookieManager()); // Save cache information final CacheManager cacheManager = getCacheManager(); if (cacheManager != null) { cacheManager.saveDetails(httpMethod, res); } // Follow redirects and download page resources if appropriate: res = resultProcessing(areFollowingRedirect, frameDepth, res); log.debug("End : sample"); httpMethod.releaseConnection(); return res; } catch (IllegalArgumentException e)// e.g. some kinds of invalid URL { res.sampleEnd(); HTTPSampleResult err = errorResult(e, res); err.setSampleLabel("Error: " + url.toString()); return err; } catch (IOException e) { res.sampleEnd(); HTTPSampleResult err = errorResult(e, res); err.setSampleLabel("Error: " + url.toString()); return err; } finally { JOrphanUtils.closeQuietly(instream); if (httpMethod != null) { httpMethod.releaseConnection(); } } } private HttpMethodBase createHttpMethod(String method, String urlStr) { HttpMethodBase httpMethod; // May generate IllegalArgumentException if (method.equals(POST)) { httpMethod = new PostMethod(urlStr); } else if (method.equals(PUT)) { httpMethod = new PutMethod(urlStr); } else if (method.equals(HEAD)) { httpMethod = new HeadMethod(urlStr); } else if (method.equals(TRACE)) { httpMethod = new TraceMethod(urlStr); } else if (method.equals(OPTIONS)) { httpMethod = new OptionsMethod(urlStr); } else if (method.equals(DELETE)) { httpMethod = new DeleteMethod(urlStr); } else if (method.equals(GET)) { httpMethod = new GetMethod(urlStr); } else { log.error("Unexpected method (converted to GET): " + method); httpMethod = new GetMethod(urlStr); } return httpMethod; } /** * Set up the PUT/POST data. * * <b>TODO</b>: should parse request headers and pass the Content-Type. For * now it will always assume text/xml */ private String sendData(EntityEnclosingMethod method) throws IOException { method.setRequestEntity(new MyRequestEntity(getRequestBody())); return getRequestBody(); } }