ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java
author Jaroslav Tulach <jtulach@netbeans.org>
Wed, 27 May 2015 23:37:25 +0200
changeset 940 bdec4103bdb2
parent 838 bdc3d696dd4a
child 1020 b5d5cbb44ce0
permissions -rw-r--r--
#252258: @OnReceive attribute to specify additional request headers
     1 /**
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     5  *
     6  * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
     7  * Other names may be trademarks of their respective owners.
     8  *
     9  * The contents of this file are subject to the terms of either the GNU
    10  * General Public License Version 2 only ("GPL") or the Common
    11  * Development and Distribution License("CDDL") (collectively, the
    12  * "License"). You may not use this file except in compliance with the
    13  * License. You can obtain a copy of the License at
    14  * http://www.netbeans.org/cddl-gplv2.html
    15  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    16  * specific language governing permissions and limitations under the
    17  * License.  When distributing the software, include this License Header
    18  * Notice in each file and include the License file at
    19  * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    20  * particular file as subject to the "Classpath" exception as provided
    21  * by Oracle in the GPL Version 2 section of the License file that
    22  * accompanied this code. If applicable, add the following below the
    23  * License Header, with the fields enclosed by brackets [] replaced by
    24  * your own identifying information:
    25  * "Portions Copyrighted [year] [name of copyright owner]"
    26  *
    27  * Contributor(s):
    28  *
    29  * The Original Software is NetBeans. The Initial Developer of the Original
    30  * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    31  *
    32  * If you wish your version of this file to be governed by only the CDDL
    33  * or only the GPL Version 2, indicate your decision by adding
    34  * "[Contributor] elects to include this software in this distribution
    35  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    36  * single choice of license, a recipient has the option to distribute
    37  * your version of this file under either the CDDL, the GPL Version 2 or
    38  * to extend the choice of license to its licensees as provided above.
    39  * However, if you add GPL Version 2 code and therefore, elected the GPL
    40  * Version 2 license, then the option applies only if the new code is
    41  * made subject to such option by the copyright holder.
    42  */
    43 package org.netbeans.html.wstyrus;
    44 
    45 import java.io.IOException;
    46 import java.io.InputStream;
    47 import java.io.InputStreamReader;
    48 import java.io.OutputStream;
    49 import java.io.PushbackInputStream;
    50 import java.io.Reader;
    51 import java.net.HttpURLConnection;
    52 import java.net.URL;
    53 import java.net.URLConnection;
    54 import java.util.ArrayList;
    55 import java.util.Iterator;
    56 import java.util.List;
    57 import java.util.concurrent.Executor;
    58 import java.util.concurrent.Executors;
    59 import java.util.concurrent.ThreadFactory;
    60 import java.util.logging.Logger;
    61 import net.java.html.js.JavaScriptBody;
    62 import org.json.JSONArray;
    63 import org.json.JSONException;
    64 import org.json.JSONObject;
    65 import org.json.JSONTokener;
    66 import org.netbeans.html.json.spi.JSONCall;
    67 
    68 /** This is an implementation package - just
    69  * include its JAR on classpath and use official {@link Context} API
    70  * to access the functionality.
    71  *
    72  * @author Jaroslav Tulach
    73  */
    74 final class LoadJSON implements Runnable {
    75     private static final Logger LOG = Logger.getLogger(LoadJSON.class.getName());
    76     private static final Executor REQ = Executors.newCachedThreadPool(new ThreadFactory() {
    77         @Override
    78         public Thread newThread(Runnable runnable) {
    79             Thread thread = Executors.defaultThreadFactory().newThread(runnable);
    80             thread.setDaemon(true);
    81             return thread;
    82         }
    83     });
    84 
    85     private final JSONCall call;
    86     private final URL base;
    87 
    88 
    89     private LoadJSON(JSONCall call) {
    90         this.call = call;
    91         this.base = null;
    92     }
    93 
    94     public static void loadJSON(JSONCall call) {
    95         assert !"WebSocket".equals(call.getMethod());
    96         REQ.execute(new LoadJSON((call)));
    97     }
    98 
    99     @Override
   100     public void run() {
   101         final String url;
   102         Throwable error = null;
   103         Object json = null;
   104 
   105         if (call.isJSONP()) {
   106             url = call.composeURL("dummy");
   107         } else {
   108             url = call.composeURL(null);
   109         }
   110         try {
   111             final URL u = new URL(base, url.replace(" ", "%20"));
   112             URLConnection conn = u.openConnection();
   113             if (call.isDoOutput()) {
   114                 conn.setDoOutput(true);
   115             }
   116             String h = call.getHeaders();
   117             if (h != null) {
   118                 int pos = 0;
   119                 while (pos < h.length()) {
   120                     int tagEnd = h.indexOf(':', pos);
   121                     if (tagEnd == -1) {
   122                         break;
   123                     }
   124                     int r = h.indexOf('\r', tagEnd);
   125                     int n = h.indexOf('\n', tagEnd);
   126                     if (r == -1) {
   127                         r = h.length();
   128                     }
   129                     if (n == -1) {
   130                         n = h.length();
   131                     }
   132                     String key = h.substring(pos, tagEnd).trim();
   133                     String val = h.substring(tagEnd + 1, Math.min(r, n)).trim();
   134                     conn.setRequestProperty(key, val);;
   135                     pos = Math.max(r, n);
   136                 }
   137             }
   138             if (call.getMethod() != null && conn instanceof HttpURLConnection) {
   139                 ((HttpURLConnection) conn).setRequestMethod(call.getMethod());
   140             }
   141             if (call.isDoOutput()) {
   142                 final OutputStream os = conn.getOutputStream();
   143                 call.writeData(os);
   144                 os.flush();
   145             }
   146             final PushbackInputStream is = new PushbackInputStream(
   147                 conn.getInputStream(), 1
   148             );
   149             boolean[] arrayOrString = { false, false };
   150             detectJSONType(call.isJSONP(), is, arrayOrString);
   151             try {
   152                 if (arrayOrString[1]) {
   153                     throw new JSONException("");
   154                 }
   155                 JSONTokener tok = createTokener(is);
   156                 Object obj;
   157                 obj = arrayOrString[0] ? new JSONArray(tok) : new JSONObject(tok);
   158                 json = convertToArray(obj);
   159             } catch (JSONException ex) {
   160                 Reader r = new InputStreamReader(is, "UTF-8");
   161                 StringBuilder sb = new StringBuilder();
   162                 for (;;) {
   163                     int ch = r.read();
   164                     if (ch == -1) {
   165                         break;
   166                     }
   167                     sb.append((char)ch);
   168                 }
   169                 json = sb.toString();
   170             }
   171         } catch (IOException ex) {
   172             error = ex;
   173         } finally {
   174             if (error != null) {
   175                 call.notifyError(error);
   176             } else {
   177                 call.notifySuccess(json);
   178             }
   179         }
   180     }
   181 
   182     private static void detectJSONType(boolean skipAnything, final PushbackInputStream is, boolean[] arrayOrString) throws IOException {
   183         for (;;) {
   184             int ch = is.read();
   185             if (ch == -1) {
   186                 arrayOrString[1] = true;
   187                 break;
   188             }
   189             if (Character.isWhitespace(ch)) {
   190                 continue;
   191             }
   192 
   193             if (ch == '[') {
   194                 is.unread(ch);
   195                 arrayOrString[0] = true;
   196                 break;
   197             }
   198             if (ch == '{') {
   199                 is.unread(ch);
   200                 break;
   201             }
   202             if (!skipAnything) {
   203                 is.unread(ch);
   204                 arrayOrString[1] = true;
   205                 break;
   206             }
   207         }
   208     }
   209 
   210     private static JSONTokener createTokener(InputStream is) throws IOException {
   211         Reader r = new InputStreamReader(is, "UTF-8");
   212         try {
   213             return new JSONTokener(r);
   214         } catch (LinkageError ex) {
   215             // phones may carry outdated version of JSONTokener
   216             StringBuilder sb = new StringBuilder();
   217             for (;;) {
   218                 int ch = r.read();
   219                 if (ch == -1) {
   220                     break;
   221                 }
   222                 sb.append((char)ch);
   223             }
   224             return new JSONTokener(sb.toString());
   225         }
   226     }
   227 
   228     static Object convertToArray(Object o) throws JSONException {
   229         if (o instanceof JSONArray) {
   230             JSONArray ja = (JSONArray)o;
   231             Object[] arr = new Object[ja.length()];
   232             for (int i = 0; i < arr.length; i++) {
   233                 arr[i] = convertToArray(ja.get(i));
   234             }
   235             return arr;
   236         } else if (o instanceof JSONObject) {
   237             JSONObject obj = (JSONObject)o;
   238             Iterator it = obj.keys();
   239             List<Object> collect = new ArrayList<Object>();
   240             while (it.hasNext()) {
   241                 String key = (String)it.next();
   242                 final Object val = obj.get(key);
   243                 final Object newVal = convertToArray(val);
   244                 if (val != newVal) {
   245                     collect.add(key);
   246                     collect.add(newVal);
   247                 }
   248             }
   249             int size = collect.size();
   250             for (int i = 0; i < size; i += 2) {
   251                 obj.put((String) collect.get(i), collect.get(i + 1));
   252             }
   253             return obj;
   254         } else if (o == JSONObject.NULL) {
   255             return null;
   256         } else {
   257             return o;
   258         }
   259     }
   260 
   261     public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
   262         if (jsonObject instanceof JSONObject) {
   263             JSONObject obj = (JSONObject)jsonObject;
   264             for (int i = 0; i < props.length; i++) {
   265                 Object val = obj.opt(props[i]);
   266                 if (val == JSONObject.NULL) {
   267                     val = null;
   268                 }
   269                 values[i] = val;
   270             }
   271             return;
   272         }
   273         for (int i = 0; i < props.length; i++) {
   274             values[i] = getProperty(jsonObject, props[i]);
   275         }
   276     }
   277 
   278     @JavaScriptBody(args = {"object", "property"},
   279             body
   280             = "if (property === null) return object;\n"
   281             + "if (object === null) return null;\n"
   282             + "var p = object[property]; return p ? p : null;"
   283     )
   284     private static Object getProperty(Object object, String property) {
   285         return null;
   286     }
   287 
   288     public static Object parse(InputStream is) throws IOException {
   289         try {
   290             PushbackInputStream push = new PushbackInputStream(is, 1);
   291             boolean[] arrayOrString = { false, false };
   292             detectJSONType(false, push, arrayOrString);
   293             JSONTokener t = createTokener(push);
   294             Object obj = arrayOrString[0] ? new JSONArray(t) : new JSONObject(t);
   295             return convertToArray(obj);
   296         } catch (JSONException ex) {
   297             throw new IOException(ex);
   298         }
   299     }
   300 }