alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Apache CXF example source code file (JsXMLHttpRequest.java)

This example Apache CXF source code file (JsXMLHttpRequest.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Apache CXF tags/keywords

bytearrayoutputstream, invalid_state_err, invalid_state_err, io, ioexception, list, net, network, object, object, parser, runtimeexception, runtimeexception, string, string, stringbuilder, syntax_err, uri, util, xml

The Apache CXF JsXMLHttpRequest.java source code

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF 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.apache.cxf.javascript;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Node;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.helpers.HttpHeaderHelper;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.JavaScriptException;
import org.mozilla.javascript.ScriptableObject;

/**
 * Implementation of XMLHttpRequest for Rhino. This might be given knowledge of
 * CXF 'local' URLs if the author is feeling frisky.
 */
public class JsXMLHttpRequest extends ScriptableObject {
    private static final Logger LOG = LogUtils.getL7dLogger(JsXMLHttpRequest.class);
    private static Charset utf8 = Charset.forName("utf-8");
    private static Set<String> validMethods;
    static {
        validMethods = new HashSet<String>();
        validMethods.add("GET");
        validMethods.add("POST");
        validMethods.add("HEAD");
        validMethods.add("PUT");
        validMethods.add("OPTIONS");
        validMethods.add("DELETE");
    }

    
    private static String[] invalidHeaders = {"Accept-Charset", "Accept-Encoding", "Connection",
                                              "Content-Length", "Content-Transfer-Encoding", "Date",
                                              "Expect", "Host", "Keep-Alive", "Referer", "TE", "Trailer",
                                              "Transfer-Encoding", "Upgrade", "Via"};
    private int readyState = jsGet_UNSENT();
    private Object readyStateChangeListener;
    private Map<String, String> requestHeaders;
    private String storedMethod;
    private String storedUser;
    private String storedPassword;
    private boolean sendFlag;
    private URI uri;
    private URL url;
    private boolean storedAsync;
    private URLConnection connection;
    private HttpURLConnection httpConnection;
    private Map<String, List responseHeaders;
    private int httpResponseCode;
    private String httpResponseText;
    private String responseText;
    private JsSimpleDomNode responseXml;
    private boolean errorFlag;

    public JsXMLHttpRequest() {
        requestHeaders = new HashMap<String, String>();
        storedMethod = null;
    }
    
    public static void register(ScriptableObject scope) {
        try {
            ScriptableObject.defineClass(scope, JsXMLHttpRequest.class);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String getClassName() {
        return "XMLHttpRequest";
    }

    private void notifyReadyStateChangeListener() {
        if (readyStateChangeListener instanceof Function) {
            LOG.fine("notify " + readyState);
            // for now, call with no args.
            Function listenerFunction = (Function)readyStateChangeListener;
            listenerFunction.call(Context.getCurrentContext(), getParentScope(), null, new Object[] {});
        }
    }

    private void doOpen(String method, String urlString, boolean async, String user, String password) {
        // ignoring auth for now.
        LOG.fine("doOpen " + method + " " + urlString + " " + Boolean.toString(async));

        storedAsync = async;
        responseText = null;
        responseXml = null;
        // see 4
        method = method.toUpperCase();
        // 1 check method
        if (!validMethods.contains(method)) {
            LOG.fine("Invalid method syntax error.");
            throwError("SYNTAX_ERR");
        }
        // 2 security check (we don't have any)
        // 3 store method
        storedMethod = method;
        // 4 we already mapped it to upper case.
        // 5 make a URL, dropping any fragment.
        uri = null;
        try {
            URI tempUri = new URI(urlString);
            if (tempUri.isOpaque()) { 
                LOG.fine("Relative URL syntax error.");
                throwError("SYNTAX_ERR");
            }
            
            uri = new URI(tempUri.getScheme(), tempUri.getUserInfo(), tempUri.getHost(), tempUri.getPort(),
                          tempUri.getPath(), tempUri.getQuery(), null /*
                                                                         * no
                                                                         * fragment
                                                                         */);
            url = uri.toURL();
        } catch (URISyntaxException e) {
            LOG.log(Level.SEVERE, "URI syntax error", e);
            throwError("SYNTAX_ERR");
        } catch (MalformedURLException e) {
            LOG.log(Level.SEVERE, "URI isn't URL", e);
            throwError("SYNTAX_ERR");
        }
        // 6 deal with relative URLs. We don't have a base. This is a limitation
        // on browser compatibility.
        if (!uri.isAbsolute()) {
            throwError("SYNTAX_ERR");
        }
        // 7 scheme check. Well, for now ...
        if (!uri.getScheme().equals("http") && !uri.getScheme().equals("https")) {
            LOG.severe("Not http " + uri.toString());
            throwError("NOT_SUPPORTED_ERR");
        }
        // 8 user:password is OK for HTTP.
        // 9, 10 user/password parsing
        if (uri.getUserInfo() != null) {
            String[] userAndPassword = uri.getUserInfo().split(":");
            storedUser = userAndPassword[0];
            if (userAndPassword.length == 2) {
                storedPassword = userAndPassword[1];
            }
        }
        // 11 cross-scripting check. We don't implement it.
        // 12 default async. Already done.
        // 13 check user for syntax. Not Our Job.
        // 14 encode the user. We think we can leave this for the Http code we
        // use below
        // 15, 16, 17, 18 more user/password glop.
        // 19: abort any pending activity.
        // TODO: abort
        // 20 cancel network activity.
        // TODO: cancel
        // 21 set state to OPENED and fire the listener.
        readyState = jsGet_OPENED();
        sendFlag = false;
        notifyReadyStateChangeListener();
    }

    private void doSetRequestHeader(String header, String value) {
        // 1 check state
        if (readyState != jsGet_OPENED()) {
            LOG.severe("setRequestHeader invalid state " + readyState);
            throwError("INVALID_STATE_ERR");
        }
        // 2 check flag
        if (sendFlag) {
            LOG.severe("setRequestHeader send flag set.");
            throwError("INVALID_STATE_ERR");
        }
        // 3 check field-name production.
        // 4 ignore null values.
        if (value == null) {
            return;
        }
        // 5 check value
        // 6 check for bad headers
        for (String invalid : invalidHeaders) {
            if (header.equalsIgnoreCase(invalid)) {
                LOG.severe("setRequestHeader invalid header.");
                throwError("SECURITY_ERR");
            }
        }
        // 7 check for proxy
        String headerLower = header.toLowerCase();
        if (headerLower.startsWith("proxy-")) {
            LOG.severe("setRequestHeader proxy header.");
            throwError("SECURITY_ERR");
        }
        // 8, 9, handle appends.
        String previous = requestHeaders.get(header);
        if (previous != null) {
            value = previous + ", " + value;
        }
        requestHeaders.put(header, value);
    }

    private void doSend(byte[] dataToSend, boolean xml) {
        // avoid warnings on stuff we arent using yet.
        if (storedUser != null || storedPassword != null) {
            //
        }
        // 1 check state
        if (readyState != jsGet_OPENED()) {
            LOG.severe("send state != OPENED.");
            throwError("INVALID_STATE_ERR");
        }
        // 2 check flag
        if (sendFlag) {
            LOG.severe("send sendFlag set.");
            throwError("INVALID_STATE_ERR");
        }
        // 3
        sendFlag = storedAsync;
        // 4 preprocess data. Handled on the way in here, we're called with
        // UTF-8 bytes.
        if (xml && !requestHeaders.containsKey("Content-Type")) {
            requestHeaders.put("Content-Type", "application/xml;charset=utf-8");
        }
        
        // 5 talk to the server.
        try {
            connection = url.openConnection();
        } catch (IOException e) {
            LOG.log(Level.SEVERE, "send connection failed.", e);
            throwError("CONNECTION_FAILED");
        }
        connection.setDoInput(true);
        connection.setUseCaches(false); // Enable tunneling.
        boolean post = false;
        httpConnection = null;
        if (connection instanceof HttpURLConnection) {
            httpConnection = (HttpURLConnection)connection;
            try {
                httpConnection.setRequestMethod(storedMethod);
                if ("POST".equalsIgnoreCase(storedMethod)) {
                    httpConnection.setDoOutput(true);
                    post = true;
                }
                for (Map.Entry<String, String> headerEntry : requestHeaders.entrySet()) {
                    httpConnection.setRequestProperty(headerEntry.getKey(), headerEntry.getValue());
                }
            } catch (ProtocolException e) {
                LOG.log(Level.SEVERE, "send http protocol exception.", e);
                throwError("HTTP_PROTOCOL_ERROR");
            }
        }
        
        if (post) {
            OutputStream outputStream = null;
            try {
                outputStream = connection.getOutputStream(); // implicitly connects?
                if (dataToSend != null) {
                    outputStream.write(dataToSend);
                    outputStream.flush();
                }
                outputStream.close();
            } catch (IOException e) {
                errorFlag = true;
                LOG.log(Level.SEVERE, "send output error.", e);
                throwError("NETWORK_ERR");
                try {
                    outputStream.close();
                } catch (IOException e1) {
                    //
                }
            }
        }
        // 6
        notifyReadyStateChangeListener();
        
        if (storedAsync) {
            new Thread() {
                public void run() {
                    try {
                        Context cx = ContextFactory.getGlobal().enterContext();
                        communicate(cx);
                    } finally {
                        Context.exit();
                    }
                }
            } .start();
        } else {
            communicate(Context.getCurrentContext());
        }
    }

    private void communicate(Context cx) {
        try {
            InputStream is = connection.getInputStream();
            httpResponseCode = -1;
            // this waits, I hope, for a response.
            responseHeaders = connection.getHeaderFields();
            readyState = jsGet_HEADERS_RECEIVED();
            notifyReadyStateChangeListener();
            
            if (httpConnection != null) {
                httpResponseCode = httpConnection.getResponseCode();
                httpResponseText = httpConnection.getResponseMessage();
            }
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int read;
            boolean notified = false;
            while ((read = is.read(buffer)) != -1) {
                if (!notified) {
                    readyState = jsGet_LOADING();
                    notifyReadyStateChangeListener();
                }
                baos.write(buffer, 0, read);
            }
            is.close();
            
            // For a one-way message or whatever, there may not be a content type.
            // throw away any encoding modifier.
            String contentType = "";
            String connectionContentType = connection.getContentType();
            String contentEncoding = null;
            if (connectionContentType != null) {
                contentEncoding = HttpHeaderHelper
                    .mapCharset(HttpHeaderHelper.findCharset(connectionContentType));
                contentType = connectionContentType.split(";")[0];
            }
            if (contentEncoding == null || contentEncoding.length() == 0) {
                contentEncoding = "iso-8859-1";
            }
            
            byte[] responseBytes = baos.toByteArray();
            
            /* We need all the text in a string, independent of the
             * XML parse. 
             */
            Charset contentCharset = Charset.forName(contentEncoding);
            byte[] contentBytes = baos.toByteArray();
            CharBuffer contentChars = 
                contentCharset.decode(ByteBuffer.wrap(contentBytes)); // not the most efficient way.
            responseText = contentChars.toString();
            LOG.fine(responseText);
            
            if (responseBytes.length > 0
                && ("text/xml".equals(contentType)
                    || "application/xml".equals(contentType) 
                    || contentType.endsWith("+xml"))) {
                
                try {
                    DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
                    documentBuilderFactory.setNamespaceAware(true);
                    DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
                    ByteArrayInputStream bais = new ByteArrayInputStream(responseBytes);
                    InputSource inputSource = new InputSource(bais);
                    inputSource.setEncoding(contentEncoding);
                    Document xmlDoc = builder.parse(inputSource);
                    responseXml = JsSimpleDomNode.wrapNode(getParentScope(), xmlDoc);
                } catch (ParserConfigurationException e) {
                    LOG.log(Level.SEVERE, "ParserConfigurationError", e);
                    responseXml = null;
                } catch (SAXException e) {
                    LOG.log(Level.SEVERE, "Error parsing XML response", e);
                    responseXml = null;
                }
            }

            readyState = jsGet_DONE();
            notifyReadyStateChangeListener();

            if (httpConnection != null) {
                httpConnection.disconnect();
            }
        } catch (IOException ioException) {
            errorFlag = true;
            readyState = jsGet_DONE();
            if (!storedAsync) {
                LOG.log(Level.SEVERE, "IO error reading response", ioException);
                throwError("NETWORK_ERR");
                notifyReadyStateChangeListener();
            }
        }
    }

    private void throwError(String errorName) {
        LOG.info("Javascript throw: " + errorName);
        throw new JavaScriptException(Context.javaToJS(errorName, getParentScope()), "XMLHttpRequest", 0);
    }

    private byte[] utf8Bytes(String data) {
        ByteBuffer bb = utf8.encode(data);
        byte[] val = new byte[bb.limit()];
        bb.get(val);
        return val;
    }

    private byte[] domToUtf8(JsSimpleDomNode xml) {
        Node node = xml.getWrappedNode();
        // entire document.
        // if that's an issue, we could code something more complex.
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        StreamResult result = new StreamResult(baos);
        DOMSource source = new DOMSource(node);
        try {
            TransformerFactory.newInstance().newTransformer().transform(source, result);
        } catch (TransformerConfigurationException e) {
            throw new RuntimeException(e);
        } catch (TransformerException e) {
            throw new RuntimeException(e);
        } catch (TransformerFactoryConfigurationError e) {
            throw new RuntimeException(e);
        }
        return baos.toByteArray();
    }
    
    public void doAbort() {
        // this is messy.
    }
    
    public String doGetAllResponseHeaders() {
        // 1 check state.
        if (readyState == jsGet_UNSENT() || readyState == jsGet_OPENED()) {
            LOG.severe("Invalid state");
            throwError("INVALID_STATE_ERR");
        }
        
        // 2 check error flag
        if (errorFlag) {
            LOG.severe("error flag set");
            return null;
        }
        
        // 3 pile up the headers.
        StringBuilder builder = new StringBuilder();
        for (Map.Entry<String, List headersEntry : responseHeaders.entrySet()) {
            if (headersEntry.getKey() == null) {
                // why does the HTTP connection return a null key with the response code and text?
                continue;
            }
            builder.append(headersEntry.getKey());
            builder.append(": ");
            for (String value : headersEntry.getValue()) {
                builder.append(value);
                builder.append(", ");
            }
            builder.setLength(builder.length() - 2); // trim extra comma/space
            builder.append("\r\n");
        }
        return builder.toString();
    }
    
    public String doGetResponseHeader(String header) {
        // 1 check state.
        if (readyState == jsGet_UNSENT() || readyState == jsGet_OPENED()) {
            LOG.severe("invalid state");
            throwError("INVALID_STATE_ERR");
        }
        
        // 2 check header format, we don't do it.
        
        // 3 check error flag
        if (errorFlag) {
            LOG.severe("error flag");
            return null;
        }
        
        //4 -- oh, it's CASE-INSENSITIVE. Well, we do it the hard way.
        for (Map.Entry<String, List headersEntry : responseHeaders.entrySet()) {
            if (header.equalsIgnoreCase(headersEntry.getKey())) {
                StringBuilder builder = new StringBuilder();
                for (String value : headersEntry.getValue()) {
                    builder.append(value);
                    builder.append(", ");
                }
                builder.setLength(builder.length() - 2); // trim extra comma/space
                return builder.toString();
            }
        }
        return null;
    }
    
    public String doGetResponseText() {
        // 1 check state.
        if (readyState == jsGet_UNSENT() || readyState == jsGet_OPENED()) {
            LOG.severe("invalid state " + readyState);
            throwError("INVALID_STATE_ERR");
        }
        
        // 2 return what we have.
        return responseText;
    }
    
    public Object doGetResponseXML() {
        // 1 check state.
        if (readyState == jsGet_UNSENT() || readyState == jsGet_OPENED()) {
            LOG.severe("invalid state");
            throwError("INVALID_STATE_ERR");
        }
        
        return responseXml;
    }
    
    public int doGetStatus() {
        if (httpResponseCode == -1) {
            LOG.severe("invalid state");
            throwError("INVALID_STATE_ERR");
        }
        return httpResponseCode;
            
    }
    
    public String doGetStatusText() {
        if (httpResponseText == null) {
            LOG.severe("invalid state");
            throwError("INVALID_STATE_ERR");
        }
        return httpResponseText;
    }

    // CHECKSTYLE:OFF

    public Object jsGet_onreadystatechange() {
        return readyStateChangeListener;
    }

    public void jsSet_onreadystatechange(Object listener) {
        readyStateChangeListener = listener;
    }

    public int jsGet_UNSENT() {
        return 0;
    }

    public int jsGet_OPENED() {
        return 1;
    }

    public int jsGet_HEADERS_RECEIVED() {
        return 2;
    }

    public int jsGet_LOADING() {
        return 3;
    }

    public int jsGet_DONE() {
        return 4;
    }

    public int jsGet_readyState() {
        return readyState;
    }

    public void jsFunction_open(String method, String url, Object asyncObj, Object user, Object password) {
        Boolean async;
        if (asyncObj == Context.getUndefinedValue()) {
            async = Boolean.TRUE;
        } else {
            async = (Boolean)Context.jsToJava(asyncObj, Boolean.class);
        }
        
        if (user == Context.getUndefinedValue()) {
            user = null;
        } else {
            user = Context.jsToJava(user, String.class);
        }
        if (password == Context.getUndefinedValue()) {
            password = null;
        } else {
            password = Context.jsToJava(password, String.class);
        }
        
        doOpen(method, url, async, (String)user, (String)password);
    }

    public void jsFunction_setRequestHeader(String header, String value) {
        doSetRequestHeader(header, value);
    }

    public void jsFunction_send(Object arg) {
        if (arg == Context.getUndefinedValue()) {
            doSend(null, false);
        } else if (arg instanceof String) {
            doSend(utf8Bytes((String)arg), false);
        } else if (arg instanceof JsSimpleDomNode) {
            doSend(domToUtf8((JsSimpleDomNode)arg), true);
        } else {
            throwError("INVALID_ARG_TO_SEND");
        }
    }


    public void jsFunction_abort() {
        doAbort();
    }

    public String jsFunction_getAllResponseHeaders() {
        return doGetAllResponseHeaders();
    }

    public String jsFunction_getResponseHeader(String header) {
        return doGetResponseHeader(header);
    }

    public String jsGet_responseText() {
        return doGetResponseText();
    }

    public Object jsGet_responseXML() {
        return doGetResponseXML();
    }

    public int jsGet_status() {
        return doGetStatus();
    }

    public String jsGet_statusText() {
        return doGetStatusText();
    }
}
 

Other Apache CXF examples (source code examples)

Here is a short list of links related to this Apache CXF JsXMLHttpRequest.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.