com.codeasylum.stress.api.HttpTask.java Source code

Java tutorial

Introduction

Here is the source code for com.codeasylum.stress.api.HttpTask.java

Source

/*
 * Copyright (c) 2007, 2008, 2009, 2010, 2011, 10212, 2013 David Berkman
 * 
 * This file is part of the CodeAsylum Code Project.
 * 
 * The CodeAsylum Code Project is free software, you can redistribute
 * it and/or modify it under the terms of GNU Affero General Public
 * License as published by the Free Software Foundation, either version 3
 * of the License, or (at your option) any later version.
 * 
 * The CodeAsylum Code Project 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 the GNU Affero General Public
 * License, along with the CodeAsylum Code Project. If not, see
 * <http://www.gnu.org/licenses/>.
 * 
 * Additional permission under the GNU Affero GPL version 3 section 7
 * ------------------------------------------------------------------
 * If you modify this Program, or any covered work, by linking or
 * combining it with other code, such other code is not for that reason
 * alone subject to any of the requirements of the GNU Affero GPL
 * version 3.
 */
package com.codeasylum.stress.api;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.DefaultRedirectStrategy;
import org.apache.http.impl.conn.SingleClientConnManager;
import org.apache.http.util.EntityUtils;
import org.smallmind.nutsnbolts.http.HttpMethod;
import org.smallmind.nutsnbolts.lang.StackTraceUtilities;
import org.smallmind.nutsnbolts.lang.StaticInitializationError;
import org.smallmind.nutsnbolts.lang.UnknownSwitchCaseException;
import org.smallmind.nutsnbolts.util.PropertyExpander;
import org.smallmind.nutsnbolts.util.PropertyExpanderException;
import org.smallmind.nutsnbolts.util.SystemPropertyMode;

public class HttpTask extends AbstractTask {

    private static final PropertyExpander PROPERTY_EXPANDER;

    private transient HttpClient httpClient = new RedirectingHttpClient();

    private HashMap<String, String> regexpMap = new HashMap<String, String>();
    private HashMap<String, String> validationMap = new HashMap<String, String>();
    private Attribute<String> serverAttribute = new Attribute<String>(String.class);
    private Attribute<String> pathAttribute = new Attribute<String>(String.class);
    private Attribute<String> contentTypeAttribute = new Attribute<String>(String.class);
    private Attribute<String> bodyAttribute = new Attribute<String>(String.class);
    private Attribute<Integer> portAttribute = new Attribute<Integer>(Integer.class, "80");
    private HttpMethod httpMethod = HttpMethod.GET;
    private String responseKey;

    static {

        try {
            PROPERTY_EXPANDER = new PropertyExpander(false, SystemPropertyMode.NEVER, false);
        } catch (PropertyExpanderException propertyExpanderException) {
            throw new StaticInitializationError(propertyExpanderException);
        }
    }

    public HttpTask() {

    }

    private HttpTask(HttpTask httpTask) {

        super(httpTask);

        regexpMap = new HashMap<String, String>(httpTask.getRegexpMap());
        validationMap = new HashMap<String, String>(httpTask.getValidationMap());
        httpMethod = httpTask.getHttpMethod();
        serverAttribute = new Attribute<String>(String.class, httpTask.getServerAttribute());
        portAttribute = new Attribute<Integer>(Integer.class, httpTask.getPortAttribute());
        pathAttribute = new Attribute<String>(String.class, httpTask.getPathAttribute());
        contentTypeAttribute = new Attribute<String>(String.class, httpTask.getContentTypeAttribute());
        bodyAttribute = new Attribute<String>(String.class, httpTask.getBodyAttribute());
        responseKey = httpTask.getResponseKey();
    }

    public HashMap<String, String> getRegexpMap() {

        return regexpMap;
    }

    public void setRegexpMap(HashMap<String, String> regexpMap) {

        this.regexpMap = regexpMap;
    }

    public HashMap<String, String> getValidationMap() {

        return validationMap;
    }

    public void setValidationMap(HashMap<String, String> validationMap) {

        this.validationMap = validationMap;
    }

    public Attribute<String> getServerAttribute() {

        return serverAttribute;
    }

    public void setServerAttribute(Attribute<String> serverAttribute) {

        this.serverAttribute = serverAttribute;
    }

    public Attribute<String> getPathAttribute() {

        return pathAttribute;
    }

    public void setPathAttribute(Attribute<String> pathAttribute) {

        this.pathAttribute = pathAttribute;
    }

    public Attribute<String> getContentTypeAttribute() {

        return contentTypeAttribute;
    }

    public void setContentTypeAttribute(Attribute<String> contentTypeAttribute) {

        this.contentTypeAttribute = contentTypeAttribute;
    }

    public Attribute<String> getBodyAttribute() {

        return bodyAttribute;
    }

    public void setBodyAttribute(Attribute<String> bodyAttribute) {

        this.bodyAttribute = bodyAttribute;
    }

    public Attribute<Integer> getPortAttribute() {

        return portAttribute;
    }

