org.jaggeryjs2.xhr.XMLHttpRequest.java Source code

Java tutorial

Introduction

Here is the source code for org.jaggeryjs2.xhr.XMLHttpRequest.java

Source

/*
 *  Copyright (c), WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */

package org.jaggeryjs2.xhr;

import jdk.nashorn.api.scripting.AbstractJSObject;
import jdk.nashorn.internal.runtime.Undefined;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.*;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
import org.apache.commons.httpclient.contrib.ssl.EasySSLProtocolSocketFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class XMLHttpRequest {

    private static final Log log = LogFactory.getLog(XMLHttpRequest.class);

    private final int HTTPS_DEFAULT_PORT = 443;
    private HttpClient httpClient = null;
    private XMLHttpRequest xhr;
    private static final String HEADER_JOINER = ", ";

    private String methodName = null;
    private String url = null;
    private boolean async = false;
    private String username = null;
    private String password = null;
    private List<Header> requestHeaders = new ArrayList<Header>();

    /**
     * XHR constants
     */
    private static final short UNSENT = 0;
    private static final short OPENED = 1;
    private static final short HEADERS_RECEIVED = 2;
    private static final short LOADING = 3;
    private static final short DONE = 4;

    /**
     * XHR properties
     */
    private short readyState;
    private StatusLine statusLine;
    private String responseText;
    private AbstractJSObject onreadystatechange;

    private HttpMethodBase method = null;
    private Header[] responseHeaders = null;
    private String responseType = null;
    private boolean error;
    private AbstractJSObject responseXML;

    public XMLHttpRequest() {
        try {
            // To ignore the hostname verification in http client side when org.wso2.ignoreHostnameVerification is set
            // This system variable should not be set in product environment due to security reasons
            String ignoreHostnameVerification = System.getProperty("org.wso2.ignoreHostnameVerification");
            if (ignoreHostnameVerification != null && "true".equalsIgnoreCase(ignoreHostnameVerification)) {
                Protocol protocolWithoutHostNameVerification = new Protocol("https",
                        (ProtocolSocketFactory) new EasySSLProtocolSocketFactory(), HTTPS_DEFAULT_PORT);
                Protocol.registerProtocol("https", protocolWithoutHostNameVerification);
            }
            httpClient = new HttpClient(new MultiThreadedHttpConnectionManager());
            ProxyHost proxyConfig = getProxyConfig();
            if (proxyConfig != null) {
                httpClient.getHostConfiguration().setProxyHost(proxyConfig);
            }
        } catch (IOException e) {
            log.error(e);
        } catch (GeneralSecurityException e) {
            log.error(e);
        }

        xhr = new XMLHttpRequest(httpClient);

    }

    private XMLHttpRequest(HttpClient httpClient) {
        this.httpClient = httpClient;
    }

    public void init() {

    }

    public int status() {
        return xhr.statusLine.getStatusCode();
    }

    public short readyState() {
        return xhr.readyState;
    }

    public String statusText() {
        return xhr.statusLine.getReasonPhrase();
    }

    public String responseText() {
        if (xhr.readyState == LOADING || xhr.readyState == DONE) {
            return xhr.responseText;
        } else {
            return "";
        }
    }

    //TODO implement responseXML method

    public void open(String method, String url) {
        setMethod(xhr, method);
        setURL(xhr, url);

        updateReadyState(xhr, OPENED);
    }

    public void open(String method, String url, String async) {
        setMethod(xhr, method);
        setURL(xhr, url);
        setAsync(xhr, async);

        updateReadyState(xhr, OPENED);
    }

    public void open(String method, String url, String userName, String password) {
        setMethod(xhr, method);
        setURL(xhr, url);
        setUsername(xhr, userName);
        setPassword(xhr, password);

        updateReadyState(xhr, OPENED);
    }

    public void open(String method, String url, String async, String userName, String password) {
        setMethod(xhr, method);
        setURL(xhr, url);
        setAsync(xhr, async);
        setUsername(xhr, userName);
        setPassword(xhr, password);

        updateReadyState(xhr, OPENED);
    }

    public void setRequestHeader(String name, String value) {
        xhr.requestHeaders.add(new Header(name, value));
    }

    public void send() throws Exception {
        xhr.sendRequest(null);
    }

    public void send(String request) throws Exception {
        xhr.sendRequest(request);
    }

    public void abort() {
        if (xhr.async) {
            xhr.method.abort();
        }
    }

    public String getResponseHeader(String header) {
        if (xhr.readyState == UNSENT || xhr.readyState == OPENED) {
            return null;
        }
        if (xhr.error) {
            return null;
        }
        if (xhr.responseHeaders == null) {
            return null;
        }
        StringBuffer value = null;
        for (Header h : xhr.responseHeaders) {
            if (h.getName().equalsIgnoreCase(header)) {
                if (value == null) {
                    value = new StringBuffer(h.getValue());
                    continue;
                }
                value.append(HEADER_JOINER).append(h.getValue());
            }
        }
        return value != null ? value.toString() : null;
    }

    public String getAllResponseHeaders() {
        if (xhr.readyState == UNSENT || xhr.readyState == OPENED) {
            return null;
        }
        if (xhr.error) {
            return null;
        }
        StringBuilder builder = new StringBuilder();
        String headers = "";
        if (xhr.responseHeaders == null) {
            return headers;
        }
        for (Header h : xhr.responseHeaders) {
            String header = h.getName();
            builder.append(h.getName() + ": " + h.getValue() + "\r\n");
        }
        headers = builder.toString();
        return headers;
    }

    private static void updateReadyState(XMLHttpRequest xhr, short readyState) {
        xhr.readyState = readyState;
        if (xhr.async && xhr.onreadystatechange != null) {
            try {
                xhr.onreadystatechange.call(xhr);
            } catch (Exception e) {
                //TODO handle error
            }
        }
    }

    private static void setURL(XMLHttpRequest xhr, Object arg) {
        if (arg instanceof String) {
            String url = (String) arg;
            String formattedUrl = url.toLowerCase();
            if (!(formattedUrl.startsWith("http://") || formattedUrl.startsWith("https://"))) {
                //TODO handle error
            }
            int lastIndex = url.indexOf("#");
            lastIndex = (lastIndex == -1) ? url.length() : lastIndex;
            xhr.url = url.substring(0, lastIndex);
        } else {
            //TODO handle error
        }
    }

    //TODO add other HTTP methods too
    private static void setMethod(XMLHttpRequest xhr, Object arg) {
        if (arg instanceof String) {
            String methodName = ((String) arg).toUpperCase();
            if ("GET".equals(methodName) || "HEAD".equals(methodName) || "POST".equals(methodName)
                    || "PUT".equals(methodName) || "DELETE".equals(methodName) || "TRACE".equals(methodName)
                    || "OPTIONS".equals(methodName)) {
                xhr.methodName = methodName;
            } else {
                //TODO handle error
            }
        } else {
            //TODO handle error
        }
    }

    private static void setAsync(XMLHttpRequest xhr, Object arg) {
        if (arg instanceof Boolean) {
            xhr.async = (Boolean) arg;
        } else {
            //TODO handle error
        }
    }

    private static void setUsername(XMLHttpRequest xhr, Object arg) {
        if (arg instanceof String) {
            xhr.username = (String) arg;
        } else {
            //TODO handle error
        }
    }

    private static void setPassword(XMLHttpRequest xhr, Object arg) {
        if (arg instanceof String) {
            xhr.password = (String) arg;
        } else {
            //TODO handle error
        }
    }

    private ProxyHost getProxyConfig() {

        ProxyHost proxyConfig = null;

        String proxyHost = System.getProperty("http.proxyHost");
        String proxyPortStr = System.getProperty("http.proxyPort");

        int proxyPort = -1;
        if (proxyHost != null) {
            proxyHost = proxyHost.trim();
        }

        if (proxyPortStr != null) {
            try {
                proxyPort = Integer.parseInt(proxyPortStr);

                //TODO test this properly
                if (proxyHost.length() > 0) {
                    proxyConfig = new ProxyHost(proxyHost, proxyPort);
                }
            } catch (NumberFormatException e) {
                log.error(e.getMessage(), e);
            }
        }

        return proxyConfig;
    }

    private void sendRequest(Object obj) throws Exception {
        final HttpMethodBase method;
        if ("GET".equalsIgnoreCase(methodName)) {
            method = new GetMethod(this.url);
        } else if ("HEAD".equalsIgnoreCase(methodName)) {
            method = new HeadMethod(this.url);
        } else if ("POST".equalsIgnoreCase(methodName)) {
            PostMethod post = new PostMethod(this.url);
            if (obj instanceof FormData) {
                FormData fd = ((FormData) obj);
                List<Part> parts = new ArrayList<Part>();
                for (Map.Entry<String, String> entry : fd) {
                    parts.add(new StringPart(entry.getKey(), entry.getValue()));
                }
                post.setRequestEntity(
                        new MultipartRequestEntity(parts.toArray(new Part[parts.size()]), post.getParams()));
            } else {
                String content = getRequestContent(obj);
                if (content != null) {
                    post.setRequestEntity(
                            new InputStreamRequestEntity(new ByteArrayInputStream(content.getBytes())));
                }
            }
            method = post;
        } else if ("PUT".equalsIgnoreCase(methodName)) {
            PutMethod put = new PutMethod(this.url);
            String content = getRequestContent(obj);
            if (content != null) {
                put.setRequestEntity(new InputStreamRequestEntity(new ByteArrayInputStream(content.getBytes())));
            }
            method = put;
        } else if ("DELETE".equalsIgnoreCase(methodName)) {
            method = new DeleteMethod(this.url);
        } else if ("TRACE".equalsIgnoreCase(methodName)) {
            method = new TraceMethod(this.url);
        } else if ("OPTIONS".equalsIgnoreCase(methodName)) {
            method = new OptionsMethod(this.url);
        } else {
            throw new Exception("Unknown HTTP method : " + methodName);
        }
        for (Header header : requestHeaders) {
            method.addRequestHeader(header);
        }
        if (username != null) {
            httpClient.getState().setCredentials(AuthScope.ANY,
                    new UsernamePasswordCredentials(username, password));
        }
        this.method = method;
        final XMLHttpRequest xhr = this;
        if (async) {
            updateReadyState(xhr, LOADING);
            final ExecutorService es = Executors.newSingleThreadExecutor();
            es.submit(new Callable() {
                public Object call() throws Exception {
                    try {
                        executeRequest(xhr);
                    } catch (Exception e) {
                        log.error(e.getMessage(), e);
                    } finally {
                        es.shutdown();
                    }
                    return null;
                }
            });
        } else {
            executeRequest(xhr);
        }
    }

    private static void executeRequest(XMLHttpRequest xhr) {
        System.out.println("XHR httpclient " + xhr.httpClient);
        try {
            xhr.httpClient.executeMethod(xhr.method);
            xhr.statusLine = xhr.method.getStatusLine();
            xhr.responseHeaders = xhr.method.getResponseHeaders();
            updateReadyState(xhr, HEADERS_RECEIVED);

            byte[] response = xhr.method.getResponseBody();
            if (response != null) {
                if (response.length > 0) {
                    xhr.responseText = new String(response);
                }
            }
            Header contentType = xhr.method.getResponseHeader("Content-Type");
            if (contentType != null) {
                xhr.responseType = contentType.getValue();
            }
            updateReadyState(xhr, DONE);
        } catch (IOException e) {
            log.error(e.getMessage(), e);

        } finally {
            xhr.method.releaseConnection();
        }
    }

    private static String getRequestContent(Object obj) {
        if (obj == null) {
            return null;
        }
        if (obj instanceof Undefined) {
            return null;
        }
        return serializeObject(obj);
    }

    //TODO start moving to a util
    public static String serializeObject(Object obj) {

        if (obj instanceof String || obj instanceof Integer || obj instanceof Long || obj instanceof Float
                || obj instanceof Double || obj instanceof Short || obj instanceof BigInteger
                || obj instanceof BigDecimal || obj instanceof Boolean) {
            return obj.toString();
        }

        //TODO serialize XML

        return serializeJSON(obj);
    }

    public static String serializeJSON(Object obj) {

        if (obj == null) {
            return "null";
        }
        if (obj instanceof Undefined) {
            return "null";
        }
        if (obj instanceof Boolean) {
            return Boolean.toString((Boolean) obj);
        }
        if (obj instanceof String) {
            return serializeString((String) obj);
        }

        if (obj instanceof Number) {
            return obj.toString();
        }

        if (obj instanceof Object[]) {
            return serializeObjectArray((Object[]) obj);
        }

        return "{}";
    }

    private static String serializeString(String obj) {
        return "\"" + obj.replace("\\", "\\\\").replace("\"", "\\\"").replace("\r", "\\r").replace("\n", "\\n")
                .replace("\t", "\\t").replace("\f", "\\f").replace("\b", "\\b").replace("\u2028", "\\u2028")
                .replace("\u2029", "\\u2029") + "\"";
    }

    private static String serializeObjectArray(Object[] obj) {
        StringWriter json = new StringWriter();
        json.append("[");
        boolean first = true;
        for (Object value : obj) {
            if (!first) {
                json.append(", ");
            } else {
                first = false;
            }
            json.append(serializeJSON(value));
        }
        json.append("]");
        return json.toString();
    }

    //TODO end moving to a util

}