org.opentestsystem.shared.test.interactioncontext.FastInteractionContext.java Source code

Java tutorial

Introduction

Here is the source code for org.opentestsystem.shared.test.interactioncontext.FastInteractionContext.java

Source

/*******************************************************************************
 * Educational Online Test Delivery System 
 * Copyright (c) 2014 American Institutes for Research
 *   
 * Distributed under the AIR Open Source License, Version 1.0 
 * See accompanying file AIR-License-1_0.txt or at
 * http://www.smarterapp.org/documents/American_Institutes_for_Research_Open_Source_Software_License.pdf
 ******************************************************************************/
package org.opentestsystem.shared.test.interactioncontext;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.HttpCookie;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.opentestsystem.shared.test.api.InteractiveUser;
import org.opentestsystem.shared.test.api.WebApplication;
import org.opentestsystem.shared.test.pagedriver.FastPageDriver;
import org.opentestsystem.shared.test.statistics.InteractionTimingRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FastInteractionContext extends AbstractInteractionContext<FastInteractionResponse> {

    private static final Logger _logger = LoggerFactory.getLogger(FastInteractionContext.class);

    private FastPageDriver _currentPage;
    private URL _baseURL;
    private URI _baseURI;
    private Map<String, Object> _contextState = new HashMap<String, Object>();

    // Note, we don't want to use the system-wide cookie manager because we want
    // each context to be independent.
    private CookieManager _cookies = new CookieManager(null, CookiePolicy.ACCEPT_ALL);

    public FastInteractionContext() {
        super();
    }

    public void init(InteractiveUser<FastInteractionResponse> interactiveUser) {
        super.init(interactiveUser);
    }

    @Override
    public FastInteractionResponse browse(WebApplication webApplication, String path, long timeout)
            throws Exception {
        _baseURL = webApplication.getIndexUrl();
        _baseURI = _baseURL.toURI();
        URL targetURL = null;
        if (path == null) {
            targetURL = _baseURL;
        } else {
            targetURL = new URL(_baseURL, path);
        }

        this.startNewInteraction("BROWSE_INDEX",
                String.format("Browse the index for web application %s", webApplication.getIndexUrl()));
        return sendGetRequest(targetURL, timeout);
    }

    public FastPageDriver getCurrentPage() {
        return _currentPage;
    }

    void setCurrentPage(FastPageDriver currentPage) {
        _currentPage = currentPage;
    }

    @Override
    public String getCookie(String key) {
        final List<HttpCookie> cookieList = _cookies.getCookieStore().get(_baseURI);
        for (HttpCookie cookie_i : cookieList) {
            if (cookie_i.getName().equalsIgnoreCase(key)) {
                return cookie_i.getValue();
            }
        }
        return null;
    }

    @Override
    public void shutdown() {
    }

    @Override
    public FastInteractionResponse sendGetRequest(URL url, long timeout) throws Exception {
        return buildResponse("GET", url, null, null, timeout);
    }

    @Override
    public URL getBaseUrl() {
        return _baseURL;
    }

    public FastInteractionResponse buildResponse(String method, URL url, Map<String, List<String>> headers,
            Reader body, long timeout) throws IOException {

        URI uri = null;
        try {
            uri = url.toURI();
        } catch (URISyntaxException e) {
            synchronized (this) {
                getActiveTimingRecord().fail(String.format("Invalid URL: %s", url.toString()), e);
            }
        }

        int iTimeout = (int) Math.min(Integer.MAX_VALUE, timeout);

        FastInteractionResponse response = null;
        HttpURLConnection connection = null;

        // This value is only used if an error causes things to fail before the connection is created.
        long t_start = System.currentTimeMillis();
        try {
            connection = (HttpURLConnection) url.openConnection();
            connection.setConnectTimeout(iTimeout);
            connection.setReadTimeout(iTimeout);

            // Expect to send ouptut if a body has been supplied
            connection.setDoOutput(body != null);

            // Set headers (except cookies)
            if (headers != null) {
                for (Entry<String, List<String>> header_i : headers.entrySet()) {
                    connection.setRequestProperty(header_i.getKey(), StringUtils.join(header_i.getValue(), ','));
                }
            } else {
                headers = new HashMap<String, List<String>>();
            }

            // Set cookies
            for (Entry<String, List<String>> header_i : _cookies.get(uri, headers).entrySet()) {
                for (String cookie_j : header_i.getValue()) {
                    connection.addRequestProperty(header_i.getKey(), cookie_j);
                }
            }

            // Log the request, if desired
            if (_logger.isDebugEnabled()) {
                StringBuilder msg = new StringBuilder("Posted HTTP request:\r\n\r\n");
                msg.append(method).append("\r\n");
                msg.append(uri.toString()).append("\r\n");
                for (Entry<String, List<String>> header_i : connection.getRequestProperties().entrySet()) {
                    for (String value_j : header_i.getValue())
                        msg.append(header_i.getKey()).append(": ").append(value_j).append("\r\n");
                }
                msg.append("\r\n");

                if (body != null) {
                    String bodyString = IOUtils.toString(body);
                    IOUtils.closeQuietly(body);
                    body = new StringReader(bodyString);
                    msg.append(bodyString);
                }

                _logger.debug(msg.toString());
            }

            // Start timing
            // We update here because we don't really want to count time spent above.
            t_start = System.currentTimeMillis();

            // Send the request
            connection.connect();

            // Send the body
            if (body != null) {
                try (OutputStream os = connection.getOutputStream();) {
                    IOUtils.copy(body, os, "UTF-8");
                } finally {
                    IOUtils.closeQuietly(body);
                }
            }

            // Record the cookies
            // SB-547 workaround: multiple set-cookie headers are sent for the same
            // cookie. Process the last.
            Map<String, String> cookieMap = new HashMap<>();
            for (Entry<String, List<String>> header_i : connection.getHeaderFields().entrySet()) {
                if (StringUtils.equals(header_i.getKey(), "Set-Cookie")) {
                    for (String value_j : header_i.getValue()) {
                        for (String value_k : StringUtils.split(value_j, ',')) {
                            String[] cookieParts = StringUtils.split(value_k, "=", 2);
                            if (cookieParts.length > 0) {
                                String oldValue = cookieMap.get(cookieParts[0]);
                                if (oldValue == null || oldValue.length() < value_k.length()) {
                                    cookieMap.put(cookieParts[0], value_k);
                                }
                            }
                        }
                    }
                }
            }
            List<String> cookieValues = new ArrayList<>(cookieMap.values());
            Map<String, List<String>> cookieHeaders = new HashMap<>();
            cookieHeaders.put("Set-Cookie", cookieValues);
            _cookies.put(uri, cookieHeaders);

            // Retrieve the response
            InputStream is = connection.getInputStream();
            response = new FastInteractionResponse(this, is, connection, timeout, t_start + timeout);
            IOUtils.closeQuietly(is);
            connection.disconnect();

            // Stop timing
            long t_end = System.currentTimeMillis();
            synchronized (this) {
                InteractionTimingRecord timingRecord = getActiveTimingRecord();
                if (timingRecord != null) {
                    timingRecord.accumulate("REMOTE_TIME", t_end - t_start);
                    timingRecord.accumulate("N_REQ", 1);
                }
            }

            // Log the response, if desired
            if (_logger.isDebugEnabled()) {
                StringBuilder msg = new StringBuilder("Response from HTTP request:\r\n\r\n");
                msg.append(uri.toString()).append("\r\n\r\n");
                for (Entry<String, List<String>> header_i : connection.getHeaderFields().entrySet()) {
                    for (String value_j : header_i.getValue()) {
                        msg.append(header_i.getKey()).append(": ").append(value_j).append("\r\n");
                    }
                }
                msg.append("\r\n");
                String bodyString = response.getResponseBodyAsString();
                if (bodyString != null) {
                    if (bodyString.length() <= 1024) {
                        msg.append(bodyString);
                    } else {
                        msg.append(String.format("Body length %d. Showing first 1024 characters\r\n\r\n",
                                bodyString.length()));
                        msg.append(bodyString.subSequence(0, 1024));
                    }
                }
                _logger.debug(msg.toString());
            }

            return response;
        } catch (IOException e) {
            synchronized (this) {
                failActiveInteraction(String.format("IO Error trying to fill request to %s", url.toString()), e);
                InputStream es = null;
                es = connection.getErrorStream();
                response = new FastInteractionResponse(this, es, connection, timeout, t_start + timeout);
                IOUtils.closeQuietly(es);
            }
        }
        return response;
    }

    public Map<String, Object> getContextState() {
        return _contextState;
    }

}