    public void setPortAttribute(Attribute<Integer> portAttribute) {

        this.portAttribute = portAttribute;
    }

    public HttpMethod getHttpMethod() {

        return httpMethod;
    }

    public void setHttpMethod(HttpMethod httpMethod) {

        this.httpMethod = httpMethod;
    }

    public String getResponseKey() {

        return responseKey;
    }

    public void setResponseKey(String responseKey) {

        this.responseKey = responseKey;
    }

    @Override
    public void execute(long timeDifferential, int hostIndex, String hostId, Ouroboros ouroboros,
            ExchangeTransport exchangeTransport) throws IOException, ScriptInterpolationException {

        if (isEnabled() && ouroboros.isEnabled()) {

            HttpUriRequest httpRequest;
            ResponseCarrier responseCarrier;
            URI requestURI;
            StringBuilder uriBuilder;
            String requestMimeType;
            String requestCharSet;
            String requestBody;
            boolean validated = true;
            long startTime;

            if ((portAttribute.getScript() == null) || (portAttribute.getScript().length() == 0)) {
                throw new TaskExecutionException("The %s(%s) has no http port value configured",
                        HttpTask.class.getSimpleName(), getName());
            }

            uriBuilder = new StringBuilder("http://").append(serverAttribute.get(this)).append(':')
                    .append(portAttribute.get(this)).append('/').append(pathAttribute.get(this));
            try {
                requestURI = new URI(uriBuilder.toString());
            } catch (URISyntaxException uriSyntaxException) {
                throw new TaskExecutionException(uriSyntaxException,
                        "The %s(%s) has been configured with a malformed URI(%s)", HttpTask.class.getSimpleName(),
                        getName(), uriBuilder.toString());
            }

            if ((requestMimeType = contentTypeAttribute.get(this)) != null) {

                int semiColonPos;

                if ((semiColonPos = requestMimeType.indexOf(';')) < 0) {
                    requestCharSet = "utf-8";
                } else {

                    int equalsPos;

                    if ((equalsPos = requestMimeType.indexOf('=', semiColonPos + 1)) <= 0) {
                        throw new TaskExecutionException(
                                "The %s(%s) contains an improperly formatted content type(%s)",
                                HttpTask.class.getSimpleName(), getName(), requestMimeType);
                    }

                    requestCharSet = requestMimeType.substring(equalsPos + 1);
                    requestMimeType = requestMimeType.substring(0, semiColonPos);
                }
            } else {
                requestMimeType = "text/plain";
                requestCharSet = "utf-8";
            }

            switch (httpMethod) {
            case GET:
                if (((requestBody = bodyAttribute.get(this)) != null) && (requestBody.length() > 0)) {
                    throw new TaskExecutionException(
                            "The %s(%s) uses the 'GET' method, but has been configured with body content",
                            HttpTask.class.getSimpleName(), getName());
                }

                httpRequest = new HttpGet(requestURI);
                break;
            case PUT:
                if (((requestBody = bodyAttribute.get(this)) == null) || (requestBody.length() == 0)) {
                    throw new TaskExecutionException(
                            "The %s(%s) uses the 'PUT' method, but has not been configured with any body content",
                            HttpTask.class.getSimpleName(), getName());
                }

                httpRequest = new HttpPut(requestURI);
                ((HttpPut) httpRequest).setEntity(new StringEntity(requestBody, requestCharSet));
                break;
            case POST:
                if (((requestBody = bodyAttribute.get(this)) == null) || (requestBody.length() == 0)) {
                    throw new TaskExecutionException(
                            "The %s(%s) uses the 'POST' method, but has not been configured with any body content",
                            HttpTask.class.getSimpleName(), getName());
                }

                httpRequest = new HttpPost(requestURI);
                ((HttpPost) httpRequest).setEntity(new StringEntity(requestBody, requestCharSet));
                break;
            case DELETE:
                if (((requestBody = bodyAttribute.get(this)) != null) && (requestBody.length() > 0)) {
                    throw new TaskExecutionException(
                            "The %s(%s) uses the 'DELETE' method, but has been configured with body content",
                            HttpTask.class.getSimpleName(), getName());
                }

                httpRequest = new HttpDelete(requestURI);
                break;
            default:
                throw new UnknownSwitchCaseException(httpMethod.name());
            }

            httpRequest.setHeader("Content-Type", requestMimeType + ";charset=" + requestCharSet);

            startTime = System.currentTimeMillis();
            try {
                responseCarrier = httpClient.execute(httpRequest, new ResponseHandler<ResponseCarrier>() {

                    @Override
                    public ResponseCarrier handleResponse(HttpResponse response) throws IOException {

                        HttpEntity entity;
                        Header contentTypeHeader = response.getFirstHeader("Content-Type");
                        String responseMimeType;
                        String responseCharSet;

                        if ((contentTypeHeader != null)
                                && ((responseMimeType = contentTypeHeader.getValue()) != null)) {

                            int semiColonPos;

                            if ((semiColonPos = responseMimeType.indexOf(';')) < 0) {
                                responseCharSet = "utf-8";
                            } else {

                                int equalsPos;

                                if ((equalsPos = responseMimeType.indexOf('=', semiColonPos + 1)) <= 0) {
                                    throw new TaskExecutionException(
                                            "Improperly formatted content type(%s) in response", responseMimeType);
                                }

                                responseCharSet = responseMimeType.substring(equalsPos + 1);
                                responseMimeType = responseMimeType.substring(0, semiColonPos);
                            }
                        } else {
                            responseMimeType = "text/plain";
                            responseCharSet = "utf-8";

                        }

                        return new ResponseCarrier(System.currentTimeMillis(),
                                response.getStatusLine().getStatusCode(), responseMimeType, responseCharSet,
                                ((entity = response.getEntity()) == null) ? null : EntityUtils.toByteArray(entity));
                    }
                });

                if (!regexpMap.isEmpty()) {

                    Matcher regexpMatcher;
                    String responseBody = (responseCarrier.getRawResponse() == null) ? null
                            : new String(responseCarrier.getRawResponse(), responseCarrier.getResponseCharSet());

                    for (Map.Entry<String, String> regexpEntry : regexpMap.entrySet()) {
                        PropertyContext.removeKeysStartingWith(regexpEntry.getKey());

                        if (responseBody != null) {
                            if ((regexpMatcher = Pattern.compile(
                                    PROPERTY_EXPANDER.expand(regexpEntry.getValue(), PropertyContext.getMap()))
                                    .matcher(responseBody)).find()) {
                                PropertyContext.put(regexpEntry.getKey(), "true");
                                for (int groupIndex = 0; groupIndex <= regexpMatcher.groupCount(); groupIndex++) {
                                    PropertyContext.put(regexpEntry.getKey() + '.' + groupIndex,
                                            regexpMatcher.group(groupIndex));
                                }
                            } else {
                                PropertyContext.put(regexpEntry.getKey(), "false");
                            }
                        }
                    }
                }

                if (!validationMap.isEmpty()) {
                    for (Map.Entry<String, String> validationEntry : validationMap.entrySet()) {
                        if (!PropertyContext.valueEquals(validationEntry.getKey(), validationEntry.getValue())) {
                            validated = false;
                            break;
                        }
                    }
                }

                if ((responseKey != null) && (responseKey.length() > 0)) {
                    PropertyContext.put(responseKey, new String(responseCarrier.getRawResponse()));
                }

                exchangeTransport.send(new HttpExchange(validated && (responseCarrier.getResponseCode() == 200),
                        hostId, getName(), startTime + timeDifferential,
                        responseCarrier.getResponseTimestamp() + timeDifferential,
                        responseCarrier.getResponseCode(), requestMimeType, requestCharSet, requestBody,
                        responseCarrier.getResponseMimeType(), responseCarrier.getResponseCharSet(),
                        responseCarrier.getRawResponse()));
            } catch (Exception exception) {
                exchangeTransport.send(new HttpExchange(false, hostId, getName(), startTime + timeDifferential,
                        System.currentTimeMillis() + timeDifferential, 503, requestMimeType, requestCharSet,
                        requestBody, "text/plain", "utf-8",
                        StackTraceUtilities.obtainStackTraceAsString(exception).getBytes()));

                if (!regexpMap.isEmpty()) {
                    for (Map.Entry<String, String> regexpEntry : regexpMap.entrySet()) {
                        PropertyContext.removeKeysStartingWith(regexpEntry.getKey());
                    }
                }

                if ((responseKey != null) && (responseKey.length() > 0)) {
                    PropertyContext.remove(responseKey);
                }
            }
        }
    }

    @Override
    public Task deepCopy() {

        return new HttpTask(this);
    }

    private Object readResolve() {

        httpClient = new DefaultHttpClient();

        return this;
    }

    private class RedirectingHttpClient extends DefaultHttpClient {

        public RedirectingHttpClient() {

            super(new SingleClientConnManager());

            setRedirectStrategy(new DefaultRedirectStrategy());
        }
    }

    private class ResponseCarrier {

        private String responseMimeType;
        private String responseCharSet;
        private byte[] rawResponse;
        private long responseTimestamp;
        private int responseCode;

        private ResponseCarrier(long responseTimestamp, int responseCode, String responseMimeType,
                String responseCharSet, byte[] rawResponse) {

            this.responseTimestamp = responseTimestamp;
            this.responseCode = responseCode;
            this.responseMimeType = responseMimeType;
            this.responseCharSet = responseCharSet;
            this.rawResponse = rawResponse;
        }

        public long getResponseTimestamp() {

            return responseTimestamp;
        }

        public int getResponseCode() {

            return responseCode;
        }

        public String getResponseMimeType() {

            return responseMimeType;
        }

        public void setResponseMimeType(String responseMimeType) {

            this.responseMimeType = responseMimeType;
        }

        public String getResponseCharSet() {

            return responseCharSet;
        }

        public void setResponseCharSet(String responseCharSet) {

            this.responseCharSet = responseCharSet;
        }

        public byte[] getRawResponse() {

            return rawResponse;
        }
    }
}