#252258: @OnReceive attribute to specify additional request headers
authorJaroslav Tulach <jtulach@netbeans.org>
Wed, 27 May 2015 23:37:25 +0200
changeset 940bdec4103bdb2
parent 939 a7bec1c91507
child 941 79a75e7f7558
#252258: @OnReceive attribute to specify additional request headers
boot-script/src/test/java/net/java/html/boot/script/ko4j/KnockoutEnvJSTest.java
json-tck/src/main/java/net/java/html/json/tests/JSONTest.java
json/src/main/java/net/java/html/json/OnReceive.java
json/src/main/java/org/netbeans/html/json/impl/JSON.java
json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java
json/src/main/java/org/netbeans/html/json/impl/PropertyBindingAccessor.java
json/src/main/java/org/netbeans/html/json/spi/JSONCall.java
json/src/main/java/org/netbeans/html/json/spi/PropertyBinding.java
json/src/main/java/org/netbeans/html/json/spi/Proto.java
json/src/test/java/net/java/html/json/ModelProcessorTest.java
json/src/test/java/net/java/html/json/ModelTest.java
ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/DynamicHTTP.java
ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/DynamicHTTP.java
ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java
ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusDynamicHTTP.java
ko4j/src/main/java/org/netbeans/html/ko4j/KOTransfer.java
ko4j/src/main/java/org/netbeans/html/ko4j/LoadJSON.java
ko4j/src/test/java/org/netbeans/html/ko4j/DynamicHTTP.java
src/main/javadoc/overview.html
     1.1 --- a/boot-script/src/test/java/net/java/html/boot/script/ko4j/KnockoutEnvJSTest.java	Wed May 27 22:40:01 2015 +0200
     1.2 +++ b/boot-script/src/test/java/net/java/html/boot/script/ko4j/KnockoutEnvJSTest.java	Wed May 27 23:37:25 2015 +0200
     1.3 @@ -83,10 +83,10 @@
     1.4      private static Class<?> browserClass;
     1.5      private static Fn.Presenter browserContext;
     1.6      private static URI baseUri;
     1.7 -    
     1.8 +
     1.9      public KnockoutEnvJSTest() {
    1.10      }
    1.11 -    
    1.12 +
    1.13      @Factory public static Object[] compatibilityTests() throws Exception {
    1.14          try {
    1.15              Class.forName("java.lang.FunctionalInterface");
    1.16 @@ -94,8 +94,8 @@
    1.17              // only runs on JDK8
    1.18              return new Object[0];
    1.19          }
    1.20 -        
    1.21 -        
    1.22 +
    1.23 +
    1.24          Class[] arr = testClasses();
    1.25          for (int i = 0; i < arr.length; i++) {
    1.26              assertEquals(
    1.27 @@ -104,9 +104,9 @@
    1.28                  "All classes loaded by the same classloader"
    1.29              );
    1.30          }
    1.31 -        
    1.32 +
    1.33          baseUri = DynamicHTTP.initServer();
    1.34 -        
    1.35 +
    1.36          final Fn.Presenter p = Scripts.createPresenter(KOCase.JS);
    1.37          InputStream is = KnockoutEnvJSTest.class.getResourceAsStream("env.nashorn.1.2-debug.js");
    1.38          p.loadScript(new InputStreamReader(is));
    1.39 @@ -116,14 +116,14 @@
    1.40              loadClass(KnockoutEnvJSTest.class).
    1.41              loadPage(baseUri.toString()).
    1.42              invoke("initialized");
    1.43 -        
    1.44 +
    1.45          Executors.newSingleThreadExecutor().submit(new Runnable() {
    1.46              @Override
    1.47              public void run() {
    1.48                  bb.showAndWait();
    1.49              }
    1.50          });
    1.51 -        
    1.52 +
    1.53          ClassLoader l = getClassLoader();
    1.54          List<Object> res = new ArrayList<Object>();
    1.55          for (int i = 0; i < arr.length; i++) {
    1.56 @@ -143,20 +143,20 @@
    1.57              }
    1.58          }
    1.59      }
    1.60 -    
    1.61 +
    1.62      private static String skipMsg(String methodName) {
    1.63          final String ver = System.getProperty("java.runtime.version"); // NOI18N
    1.64          if (
    1.65              ver.startsWith("1.8.0_25") ||
    1.66 -            ver.startsWith("1.8.0_40") 
    1.67 +            ver.startsWith("1.8.0_40")
    1.68          ) {
    1.69              return "Broken due to JDK-8047764";
    1.70          }
    1.71          if (
    1.72              !"1.8.0_05-b13".equals(ver) &&
    1.73 -            !"1.8.0_11-b12".equals(ver) 
    1.74 +            !"1.8.0_11-b12".equals(ver)
    1.75          ) {
    1.76 -            // we know that 1.8.0_05 and 1.8.0_11 are broken, 
    1.77 +            // we know that 1.8.0_05 and 1.8.0_11 are broken,
    1.78              // let's not speculate about anything else
    1.79              return null;
    1.80          }
    1.81 @@ -178,13 +178,13 @@
    1.82          }
    1.83          return browserClass.getClassLoader();
    1.84      }
    1.85 -    
    1.86 +
    1.87      public static synchronized void initialized(Class<?> browserCls) throws Exception {
    1.88          browserClass = browserCls;
    1.89          browserContext = Fn.activePresenter();
    1.90          KnockoutEnvJSTest.class.notifyAll();
    1.91      }
    1.92 -    
    1.93 +
    1.94      public static void initialized() throws Exception {
    1.95          Assert.assertSame(
    1.96              KnockoutEnvJSTest.class.getClassLoader(),
    1.97 @@ -194,7 +194,7 @@
    1.98          KnockoutEnvJSTest.initialized(KnockoutEnvJSTest.class);
    1.99          browserContext = Fn.activePresenter();
   1.100      }
   1.101 -    
   1.102 +
   1.103      @Override
   1.104      public BrwsrCtx createContext() {
   1.105          KO4J fx = new KO4J(browserContext);
   1.106 @@ -216,7 +216,7 @@
   1.107          }
   1.108          return json;
   1.109      }
   1.110 -    
   1.111 +
   1.112      @JavaScriptBody(args = {}, body = "return new Object();")
   1.113      private static native Object createJSON();
   1.114      @JavaScriptBody(args = { "json", "key", "value" }, body = "json[key] = value;")
   1.115 @@ -232,7 +232,7 @@
   1.116      private static String findBaseURL() {
   1.117          return baseUri.toString();
   1.118      }
   1.119 -    
   1.120 +
   1.121      @Override
   1.122      public URI prepareURL(String content, String mimeType, String[] parameters) {
   1.123          try {
     2.1 --- a/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java	Wed May 27 22:40:01 2015 +0200
     2.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java	Wed May 27 23:37:25 2015 +0200
     2.3 @@ -51,11 +51,11 @@
     2.4  import net.java.html.json.Models;
     2.5  import net.java.html.json.OnReceive;
     2.6  import net.java.html.json.Property;
     2.7 +import static net.java.html.json.tests.Utils.assertEquals;
     2.8 +import static net.java.html.json.tests.Utils.assertNotNull;
     2.9 +import static net.java.html.json.tests.Utils.assertNull;
    2.10 +import static net.java.html.json.tests.Utils.assertTrue;
    2.11  import org.netbeans.html.json.tck.KOTest;
    2.12 -import static net.java.html.json.tests.Utils.assertEquals;
    2.13 -import static net.java.html.json.tests.Utils.assertNull;
    2.14 -import static net.java.html.json.tests.Utils.assertNotNull;
    2.15 -import static net.java.html.json.tests.Utils.assertTrue;
    2.16  
    2.17  /** Need to verify that models produce reasonable JSON objects.
    2.18   *
    2.19 @@ -71,12 +71,12 @@
    2.20      private JSONik js;
    2.21      private Integer orig;
    2.22      private String url;
    2.23 -    
    2.24 +
    2.25      @ModelOperation static void assignFetched(JSONik m, Person p) {
    2.26          m.setFetched(p);
    2.27      }
    2.28      private BrwsrCtx ctx;
    2.29 -    
    2.30 +
    2.31      @KOTest public void toJSONInABrowser() throws Throwable {
    2.32          Person p = Models.bind(new Person(), newContext());
    2.33          p.setSex(Sex.MALE);
    2.34 @@ -89,19 +89,19 @@
    2.35          } catch (Throwable ex) {
    2.36              throw new IllegalStateException("Can't parse " + p).initCause(ex);
    2.37          }
    2.38 -        
    2.39 +
    2.40          Person p2 = Models.fromRaw(newContext(), Person.class, json);
    2.41 -        
    2.42 -        assertEquals(p2.getFirstName(), p.getFirstName(), 
    2.43 +
    2.44 +        assertEquals(p2.getFirstName(), p.getFirstName(),
    2.45              "Should be the same: " + p.getFirstName() + " != " + p2.getFirstName());
    2.46      }
    2.47 -    
    2.48 +
    2.49      @KOTest public void toJSONWithEscapeCharactersInABrowser() throws Throwable {
    2.50          Person p = Models.bind(new Person(), newContext());
    2.51          p.setSex(Sex.MALE);
    2.52          p.setFirstName("/*\n * Copyright (c) 2013");
    2.53  
    2.54 -        
    2.55 +
    2.56          final String txt = p.toString();
    2.57          Object json;
    2.58          try {
    2.59 @@ -109,19 +109,19 @@
    2.60          } catch (Throwable ex) {
    2.61              throw new IllegalStateException("Can't parse " + txt).initCause(ex);
    2.62          }
    2.63 -        
    2.64 +
    2.65          Person p2 = Models.fromRaw(newContext(), Person.class, json);
    2.66 -        
    2.67 +
    2.68          assertEquals(p2.getFirstName(), p.getFirstName(),
    2.69              "Should be the same: " + p.getFirstName() + " != " + p2.getFirstName());
    2.70      }
    2.71 -    
    2.72 +
    2.73      @KOTest public void toJSONWithDoubleSlashInABrowser() throws Throwable {
    2.74          Person p = Models.bind(new Person(), newContext());
    2.75          p.setSex(Sex.MALE);
    2.76          p.setFirstName("/*\\n * Copyright (c) 2013");
    2.77  
    2.78 -        
    2.79 +
    2.80          final String txt = p.toString();
    2.81          Object json;
    2.82          try {
    2.83 @@ -129,19 +129,19 @@
    2.84          } catch (Throwable ex) {
    2.85              throw new IllegalStateException("Can't parse " + txt).initCause(ex);
    2.86          }
    2.87 -        
    2.88 +
    2.89          Person p2 = Models.fromRaw(newContext(), Person.class, json);
    2.90 -        
    2.91 +
    2.92          assertEquals(p2.getFirstName(), p.getFirstName(),
    2.93              "Should be the same: " + p.getFirstName() + " != " + p2.getFirstName());
    2.94      }
    2.95 -    
    2.96 +
    2.97      @KOTest public void toJSONWithApostrophInABrowser() throws Throwable {
    2.98          Person p = Models.bind(new Person(), newContext());
    2.99          p.setSex(Sex.MALE);
   2.100          p.setFirstName("Jimmy 'Jim' Rambo");
   2.101  
   2.102 -        
   2.103 +
   2.104          final String txt = p.toString();
   2.105          Object json;
   2.106          try {
   2.107 @@ -149,15 +149,15 @@
   2.108          } catch (Throwable ex) {
   2.109              throw new IllegalStateException("Can't parse " + txt).initCause(ex);
   2.110          }
   2.111 -        
   2.112 +
   2.113          Person p2 = Models.fromRaw(newContext(), Person.class, json);
   2.114 -        
   2.115 +
   2.116          assertEquals(p2.getFirstName(), p.getFirstName(),
   2.117              "Should be the same: " + p.getFirstName() + " != " + p2.getFirstName());
   2.118      }
   2.119  
   2.120      private static BrwsrCtx onCallback;
   2.121 -    
   2.122 +
   2.123      @OnReceive(url="{url}")
   2.124      static void fetch(JSONik model, Person p) {
   2.125          model.setFetched(p);
   2.126 @@ -176,12 +176,12 @@
   2.127          model.setFetched(p[0]);
   2.128          onCallback = BrwsrCtx.findDefault(model.getClass());
   2.129      }
   2.130 -    
   2.131 +
   2.132      static void setMessage(JSONik m, Exception t) {
   2.133          assertNotNull(t, "Exception provided");
   2.134          m.setFetchedResponse("Exception");
   2.135      }
   2.136 -    
   2.137 +
   2.138      @OnReceive(url="{url}")
   2.139      static void fetchPeople(JSONik model, People p) {
   2.140          final int size = p.getInfo().size();
   2.141 @@ -199,7 +199,7 @@
   2.142          }
   2.143          model.setFetchedCount(sum);
   2.144      }
   2.145 -    
   2.146 +
   2.147      @KOTest public void loadAndParseJSON() throws InterruptedException {
   2.148          if (js == null) {
   2.149              url = Utils.prepareURL(
   2.150 @@ -212,18 +212,18 @@
   2.151              js.setFetched(null);
   2.152              js.fetch(url);
   2.153          }
   2.154 -    
   2.155 +
   2.156          Person p = js.getFetched();
   2.157          if (p == null) {
   2.158              throw new InterruptedException();
   2.159          }
   2.160 -        
   2.161 +
   2.162          assertEquals("Sitar", p.getFirstName(), "Expecting Sitar: " + p.getFirstName());
   2.163          assertEquals(Sex.MALE, p.getSex(), "Expecting MALE: " + p.getSex());
   2.164 -        
   2.165 +
   2.166          assertEquals(ctx, onCallback, "Context is the same");
   2.167      }
   2.168 -    
   2.169 +
   2.170      @KOTest public void loadAndParsePlainText() throws Exception {
   2.171          if (js == null) {
   2.172              url = Utils.prepareURL(
   2.173 @@ -236,23 +236,23 @@
   2.174              js.setFetched(null);
   2.175              js.fetchPlain(url);
   2.176          }
   2.177 -    
   2.178 +
   2.179          String s = js.getFetchedResponse();
   2.180          if (s == null) {
   2.181              throw new InterruptedException();
   2.182          }
   2.183 -        
   2.184 +
   2.185          assertTrue(s.contains("Sitar"), "The text contains Sitar value: " + s);
   2.186          assertTrue(s.contains("MALE"), "The text contains MALE value: " + s);
   2.187 -        
   2.188 +
   2.189          Person p = Models.parse(ctx, Person.class, new ByteArrayInputStream(s.getBytes()));
   2.190 -        
   2.191 +
   2.192          assertEquals("Sitar", p.getFirstName(), "Expecting Sitar: " + p.getFirstName());
   2.193          assertEquals(Sex.MALE, p.getSex(), "Expecting MALE: " + p.getSex());
   2.194 -        
   2.195 +
   2.196          assertEquals(ctx, onCallback, "Same context");
   2.197      }
   2.198 -    
   2.199 +
   2.200      @KOTest public void loadAndParsePlainTextOnArray() throws Exception {
   2.201          if (js == null) {
   2.202              url = Utils.prepareURL(
   2.203 @@ -265,60 +265,60 @@
   2.204              js.setFetched(null);
   2.205              js.fetchPlain(url);
   2.206          }
   2.207 -    
   2.208 +
   2.209          String s = js.getFetchedResponse();
   2.210          if (s == null) {
   2.211              throw new InterruptedException();
   2.212          }
   2.213 -        
   2.214 +
   2.215          assertTrue(s.contains("Sitar"), "The text contains Sitar value: " + s);
   2.216          assertTrue(s.contains("MALE"), "The text contains MALE value: " + s);
   2.217 -        
   2.218 +
   2.219          Person p = Models.parse(ctx, Person.class, new ByteArrayInputStream(s.getBytes()));
   2.220 -        
   2.221 +
   2.222          assertEquals("Sitar", p.getFirstName(), "Expecting Sitar: " + p.getFirstName());
   2.223          assertEquals(Sex.MALE, p.getSex(), "Expecting MALE: " + p.getSex());
   2.224 -        
   2.225 +
   2.226          assertEquals(ctx, onCallback, "Same context");
   2.227      }
   2.228 -    
   2.229 +
   2.230      @OnReceive(url="{url}?callme={me}", jsonp = "me")
   2.231      static void fetchViaJSONP(JSONik model, Person p) {
   2.232          model.setFetched(p);
   2.233      }
   2.234 -    
   2.235 +
   2.236      @KOTest public void loadAndParseJSONP() throws InterruptedException, Exception {
   2.237          if (js == null) {
   2.238              url = Utils.prepareURL(
   2.239 -                JSONTest.class, "$0({'firstName': 'Mitar', 'sex': 'MALE'})", 
   2.240 +                JSONTest.class, "$0({'firstName': 'Mitar', 'sex': 'MALE'})",
   2.241                  "application/javascript",
   2.242                  "callme"
   2.243              );
   2.244              orig = scriptElements();
   2.245              assertTrue(orig > 0, "There should be some scripts on the page");
   2.246 -            
   2.247 +
   2.248              js = Models.bind(new JSONik(), newContext());
   2.249              js.applyBindings();
   2.250  
   2.251              js.setFetched(null);
   2.252              js.fetchViaJSONP(url);
   2.253          }
   2.254 -    
   2.255 +
   2.256          Person p = js.getFetched();
   2.257          if (p == null) {
   2.258              throw new InterruptedException();
   2.259          }
   2.260 -        
   2.261 +
   2.262          assertEquals("Mitar", p.getFirstName(), "Unexpected: " + p.getFirstName());
   2.263          assertEquals(Sex.MALE, p.getSex(), "Expecting MALE: " + p.getSex());
   2.264 -        
   2.265 +
   2.266          int now = scriptElements();
   2.267 -        
   2.268 +
   2.269          assertEquals(orig, now, "The set of elements is unchanged. Delta: " + (now - orig));
   2.270      }
   2.271 -    
   2.272 -    
   2.273 -    
   2.274 +
   2.275 +
   2.276 +
   2.277      @OnReceive(url="{url}", method = "PUT", data = Person.class)
   2.278      static void putPerson(JSONik model, String reply) {
   2.279          model.setFetchedCount(1);
   2.280 @@ -328,13 +328,13 @@
   2.281      @KOTest public void putPeopleUsesRightMethod() throws InterruptedException, Exception {
   2.282          if (js == null) {
   2.283              url = Utils.prepareURL(
   2.284 -                JSONTest.class, "$0\n$1", 
   2.285 +                JSONTest.class, "$0\n$1",
   2.286                  "text/plain",
   2.287                  "http.method", "http.requestBody"
   2.288              );
   2.289              orig = scriptElements();
   2.290              assertTrue(orig > 0, "There should be some scripts on the page");
   2.291 -            
   2.292 +
   2.293              js = Models.bind(new JSONik(), newContext());
   2.294              js.applyBindings();
   2.295  
   2.296 @@ -342,7 +342,7 @@
   2.297              p.setFirstName("Jarda");
   2.298              js.putPerson(url, p);
   2.299          }
   2.300 -    
   2.301 +
   2.302          int cnt = js.getFetchedCount();
   2.303          if (cnt == 0) {
   2.304              throw new InterruptedException();
   2.305 @@ -356,186 +356,231 @@
   2.306          } else {
   2.307              msg = res;
   2.308          }
   2.309 -        
   2.310 +
   2.311          assertEquals("PUT", res, "Server was queried with PUT method: " + js.getFetchedResponse());
   2.312 -        
   2.313 +
   2.314          assertTrue(msg.contains("Jarda"), "Data transferred to the server: " + msg);
   2.315      }
   2.316 -    
   2.317 +
   2.318      private static int scriptElements() throws Exception {
   2.319          return ((Number)Utils.executeScript(
   2.320 -            JSONTest.class, 
   2.321 +            JSONTest.class,
   2.322              "return window.document.getElementsByTagName('script').length;")).intValue();
   2.323      }
   2.324  
   2.325      private static Object parseJSON(String s) throws Exception {
   2.326          return Utils.executeScript(
   2.327 -            JSONTest.class, 
   2.328 +            JSONTest.class,
   2.329              "return window.JSON.parse(arguments[0]);", s);
   2.330      }
   2.331 -    
   2.332 +
   2.333      @KOTest public void loadAndParseJSONSentToArray() throws InterruptedException {
   2.334          if (js == null) {
   2.335              url = Utils.prepareURL(
   2.336 -                JSONTest.class, "{'firstName': 'Sitar', 'sex': 'MALE'}", 
   2.337 +                JSONTest.class, "{'firstName': 'Sitar', 'sex': 'MALE'}",
   2.338                  "application/json"
   2.339              );
   2.340 -            
   2.341 +
   2.342              js = Models.bind(new JSONik(), newContext());
   2.343              js.applyBindings();
   2.344  
   2.345              js.setFetched(null);
   2.346              js.fetchArray(url);
   2.347          }
   2.348 -        
   2.349 +
   2.350          Person p = js.getFetched();
   2.351          if (p == null) {
   2.352              throw new InterruptedException();
   2.353          }
   2.354 -        
   2.355 +
   2.356          assertEquals("Sitar", p.getFirstName(), "Expecting Sitar: " + p.getFirstName());
   2.357          assertEquals(Sex.MALE, p.getSex(), "Expecting MALE: " + p.getSex());
   2.358      }
   2.359 -    
   2.360 +
   2.361      @KOTest public void loadAndParseJSONArraySingle() throws InterruptedException {
   2.362          if (js == null) {
   2.363              url = Utils.prepareURL(
   2.364 -                JSONTest.class, "[{'firstName': 'Gitar', 'sex': 'FEMALE'}]", 
   2.365 +                JSONTest.class, "[{'firstName': 'Gitar', 'sex': 'FEMALE'}]",
   2.366                  "application/json"
   2.367              );
   2.368              js = Models.bind(new JSONik(), newContext());
   2.369              js.applyBindings();
   2.370 -        
   2.371 +
   2.372              js.setFetched(null);
   2.373              js.fetch(url);
   2.374          }
   2.375 -        
   2.376 +
   2.377          Person p = js.getFetched();
   2.378          if (p == null) {
   2.379              throw new InterruptedException();
   2.380          }
   2.381 -        
   2.382 +
   2.383          assertEquals("Gitar", p.getFirstName(), "Expecting Gitar: " + p.getFirstName());
   2.384          assertEquals(Sex.FEMALE, p.getSex(), "Expecting FEMALE: " + p.getSex());
   2.385      }
   2.386 -    
   2.387 +
   2.388      @KOTest public void loadAndParseArrayInPeople() throws InterruptedException {
   2.389          if (js == null) {
   2.390              url = Utils.prepareURL(
   2.391 -                JSONTest.class, "{'info':[{'firstName': 'Gitar', 'sex': 'FEMALE'}]}", 
   2.392 +                JSONTest.class, "{'info':[{'firstName': 'Gitar', 'sex': 'FEMALE'}]}",
   2.393                  "application/json"
   2.394              );
   2.395              js = Models.bind(new JSONik(), newContext());
   2.396              js.applyBindings();
   2.397 -        
   2.398 +
   2.399              js.fetchPeople(url);
   2.400          }
   2.401 -        
   2.402 +
   2.403          if (0 == js.getFetchedCount()) {
   2.404              throw new InterruptedException();
   2.405          }
   2.406  
   2.407          assertEquals(js.getFetchedCount(), 1, "One person loaded: " + js.getFetchedCount());
   2.408 -        
   2.409 +
   2.410          Person p = js.getFetched();
   2.411 -        
   2.412 +
   2.413          assertNotNull(p, "We should get our person back: " + p);
   2.414          assertEquals("Gitar", p.getFirstName(), "Expecting Gitar: " + p.getFirstName());
   2.415          assertEquals(Sex.FEMALE, p.getSex(), "Expecting FEMALE: " + p.getSex());
   2.416      }
   2.417 -    
   2.418 +
   2.419 +    @KOTest public void loadAndParseArrayInPeopleWithHeaders() throws InterruptedException {
   2.420 +        if (js == null) {
   2.421 +            url = Utils.prepareURL(
   2.422 +                JSONTest.class, "{'info':[{'firstName': '$0$1$2$3$4', 'sex': 'FEMALE'}]}",
   2.423 +                "application/json",
   2.424 +                "http.header.Easy",
   2.425 +                "http.header.H-a!r*d^e.r",
   2.426 +                "http.header.Repeat-ed",
   2.427 +                "http.header.Repeat*ed",
   2.428 +                "http.header.Same-URL"
   2.429 +            );
   2.430 +            js = Models.bind(new JSONik(), newContext());
   2.431 +            js.applyBindings();
   2.432 +
   2.433 +            js.fetchPeopleWithHeaders(url, "easy", "harder", "rep");
   2.434 +        }
   2.435 +
   2.436 +        if (0 == js.getFetchedCount()) {
   2.437 +            throw new InterruptedException();
   2.438 +        }
   2.439 +
   2.440 +        assertEquals(js.getFetchedCount(), 1, "One person loaded: " + js.getFetchedCount());
   2.441 +
   2.442 +        Person p = js.getFetched();
   2.443 +
   2.444 +        assertNotNull(p, "We should get our person back: " + p);
   2.445 +        assertEquals("easyharderreprep" + url, p.getFirstName(), "Expecting header mess: " + p.getFirstName());
   2.446 +        assertEquals(Sex.FEMALE, p.getSex(), "Expecting FEMALE: " + p.getSex());
   2.447 +    }
   2.448 +
   2.449 +    @OnReceive(url="{url}", headers={
   2.450 +        "Easy: {easy}",
   2.451 +        "H-a!r*d^e.r: {harder}",
   2.452 +        "Repeat-ed: {rep}",
   2.453 +        "Repeat*ed: {rep}",
   2.454 +        "Same-URL: {url}"
   2.455 +    })
   2.456 +    static void fetchPeopleWithHeaders(JSONik model, People p) {
   2.457 +        final int size = p.getInfo().size();
   2.458 +        if (size > 0) {
   2.459 +            model.setFetched(p.getInfo().get(0));
   2.460 +        }
   2.461 +        model.setFetchedCount(size);
   2.462 +    }
   2.463 +
   2.464      @KOTest public void loadAndParseArrayOfIntegers() throws InterruptedException {
   2.465          if (js == null) {
   2.466              url = Utils.prepareURL(
   2.467 -                JSONTest.class, "{'age':[1, 2, 3]}", 
   2.468 +                JSONTest.class, "{'age':[1, 2, 3]}",
   2.469                  "application/json"
   2.470              );
   2.471              js = Models.bind(new JSONik(), newContext());
   2.472              js.applyBindings();
   2.473 -        
   2.474 +
   2.475              js.fetchPeopleAge(url);
   2.476          }
   2.477 -        
   2.478 +
   2.479          if (0 == js.getFetchedCount()) {
   2.480              throw new InterruptedException();
   2.481          }
   2.482  
   2.483          assertEquals(js.getFetchedCount(), 6, "1 + 2 + 3 is " + js.getFetchedCount());
   2.484      }
   2.485 -    
   2.486 +
   2.487      @OnReceive(url="{url}")
   2.488      static void fetchPeopleSex(JSONik model, People p) {
   2.489          model.setFetchedCount(1);
   2.490          model.getFetchedSex().addAll(p.getSex());
   2.491      }
   2.492 -    
   2.493 +
   2.494      @KOTest public void loadAndParseArrayOfEnums() throws InterruptedException {
   2.495          if (js == null) {
   2.496              url = Utils.prepareURL(
   2.497 -                JSONTest.class, "{'sex':['FEMALE', 'MALE', 'MALE']}", 
   2.498 +                JSONTest.class, "{'sex':['FEMALE', 'MALE', 'MALE']}",
   2.499                  "application/json"
   2.500              );
   2.501              js = Models.bind(new JSONik(), newContext());
   2.502              js.applyBindings();
   2.503 -        
   2.504 +
   2.505              js.fetchPeopleSex(url);
   2.506          }
   2.507 -        
   2.508 +
   2.509          if (0 == js.getFetchedCount()) {
   2.510              throw new InterruptedException();
   2.511          }
   2.512  
   2.513          assertEquals(js.getFetchedCount(), 1, "Loaded");
   2.514 -        
   2.515 +
   2.516          assertEquals(js.getFetchedSex().size(), 3, "Three values " + js.getFetchedSex());
   2.517          assertEquals(js.getFetchedSex().get(0), Sex.FEMALE, "Female first " + js.getFetchedSex());
   2.518          assertEquals(js.getFetchedSex().get(1), Sex.MALE, "male 2nd " + js.getFetchedSex());
   2.519          assertEquals(js.getFetchedSex().get(2), Sex.MALE, "male 3rd " + js.getFetchedSex());
   2.520      }
   2.521 -    
   2.522 +
   2.523      @KOTest public void loadAndParseJSONArray() throws InterruptedException {
   2.524          if (js == null) {
   2.525              url = Utils.prepareURL(
   2.526                  JSONTest.class, "[{'firstName': 'Gitar', 'sex': 'FEMALE'},"
   2.527                  + "{'firstName': 'Peter', 'sex': 'MALE'}"
   2.528 -                + "]", 
   2.529 +                + "]",
   2.530                  "application/json"
   2.531              );
   2.532              js = Models.bind(new JSONik(), newContext());
   2.533              js.applyBindings();
   2.534              js.setFetched(null);
   2.535 -            
   2.536 +
   2.537              js.fetchArray(url);
   2.538          }
   2.539 -        
   2.540 -        
   2.541 +
   2.542 +
   2.543          Person p = js.getFetched();
   2.544          if (p == null) {
   2.545              throw new InterruptedException();
   2.546          }
   2.547 -        
   2.548 +
   2.549          assertEquals(js.getFetchedCount(), 2, "We got two values: " + js.getFetchedCount());
   2.550          assertEquals("Gitar", p.getFirstName(), "Expecting Gitar: " + p.getFirstName());
   2.551          assertEquals(Sex.FEMALE, p.getSex(), "Expecting FEMALE: " + p.getSex());
   2.552      }
   2.553 -    
   2.554 +
   2.555      @KOTest public void loadError() throws InterruptedException {
   2.556          if (js == null) {
   2.557              js = Models.bind(new JSONik(), newContext());
   2.558              js.applyBindings();
   2.559              js.setFetched(null);
   2.560 -            
   2.561 +
   2.562              js.fetchArray("http://127.0.0.1:54253/does/not/exist.txt");
   2.563          }
   2.564 -        
   2.565 -        
   2.566 +
   2.567 +
   2.568          if (js.getFetchedResponse() == null) {
   2.569              throw new InterruptedException();
   2.570          }
   2.571  
   2.572          assertEquals("Exception", js.getFetchedResponse(), "Response " + js.getFetchedResponse());
   2.573      }
   2.574 -    
   2.575 +
   2.576      @Model(className = "NameAndValue", properties = {
   2.577          @Property(name = "name", type = String.class),
   2.578          @Property(name = "value", type = long.class),
   2.579 @@ -543,7 +588,7 @@
   2.580      })
   2.581      static class NandV {
   2.582      }
   2.583 -    
   2.584 +
   2.585      @KOTest public void parseNullNumber() throws Exception {
   2.586          String txt = "{ \"name\":\"M\" }";
   2.587          ByteArrayInputStream is = new ByteArrayInputStream(txt.getBytes("UTF-8"));
   2.588 @@ -566,12 +611,12 @@
   2.589              err = null;
   2.590              prev = null;
   2.591          }
   2.592 -        
   2.593 +
   2.594          String str = "{ \"sex\" : \"unknown\" }";
   2.595          ByteArrayInputStream is = new ByteArrayInputStream(str.getBytes("UTF-8"));
   2.596          Person p = Models.parse(newContext(), Person.class, is);
   2.597          assertNull(p.getSex(), "Wrong sex means null, but was: " + p.getSex());
   2.598 -        
   2.599 +
   2.600          if (err != null) {
   2.601              assertTrue(err.toString().contains("unknown") && err.toString().contains("Sex"), "Expecting error: " + err.toString());
   2.602          }
   2.603 @@ -584,9 +629,9 @@
   2.604          }
   2.605      }
   2.606  
   2.607 -    
   2.608 +
   2.609      private static BrwsrCtx newContext() {
   2.610          return Utils.newContext(JSONTest.class);
   2.611      }
   2.612 -    
   2.613 +
   2.614  }
     3.1 --- a/json/src/main/java/net/java/html/json/OnReceive.java	Wed May 27 22:40:01 2015 +0200
     3.2 +++ b/json/src/main/java/net/java/html/json/OnReceive.java	Wed May 27 23:37:25 2015 +0200
     3.3 @@ -48,7 +48,7 @@
     3.4  import java.lang.annotation.Target;
     3.5  
     3.6  /** Static methods in classes annotated by {@link Model}
     3.7 - * can be marked by this annotation to establish a 
     3.8 + * can be marked by this annotation to establish a
     3.9   * <a href="http://en.wikipedia.org/wiki/JSON">JSON</a>
    3.10   * communication point. The first argument should be the
    3.11   * associated {@link Model} class. The second argument can
    3.12 @@ -57,7 +57,7 @@
    3.13   * {@link java.util.List} of such classes.
    3.14   * The associated model class then gets new method to invoke a network
    3.15   * connection asynchronously. Example follows:
    3.16 - * 
    3.17 + *
    3.18   * <pre>
    3.19   * {@link Model @Model}(className="MyModel", properties={
    3.20   *   {@link Property @Property}(name = "people", type=Person.class, array=true)
    3.21 @@ -73,18 +73,18 @@
    3.22   *       return firstName + " " + lastName;
    3.23   *     }
    3.24   *   }
    3.25 - * 
    3.26 + *
    3.27   *   {@link OnReceive @OnReceive}(url = "{protocol}://your.server.com/person/{name}")
    3.28   *   static void getANewPerson(MyModel m, Person p) {
    3.29   *     System.out.println("Adding " + p.getFullName() + '!');
    3.30   *     m.getPeople().add(p);
    3.31   *   }
    3.32 - * 
    3.33 + *
    3.34   *   // the above will generate method <code>getANewPerson</code> in class <code>MyModel</code>.
    3.35   *   // with <code>protocol</code> and <code>name</code> arguments
    3.36   *   // which asynchronously contacts the server and in case of success calls
    3.37   *   // your {@link OnReceive @OnReceive} with parsed in data
    3.38 - * 
    3.39 + *
    3.40   *   {@link Function @Function}
    3.41   *   static void requestSmith(MyModel m) {
    3.42   *     m.getANewPerson("http", "Smith");
    3.43 @@ -92,7 +92,7 @@
    3.44   * }
    3.45   * </pre>
    3.46   * When the server returns <code>{ "firstName" : "John", "lastName" : "Smith" }</code>
    3.47 - * the system will print a message <em>Adding John Smith!</em>. It is not 
    3.48 + * the system will print a message <em>Adding John Smith!</em>. It is not
    3.49   * necessary to fully describe the server message - enumerate only the fields
    3.50   * in the response you are interested in. The others will be discarded. So,
    3.51   * if the server <code>{ "firstName" : "John", "lastName" : "Smith", "age" : 33 }</code>
    3.52 @@ -105,7 +105,7 @@
    3.53   * <p>
    3.54   * Visit an <a target="_blank" href="http://dew.apidesign.org/dew/#7138581">on-line demo</a>
    3.55   * to see REST access via {@link OnReceive} annotation.
    3.56 - * 
    3.57 + *
    3.58   * @author Jaroslav Tulach
    3.59   * @since 0.3
    3.60   */
    3.61 @@ -113,59 +113,90 @@
    3.62  @Target(ElementType.METHOD)
    3.63  public @interface OnReceive {
    3.64      /** The URL to connect to. Can contain variable names surrounded by '{' and '}'.
    3.65 -     * Those parameters will then become variables of the associated method.
    3.66 -     * 
    3.67 +     * Those names will then become parameters of the associated method.
    3.68 +     *
    3.69       * @return the (possibly parametrized) url to connect to
    3.70       */
    3.71      String url();
    3.72 -    
    3.73 +
    3.74 +    /** Specifies HTTP request headers. Array of header lines
    3.75 +     * can contain variable names surrounded by '{' and '}'.
    3.76 +     * Those names will then become parameters of the associated method
    3.77 +     * (in addition to those added by {@link #url()} specification)
    3.78 +     * and can only be used with plain JSON(P) requests.
    3.79 +     * Headers are currently <b>not</b> supported by the
    3.80 +     * <a href="doc-files/websockets.html">WebSockets protocol</a>.
    3.81 +     * A sample follows. If you want to transmit <b>X-Birthday</b> header,
    3.82 +     * you can do it like this:
    3.83 +     * <pre>
    3.84 +     * {@code @}{@link OnReceive}(url="http://your.server.org", headers = {
    3.85 +     *   "X-Birthday: {dayOfBirth}"
    3.86 +     * })
    3.87 +     * <b>static void</b> knowingTheBirth({@link Model YourModel} model) {
    3.88 +     *   // handle the reply
    3.89 +     * }</pre>
    3.90 +     * a method <b>knowingTheBirth</b> is generated in
    3.91 +     * <code>YourModel</code> class with the <code>dayOfBirth</code> argument
    3.92 +     * which can be called like this:
    3.93 +     * <pre>
    3.94 +     * yourModel.knowingTheBirth("10. 12. 1973");
    3.95 +     * </pre>
    3.96 +     *
    3.97 +     * @return array of header lines - each line should be plain text with
    3.98 +     *   a header name, followed by ":" and value usually specified as
    3.99 +     *   '{' and '}' surrounded variable. The line shouldn't contain
   3.100 +     *   newline or other control characters
   3.101 +     * @since 1.2
   3.102 +     */
   3.103 +    String[] headers() default {};
   3.104 +
   3.105      /** Support for <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a> requires
   3.106       * a callback from the server generated page to a function defined in the
   3.107       * system. The name of such function is usually specified as a property
   3.108       * (of possibly different names). By defining the <code>jsonp</code> attribute
   3.109 -     * one turns on the <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a> 
   3.110 +     * one turns on the <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>
   3.111       * transmission and specifies the name of the property. The property should
   3.112       * also be used in the {@link #url()} attribute on appropriate place.
   3.113 -     * 
   3.114 +     *
   3.115       * @return name of a property to carry the name of <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a>
   3.116       *    callback function.
   3.117       */
   3.118      String jsonp() default "";
   3.119 -    
   3.120 +
   3.121      /** The model class to be send to the server as JSON data.
   3.122       * By default no data are sent. However certain {@link #method() transport methods}
   3.123 -     * (like <code>"PUT"</code> and <code>"POST"</code>) require the 
   3.124 +     * (like <code>"PUT"</code> and <code>"POST"</code>) require the
   3.125       * data to be specified.
   3.126 -     * 
   3.127 +     *
   3.128       * @return name of a class generated using {@link Model @Model} annotation
   3.129       * @since 0.3
   3.130       */
   3.131      Class<?> data() default Object.class;
   3.132 -    
   3.133 +
   3.134      /** The HTTP transfer method to use. Defaults to <code>"GET"</code>.
   3.135 -     * Other typical methods include <code>"HEAD"</code>, 
   3.136 +     * Other typical methods include <code>"HEAD"</code>,
   3.137       * <code>"DELETE"</code>, <code>"POST"</code>, <code>"PUT"</code>.
   3.138       * The last two mentioned methods require {@link #data()} to be specified.
   3.139       * <p>
   3.140 -     * When {@link #jsonp() JSONP} transport is requested, the method 
   3.141 +     * When {@link #jsonp() JSONP} transport is requested, the method
   3.142       * has to be <code>"GET"</code>.
   3.143       * <p>
   3.144       * Since version 0.5 one can specify "<a href="doc-files/websockets.html">WebSocket</a>"
   3.145       * as the communication method.
   3.146 -     * 
   3.147 +     *
   3.148       * @return name of the HTTP transfer method
   3.149       * @since 0.3
   3.150       */
   3.151      String method() default "GET";
   3.152 -    
   3.153 -    /** Name of a method in this class which should be called in case of 
   3.154 -     * an error. The method has to be non-private and take one model and 
   3.155 -     * one {@link Exception} 
   3.156 +
   3.157 +    /** Name of a method in this class which should be called in case of
   3.158 +     * an error. The method has to be non-private and take one model and
   3.159 +     * one {@link Exception}
   3.160       * parameter. If this method is not specified, the exception is just
   3.161       * printed to console.
   3.162 -     * 
   3.163 +     *
   3.164       * @return name of method in this class
   3.165       * @since 0.5
   3.166       */
   3.167 -    public String onError() default "";    
   3.168 +    public String onError() default "";
   3.169  }
     4.1 --- a/json/src/main/java/org/netbeans/html/json/impl/JSON.java	Wed May 27 22:40:01 2015 +0200
     4.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/JSON.java	Wed May 27 23:37:25 2015 +0200
     4.3 @@ -71,24 +71,24 @@
     4.4          return t == null ? EmptyTech.EMPTY : t;
     4.5      }
     4.6  
     4.7 -    static Transfer findTransfer(BrwsrCtx c) {
     4.8 +    public static Transfer findTransfer(BrwsrCtx c) {
     4.9          Transfer t = Contexts.find(c, Transfer.class);
    4.10          return t == null ? EmptyTech.EMPTY : t;
    4.11      }
    4.12  
    4.13 -    static WSTransfer<?> findWSTransfer(BrwsrCtx c) {
    4.14 +    public static WSTransfer<?> findWSTransfer(BrwsrCtx c) {
    4.15          WSTransfer<?> t = Contexts.find(c, WSTransfer.class);
    4.16          return t == null ? EmptyTech.EMPTY : t;
    4.17      }
    4.18 -    
    4.19 +
    4.20      public static void extract(BrwsrCtx c, Object value, String[] props, Object[] values) {
    4.21          Transfer t = findTransfer(c);
    4.22          t.extract(value, props, values);
    4.23      }
    4.24 -    
    4.25 +
    4.26      private static Object getProperty(BrwsrCtx c, Object obj, String prop) {
    4.27          if (prop == null) return obj;
    4.28 -        
    4.29 +
    4.30          String[] arr = { prop };
    4.31          Object[] val = { null };
    4.32          extract(c, obj, arr, val);
    4.33 @@ -146,15 +146,15 @@
    4.34          Object o = t.toModel(aClass, data);
    4.35          return aClass.cast(o);
    4.36      }
    4.37 -    
    4.38 +
    4.39      public static boolean isSame(int a, int b) {
    4.40          return a == b;
    4.41      }
    4.42 -    
    4.43 +
    4.44      public static boolean isSame(double a, double b) {
    4.45          return a == b;
    4.46      }
    4.47 -    
    4.48 +
    4.49      public static boolean isSame(Object a, Object b) {
    4.50          if (a == b) {
    4.51              return true;
    4.52 @@ -164,7 +164,7 @@
    4.53          }
    4.54          return a.equals(b);
    4.55      }
    4.56 -    
    4.57 +
    4.58      public static int hashPlus(Object o, int h) {
    4.59          return o == null ? h : h ^ o.hashCode();
    4.60      }
    4.61 @@ -193,7 +193,7 @@
    4.62          }
    4.63          if (Byte.class == type) {
    4.64              val = val instanceof Number ? ((Number)val).byteValue() : 0;
    4.65 -        }        
    4.66 +        }
    4.67          if (Double.class == type) {
    4.68              val = val instanceof Number ? ((Number)val).doubleValue() : Double.NaN;
    4.69          }
    4.70 @@ -202,11 +202,11 @@
    4.71          }
    4.72          return type.cast(val);
    4.73      }
    4.74 -    
    4.75 +
    4.76      static boolean isNumeric(Object val) {
    4.77          return ((val instanceof Integer) || (val instanceof Long) || (val instanceof Short) || (val instanceof Byte));
    4.78      }
    4.79 -    
    4.80 +
    4.81      public static String stringValue(Object val) {
    4.82          if (val instanceof Boolean) {
    4.83              return ((Boolean)val ? "true" : "false");
    4.84 @@ -222,7 +222,7 @@
    4.85          }
    4.86          return (String)val;
    4.87      }
    4.88 -    
    4.89 +
    4.90      public static Number numberValue(Object val) {
    4.91          if (val instanceof String) {
    4.92              try {
    4.93 @@ -250,7 +250,7 @@
    4.94          }
    4.95          return (Character)val;
    4.96      }
    4.97 -    
    4.98 +
    4.99      public static Boolean boolValue(Object val) {
   4.100          if (val instanceof String) {
   4.101              return Boolean.parseBoolean((String)val);
   4.102 @@ -258,10 +258,10 @@
   4.103          if (val instanceof Number) {
   4.104              return numberValue(val).doubleValue() != 0.0;
   4.105          }
   4.106 -    
   4.107 +
   4.108          return Boolean.TRUE.equals(val);
   4.109      }
   4.110 -    
   4.111 +
   4.112      public static Object find(Object object, Bindings model) {
   4.113          if (object == null) {
   4.114              return null;
   4.115 @@ -288,7 +288,7 @@
   4.116          final Bindings b = PropertyBindingAccessor.getBindings(proto, true);
   4.117          return b == null ? null : b.koData();
   4.118      }
   4.119 -    
   4.120 +
   4.121      private static Proto findProto(Object object) {
   4.122          Proto.Type<?> type = JSON.findType(object.getClass());
   4.123          if (type == null) {
   4.124 @@ -301,7 +301,7 @@
   4.125      public static Object find(Object object) {
   4.126          return find(object, null);
   4.127      }
   4.128 -    
   4.129 +
   4.130      public static void applyBindings(Object object, String id) {
   4.131          final Proto proto = findProto(object);
   4.132          if (proto == null) {
   4.133 @@ -309,31 +309,17 @@
   4.134          }
   4.135          proto.applyBindings(id);
   4.136      }
   4.137 -    
   4.138 -    public static void loadJSON(
   4.139 -        BrwsrCtx c, RcvrJSON callback,
   4.140 -        String urlBefore, String urlAfter, String method,
   4.141 -        Object data
   4.142 -    ) {
   4.143 -        JSONCall call = PropertyBindingAccessor.createCall(c, callback, urlBefore, urlAfter, method, data);
   4.144 -        Transfer t = findTransfer(c);
   4.145 -        t.loadJSON(call);
   4.146 -    }
   4.147 -    public static WS openWS(
   4.148 -        BrwsrCtx c, RcvrJSON r, String url, Object data
   4.149 -    ) {
   4.150 -        WS ws = WSImpl.create(findWSTransfer(c), r);
   4.151 -        ws.send(c, url, data);
   4.152 -        return ws;
   4.153 -    }
   4.154 -    
   4.155 +
   4.156      public static abstract class WS {
   4.157          private WS() {
   4.158          }
   4.159 -        
   4.160 -        public abstract void send(BrwsrCtx ctx, String url, Object model);
   4.161 +
   4.162 +        public abstract void send(BrwsrCtx ctx, String headers, String url, Object model);
   4.163 +        public static <Socket> WS create(WSTransfer<Socket> t, RcvrJSON r) {
   4.164 +            return new WSImpl<Socket>(t, r);
   4.165 +        }
   4.166      }
   4.167 -    
   4.168 +
   4.169      private static final class WSImpl<Socket> extends WS {
   4.170  
   4.171          private final WSTransfer<Socket> trans;
   4.172 @@ -345,19 +331,15 @@
   4.173              this.trans = trans;
   4.174              this.rcvr = rcvr;
   4.175          }
   4.176 -        
   4.177 -        static <Socket> WS create(WSTransfer<Socket> t, RcvrJSON r) {
   4.178 -            return new WSImpl<Socket>(t, r);
   4.179 -        }
   4.180  
   4.181          @Override
   4.182 -        public void send(BrwsrCtx ctx, String url, Object data) {
   4.183 +        public void send(BrwsrCtx ctx, String headers, String url, Object data) {
   4.184              Socket s = socket;
   4.185              if (s == null) {
   4.186                  if (data != null) {
   4.187                      throw new IllegalStateException("WebSocket is not opened yet. Call with null data, was: " + data);
   4.188                  }
   4.189 -                JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, url, null, "WebSocket", null);
   4.190 +                JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, headers, url, null, "WebSocket", null);
   4.191                  socket = trans.open(url, call);
   4.192                  prevURL = url;
   4.193                  return;
   4.194 @@ -373,12 +355,12 @@
   4.195                      + " Close the socket by calling it will null data first!"
   4.196                  );
   4.197              }
   4.198 -            JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, prevURL, null, "WebSocket", data);
   4.199 +            JSONCall call = PropertyBindingAccessor.createCall(ctx, rcvr, headers, prevURL, null, "WebSocket", data);
   4.200              trans.send(s, call);
   4.201          }
   4.202 -        
   4.203 +
   4.204      }
   4.205 -    
   4.206 +
   4.207      private static final Map<Class,Proto.Type<?>> modelTypes;
   4.208      static {
   4.209          modelTypes = new HashMap<Class, Proto.Type<?>>();
   4.210 @@ -386,11 +368,11 @@
   4.211      public static void register(Class c, Proto.Type<?> type) {
   4.212          modelTypes.put(c, type);
   4.213      }
   4.214 -    
   4.215 +
   4.216      public static boolean isModel(Class<?> clazz) {
   4.217 -        return findType(clazz) != null; 
   4.218 +        return findType(clazz) != null;
   4.219      }
   4.220 -    
   4.221 +
   4.222      static Proto.Type<?> findType(Class<?> clazz) {
   4.223          for (int i = 0; i < 2; i++) {
   4.224              Proto.Type<?> from = modelTypes.get(clazz);
   4.225 @@ -402,7 +384,7 @@
   4.226          }
   4.227          return null;
   4.228      }
   4.229 -    
   4.230 +
   4.231      public static <Model> Model bindTo(Model model, BrwsrCtx c) {
   4.232          Proto.Type<Model> from = (Proto.Type<Model>) findType(model.getClass());
   4.233          if (from == null) {
   4.234 @@ -410,8 +392,8 @@
   4.235          }
   4.236          return PropertyBindingAccessor.clone(from, model, c);
   4.237      }
   4.238 -    
   4.239 -    public static <T> T readStream(BrwsrCtx c, Class<T> modelClazz, InputStream data, Collection<? super T> collectTo) 
   4.240 +
   4.241 +    public static <T> T readStream(BrwsrCtx c, Class<T> modelClazz, InputStream data, Collection<? super T> collectTo)
   4.242      throws IOException {
   4.243          Transfer tr = findTransfer(c);
   4.244          Object rawJSON = tr.toJSON((InputStream)data);
   4.245 @@ -468,7 +450,7 @@
   4.246              // ignore and try again
   4.247          }
   4.248      }
   4.249 -    
   4.250 +
   4.251      private static final class EmptyTech
   4.252      implements Technology<Object>, Transfer, WSTransfer<Void> {
   4.253          private static final EmptyTech EMPTY = new EmptyTech();
   4.254 @@ -540,5 +522,5 @@
   4.255          public void close(Void socket) {
   4.256          }
   4.257      }
   4.258 -    
   4.259 +
   4.260  }
     5.1 --- a/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java	Wed May 27 22:40:01 2015 +0200
     5.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java	Wed May 27 23:37:25 2015 +0200
     5.3 @@ -1132,15 +1132,31 @@
     5.4                  body.append("  * to open the connection (even if not required). Call with non-null data to\n");
     5.5                  body.append("  * send messages to server. Call again with <code>null</code> data to close the socket.\n");
     5.6                  body.append("  */\n");
     5.7 +                if (onR.headers().length > 0) {
     5.8 +                    error("WebSocket spec does not support headers", e);
     5.9 +                }
    5.10              }
    5.11              body.append("  public void ").append(n).append("(");
    5.12              StringBuilder urlBefore = new StringBuilder();
    5.13              StringBuilder urlAfter = new StringBuilder();
    5.14 +            StringBuilder headers = new StringBuilder();
    5.15              String jsonpVarName = null;
    5.16              {
    5.17                  String sep = "";
    5.18                  boolean skipJSONP = onR.jsonp().isEmpty();
    5.19 -                for (String p : findParamNames(e, onR.url(), onR.jsonp(), urlBefore, urlAfter)) {
    5.20 +                Set<String> receiveParams = new LinkedHashSet<String>();
    5.21 +                findParamNames(receiveParams, e, onR.url(), onR.jsonp(), urlBefore, urlAfter);
    5.22 +                for (String headerLine : onR.headers()) {
    5.23 +                    if (headerLine.contains("\r") || headerLine.contains("\n")) {
    5.24 +                        error("Header line cannot contain line separator", e);
    5.25 +                    }
    5.26 +                    findParamNames(receiveParams, e, headerLine, null, headers);
    5.27 +                    headers.append("+ \"\\r\\n\" +\n");
    5.28 +                }
    5.29 +                if (headers.length() > 0) {
    5.30 +                    headers.append("\"\"");
    5.31 +                }
    5.32 +                for (String p : receiveParams) {
    5.33                      if (!skipJSONP && p.equals(onR.jsonp())) {
    5.34                          skipJSONP = true;
    5.35                          jsonpVarName = p;
    5.36 @@ -1180,13 +1196,13 @@
    5.37              body.append(") {\n");
    5.38              boolean webSocket = onR.method().equals("WebSocket");
    5.39              if (webSocket) {
    5.40 -                if (generateWSReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList != 0, modelClass, n, args, params, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
    5.41 +                if (generateWSReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList != 0, modelClass, n, args, params, urlBefore, jsonpVarName, urlAfter, dataMirror, headers)) {
    5.42                      return false;
    5.43                  }
    5.44                  body.append("  }\n");
    5.45                  body.append("  private Object ws_" + e.getSimpleName() + ";\n");
    5.46              } else {
    5.47 -                if (generateJSONReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList != 0, modelClass, n, args, params, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
    5.48 +                if (generateJSONReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList != 0, modelClass, n, args, params, urlBefore, jsonpVarName, urlAfter, dataMirror, headers)) {
    5.49                      return false;
    5.50                  }
    5.51                  body.append("  }\n");
    5.52 @@ -1198,7 +1214,7 @@
    5.53          return true;
    5.54      }
    5.55  
    5.56 -    private boolean generateJSONReceiveBody(int index, StringWriter method, StringBuilder body, OnReceive onR, ExecutableElement e, Element clazz, String className, boolean expectsList, String modelClass, String n, List<String> args, List<String> params, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror) {
    5.57 +    private boolean generateJSONReceiveBody(int index, StringWriter method, StringBuilder body, OnReceive onR, ExecutableElement e, Element clazz, String className, boolean expectsList, String modelClass, String n, List<String> args, List<String> params, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror, StringBuilder headers) {
    5.58          body.append(
    5.59              "    case " + index + ": {\n" +
    5.60              "      if (type == 2) { /* on error */\n" +
    5.61 @@ -1247,7 +1263,8 @@
    5.62              "      }\n" +
    5.63              "    }\n"
    5.64              );
    5.65 -        method.append("    proto.loadJSON(" + index + ",\n        ");
    5.66 +        method.append("    proto.loadJSONWithHeaders(" + index + ",\n        ");
    5.67 +        method.append(headers.length() == 0 ? "null" : headers).append(",\n        ");
    5.68          method.append(urlBefore).append(", ");
    5.69          if (jsonpVarName != null) {
    5.70              method.append(urlAfter);
    5.71 @@ -1271,7 +1288,7 @@
    5.72          return false;
    5.73      }
    5.74  
    5.75 -    private boolean generateWSReceiveBody(int index, StringWriter method, StringBuilder body, OnReceive onR, ExecutableElement e, Element clazz, String className, boolean expectsList, String modelClass, String n, List<String> args, List<String> params, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror) {
    5.76 +    private boolean generateWSReceiveBody(int index, StringWriter method, StringBuilder body, OnReceive onR, ExecutableElement e, Element clazz, String className, boolean expectsList, String modelClass, String n, List<String> args, List<String> params, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror, StringBuilder headers) {
    5.77          body.append(
    5.78              "    case " + index + ": {\n" +
    5.79              "      if (type == 0) { /* on open */\n" +
    5.80 @@ -1655,37 +1672,35 @@
    5.81          return false;
    5.82      }
    5.83  
    5.84 -    private Iterable<String> findParamNames(
    5.85 -        Element e, String url, String jsonParam, StringBuilder... both
    5.86 +    private void findParamNames(
    5.87 +        Set<String> params, Element e, String url, String jsonParam, StringBuilder... both
    5.88      ) {
    5.89 -        Set<String> params = new LinkedHashSet<String>();
    5.90          int wasJSON = 0;
    5.91  
    5.92          for (int pos = 0; ;) {
    5.93              int next = url.indexOf('{', pos);
    5.94              if (next == -1) {
    5.95                  both[wasJSON].append('"')
    5.96 -                    .append(url.substring(pos))
    5.97 +                    .append(url.substring(pos).replace("\"", "\\\""))
    5.98                      .append('"');
    5.99 -                return params;
   5.100 +                return;
   5.101              }
   5.102              int close = url.indexOf('}', next);
   5.103              if (close == -1) {
   5.104                  error("Unbalanced '{' and '}' in " + url, e);
   5.105 -                return params;
   5.106 +                return;
   5.107              }
   5.108              final String paramName = url.substring(next + 1, close);
   5.109 -            if (params.add(paramName)) {
   5.110 -                if (paramName.equals(jsonParam) && !jsonParam.isEmpty()) {
   5.111 -                    both[wasJSON].append('"')
   5.112 -                        .append(url.substring(pos, next))
   5.113 -                        .append('"');
   5.114 -                    wasJSON = 1;
   5.115 -                } else {
   5.116 -                    both[wasJSON].append('"')
   5.117 -                        .append(url.substring(pos, next))
   5.118 -                        .append("\" + ").append(paramName).append(" + ");
   5.119 -                }
   5.120 +            params.add(paramName);
   5.121 +            if (paramName.equals(jsonParam) && !jsonParam.isEmpty()) {
   5.122 +                both[wasJSON].append('"')
   5.123 +                    .append(url.substring(pos, next).replace("\"", "\\\""))
   5.124 +                    .append('"');
   5.125 +                wasJSON = 1;
   5.126 +            } else {
   5.127 +                both[wasJSON].append('"')
   5.128 +                    .append(url.substring(pos, next).replace("\"", "\\\""))
   5.129 +                    .append("\" + ").append(paramName).append(" + ");
   5.130              }
   5.131              pos = close + 1;
   5.132          }
     6.1 --- a/json/src/main/java/org/netbeans/html/json/impl/PropertyBindingAccessor.java	Wed May 27 22:40:01 2015 +0200
     6.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/PropertyBindingAccessor.java	Wed May 27 23:37:25 2015 +0200
     6.3 @@ -43,7 +43,6 @@
     6.4  package org.netbeans.html.json.impl;
     6.5  
     6.6  import net.java.html.BrwsrCtx;
     6.7 -import org.netbeans.html.json.spi.FunctionBinding;
     6.8  import org.netbeans.html.json.spi.JSONCall;
     6.9  import org.netbeans.html.json.spi.PropertyBinding;
    6.10  import org.netbeans.html.json.spi.Proto;
    6.11 @@ -59,7 +58,7 @@
    6.12          if (DEFAULT != null) throw new IllegalStateException();
    6.13          DEFAULT = this;
    6.14      }
    6.15 -    
    6.16 +
    6.17      static {
    6.18          JSON.initClass(PropertyBinding.class);
    6.19      }
    6.20 @@ -68,34 +67,36 @@
    6.21          Proto.Type<M> access, Bindings<?> bindings, String name, int index, M model, boolean readOnly
    6.22      );
    6.23      protected abstract JSONCall newCall(
    6.24 -        BrwsrCtx ctx, RcvrJSON callback, String urlBefore, String urlAfter,
    6.25 +        BrwsrCtx ctx, RcvrJSON callback,
    6.26 +        String headers, String urlBefore, String urlAfter,
    6.27          String method, Object data
    6.28      );
    6.29 -    
    6.30 +
    6.31      protected abstract Bindings bindings(Proto proto, boolean initialize);
    6.32      protected abstract void notifyChange(Proto proto, int propIndex);
    6.33      protected abstract Proto findProto(Proto.Type<?> type, Object object);
    6.34      protected abstract <Model> Model cloneTo(Proto.Type<Model> type, Model model, BrwsrCtx c);
    6.35      protected abstract Object read(Proto.Type<?> from, BrwsrCtx c, Object data);
    6.36 -    
    6.37 +
    6.38      static Bindings getBindings(Proto proto, boolean initialize) {
    6.39          return DEFAULT.bindings(proto, initialize);
    6.40      }
    6.41 -    
    6.42 +
    6.43      static void notifyProtoChange(Proto proto, int propIndex) {
    6.44          DEFAULT.notifyChange(proto, propIndex);
    6.45      }
    6.46 -    
    6.47 +
    6.48      static <M> PropertyBinding create(
    6.49          Proto.Type<M> access, Bindings<?> bindings, String name, int index, M model , boolean readOnly
    6.50      ) {
    6.51          return DEFAULT.newBinding(access, bindings, name, index, model, readOnly);
    6.52      }
    6.53 -    static JSONCall createCall(
    6.54 -        BrwsrCtx ctx, RcvrJSON callback, String urlBefore, String urlAfter, 
    6.55 +    public static JSONCall createCall(
    6.56 +        BrwsrCtx ctx, RcvrJSON callback,
    6.57 +        String headers, String urlBefore, String urlAfter,
    6.58          String method, Object data
    6.59      ) {
    6.60 -        return DEFAULT.newCall(ctx, callback, urlBefore, urlAfter, method, data);
    6.61 +        return DEFAULT.newCall(ctx, callback, headers, urlBefore, urlAfter, method, data);
    6.62      }
    6.63      static Proto protoFor(Proto.Type<?> type, Object object) {
    6.64          return DEFAULT.findProto(type, object);
     7.1 --- a/json/src/main/java/org/netbeans/html/json/spi/JSONCall.java	Wed May 27 22:40:01 2015 +0200
     7.2 +++ b/json/src/main/java/org/netbeans/html/json/spi/JSONCall.java	Wed May 27 23:37:25 2015 +0200
     7.3 @@ -54,30 +54,36 @@
     7.4   */
     7.5  public final class JSONCall {
     7.6      private final RcvrJSON whenDone;
     7.7 +    private final String headers;
     7.8      private final String urlBefore;
     7.9      private final String urlAfter;
    7.10      private final String method;
    7.11      private final Object data;
    7.12      private final BrwsrCtx ctx;
    7.13  
    7.14 -    JSONCall(BrwsrCtx ctx, RcvrJSON whenDone, String urlBefore, String urlAfter, String method, Object data) {
    7.15 +    JSONCall(
    7.16 +        BrwsrCtx ctx, RcvrJSON whenDone,
    7.17 +        String headers, String urlBefore, String urlAfter,
    7.18 +        String method, Object data
    7.19 +    ) {
    7.20          this.ctx = ctx;
    7.21          this.whenDone = whenDone;
    7.22 +        this.headers = headers;
    7.23          this.urlBefore = urlBefore;
    7.24          this.urlAfter = urlAfter;
    7.25          this.method = method;
    7.26          this.data = data;
    7.27      }
    7.28 -    
    7.29 -    /** Do we have some data to send? Can the {@link #writeData(java.io.OutputStream)} method be 
    7.30 +
    7.31 +    /** Do we have some data to send? Can the {@link #writeData(java.io.OutputStream)} method be
    7.32       * called?
    7.33 -     * 
    7.34 +     *
    7.35       * @return true, if the call has some data to send
    7.36       */
    7.37      public boolean isDoOutput() {
    7.38          return this.data != null;
    7.39      }
    7.40 -    
    7.41 +
    7.42      public void writeData(OutputStream os) throws IOException {
    7.43          if (this.data == null) {
    7.44              throw new IOException("No data!");
    7.45 @@ -85,15 +91,25 @@
    7.46          os.write(this.data.toString().getBytes("UTF-8"));
    7.47          os.flush();
    7.48      }
    7.49 -    
    7.50 +
    7.51 +    /** Additional headers to be included in the request.
    7.52 +     * Usually multiline string to be appended into the header.
    7.53 +     *
    7.54 +     * @return <code>null</code> or string with prepared (HTTP) request headers
    7.55 +     * @since 1.2
    7.56 +     */
    7.57 +    public String getHeaders() {
    7.58 +        return headers;
    7.59 +    }
    7.60 +
    7.61      public String getMethod() {
    7.62          return method;
    7.63      }
    7.64 -    
    7.65 +
    7.66      public boolean isJSONP() {
    7.67          return urlAfter != null;
    7.68      }
    7.69 -    
    7.70 +
    7.71      public String composeURL(String jsonpCallback) {
    7.72          if ((urlAfter == null) != (jsonpCallback == null)) {
    7.73              throw new IllegalStateException();
    7.74 @@ -112,7 +128,7 @@
    7.75              dispatch(RcvrJSON.MsgEvnt.createMessage(result));
    7.76          }
    7.77      }
    7.78 -    
    7.79 +
    7.80      public void notifyError(Throwable error) {
    7.81          if (error == null) {
    7.82              dispatch(RcvrJSON.MsgEvnt.createClose());
    7.83 @@ -120,7 +136,7 @@
    7.84              dispatch(RcvrJSON.MsgEvnt.createError(error));
    7.85          }
    7.86      }
    7.87 -    
    7.88 +
    7.89      private void dispatch(final RcvrJSON.MsgEvnt ev) {
    7.90          ctx.execute(new Runnable() {
    7.91              @Override
     8.1 --- a/json/src/main/java/org/netbeans/html/json/spi/PropertyBinding.java	Wed May 27 22:40:01 2015 +0200
     8.2 +++ b/json/src/main/java/org/netbeans/html/json/spi/PropertyBinding.java	Wed May 27 23:37:25 2015 +0200
     8.3 @@ -50,8 +50,8 @@
     8.4  import org.netbeans.html.json.impl.PropertyBindingAccessor;
     8.5  import org.netbeans.html.json.impl.RcvrJSON;
     8.6  
     8.7 -/** Describes a property when one is asked to 
     8.8 - * bind it 
     8.9 +/** Describes a property when one is asked to
    8.10 + * bind it
    8.11   *
    8.12   * @author Jaroslav Tulach
    8.13   */
    8.14 @@ -62,8 +62,8 @@
    8.15      static {
    8.16          new PropertyBindingAccessor() {
    8.17              @Override
    8.18 -            protected JSONCall newCall(BrwsrCtx ctx, RcvrJSON callback, String urlBefore, String urlAfter, String method, Object data) {
    8.19 -                return new JSONCall(ctx, callback, urlBefore, urlAfter, method, data);
    8.20 +            protected JSONCall newCall(BrwsrCtx ctx, RcvrJSON callback, String headers, String urlBefore, String urlAfter, String method, Object data) {
    8.21 +                return new JSONCall(ctx, callback, headers, urlBefore, urlAfter, method, data);
    8.22              }
    8.23  
    8.24              @Override
    8.25 @@ -108,33 +108,33 @@
    8.26  
    8.27      /** Changes value of the property. Can be called only on dedicated
    8.28       * thread. See {@link Technology#runSafe(java.lang.Runnable)}.
    8.29 -     * 
    8.30 +     *
    8.31       * @param v new value of the property
    8.32       */
    8.33      public abstract void setValue(Object v);
    8.34 -    
    8.35 +
    8.36      /** Obtains current value of the property this binding represents.
    8.37       * Can be called only on dedicated
    8.38       * thread. See {@link Technology#runSafe(java.lang.Runnable)}.
    8.39 -     * 
    8.40 +     *
    8.41       * @return the value or <code>null</code>
    8.42       */
    8.43      public abstract Object getValue();
    8.44 -    
    8.45 +
    8.46      /** Is this property read only? Or can one call {@link #setValue(java.lang.Object)}?
    8.47 -     * 
    8.48 +     *
    8.49       * @return true, if this property is read only
    8.50       */
    8.51      public abstract boolean isReadOnly();
    8.52  
    8.53      /** Returns identical version of the binding, but one that holds on the
    8.54       * original model object via weak reference.
    8.55 -     * 
    8.56 +     *
    8.57       * @return binding that uses weak reference
    8.58       * @since 1.1
    8.59       */
    8.60      public abstract PropertyBinding weak();
    8.61 -    
    8.62 +
    8.63      private static abstract class AImpl<M> extends PropertyBinding {
    8.64          public final String name;
    8.65          public final boolean readOnly;
    8.66 @@ -149,7 +149,7 @@
    8.67              this.access = access;
    8.68              this.readOnly = readOnly;
    8.69          }
    8.70 -        
    8.71 +
    8.72          protected abstract M model();
    8.73  
    8.74          @Override
    8.75 @@ -182,7 +182,7 @@
    8.76              return name;
    8.77          }
    8.78      } // end of PBData
    8.79 -    
    8.80 +
    8.81      private static final class Impl<M> extends AImpl<M> {
    8.82          private final M model;
    8.83  
    8.84 @@ -201,7 +201,7 @@
    8.85              return new Weak(model, bindings, name, index, access, readOnly);
    8.86          }
    8.87      }
    8.88 -    
    8.89 +
    8.90      private static final class Weak<M> extends AImpl<M> {
    8.91          private final Reference<M> ref;
    8.92          public Weak(M model, Bindings<?> bindings, String name, int index, Proto.Type<M> access, boolean readOnly) {
     9.1 --- a/json/src/main/java/org/netbeans/html/json/spi/Proto.java	Wed May 27 22:40:01 2015 +0200
     9.2 +++ b/json/src/main/java/org/netbeans/html/json/spi/Proto.java	Wed May 27 23:37:25 2015 +0200
     9.3 @@ -49,7 +49,9 @@
     9.4  import net.java.html.json.Model;
     9.5  import org.netbeans.html.json.impl.Bindings;
     9.6  import org.netbeans.html.json.impl.JSON;
     9.7 +import org.netbeans.html.json.impl.JSON.WS;
     9.8  import org.netbeans.html.json.impl.JSONList;
     9.9 +import org.netbeans.html.json.impl.PropertyBindingAccessor;
    9.10  import org.netbeans.html.json.impl.RcvrJSON;
    9.11  import org.netbeans.html.json.impl.RcvrJSON.MsgEvnt;
    9.12  
    9.13 @@ -57,8 +59,8 @@
    9.14   * {@link Model} annotation. Contains methods the generated class can
    9.15   * use to communicate with behind the scene associated {@link Technology}.
    9.16   * Each {@link Proto} object is associated with <a href="http://wiki.apidesign.org/wiki/Singletonizer">
    9.17 - * singletonizer</a>-like interface {@link Type} which provides the 
    9.18 - * associated {@link Technology} the necessary information about the 
    9.19 + * singletonizer</a>-like interface {@link Type} which provides the
    9.20 + * associated {@link Technology} the necessary information about the
    9.21   * generated {@link Model} class.
    9.22   *
    9.23   * @author Jaroslav Tulach
    9.24 @@ -79,8 +81,8 @@
    9.25  
    9.26      /** Browser context this proto object and its associated model
    9.27       * are operating-in.
    9.28 -     * 
    9.29 -     * @return the associated context 
    9.30 +     *
    9.31 +     * @return the associated context
    9.32       */
    9.33      public BrwsrCtx getContext() {
    9.34          return context;
    9.35 @@ -89,19 +91,19 @@
    9.36      /** Acquires global lock to compute a {@link ComputedProperty derived property}
    9.37       * on this proto object. This proto object must not be locked yet. No
    9.38       * dependency tracking is performed.
    9.39 -     * 
    9.40 +     *
    9.41       * @throws IllegalStateException if already locked
    9.42       */
    9.43      public void acquireLock() throws IllegalStateException {
    9.44          acquireLock(null);
    9.45      }
    9.46 -    
    9.47 +
    9.48      /** Acquires global lock to compute a {@link ComputedProperty derived property}
    9.49       * on this proto object. This proto object must not be locked yet. The
    9.50       * name of the property is used to track dependencies on own
    9.51       * properties of other proto objects - when they are changed, this
    9.52       * {@link #valueHasMutated(java.lang.String) property is changed too}.
    9.53 -     * 
    9.54 +     *
    9.55       * @param propName name of property we are about to compute
    9.56       * @throws IllegalStateException thrown when there is a cyclic
    9.57       *   call is detected
    9.58 @@ -110,14 +112,14 @@
    9.59      public void acquireLock(String propName) throws IllegalStateException {
    9.60          Observers.beginComputing(this, propName);
    9.61      }
    9.62 -    
    9.63 +
    9.64      /** A property on this proto object is about to be accessed. Verifies
    9.65       * whether this proto object is accessible - e.g. it has not been
    9.66       * {@link #acquireLock() locked yet}. If everything is OK, the
    9.67       * <code>propName</code> is recorded in the chain of dependencies
    9.68       * tracked by {@link #acquireLock(java.lang.String)} and watched by
    9.69       * {@link #valueHasMutated(java.lang.String)}.
    9.70 -     * 
    9.71 +     *
    9.72       * @param propName name of the property that is requested
    9.73       * @throws IllegalStateException if the model is locked
    9.74       * @since 0.9
    9.75 @@ -125,27 +127,27 @@
    9.76      public void accessProperty(String propName) throws IllegalStateException {
    9.77          Observers.accessingValue(this, propName);
    9.78      }
    9.79 -    
    9.80 +
    9.81      /** Verifies the model is not locked otherwise throws an exception.
    9.82       * @throws IllegalStateException if the model is locked
    9.83       */
    9.84      public void verifyUnlocked() throws IllegalStateException {
    9.85          Observers.verifyUnlocked(this);
    9.86      }
    9.87 -    
    9.88 -    /** When modifications are over, the model is switched into 
    9.89 +
    9.90 +    /** When modifications are over, the model is switched into
    9.91       * unlocked state by calling this method.
    9.92       */
    9.93      public void releaseLock() {
    9.94          Observers.finishComputing(this);
    9.95      }
    9.96 -    
    9.97 +
    9.98      /** Whenever model changes a property. It should notify the
    9.99 -     * associated technology by calling this method. 
   9.100 +     * associated technology by calling this method.
   9.101       * Since 0.8.3: This method may be called by any thread - it reschedules
   9.102       * its actual execution into appropriate one by using
   9.103       * {@link BrwsrCtx#execute(java.lang.Runnable)}.
   9.104 -     * 
   9.105 +     *
   9.106       * @param propName name of the changed property
   9.107       */
   9.108      public void valueHasMutated(final String propName) {
   9.109 @@ -168,7 +170,7 @@
   9.110       * Since 0.8.3: This method may be called by any thread - it reschedules
   9.111       * its actual execution into appropriate one by using
   9.112       * {@link BrwsrCtx#execute(java.lang.Runnable)}.
   9.113 -     * 
   9.114 +     *
   9.115       * @param propName name of the changed property
   9.116       * @param oldValue provides previous value of the property
   9.117       * @param newValue provides new value of the property
   9.118 @@ -187,21 +189,21 @@
   9.119              }
   9.120          });
   9.121      }
   9.122 -    
   9.123 +
   9.124      /** Initializes the associated model in the current {@link #getContext() context}.
   9.125 -     * In case of <em>knockout.js</em> technology, applies given bindings 
   9.126 +     * In case of <em>knockout.js</em> technology, applies given bindings
   9.127       * of the current model to the <em>body</em> element of the page.
   9.128       */
   9.129      public void applyBindings() {
   9.130          initBindings().applyBindings(null);
   9.131      }
   9.132 -    
   9.133 +
   9.134      /** Initializes the associated model to the specified element's subtree.
   9.135       * The technology is taken from the current {@link #getContext() context} and
   9.136 -     * in case of <em>knockout.js</em> applies given bindings 
   9.137 +     * in case of <em>knockout.js</em> applies given bindings
   9.138       * of the current model to the element of the page with 'id' attribute
   9.139       * set to the specified <code>id</code> value.
   9.140 -     * 
   9.141 +     *
   9.142       * @param id the id of element to apply the binding to
   9.143       * @since 1.1
   9.144       * @see Technology.ApplyId
   9.145 @@ -209,13 +211,13 @@
   9.146      public void applyBindings(String id) {
   9.147          initBindings().applyBindings(id);
   9.148      }
   9.149 -    
   9.150 +
   9.151      /** Invokes the provided runnable in the {@link #getContext() context}
   9.152       * of the browser. If the caller is already on the right thread, the
   9.153 -     * <code>run.run()</code> is invoked immediately and synchronously. 
   9.154 +     * <code>run.run()</code> is invoked immediately and synchronously.
   9.155       * Otherwise the method returns immediately and the <code>run()</code>
   9.156       * method is performed later
   9.157 -     * 
   9.158 +     *
   9.159       * @param run the action to execute
   9.160       */
   9.161      public void runInBrowser(Runnable run) {
   9.162 @@ -224,10 +226,10 @@
   9.163  
   9.164      /** Invokes the specified function index in the {@link #getContext() context}
   9.165       * of the browser. If the caller is already on the right thread, the
   9.166 -     * index-th function is invoked immediately and synchronously. 
   9.167 +     * index-th function is invoked immediately and synchronously.
   9.168       * Otherwise the method returns immediately and the function is invoked
   9.169       * later.
   9.170 -     * 
   9.171 +     *
   9.172       * @param index the index of the function as will be passed to
   9.173       *   {@link Type#call(java.lang.Object, int, java.lang.Object, java.lang.Object)}
   9.174       *   method
   9.175 @@ -247,11 +249,11 @@
   9.176              }
   9.177          });
   9.178      }
   9.179 -    
   9.180 +
   9.181      /** Initializes the provided collection with a content of the <code>array</code>.
   9.182 -     * The initialization can only be done soon after the the collection 
   9.183 +     * The initialization can only be done soon after the the collection
   9.184       * is created, otherwise an exception is throw
   9.185 -     * 
   9.186 +     *
   9.187       * @param to the collection to initialize (assumed to be empty)
   9.188       * @param array the array to add to the collection
   9.189       * @throws IllegalStateException if the system has already been initialized
   9.190 @@ -270,7 +272,7 @@
   9.191      /** Takes an object representing JSON result and extract some of its
   9.192       * properties. It is assumed that the <code>props</code> and
   9.193       * <code>values</code> arrays have the same length.
   9.194 -     * 
   9.195 +     *
   9.196       * @param json the JSON object (actual type depends on the associated
   9.197       *   {@link Technology})
   9.198       * @param props list of properties to extract
   9.199 @@ -281,7 +283,7 @@
   9.200      }
   9.201  
   9.202      /** Converts raw JSON <code>data</code> into a Java {@link Model} class.
   9.203 -     * 
   9.204 +     *
   9.205       * @param <T> type of the model class
   9.206       * @param modelClass the type of the class to create
   9.207       * @param data the raw JSON data
   9.208 @@ -294,29 +296,29 @@
   9.209      /** Initializes asynchronous JSON connection to specified URL. Delegates
   9.210       * to {@link #loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...) }
   9.211       * with no extra parameters.
   9.212 -     * 
   9.213 +     *
   9.214       * @param index the callback index to be used when a reply is received
   9.215       *   to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
   9.216 -     * 
   9.217 +     *
   9.218       * @param urlBefore the part of the URL before JSON-P callback parameter
   9.219       * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
   9.220       * @param method method to use for connection to the server
   9.221       * @param data string, number or a {@link Model} generated class to send to
   9.222       *    the server when doing a query
   9.223       */
   9.224 -    public void loadJSON(final int index, 
   9.225 +    public void loadJSON(final int index,
   9.226          String urlBefore, String urlAfter, String method,
   9.227          final Object data
   9.228      ) {
   9.229          loadJSON(index, urlBefore, urlAfter, method, data, new Object[0]);
   9.230      }
   9.231 -    
   9.232 -    /** Initializes asynchronous JSON connection to specified URL. The 
   9.233 +
   9.234 +    /** Initializes asynchronous JSON connection to specified URL. The
   9.235       * method returns immediately and later does callback later.
   9.236 -     * 
   9.237 +     *
   9.238       * @param index the callback index to be used when a reply is received
   9.239       *   to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
   9.240 -     * 
   9.241 +     *
   9.242       * @param urlBefore the part of the URL before JSON-P callback parameter
   9.243       * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
   9.244       * @param method method to use for connection to the server
   9.245 @@ -326,7 +328,31 @@
   9.246       *   {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object, java.lang.Object[])}
   9.247       * @since 0.8.1
   9.248       */
   9.249 -    public void loadJSON(final int index, 
   9.250 +    public void loadJSON(final int index,
   9.251 +        String urlBefore, String urlAfter, String method,
   9.252 +        final Object data, final Object... params
   9.253 +    ) {
   9.254 +        loadJSONWithHeaders(index, null, urlBefore, urlAfter, method, data, params);
   9.255 +    }
   9.256 +
   9.257 +    /** Initializes asynchronous JSON connection to specified URL. The
   9.258 +     * method returns immediately and later does callback later.
   9.259 +     *
   9.260 +     * @param index the callback index to be used when a reply is received
   9.261 +     *   to call {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}.
   9.262 +     *
   9.263 +     * @param headers headers to use for the request or <code>null</code> to use default ones
   9.264 +     * @param urlBefore the part of the URL before JSON-P callback parameter
   9.265 +     * @param urlAfter the rest of the URL or <code>null</code> if no JSON-P is used
   9.266 +     * @param method method to use for connection to the server
   9.267 +     * @param data string, number or a {@link Model} generated class to send to
   9.268 +     *    the server when doing a query
   9.269 +     * @param params extra params to pass back when calling
   9.270 +     *   {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object, java.lang.Object[])}
   9.271 +     * @since 1.2
   9.272 +     */
   9.273 +    public void loadJSONWithHeaders(final int index,
   9.274 +        String headers,
   9.275          String urlBefore, String urlAfter, String method,
   9.276          final Object data, final Object... params
   9.277      ) {
   9.278 @@ -341,12 +367,16 @@
   9.279                  type.onMessage(obj, index, 2, msg.getException(), params);
   9.280              }
   9.281          }
   9.282 -        JSON.loadJSON(context, new Rcvr(), urlBefore, urlAfter, method, data);
   9.283 +        JSONCall call = PropertyBindingAccessor.createCall(
   9.284 +            context, new Rcvr(), headers, urlBefore, urlAfter, method, data
   9.285 +        );
   9.286 +        Transfer t = JSON.findTransfer(context);
   9.287 +        t.loadJSON(call);
   9.288      }
   9.289 -    
   9.290 -    /** Opens new WebSocket connection to the specified URL. 
   9.291 -     * 
   9.292 -     * @param index the index to use later during callbacks to 
   9.293 +
   9.294 +    /** Opens new WebSocket connection to the specified URL.
   9.295 +     *
   9.296 +     * @param index the index to use later during callbacks to
   9.297       *   {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object)}
   9.298       * @param url the <code>ws://</code> or <code>wss://</code> URL to connect to
   9.299       * @param data data to send to server (usually <code>null</code>)
   9.300 @@ -359,12 +389,12 @@
   9.301              protected void onError(MsgEvnt msg) {
   9.302                  type.onMessage(obj, index, 2, msg.getException());
   9.303              }
   9.304 -            
   9.305 +
   9.306              @Override
   9.307              protected void onMessage(MsgEvnt msg) {
   9.308                  type.onMessage(obj, index, 1, msg.getValues());
   9.309              }
   9.310 -            
   9.311 +
   9.312              @Override
   9.313              protected void onClose(MsgEvnt msg) {
   9.314                  type.onMessage(obj, index, 3, null);
   9.315 @@ -375,24 +405,26 @@
   9.316                  type.onMessage(obj, index, 0, null);
   9.317              }
   9.318          }
   9.319 -        return JSON.openWS(context, new WSrcvr(), url, data);
   9.320 +        WS ws = WS.create(JSON.findWSTransfer(context), new WSrcvr());
   9.321 +        ws.send(context, null, url, data);
   9.322 +        return ws;
   9.323      }
   9.324 -    
   9.325 +
   9.326      /** Sends a message to existing socket.
   9.327 -     * 
   9.328 +     *
   9.329       * @param webSocket the socket to send message to
   9.330       * @param url the <code>ws://</code> or <code>wss://</code> URL to connect to,
   9.331 -     *    preferably the same as the one used when the socket was 
   9.332 +     *    preferably the same as the one used when the socket was
   9.333       *    {@link #wsOpen(int, java.lang.String, java.lang.Object) opened}
   9.334       * @param data the data to send or <code>null</code> if the socket is
   9.335       *    supposed to be closed
   9.336       */
   9.337      public void wsSend(Object webSocket, String url, Object data) {
   9.338 -        ((JSON.WS)webSocket).send(context, url, data);
   9.339 +        ((JSON.WS)webSocket).send(context, null, url, data);
   9.340      }
   9.341  
   9.342      /** Converts raw data (one of its properties) to string representation.
   9.343 -     * 
   9.344 +     *
   9.345       * @param data the object
   9.346       * @param propName the name of object property or <code>null</code>
   9.347       *   if the whole object should be converted
   9.348 @@ -401,9 +433,9 @@
   9.349      public String toString(Object data, String propName) {
   9.350          return JSON.toString(context, data, propName);
   9.351      }
   9.352 -    
   9.353 +
   9.354      /** Converts raw data (one of its properties) to a number representation.
   9.355 -     * 
   9.356 +     *
   9.357       * @param data the object
   9.358       * @param propName the name of object property or <code>null</code>
   9.359       *   if the whole object should be converted
   9.360 @@ -414,7 +446,7 @@
   9.361      }
   9.362  
   9.363      /** Converts raw JSON data into a {@link Model} class representation.
   9.364 -     * 
   9.365 +     *
   9.366       * @param <T> type of the model to create
   9.367       * @param type class of the model to create
   9.368       * @param data raw JSON data (depends on associated {@link Technology})
   9.369 @@ -426,7 +458,7 @@
   9.370      }
   9.371  
   9.372      /** Creates new JSON like observable list.
   9.373 -     * 
   9.374 +     *
   9.375       * @param <T> the type of the list elements
   9.376       * @param propName name of a property this list is associated with
   9.377       * @param onChange index of the property to use when the list is modified
   9.378 @@ -441,7 +473,7 @@
   9.379  
   9.380      /** Copies content of one collection to another, re-assigning all its
   9.381       * elements from their current context to the new <code>ctx</code>.
   9.382 -     * 
   9.383 +     *
   9.384       * @param <T> type of the collections
   9.385       * @param to the target collection to be filled with cloned values
   9.386       * @param ctx context for the new collection
   9.387 @@ -460,15 +492,15 @@
   9.388              }
   9.389          }
   9.390      }
   9.391 -    
   9.392 +
   9.393      //
   9.394      // internal state
   9.395      //
   9.396 -    
   9.397 +
   9.398      final String toStr() {
   9.399          return "Proto[" + obj + "]@" + Integer.toHexString(System.identityHashCode(this));
   9.400      }
   9.401 -    
   9.402 +
   9.403      final Bindings initBindings() {
   9.404          if (ko == null) {
   9.405              Bindings b = Bindings.apply(context, obj);
   9.406 @@ -507,7 +539,7 @@
   9.407  
   9.408      /** Functionality used by the code generated by annotation
   9.409       * processor for the {@link net.java.html.json.Model} annotation.
   9.410 -     * 
   9.411 +     *
   9.412       * @param <Model> the generated class
   9.413       * @since 0.7
   9.414       */
   9.415 @@ -519,7 +551,7 @@
   9.416  
   9.417          /** Constructor for subclasses generated by the annotation processor
   9.418           * associated with {@link net.java.html.json.Model} annotation.
   9.419 -         * 
   9.420 +         *
   9.421           * @param clazz the generated model class
   9.422           * @param modelFor the original class annotated by the {@link net.java.html.json.Model} annotation.
   9.423           * @param properties number of properties the class has
   9.424 @@ -543,7 +575,7 @@
   9.425  
   9.426          /** Registers property for the type. It is expected each index
   9.427           * is initialized only once.
   9.428 -         * 
   9.429 +         *
   9.430           * @param name name of the property
   9.431           * @param index index of the property
   9.432           * @param readOnly is the property read only?
   9.433 @@ -555,7 +587,7 @@
   9.434          }
   9.435  
   9.436          /** Registers function of given name at given index.
   9.437 -         * 
   9.438 +         *
   9.439           * @param name name of the function
   9.440           * @param index name of the type
   9.441           */
   9.442 @@ -563,10 +595,10 @@
   9.443              assert functions[index] == null;
   9.444              functions[index] = name;
   9.445          }
   9.446 -        
   9.447 +
   9.448          /** Creates new proto-object for given {@link Model} class bound to
   9.449           * provided context.
   9.450 -         * 
   9.451 +         *
   9.452           * @param obj instance of appropriate {@link Model} class
   9.453           * @param context the browser context
   9.454           * @return new proto-object that the generated class can use for
   9.455 @@ -575,32 +607,32 @@
   9.456          public Proto createProto(Object obj, BrwsrCtx context) {
   9.457              return new Proto(obj, this, context);
   9.458          }
   9.459 -        
   9.460 +
   9.461          //
   9.462          // Implemented by subclasses
   9.463          //
   9.464 -        
   9.465 +
   9.466          /** Sets value of a {@link #registerProperty(java.lang.String, int, boolean) registered property}
   9.467           * to new value.
   9.468 -         * 
   9.469 +         *
   9.470           * @param model the instance of {@link Model model class}
   9.471           * @param index index of the property used during registration
   9.472           * @param value the value to set the property to
   9.473           */
   9.474          protected abstract void setValue(Model model, int index, Object value);
   9.475 -        
   9.476 -        /** Obtains and returns value of a 
   9.477 +
   9.478 +        /** Obtains and returns value of a
   9.479           * {@link #registerProperty(java.lang.String, int, boolean) registered property}.
   9.480 -         * 
   9.481 +         *
   9.482           * @param model the instance of {@link Model model class}
   9.483           * @param index index of the property used during registration
   9.484           * @return current value of the property
   9.485           */
   9.486          protected abstract Object getValue(Model model, int index);
   9.487 -        
   9.488 +
   9.489          /** Invokes a {@link #registerFunction(java.lang.String, int)} registered function
   9.490           * on given object.
   9.491 -         * 
   9.492 +         *
   9.493           * @param model the instance of {@link Model model class}
   9.494           * @param index index of the property used during registration
   9.495           * @param data the currently selected object the function is about to operate on
   9.496 @@ -609,43 +641,43 @@
   9.497           */
   9.498          protected abstract void call(Model model, int index, Object data, Object event)
   9.499          throws Exception;
   9.500 -        
   9.501 +
   9.502          /** Re-binds the model object to new browser context.
   9.503 -         * 
   9.504 +         *
   9.505           * @param model the instance of {@link Model model class}
   9.506           * @param ctx browser context to clone the object to
   9.507           * @return new instance of the model suitable for new context
   9.508           */
   9.509          protected abstract Model cloneTo(Model model, BrwsrCtx ctx);
   9.510 -        
   9.511 +
   9.512          /** Reads raw JSON data and converts them to our model class.
   9.513 -         * 
   9.514 +         *
   9.515           * @param c the browser context to work in
   9.516           * @param json raw JSON data to get values from
   9.517           * @return new instance of model class filled by the data
   9.518           */
   9.519          protected abstract Model read(BrwsrCtx c, Object json);
   9.520 -        
   9.521 +
   9.522          /** Called when a {@link #registerProperty(java.lang.String, int, boolean) registered property}
   9.523           * changes its value.
   9.524 -         * 
   9.525 +         *
   9.526           * @param model the object that has the property
   9.527           * @param index the index of the property during registration
   9.528           */
   9.529          protected abstract void onChange(Model model, int index);
   9.530 -        
   9.531 +
   9.532          /** Finds out if there is an associated proto-object for given
   9.533           * object.
   9.534 -         * 
   9.535 +         *
   9.536           * @param object an object, presumably (but not necessarily) instance of Model class
   9.537           * @return associated proto-object or <code>null</code>
   9.538           */
   9.539          protected abstract Proto protoFor(Object object);
   9.540  
   9.541 -        /** Called to report results of asynchronous over-the-wire 
   9.542 +        /** Called to report results of asynchronous over-the-wire
   9.543           * communication. Result of calling {@link Proto#wsOpen(int, java.lang.String, java.lang.Object)}
   9.544           * or {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}.
   9.545 -         * 
   9.546 +         *
   9.547           * @param model the instance of the model class
   9.548           * @param index index used during initiating the communication (via <code>loadJSON</code> or <code>wsOpen</code> calls)
   9.549           * @param type type of the message: 0 - onOpen, 1 - onMessage, 2 - onError, 3 - onClose -
   9.550 @@ -656,11 +688,11 @@
   9.551          protected void onMessage(Model model, int index, int type, Object data) {
   9.552              onMessage(model, index, type, data, new Object[0]);
   9.553          }
   9.554 -        
   9.555 -        /** Called to report results of asynchronous over-the-wire 
   9.556 +
   9.557 +        /** Called to report results of asynchronous over-the-wire
   9.558           * communication. Result of calling {@link Proto#wsOpen(int, java.lang.String, java.lang.Object)}
   9.559           * or {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}.
   9.560 -         * 
   9.561 +         *
   9.562           * @param model the instance of the model class
   9.563           * @param index index used during initiating the communication (via <code>loadJSON</code> or <code>wsOpen</code> calls)
   9.564           * @param type type of the message: 0 - onOpen, 1 - onMessage, 2 - onError, 3 - onClose -
   9.565 @@ -682,7 +714,7 @@
   9.566  
   9.567          /** Converts and array of raw JSON objects into an array of typed
   9.568           * Java {@link Model} classes.
   9.569 -         * 
   9.570 +         *
   9.571           * @param <T> the type of the destination array
   9.572           * @param context browser context to use
   9.573           * @param src array of raw JSON objects
   9.574 @@ -694,7 +726,7 @@
   9.575                  dest[i] = org.netbeans.html.json.impl.JSON.read(context, destType, src[i]);
   9.576              }
   9.577          }
   9.578 -        
   9.579 +
   9.580          /** Compares two objects that can be converted to integers.
   9.581           * @param a first value
   9.582           * @param b second value
   9.583 @@ -719,7 +751,7 @@
   9.584           * @param a first value
   9.585           * @param b second value
   9.586           * @return true if they are equals
   9.587 -         */ 
   9.588 +         */
   9.589          public final boolean isSame(Object a, Object b) {
   9.590              if (a == b) {
   9.591                  return true;
   9.592 @@ -739,18 +771,18 @@
   9.593          public final int hashPlus(Object o, int h) {
   9.594              return o == null ? h : h ^ o.hashCode();
   9.595          }
   9.596 -        
   9.597 +
   9.598          /** Converts an object to its JSON value.
   9.599 -         * 
   9.600 +         *
   9.601           * @param obj the object to convert
   9.602           * @return JSON representation of the object
   9.603           */
   9.604          public final String toJSON(Object obj) {
   9.605              return JSON.toJSON(obj);
   9.606          }
   9.607 -        
   9.608 +
   9.609          /** Converts the value to string.
   9.610 -         * 
   9.611 +         *
   9.612           * @param val the value
   9.613           * @return the converted value
   9.614           */
   9.615 @@ -759,7 +791,7 @@
   9.616          }
   9.617  
   9.618          /** Converts the value to number.
   9.619 -         * 
   9.620 +         *
   9.621           * @param val the value
   9.622           * @return the converted value
   9.623           */
   9.624 @@ -768,7 +800,7 @@
   9.625          }
   9.626  
   9.627          /** Converts the value to character.
   9.628 -         * 
   9.629 +         *
   9.630           * @param val the value
   9.631           * @return the converted value
   9.632           */
   9.633 @@ -777,16 +809,16 @@
   9.634          }
   9.635  
   9.636          /** Converts the value to boolean.
   9.637 -         * 
   9.638 +         *
   9.639           * @param val the value
   9.640           * @return the converted value
   9.641           */
   9.642          public final Boolean boolValue(Object val) {
   9.643              return JSON.boolValue(val);
   9.644          }
   9.645 -        
   9.646 +
   9.647          /** Extracts value of specific type from given object.
   9.648 -         * 
   9.649 +         *
   9.650           * @param <T> the type of object one is interested in
   9.651           * @param type the type
   9.652           * @param val the object to convert to type
   9.653 @@ -833,7 +865,7 @@
   9.654           * takes the provided collection, empties it and fills it again
   9.655           * with values extracted from <code>value</code> (which is supposed
   9.656           * to be an array).
   9.657 -         * 
   9.658 +         *
   9.659           * @param <T> the type of list elements
   9.660           * @param arr collection to fill with elements in value
   9.661           * @param type the type of elements in the collection
    10.1 --- a/json/src/test/java/net/java/html/json/ModelProcessorTest.java	Wed May 27 22:40:01 2015 +0200
    10.2 +++ b/json/src/test/java/net/java/html/json/ModelProcessorTest.java	Wed May 27 23:37:25 2015 +0200
    10.3 @@ -65,7 +65,7 @@
    10.4              + "})\n"
    10.5              + "class X {\n"
    10.6              + "}\n";
    10.7 -        
    10.8 +
    10.9          Compile c = Compile.create(html, code);
   10.10          assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
   10.11          boolean ok = false;
   10.12 @@ -81,7 +81,7 @@
   10.13              fail("Should contain warning about Runnable:" + msgs);
   10.14          }
   10.15      }
   10.16 -    
   10.17 +
   10.18      @Test public void verifyWrongTypeInInnerClass() throws IOException {
   10.19          String html = "<html><body>"
   10.20              + "</body></html>";
   10.21 @@ -95,7 +95,7 @@
   10.22              + "  static class Inner {\n"
   10.23              + "  }\n"
   10.24              + "}\n";
   10.25 -        
   10.26 +
   10.27          Compile c = Compile.create(html, code);
   10.28          assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
   10.29          boolean ok = false;
   10.30 @@ -111,7 +111,7 @@
   10.31              fail("Should contain warning about Runnable:" + msgs);
   10.32          }
   10.33      }
   10.34 -    
   10.35 +
   10.36      @Test public void warnOnNonStatic() throws IOException {
   10.37          String html = "<html><body>"
   10.38              + "</body></html>";
   10.39 @@ -127,7 +127,7 @@
   10.40              + "        return prop;\n"
   10.41              + "    }\n"
   10.42              + "}\n";
   10.43 -        
   10.44 +
   10.45          Compile c = Compile.create(html, code);
   10.46          assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
   10.47          boolean ok = false;
   10.48 @@ -143,7 +143,7 @@
   10.49              fail("Should contain warning about non-static method:" + msgs);
   10.50          }
   10.51      }
   10.52 -    
   10.53 +
   10.54      @Test public void computedCantReturnVoid() throws IOException {
   10.55          String html = "<html><body>"
   10.56              + "</body></html>";
   10.57 @@ -158,7 +158,7 @@
   10.58              + "    @ComputedProperty static void y(int prop) {\n"
   10.59              + "    }\n"
   10.60              + "}\n";
   10.61 -        
   10.62 +
   10.63          Compile c = Compile.create(html, code);
   10.64          assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
   10.65          boolean ok = false;
   10.66 @@ -174,7 +174,7 @@
   10.67              fail("Should contain warning about non-static method:" + msgs);
   10.68          }
   10.69      }
   10.70 -    
   10.71 +
   10.72      @Test public void computedCantReturnRunnable() throws IOException {
   10.73          String html = "<html><body>"
   10.74              + "</body></html>";
   10.75 @@ -190,7 +190,7 @@
   10.76              + "       return null;\n"
   10.77              + "    }\n"
   10.78              + "}\n";
   10.79 -        
   10.80 +
   10.81          Compile c = Compile.create(html, code);
   10.82          assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
   10.83          boolean ok = false;
   10.84 @@ -206,7 +206,7 @@
   10.85              fail("Should contain warning about non-static method:" + msgs);
   10.86          }
   10.87      }
   10.88 -    
   10.89 +
   10.90      @Test public void canWeCompileWithJDK1_5SourceLevel() throws IOException {
   10.91          String html = "<html><body>"
   10.92              + "</body></html>";
   10.93 @@ -220,11 +220,11 @@
   10.94              + "class X {\n"
   10.95              + "  @ComputedProperty static double derived(long prop) { return prop; }"
   10.96              + "}\n";
   10.97 -        
   10.98 +
   10.99          Compile c = Compile.create(html, code, "1.5");
  10.100          assertTrue(c.getErrors().isEmpty(), "No errors: " + c.getErrors());
  10.101      }
  10.102 -    
  10.103 +
  10.104      @Test public void putNeedsDataArgument() throws Exception {
  10.105          needsAnArg("PUT");
  10.106      }
  10.107 @@ -232,7 +232,7 @@
  10.108      @Test public void postNeedsDataArgument() throws Exception {
  10.109          needsAnArg("POST");
  10.110      }
  10.111 -    
  10.112 +
  10.113      private void needsAnArg(String method) throws Exception {
  10.114          String html = "<html><body>"
  10.115              + "</body></html>";
  10.116 @@ -250,7 +250,7 @@
  10.117              + "  @OnReceive(method=\"" + method + "\", url=\"whereever\")\n"
  10.118              + "  static void obtained(XModel m, PQ p) { }\n"
  10.119              + "}\n";
  10.120 -        
  10.121 +
  10.122          Compile c = Compile.create(html, code);
  10.123          assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
  10.124          for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
  10.125 @@ -260,10 +260,10 @@
  10.126              }
  10.127          }
  10.128          fail("Needs an error message about missing data():\n" + c.getErrors());
  10.129 -        
  10.130 +
  10.131      }
  10.132 -    
  10.133 -    
  10.134 +
  10.135 +
  10.136      @Test public void jsonNeedsToUseGet () throws Exception {
  10.137          String html = "<html><body>"
  10.138              + "</body></html>";
  10.139 @@ -281,7 +281,7 @@
  10.140              + "  @OnReceive(method=\"POST\", jsonp=\"callback\", url=\"whereever\")\n"
  10.141              + "  static void obtained(XModel m, PQ p) { }\n"
  10.142              + "}\n";
  10.143 -        
  10.144 +
  10.145          Compile c = Compile.create(html, code);
  10.146          assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
  10.147          for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
  10.148 @@ -291,9 +291,99 @@
  10.149              }
  10.150          }
  10.151          fail("Needs an error message about wrong method:\n" + c.getErrors());
  10.152 -        
  10.153 +
  10.154      }
  10.155 -    
  10.156 +
  10.157 +    @Test public void noHeadersForWebSockets() throws Exception {
  10.158 +        String html = "<html><body>"
  10.159 +            + "</body></html>";
  10.160 +        String code = "package x.y.z;\n"
  10.161 +            + "import net.java.html.json.Model;\n"
  10.162 +            + "import net.java.html.json.Property;\n"
  10.163 +            + "import net.java.html.json.OnReceive;\n"
  10.164 +            + "@Model(className=\"XModel\", properties={\n"
  10.165 +            + "  @Property(name=\"prop\", type=long.class)\n"
  10.166 +            + "})\n"
  10.167 +            + "class X {\n"
  10.168 +            + "  @Model(className=\"PQ\", properties={})\n"
  10.169 +            + "  class PImpl {\n"
  10.170 +            + "  }\n"
  10.171 +            + "  @OnReceive(method=\"WebSocket\", data = PQ.class, headers=\"SomeHeader: {some}\", url=\"whereever\")\n"
  10.172 +            + "  static void obtained(XModel m, PQ p) { }\n"
  10.173 +            + "}\n";
  10.174 +
  10.175 +        Compile c = Compile.create(html, code);
  10.176 +        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
  10.177 +        for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
  10.178 +            String msg = diagnostic.getMessage(Locale.ENGLISH);
  10.179 +            if (msg.contains("WebSocket spec does not support headers")) {
  10.180 +                return;
  10.181 +            }
  10.182 +        }
  10.183 +        fail("Needs an error message about headers:\n" + c.getErrors());
  10.184 +
  10.185 +    }
  10.186 +
  10.187 +    @Test public void noNewLinesInHeaderLines() throws Exception {
  10.188 +        String html = "<html><body>"
  10.189 +            + "</body></html>";
  10.190 +        String code = "package x.y.z;\n"
  10.191 +            + "import net.java.html.json.Model;\n"
  10.192 +            + "import net.java.html.json.Property;\n"
  10.193 +            + "import net.java.html.json.OnReceive;\n"
  10.194 +            + "@Model(className=\"XModel\", properties={\n"
  10.195 +            + "  @Property(name=\"prop\", type=long.class)\n"
  10.196 +            + "})\n"
  10.197 +            + "class X {\n"
  10.198 +            + "  @Model(className=\"PQ\", properties={})\n"
  10.199 +            + "  class PImpl {\n"
  10.200 +            + "  }\n"
  10.201 +            + "  @OnReceive(headers=\"SomeHeader\\n: {some}\", url=\"whereever\")\n"
  10.202 +            + "  static void obtained(XModel m, PQ p) { }\n"
  10.203 +            + "}\n";
  10.204 +
  10.205 +        Compile c = Compile.create(html, code);
  10.206 +        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
  10.207 +        for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
  10.208 +            String msg = diagnostic.getMessage(Locale.ENGLISH);
  10.209 +            if (msg.contains("Header line cannot contain line separator")) {
  10.210 +                return;
  10.211 +            }
  10.212 +        }
  10.213 +        fail("Needs an error message about headers:\n" + c.getErrors());
  10.214 +
  10.215 +    }
  10.216 +
  10.217 +    @Test public void noReturnInHeaderLines() throws Exception {
  10.218 +        String html = "<html><body>"
  10.219 +            + "</body></html>";
  10.220 +        String code = "package x.y.z;\n"
  10.221 +            + "import net.java.html.json.Model;\n"
  10.222 +            + "import net.java.html.json.Property;\n"
  10.223 +            + "import net.java.html.json.OnReceive;\n"
  10.224 +            + "@Model(className=\"XModel\", properties={\n"
  10.225 +            + "  @Property(name=\"prop\", type=long.class)\n"
  10.226 +            + "})\n"
  10.227 +            + "class X {\n"
  10.228 +            + "  @Model(className=\"PQ\", properties={})\n"
  10.229 +            + "  class PImpl {\n"
  10.230 +            + "  }\n"
  10.231 +            + "  @OnReceive(headers=\"Some\\rHeader: {some}\", url=\"whereever\")\n"
  10.232 +            + "  static void obtained(XModel m, PQ p) { }\n"
  10.233 +            + "}\n";
  10.234 +
  10.235 +        Compile c = Compile.create(html, code);
  10.236 +        assertFalse(c.getErrors().isEmpty(), "One error: " + c.getErrors());
  10.237 +        for (Diagnostic<? extends JavaFileObject> diagnostic : c.getErrors()) {
  10.238 +            String msg = diagnostic.getMessage(Locale.ENGLISH);
  10.239 +            if (msg.contains("Header line cannot contain line separator")) {
  10.240 +                return;
  10.241 +            }
  10.242 +        }
  10.243 +        fail("Needs an error message about headers:\n" + c.getErrors());
  10.244 +
  10.245 +    }
  10.246 +
  10.247      @Test public void onErrorHasToExist() throws IOException {
  10.248          Compile res = Compile.create("", "package x;\n"
  10.249              + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
  10.250 @@ -308,7 +398,7 @@
  10.251          res.assertErrors();
  10.252          res.assertError("not find doesNotExist");
  10.253      }
  10.254 -    
  10.255 +
  10.256      @Test public void usingListIsOK() throws IOException {
  10.257          Compile res = Compile.create("", "package x;\n"
  10.258              + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
  10.259 @@ -370,7 +460,7 @@
  10.260          res.assertErrors();
  10.261          res.assertError("Cannot have the name");
  10.262      }
  10.263 -    
  10.264 +
  10.265      @Test public void onWebSocketJustTwoArgs() throws IOException {
  10.266          Compile res = Compile.create("", "package x;\n"
  10.267              + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
  10.268 @@ -385,7 +475,7 @@
  10.269          res.assertErrors();
  10.270          res.assertError("only have two arg");
  10.271      }
  10.272 -    
  10.273 +
  10.274      @Test public void onErrorWouldHaveToBeStatic() throws IOException {
  10.275          Compile res = Compile.create("", "package x;\n"
  10.276              + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
    11.1 --- a/json/src/test/java/net/java/html/json/ModelTest.java	Wed May 27 22:40:01 2015 +0200
    11.2 +++ b/json/src/test/java/net/java/html/json/ModelTest.java	Wed May 27 23:37:25 2015 +0200
    11.3 @@ -227,6 +227,18 @@
    11.4  
    11.5      }
    11.6  
    11.7 +    @OnReceive(url="{url}", headers={
    11.8 +        "Easy: {easy}",
    11.9 +        "H-a+r!d?e.r: {harder}",
   11.10 +        "H-a+r!d?e's\"t: {harder}",
   11.11 +        "Repeat-ed: {rep}",
   11.12 +        "Repeat+ed: {rep}",
   11.13 +        "Same-URL: {url}"
   11.14 +    })
   11.15 +    static void fetchPeopleWithHeaders(Modelik model, People p) {
   11.16 +        model.fetchPeopleWithHeaders("url", "easy", "harder", "rep");
   11.17 +    }
   11.18 +
   11.19      @OnReceive(url = "{protocol}://{host}?callback={back}&query={query}", jsonp = "back")
   11.20      static void loadPeopleViaJSONP(Modelik thiz, People p) {
   11.21          Modelik m = null;
    12.1 --- a/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/DynamicHTTP.java	Wed May 27 22:40:01 2015 +0200
    12.2 +++ b/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/DynamicHTTP.java	Wed May 27 23:37:25 2015 +0200
    12.3 @@ -76,28 +76,28 @@
    12.4      private static List<Resource> resources;
    12.5      private static ServerConfiguration conf;
    12.6      private static HttpServer server;
    12.7 -    
    12.8 +
    12.9      private DynamicHTTP() {
   12.10      }
   12.11 -    
   12.12 +
   12.13      static URI initServer() throws Exception {
   12.14          server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
   12.15          final WebSocketAddOn addon = new WebSocketAddOn();
   12.16          for (NetworkListener listener : server.getListeners()) {
   12.17              listener.registerAddOn(addon);
   12.18 -        }        
   12.19 +        }
   12.20          resources = new ArrayList<Resource>();
   12.21  
   12.22          conf = server.getServerConfiguration();
   12.23          final DynamicHTTP dh = new DynamicHTTP();
   12.24  
   12.25          conf.addHttpHandler(dh, "/");
   12.26 -        
   12.27 +
   12.28          server.start();
   12.29  
   12.30          return pageURL("http", server, "/test.html");
   12.31      }
   12.32 -    
   12.33 +
   12.34      @Override
   12.35      public void service(Request request, Response response) throws Exception {
   12.36          if ("/test.html".equals(request.getRequestURI())) {
   12.37 @@ -159,6 +159,8 @@
   12.38                                      sb.append((char) ch);
   12.39                                  }
   12.40                                  params[i] = sb.toString();
   12.41 +                            } else if (r.parameters[i].startsWith("http.header.")) {
   12.42 +                                params[i] = request.getHeader(r.parameters[i].substring(12));
   12.43                              }
   12.44                          }
   12.45                          if (params[i] == null) {
   12.46 @@ -171,7 +173,7 @@
   12.47              }
   12.48          }
   12.49      }
   12.50 -    
   12.51 +
   12.52      private URI registerWebSocket(Resource r) {
   12.53          WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
   12.54          return pageURL("ws", server, r.httpPath);
   12.55 @@ -184,7 +186,7 @@
   12.56          }
   12.57          return pageURL("http", server, r.httpPath);
   12.58      }
   12.59 -    
   12.60 +
   12.61      private static URI pageURL(String proto, HttpServer server, final String page) {
   12.62          NetworkListener listener = server.getListeners().iterator().next();
   12.63          int port = listener.getPort();
   12.64 @@ -194,7 +196,7 @@
   12.65              throw new IllegalStateException(ex);
   12.66          }
   12.67      }
   12.68 -    
   12.69 +
   12.70      static final class Resource {
   12.71  
   12.72          final InputStream httpContent;
   12.73 @@ -235,7 +237,7 @@
   12.74              }
   12.75          }
   12.76      }
   12.77 -    
   12.78 +
   12.79      private static class WS extends WebSocketApplication {
   12.80          private final Resource r;
   12.81  
    13.1 --- a/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/DynamicHTTP.java	Wed May 27 22:40:01 2015 +0200
    13.2 +++ b/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/DynamicHTTP.java	Wed May 27 23:37:25 2015 +0200
    13.3 @@ -76,28 +76,28 @@
    13.4      private static List<Resource> resources;
    13.5      private static ServerConfiguration conf;
    13.6      private static HttpServer server;
    13.7 -    
    13.8 +
    13.9      private DynamicHTTP() {
   13.10      }
   13.11 -    
   13.12 +
   13.13      static URI initServer() throws Exception {
   13.14          server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
   13.15          final WebSocketAddOn addon = new WebSocketAddOn();
   13.16          for (NetworkListener listener : server.getListeners()) {
   13.17              listener.registerAddOn(addon);
   13.18 -        }        
   13.19 +        }
   13.20          resources = new ArrayList<Resource>();
   13.21  
   13.22          conf = server.getServerConfiguration();
   13.23          final DynamicHTTP dh = new DynamicHTTP();
   13.24  
   13.25          conf.addHttpHandler(dh, "/");
   13.26 -        
   13.27 +
   13.28          server.start();
   13.29  
   13.30          return pageURL("http", server, "/test.html");
   13.31      }
   13.32 -    
   13.33 +
   13.34      @Override
   13.35      public void service(Request request, Response response) throws Exception {
   13.36          if ("/test.html".equals(request.getRequestURI())) {
   13.37 @@ -159,6 +159,8 @@
   13.38                                      sb.append((char) ch);
   13.39                                  }
   13.40                                  params[i] = sb.toString();
   13.41 +                            } else if (r.parameters[i].startsWith("http.header.")) {
   13.42 +                                params[i] = request.getHeader(r.parameters[i].substring(12));
   13.43                              }
   13.44                          }
   13.45                          if (params[i] == null) {
   13.46 @@ -171,7 +173,7 @@
   13.47              }
   13.48          }
   13.49      }
   13.50 -    
   13.51 +
   13.52      private URI registerWebSocket(Resource r) {
   13.53          WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
   13.54          return pageURL("ws", server, r.httpPath);
   13.55 @@ -184,7 +186,7 @@
   13.56          }
   13.57          return pageURL("http", server, r.httpPath);
   13.58      }
   13.59 -    
   13.60 +
   13.61      private static URI pageURL(String proto, HttpServer server, final String page) {
   13.62          NetworkListener listener = server.getListeners().iterator().next();
   13.63          int port = listener.getPort();
   13.64 @@ -194,7 +196,7 @@
   13.65              throw new IllegalStateException(ex);
   13.66          }
   13.67      }
   13.68 -    
   13.69 +
   13.70      static final class Resource {
   13.71  
   13.72          final InputStream httpContent;
   13.73 @@ -235,7 +237,7 @@
   13.74              }
   13.75          }
   13.76      }
   13.77 -    
   13.78 +
   13.79      private static class WS extends WebSocketApplication {
   13.80          private final Resource r;
   13.81  
    14.1 --- a/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java	Wed May 27 22:40:01 2015 +0200
    14.2 +++ b/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java	Wed May 27 23:37:25 2015 +0200
    14.3 @@ -59,11 +59,11 @@
    14.4  import java.util.concurrent.ThreadFactory;
    14.5  import java.util.logging.Logger;
    14.6  import net.java.html.js.JavaScriptBody;
    14.7 -import org.netbeans.html.json.spi.JSONCall;
    14.8  import org.json.JSONArray;
    14.9  import org.json.JSONException;
   14.10  import org.json.JSONObject;
   14.11  import org.json.JSONTokener;
   14.12 +import org.netbeans.html.json.spi.JSONCall;
   14.13  
   14.14  /** This is an implementation package - just
   14.15   * include its JAR on classpath and use official {@link Context} API
   14.16 @@ -101,7 +101,7 @@
   14.17          final String url;
   14.18          Throwable error = null;
   14.19          Object json = null;
   14.20 -        
   14.21 +
   14.22          if (call.isJSONP()) {
   14.23              url = call.composeURL("dummy");
   14.24          } else {
   14.25 @@ -113,6 +113,28 @@
   14.26              if (call.isDoOutput()) {
   14.27                  conn.setDoOutput(true);
   14.28              }
   14.29 +            String h = call.getHeaders();
   14.30 +            if (h != null) {
   14.31 +                int pos = 0;
   14.32 +                while (pos < h.length()) {
   14.33 +                    int tagEnd = h.indexOf(':', pos);
   14.34 +                    if (tagEnd == -1) {
   14.35 +                        break;
   14.36 +                    }
   14.37 +                    int r = h.indexOf('\r', tagEnd);
   14.38 +                    int n = h.indexOf('\n', tagEnd);
   14.39 +                    if (r == -1) {
   14.40 +                        r = h.length();
   14.41 +                    }
   14.42 +                    if (n == -1) {
   14.43 +                        n = h.length();
   14.44 +                    }
   14.45 +                    String key = h.substring(pos, tagEnd).trim();
   14.46 +                    String val = h.substring(tagEnd + 1, Math.min(r, n)).trim();
   14.47 +                    conn.setRequestProperty(key, val);;
   14.48 +                    pos = Math.max(r, n);
   14.49 +                }
   14.50 +            }
   14.51              if (call.getMethod() != null && conn instanceof HttpURLConnection) {
   14.52                  ((HttpURLConnection) conn).setRequestMethod(call.getMethod());
   14.53              }
   14.54 @@ -235,7 +257,7 @@
   14.55              return o;
   14.56          }
   14.57      }
   14.58 -    
   14.59 +
   14.60      public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
   14.61          if (jsonObject instanceof JSONObject) {
   14.62              JSONObject obj = (JSONObject)jsonObject;
   14.63 @@ -252,7 +274,7 @@
   14.64              values[i] = getProperty(jsonObject, props[i]);
   14.65          }
   14.66      }
   14.67 -    
   14.68 +
   14.69      @JavaScriptBody(args = {"object", "property"},
   14.70              body
   14.71              = "if (property === null) return object;\n"
   14.72 @@ -262,7 +284,7 @@
   14.73      private static Object getProperty(Object object, String property) {
   14.74          return null;
   14.75      }
   14.76 -    
   14.77 +
   14.78      public static Object parse(InputStream is) throws IOException {
   14.79          try {
   14.80              PushbackInputStream push = new PushbackInputStream(is, 1);
    15.1 --- a/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusDynamicHTTP.java	Wed May 27 22:40:01 2015 +0200
    15.2 +++ b/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusDynamicHTTP.java	Wed May 27 23:37:25 2015 +0200
    15.3 @@ -75,28 +75,28 @@
    15.4      private static List<Resource> resources;
    15.5      private static ServerConfiguration conf;
    15.6      private static HttpServer server;
    15.7 -    
    15.8 +
    15.9      private TyrusDynamicHTTP() {
   15.10      }
   15.11 -    
   15.12 +
   15.13      static URI initServer() throws Exception {
   15.14          server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
   15.15          final WebSocketAddOn addon = new WebSocketAddOn();
   15.16          for (NetworkListener listener : server.getListeners()) {
   15.17              listener.registerAddOn(addon);
   15.18 -        }        
   15.19 +        }
   15.20          resources = new ArrayList<Resource>();
   15.21  
   15.22          conf = server.getServerConfiguration();
   15.23          final TyrusDynamicHTTP dh = new TyrusDynamicHTTP();
   15.24  
   15.25          conf.addHttpHandler(dh, "/");
   15.26 -        
   15.27 +
   15.28          server.start();
   15.29  
   15.30          return pageURL("http", server, "/test.html");
   15.31      }
   15.32 -    
   15.33 +
   15.34      @Override
   15.35      public void service(Request request, Response response) throws Exception {
   15.36          if ("/test.html".equals(request.getRequestURI())) {
   15.37 @@ -158,6 +158,8 @@
   15.38                                      sb.append((char) ch);
   15.39                                  }
   15.40                                  params[i] = sb.toString();
   15.41 +                            } else if (r.parameters[i].startsWith("http.header.")) {
   15.42 +                                params[i] = request.getHeader(r.parameters[i].substring(12));
   15.43                              }
   15.44                          }
   15.45                          if (params[i] == null) {
   15.46 @@ -170,7 +172,7 @@
   15.47              }
   15.48          }
   15.49      }
   15.50 -    
   15.51 +
   15.52      private URI registerWebSocket(Resource r) {
   15.53          WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
   15.54          return pageURL("ws", server, r.httpPath);
   15.55 @@ -183,7 +185,7 @@
   15.56          }
   15.57          return pageURL("http", server, r.httpPath);
   15.58      }
   15.59 -    
   15.60 +
   15.61      private static URI pageURL(String proto, HttpServer server, final String page) {
   15.62          NetworkListener listener = server.getListeners().iterator().next();
   15.63          int port = listener.getPort();
   15.64 @@ -193,7 +195,7 @@
   15.65              throw new IllegalStateException(ex);
   15.66          }
   15.67      }
   15.68 -    
   15.69 +
   15.70      static final class Resource {
   15.71  
   15.72          final InputStream httpContent;
   15.73 @@ -234,7 +236,7 @@
   15.74              }
   15.75          }
   15.76      }
   15.77 -    
   15.78 +
   15.79      private static class WS extends WebSocketApplication {
   15.80          private final Resource r;
   15.81  
   15.82 @@ -255,6 +257,6 @@
   15.83              }
   15.84          }
   15.85          private static final Logger LOG = Logger.getLogger(WS.class.getName());
   15.86 -        
   15.87 +
   15.88      }
   15.89  }
    16.1 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/KOTransfer.java	Wed May 27 22:40:01 2015 +0200
    16.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTransfer.java	Wed May 27 23:37:25 2015 +0200
    16.3 @@ -46,6 +46,8 @@
    16.4  import java.io.IOException;
    16.5  import java.io.InputStream;
    16.6  import java.io.InputStreamReader;
    16.7 +import java.util.ArrayList;
    16.8 +import java.util.List;
    16.9  import org.netbeans.html.context.spi.Contexts;
   16.10  import org.netbeans.html.json.spi.JSONCall;
   16.11  import org.netbeans.html.json.spi.Transfer;
   16.12 @@ -62,7 +64,7 @@
   16.13  implements Transfer {
   16.14      KOTransfer() {
   16.15      }
   16.16 -    
   16.17 +
   16.18      @Override
   16.19      public void extract(Object obj, String[] props, Object[] values) {
   16.20          if (obj instanceof JSObjToStr) {
   16.21 @@ -87,7 +89,29 @@
   16.22                      call.notifyError(ex);
   16.23                  }
   16.24              }
   16.25 -            LoadJSON.loadJSON(call.composeURL(null), call, call.getMethod(), data);
   16.26 +            List<String> headerPairs = new ArrayList<String>();
   16.27 +            String h = call.getHeaders();
   16.28 +            if (h != null) {
   16.29 +                int pos = 0;
   16.30 +                while (pos < h.length()) {
   16.31 +                    int tagEnd = h.indexOf(':', pos);
   16.32 +                    if (tagEnd == -1) {
   16.33 +                        break;
   16.34 +                    }
   16.35 +                    int r = h.indexOf('\r', tagEnd);
   16.36 +                    int n = h.indexOf('\n', tagEnd);
   16.37 +                    if (r == -1) {
   16.38 +                        r = h.length();
   16.39 +                    }
   16.40 +                    if (n == -1) {
   16.41 +                        n = h.length();
   16.42 +                    }
   16.43 +                    headerPairs.add(h.substring(pos, tagEnd).trim());
   16.44 +                    headerPairs.add(h.substring(tagEnd + 1, Math.min(r, n)).trim());
   16.45 +                    pos = Math.max(r, n);
   16.46 +                }
   16.47 +            }
   16.48 +            LoadJSON.loadJSON(call.composeURL(null), call, call.getMethod(), data, headerPairs.toArray());
   16.49          }
   16.50      }
   16.51  
   16.52 @@ -104,7 +128,7 @@
   16.53          }
   16.54          return LoadJSON.parse(sb.toString());
   16.55      }
   16.56 -    
   16.57 +
   16.58      static void notifySuccess(Object done, Object str, Object data) {
   16.59          Object notifyObj;
   16.60          if (data instanceof Object[]) {
   16.61 @@ -118,11 +142,11 @@
   16.62          }
   16.63          ((JSONCall)done).notifySuccess(notifyObj);
   16.64      }
   16.65 -    
   16.66 +
   16.67      static void notifyError(Object done, Object msg) {
   16.68          ((JSONCall)done).notifyError(new Exception(msg.toString()));
   16.69      }
   16.70 -    
   16.71 +
   16.72      private static final class JSObjToStr {
   16.73          final String str;
   16.74          final Object obj;
    17.1 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/LoadJSON.java	Wed May 27 22:40:01 2015 +0200
    17.2 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/LoadJSON.java	Wed May 27 23:37:25 2015 +0200
    17.3 @@ -42,80 +42,16 @@
    17.4   */
    17.5  package org.netbeans.html.ko4j;
    17.6  
    17.7 -import java.io.ByteArrayOutputStream;
    17.8 -import java.io.IOException;
    17.9 -import java.io.InputStream;
   17.10 -import java.io.InputStreamReader;
   17.11  import net.java.html.js.JavaScriptBody;
   17.12  import org.netbeans.html.json.spi.JSONCall;
   17.13 -import org.netbeans.html.json.spi.Transfer;
   17.14 -import org.netbeans.html.json.spi.WSTransfer;
   17.15  
   17.16  /**
   17.17   *
   17.18   * @author Jaroslav Tulach
   17.19   */
   17.20 -final class LoadJSON implements Transfer, WSTransfer<LoadWS> {
   17.21 +final class LoadJSON {
   17.22      private LoadJSON() {}
   17.23 -    
   17.24 -    @Override
   17.25 -    public void extract(Object obj, String[] props, Object[] values) {
   17.26 -        extractJSON(obj, props, values);
   17.27 -    }
   17.28  
   17.29 -    @Override
   17.30 -    public void loadJSON(final JSONCall call) {
   17.31 -        if (call.isJSONP()) {
   17.32 -            String me = createJSONP(call);
   17.33 -            loadJSONP(call.composeURL(me), me);
   17.34 -        } else {
   17.35 -            String data = null;
   17.36 -            if (call.isDoOutput()) {
   17.37 -                try {
   17.38 -                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
   17.39 -                    call.writeData(bos);
   17.40 -                    data = new String(bos.toByteArray(), "UTF-8");
   17.41 -                } catch (IOException ex) {
   17.42 -                    call.notifyError(ex);
   17.43 -                }
   17.44 -            }
   17.45 -            loadJSON(call.composeURL(null), call, call.getMethod(), data);
   17.46 -        }
   17.47 -    }
   17.48 -
   17.49 -    @Override
   17.50 -    public Object toJSON(InputStream is) throws IOException {
   17.51 -        StringBuilder sb = new StringBuilder();
   17.52 -        InputStreamReader r = new InputStreamReader(is);
   17.53 -        for (;;) {
   17.54 -            int ch = r.read();
   17.55 -            if (ch == -1) {
   17.56 -                break;
   17.57 -            }
   17.58 -            sb.append((char)ch);
   17.59 -        }
   17.60 -        return parse(sb.toString());
   17.61 -    }
   17.62 -
   17.63 -    @Override
   17.64 -    public LoadWS open(String url, JSONCall callback) {
   17.65 -        return new LoadWS(callback, url);
   17.66 -    }
   17.67 -
   17.68 -    @Override
   17.69 -    public void send(LoadWS socket, JSONCall data) {
   17.70 -        socket.send(data);
   17.71 -    }
   17.72 -
   17.73 -    @Override
   17.74 -    public void close(LoadWS socket) {
   17.75 -        socket.close();
   17.76 -    }
   17.77 -    
   17.78 -    //
   17.79 -    // implementations
   17.80 -    //
   17.81 -    
   17.82      @JavaScriptBody(args = {"object", "property"},
   17.83          body
   17.84          = "if (property === null) return object;\n"
   17.85 @@ -157,11 +93,16 @@
   17.86          return s;
   17.87      }
   17.88  
   17.89 -    @JavaScriptBody(args = {"url", "done", "method", "data"}, javacall = true, body = ""
   17.90 +    @JavaScriptBody(args = {"url", "done", "method", "data", "hp"}, javacall = true, body = ""
   17.91          + "var request = new XMLHttpRequest();\n"
   17.92          + "if (!method) method = 'GET';\n"
   17.93          + "request.open(method, url, true);\n"
   17.94          + "request.setRequestHeader('Content-Type', 'application/json; charset=utf-8');\n"
   17.95 +        + "for (var i = 0; i < hp.length; i += 2) {\n"
   17.96 +        + "  var h = hp[i];\n"
   17.97 +        + "  var v = hp[i + 1];\n"
   17.98 +        + "  request.setRequestHeader(h, v);\n"
   17.99 +        + "}\n"
  17.100          + "request.onreadystatechange = function() {\n"
  17.101          + "  if (request.readyState !== 4) return;\n"
  17.102          + "  var r = request.response || request.responseText;\n"
  17.103 @@ -182,10 +123,10 @@
  17.104          + "else request.send();\n"
  17.105      )
  17.106      static void loadJSON(
  17.107 -        String url, JSONCall done, String method, String data
  17.108 +        String url, JSONCall done, String method, String data, Object[] headerPairs
  17.109      ) {
  17.110      }
  17.111 -    
  17.112 +
  17.113      @JavaScriptBody(args = {"url", "jsonp"}, body
  17.114          = "var scrpt = window.document.createElement('script');\n "
  17.115          + "scrpt.setAttribute('src', url);\n "
  17.116 @@ -203,5 +144,5 @@
  17.117              values[i] = getProperty(jsonObject, props[i]);
  17.118          }
  17.119      }
  17.120 -    
  17.121 +
  17.122  }
    18.1 --- a/ko4j/src/test/java/org/netbeans/html/ko4j/DynamicHTTP.java	Wed May 27 22:40:01 2015 +0200
    18.2 +++ b/ko4j/src/test/java/org/netbeans/html/ko4j/DynamicHTTP.java	Wed May 27 23:37:25 2015 +0200
    18.3 @@ -76,28 +76,28 @@
    18.4      private static List<Resource> resources;
    18.5      private static ServerConfiguration conf;
    18.6      private static HttpServer server;
    18.7 -    
    18.8 +
    18.9      private DynamicHTTP() {
   18.10      }
   18.11 -    
   18.12 +
   18.13      static URI initServer() throws Exception {
   18.14          server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
   18.15          final WebSocketAddOn addon = new WebSocketAddOn();
   18.16          for (NetworkListener listener : server.getListeners()) {
   18.17              listener.registerAddOn(addon);
   18.18 -        }        
   18.19 +        }
   18.20          resources = new ArrayList<Resource>();
   18.21  
   18.22          conf = server.getServerConfiguration();
   18.23          final DynamicHTTP dh = new DynamicHTTP();
   18.24  
   18.25          conf.addHttpHandler(dh, "/");
   18.26 -        
   18.27 +
   18.28          server.start();
   18.29  
   18.30          return pageURL("http", server, "/test.html");
   18.31      }
   18.32 -    
   18.33 +
   18.34      @Override
   18.35      public void service(Request request, Response response) throws Exception {
   18.36          if ("/test.html".equals(request.getRequestURI())) {
   18.37 @@ -159,6 +159,8 @@
   18.38                                      sb.append((char) ch);
   18.39                                  }
   18.40                                  params[i] = sb.toString();
   18.41 +                            } else if (r.parameters[i].startsWith("http.header.")) {
   18.42 +                                params[i] = request.getHeader(r.parameters[i].substring(12));
   18.43                              }
   18.44                          }
   18.45                          if (params[i] == null) {
   18.46 @@ -171,7 +173,7 @@
   18.47              }
   18.48          }
   18.49      }
   18.50 -    
   18.51 +
   18.52      private URI registerWebSocket(Resource r) {
   18.53          WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
   18.54          return pageURL("ws", server, r.httpPath);
   18.55 @@ -184,7 +186,7 @@
   18.56          }
   18.57          return pageURL("http", server, r.httpPath);
   18.58      }
   18.59 -    
   18.60 +
   18.61      private static URI pageURL(String proto, HttpServer server, final String page) {
   18.62          NetworkListener listener = server.getListeners().iterator().next();
   18.63          int port = listener.getPort();
   18.64 @@ -194,7 +196,7 @@
   18.65              throw new IllegalStateException(ex);
   18.66          }
   18.67      }
   18.68 -    
   18.69 +
   18.70      static final class Resource {
   18.71  
   18.72          final InputStream httpContent;
   18.73 @@ -235,7 +237,7 @@
   18.74              }
   18.75          }
   18.76      }
   18.77 -    
   18.78 +
   18.79      private static class WS extends WebSocketApplication {
   18.80          private final Resource r;
   18.81  
    19.1 --- a/src/main/javadoc/overview.html	Wed May 27 22:40:01 2015 +0200
    19.2 +++ b/src/main/javadoc/overview.html	Wed May 27 23:37:25 2015 +0200
    19.3 @@ -51,62 +51,65 @@
    19.4      </head>
    19.5      <body>
    19.6          <p>
    19.7 -         Use Java to write application logic; Use HTML5 to render the UI; 
    19.8 +         Use Java to write application logic; Use HTML5 to render the UI;
    19.9           {@link net.java.html.json.Model Animate an HTML page from Java}
   19.10           (see <a target="_blank" href="http://dew.apidesign.org/dew/#7212206">Duke being rotated</a> by CSS);
   19.11           Use {@link net.java.html.json.OnReceive REST} or
   19.12           <a href="net/java/html/json/doc-files/websockets.html">WebSockets</a>;
   19.13           interact with <a href="net/java/html/js/package-summary.html">JavaScript</a>;
   19.14           Get the best of both worlds!
   19.15 -         
   19.16 -         The goal of these APIs is to use full featured Java runtime 
   19.17 -         (like real <a href="http://wiki.apidesign.org/wiki/HotSpot">HotSpot VM</a>), 
   19.18 -         but still rely on a very lightweight rendering technology 
   19.19 -         (so it can potentially fit 
   19.20 +
   19.21 +         The goal of these APIs is to use full featured Java runtime
   19.22 +         (like real <a href="http://wiki.apidesign.org/wiki/HotSpot">HotSpot VM</a>),
   19.23 +         but still rely on a very lightweight rendering technology
   19.24 +         (so it can potentially fit
   19.25           <a href="http://bck2brwsr.apidesign.org">Bck2Brwsr</a> and definitely
   19.26 -         to various types of phones). What can be more lightweight 
   19.27 -         (from a browser perspective) than 
   19.28 +         to various types of phones). What can be more lightweight
   19.29 +         (from a browser perspective) than
   19.30           <a href="http://wiki.apidesign.org/wiki/HTML">HTML</a>!?
   19.31 -         By default we use {@link net.java.html.boot.fx JavaFX's WebView} 
   19.32 -         component to display the <a href="http://wiki.apidesign.org/wiki/HTML">HTML</a>. 
   19.33 -         We eliminate the need to manipulate the DOM directly, 
   19.34 -         there is a special {@link net.java.html.json Java to Knockout.js binding}. 
   19.35 -         As a result the <a href="http://knockoutjs.com">HTML uses Knockout.js syntax</a>, 
   19.36 +         By default we use {@link net.java.html.boot.fx JavaFX's WebView}
   19.37 +         component to display the <a href="http://wiki.apidesign.org/wiki/HTML">HTML</a>.
   19.38 +         We eliminate the need to manipulate the DOM directly,
   19.39 +         there is a special {@link net.java.html.json Java to Knockout.js binding}.
   19.40 +         As a result the <a href="http://knockoutjs.com">HTML uses Knockout.js syntax</a>,
   19.41           yet the application code can be written in Java.
   19.42          </p>
   19.43 -        
   19.44 +
   19.45          <h3>What's Been Improved in Version 1.2?</h3>
   19.46 -        
   19.47 +
   19.48 +        One can control {@link net.java.html.json.OnReceive#headers() HTTP request headers}
   19.49 +        when connecting to server using the {@link net.java.html.json.OnReceive}
   19.50 +        annotation.
   19.51          Bugfix of issue <a target="_blank" href='https://netbeans.org/bugzilla/show_bug.cgi?id=250503'>250503</a>.
   19.52 -        
   19.53 +
   19.54          <h3>What's New in Version 1.1?</h3>
   19.55 -        
   19.56 +
   19.57          <p>
   19.58              The content of a {@link net.java.html.BrwsrCtx context}
   19.59              can be selected by registering implementations under specific
   19.60 -            {@link org.netbeans.html.context.spi.Contexts.Id technology identifiers} 
   19.61 -            and requesting them during 
   19.62 -            {@link org.netbeans.html.context.spi.Contexts#newBuilder(java.lang.Object...) construction} 
   19.63 +            {@link org.netbeans.html.context.spi.Contexts.Id technology identifiers}
   19.64 +            and requesting them during
   19.65 +            {@link org.netbeans.html.context.spi.Contexts#newBuilder(java.lang.Object...) construction}
   19.66              of the context. <code>org.netbeans.html:ko4j</code> module's implementation
   19.67              offers <b>ko4j</b>, <b>xhr</b> and <b>websocket</b> identifiers
   19.68 -            for its registered services 
   19.69 +            for its registered services
   19.70              (e.g. {@link org.netbeans.html.json.spi.Technology},
   19.71              {@link org.netbeans.html.json.spi.Transfer} and
   19.72              {@link org.netbeans.html.json.spi.WSTransfer}).
   19.73              <code>org.netbeans.html:ko-ws-tyrus</code>
   19.74 -            module registers its 
   19.75 -            {@link org.netbeans.html.json.spi.Transfer Java based JSON} and 
   19.76 +            module registers its
   19.77 +            {@link org.netbeans.html.json.spi.Transfer Java based JSON} and
   19.78              {@link org.netbeans.html.json.spi.WSTransfer WebSocket} implementations
   19.79              under the name <b>tyrus</b>.
   19.80          </p>
   19.81          <p>
   19.82              A particular DOM subtree
   19.83              that a <a target="_blank" href="http://knockoutjs.com">knockout.js</a> model gets
   19.84 -            applied to can be selected by using 
   19.85 +            applied to can be selected by using
   19.86              {@link net.java.html.json.Models#applyBindings(java.lang.Object,java.lang.String)
   19.87              Models.applyBindings(m, id)} with an id of an HTML element.
   19.88 -            There is new {@link net.java.html.json.Model#targetId()} attribute 
   19.89 -            which controls behavior of the generated <code>applyBindings</code> method. 
   19.90 +            There is new {@link net.java.html.json.Model#targetId()} attribute
   19.91 +            which controls behavior of the generated <code>applyBindings</code> method.
   19.92              If <em>specified and non-empty</em>, then the generated method
   19.93              will call {@link net.java.html.json.Models#applyBindings(java.lang.Object,java.lang.String)}
   19.94              with <code>this</code> and the provided {@link net.java.html.json.Model#targetId() target id}.
   19.95 @@ -124,15 +127,15 @@
   19.96              {@link org.netbeans.html.json.spi.PropertyBinding#weak()} and
   19.97              {@link org.netbeans.html.json.spi.FunctionBinding#weak()}) and
   19.98              now the Java {@link net.java.html.json.Model models} can garbage collect,
   19.99 -            when no longer used. Library writers that use 
  19.100 -            {@link net.java.html.js.JavaScriptBody} annotation can also 
  19.101 +            when no longer used. Library writers that use
  19.102 +            {@link net.java.html.js.JavaScriptBody} annotation can also
  19.103              control garbage collection behavior of method arguments by
  19.104              setting {@link net.java.html.js.JavaScriptBody#keepAlive() keepAlive=false}
  19.105              attribute.
  19.106          </p>
  19.107 -        
  19.108 +
  19.109          <h3>What's New in Version 1.0?</h3>
  19.110 -        
  19.111 +
  19.112          <p>
  19.113              {@link net.java.html.json.Property#array() Array properties} are now
  19.114              mutable from the <a href="http://knockoutjs.com">knockout.js</a>
  19.115 @@ -151,14 +154,14 @@
  19.116              {@link net.java.html.BrwsrCtx#execute browser context} to
  19.117              prevent endless debugging when one forgets to do so.
  19.118          </p>
  19.119 -        
  19.120 +
  19.121          <p>
  19.122 -        What's new in older versions? Click the 
  19.123 -        <a href="#" onclick="return showHistoric(true)">link</a> 
  19.124 -        to view even more 
  19.125 +        What's new in older versions? Click the
  19.126 +        <a href="#" onclick="return showHistoric(true)">link</a>
  19.127 +        to view even more
  19.128          <a href="#" onclick="return showHistoric(true)">historic changes</a> below:
  19.129          </p>
  19.130 -        
  19.131 +
  19.132          <a name="historic.changes"></a>
  19.133          <div id="historic.changes">
  19.134              <script>
  19.135 @@ -173,9 +176,9 @@
  19.136              }
  19.137              showHistoric(false);
  19.138              </script>
  19.139 -            
  19.140 +
  19.141          <h3>What's New in Version 0.9?</h3>
  19.142 -        
  19.143 +
  19.144          <p>
  19.145              System can run in {@link net.java.html.boot.BrowserBuilder#classloader(java.lang.ClassLoader) Felix OSGi container} (originally only Equinox).
  19.146              {@link net.java.html.json.ComputedProperty Derived properties}
  19.147 @@ -184,32 +187,32 @@
  19.148              <a target="_blank" href="http://knockoutjs.com">Knockout.js</a> library has been updated
  19.149              to version 3.2.0.
  19.150          </p>
  19.151 -        
  19.152 +
  19.153          <h3>What's New in 0.8.x Versions?</h3>
  19.154 -        
  19.155 +
  19.156          <p>
  19.157              Setters or array properties on classes generated by {@link net.java.html.json.Model}
  19.158              annotation can be accessed from any thread. {@link org.netbeans.html.sound.spi.AudioEnvironment}
  19.159              can be registered into {@link net.java.html.BrwsrCtx}. There is
  19.160              a {@link net.java.html.json.Models#parse(net.java.html.BrwsrCtx, java.lang.Class, java.io.InputStream, java.util.Collection)  method}
  19.161 -            to parse a JSON array and convert it into 
  19.162 +            to parse a JSON array and convert it into
  19.163              {@link net.java.html.json.Model model classes}.
  19.164 -            Improved behavior of <code>enum</code> values in 
  19.165 +            Improved behavior of <code>enum</code> values in
  19.166              {@link net.java.html.json.Model knockout bindings}.
  19.167          </p>
  19.168 -        
  19.169 +
  19.170          <p>
  19.171 -            Few bugfixes for better portability. 
  19.172 +            Few bugfixes for better portability.
  19.173              New API for {@link net.java.html.boot.script.Scripts headless execution}
  19.174              on top of <em>Nashorn</em> - does not run <em>knockout for Java</em>
  19.175 -            fully yet 
  19.176 +            fully yet
  19.177              (reported as <a href="https://bugs.openjdk.java.net/browse/JDK-8046013">JDK-8046013</a>),
  19.178 -            however even in current state it is quite 
  19.179 +            however even in current state it is quite
  19.180              {@link net.java.html.boot.script.Scripts useful for testing}
  19.181 -            of 
  19.182 +            of
  19.183              {@link net.java.html.js Java/JavaScript interactions}.
  19.184          </p>
  19.185 -        
  19.186 +
  19.187          <p>
  19.188              {@link net.java.html.boot.fx.FXBrowsers} has been extended
  19.189              with new helper methods to make it easier to use HTML+Java
  19.190 @@ -226,22 +229,22 @@
  19.191              extended to work on systems that don't support
  19.192              {@link java.lang.Class#getProtectionDomain}.
  19.193          </p>
  19.194 -        
  19.195 +
  19.196          <p>
  19.197              The first argument of method annotated by
  19.198              {@link net.java.html.json.OnReceive} annotation has to
  19.199              be the associated {@link net.java.html.json.Model model class}.
  19.200          </p>
  19.201 -        
  19.202 +
  19.203          <p>
  19.204              {@link net.java.html.json.OnReceive} annotation now accepts
  19.205              {@link java.util.List} of data values as second argument
  19.206              (previously required an array).
  19.207          </p>
  19.208 -        
  19.209 -        
  19.210 +
  19.211 +
  19.212          <h3>What's New in 0.7.x Versions?</h3>
  19.213 -        
  19.214 +
  19.215          <p>
  19.216              {@link net.java.html.js.JavaScriptBody} annotation has new attribute
  19.217              {@link net.java.html.js.JavaScriptBody#wait4js()} which allows
  19.218 @@ -250,17 +253,17 @@
  19.219              new attribute as much as possible, as it can speed up execution
  19.220              in certain environments.
  19.221          </p>
  19.222 -        
  19.223 +
  19.224          <p>
  19.225              Use {@link net.java.html.BrwsrCtx#execute(java.lang.Runnable)} in
  19.226              multi-threaded environment to execute your code on the browser thread.
  19.227 -            See example 
  19.228 +            See example
  19.229              {@link net.java.html.BrwsrCtx#execute(java.lang.Runnable) using Java timer}.
  19.230          </p>
  19.231          </div>
  19.232 -        
  19.233 +
  19.234          <h3>Interesting Entry Points</h3>
  19.235 -        
  19.236 +
  19.237          <p>Learn how to {@link net.java.html.json.Model animate an HTML page from Java}
  19.238              without referencing single HTML element from the Java code.
  19.239          </p>
  19.240 @@ -276,51 +279,51 @@
  19.241  
  19.242          <h3>Getting Started</h3>
  19.243  
  19.244 -        There are <a href="http://wiki.apidesign.org/wiki/DukeScriptInNetBeans">many ways</a> 
  19.245 -        to start developing 
  19.246 -        <a href="http://html.java.net">Html for Java</a> application. 
  19.247 +        There are <a href="http://wiki.apidesign.org/wiki/DukeScriptInNetBeans">many ways</a>
  19.248 +        to start developing
  19.249 +        <a href="http://html.java.net">Html for Java</a> application.
  19.250          However to be sure one chooses the most recent setup, it is recommended
  19.251 -        to switch to good old command line and use a 
  19.252 +        to switch to good old command line and use a
  19.253          <a href="http://wiki.apidesign.org/wiki/Knockout4Java">Maven archetype</a>
  19.254 -        associated with every version of this project. Make sure at least 
  19.255 +        associated with every version of this project. Make sure at least
  19.256          <em>JDK7</em> is your installed Java and type:
  19.257 -        <pre>      
  19.258 +        <pre>
  19.259  $ mvn archetype:generate \
  19.260   -DarchetypeGroupId=org.apidesign.html \
  19.261   -DarchetypeArtifactId=knockout4j-archetype \
  19.262   -DarchetypeVersion=0.8 <em># or newer version, if available</em>
  19.263          </pre>
  19.264 -        Answer few questions (for example choose myfirstbrwsrpage as artifactId) 
  19.265 +        Answer few questions (for example choose myfirstbrwsrpage as artifactId)
  19.266          and then you can:
  19.267          <pre>
  19.268  $ cd myfirstbrwsrpage
  19.269  $ mvn process-classes exec:java
  19.270          </pre>
  19.271 -        In a few seconds (or minutes if 
  19.272 +        In a few seconds (or minutes if
  19.273          <a href="http://wiki.apidesign.org/wiki/Maven">Maven</a>
  19.274 -        decides to download the whole Internet of dependencies) you should 
  19.275 -        see a sample Hello World application rendered in a 
  19.276 +        decides to download the whole Internet of dependencies) you should
  19.277 +        see a sample Hello World application rendered in a
  19.278          <a href="http://wiki.apidesign.org/wiki/JavaFX">JavaFX</a>
  19.279          web view component (that of course requires your JDK to come
  19.280 -        with <a href="http://wiki.apidesign.org/wiki/JavaFX">JavaFX</a>; 
  19.281 -        <a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html">JDK7 
  19.282 -            and JDK8 from Oracle</a> contain everything that is needed). 
  19.283 -        The generated application is built around one 
  19.284 +        with <a href="http://wiki.apidesign.org/wiki/JavaFX">JavaFX</a>;
  19.285 +        <a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html">JDK7
  19.286 +            and JDK8 from Oracle</a> contain everything that is needed).
  19.287 +        The generated application is built around one
  19.288          Java source (uses the {@link net.java.html.json.Model} annotation to
  19.289          auto-generate another <code>Data.java</code> class during compilation)
  19.290          and one HTML file (uses the <a href="http://knockoutjs.com">Knockout</a>
  19.291 -        syntax to <code>data-bind</code> the HTML elements to the 
  19.292 +        syntax to <code>data-bind</code> the HTML elements to the
  19.293          generated <code>Data</code> model):
  19.294          <pre>
  19.295  $ ls src/main/java/**/DataModel.java
  19.296  $ ls src/main/webapp/pages/index.html
  19.297          </pre>
  19.298 -        That is all you need to get started. Play with the sources, 
  19.299 +        That is all you need to get started. Play with the sources,
  19.300          modify them and enjoy
  19.301          <a href="http://html.java.net">Html for Java</a>!
  19.302 -        
  19.303 +
  19.304          <h2>IDE Support</h2>
  19.305 -        
  19.306 +
  19.307          <p>
  19.308              This API is part of <a target="_blank"
  19.309              href="http://netbeans.org">NetBeans.org</a> project and as such
  19.310 @@ -330,77 +333,77 @@
  19.311              it builds on standard Java6 APIs and as such it shall work fine
  19.312              in any IDE.
  19.313          </p>
  19.314 -        
  19.315 +
  19.316          <p>
  19.317 -            A lot of work is done by 
  19.318 +            A lot of work is done by
  19.319              <a href="http://wiki.apidesign.org/wiki/AnnotationProcessor">
  19.320              annotation processors</a>
  19.321              that generate various boiler plate code during compilation. This
  19.322              is a standard part of Java since JDK6, but for example Eclipse
  19.323              is known not to deal with processors well and developers using
  19.324              it need to be careful. IntelliJ users hasn't reported any issues
  19.325 -            and of course, NetBeans IDE support for 
  19.326 +            and of course, NetBeans IDE support for
  19.327              <a href="http://wiki.apidesign.org/wiki/AnnotationProcessor">processors</a>
  19.328              is outstanding.
  19.329          </p>
  19.330 -        
  19.331 +
  19.332          <p>
  19.333              When using {@link net.java.html.js.JavaScriptBody} annotation, it is
  19.334              useful to do a bit of post processing of classes. There is a
  19.335 -            <a href="http://wiki.apidesign.org/wiki/Maven">Maven</a> 
  19.336 +            <a href="http://wiki.apidesign.org/wiki/Maven">Maven</a>
  19.337              plugin for that.
  19.338 -            NetBeans IDE will invoke it when doing a build. Other IDEs may 
  19.339 -            need some hint to do so. 
  19.340 +            NetBeans IDE will invoke it when doing a build. Other IDEs may
  19.341 +            need some hint to do so.
  19.342              Anyway: If one does not see all (generated) sources or is getting
  19.343 -            {@link java.lang.LinkageError}s when executing the application, 
  19.344 +            {@link java.lang.LinkageError}s when executing the application,
  19.345              switch to command line and do clean build
  19.346              from there:
  19.347          </p>
  19.348          <pre>$ mvn clean install</pre>
  19.349          <p>
  19.350              If that succeeds, your IDE of choice will hopefully
  19.351 -            pick the generated sources up and present the result of the build 
  19.352 -            properly. If not, 
  19.353 -            <a href="https://netbeans.org/downloads/">download NetBeans</a>, 
  19.354 -            you will be pleasantly 
  19.355 -            surprised - for example with our excellent 
  19.356 -            <a href="net/java/html/js/package-summary.html#debugging">Java/JavaScript 
  19.357 +            pick the generated sources up and present the result of the build
  19.358 +            properly. If not,
  19.359 +            <a href="https://netbeans.org/downloads/">download NetBeans</a>,
  19.360 +            you will be pleasantly
  19.361 +            surprised - for example with our excellent
  19.362 +            <a href="net/java/html/js/package-summary.html#debugging">Java/JavaScript
  19.363              debugging</a> support.
  19.364          </p>
  19.365 -        
  19.366 +
  19.367          <a name="deploy">
  19.368          <h2>Deploy Your Application</h2>
  19.369          </a>
  19.370 -        
  19.371 +
  19.372          <p>
  19.373          It is not goal of this documentation to list all possible ways
  19.374 -        to package and deploy applications which use this API. However it is 
  19.375 +        to package and deploy applications which use this API. However it is
  19.376          important for new comers to see the benefits of using the
  19.377          <a href="http://html.java.net">HTML for Java</a> API and as such
  19.378 -        let's list at least few bundling options, known to work at the time of writing 
  19.379 +        let's list at least few bundling options, known to work at the time of writing
  19.380          this documentation.
  19.381          </p>
  19.382 -        
  19.383 +
  19.384          <p>
  19.385          First of all, this is a <em>client technology</em>. You write client applications
  19.386          with it which may, but need not connect to a server. You don't need
  19.387 -        Tomcat or WebLogic to deploy 
  19.388 +        Tomcat or WebLogic to deploy
  19.389          <a href="http://html.java.net">HTML for Java</a> applications.
  19.390          </p>
  19.391 -        
  19.392 +
  19.393          <p>
  19.394              <img src='resources/javafx_logo.jpg' width="64"
  19.395                   height="64" align="left"/>
  19.396              The sample project generated by
  19.397              <code>org.apidesign.html knockout4j-archetype</code> is configured
  19.398              to use <a href="http://wiki.apidesign.org/wiki/JavaFX">JavaFX</a>
  19.399 -            as the rendering technology. This setup is primarily suitable for 
  19.400 +            as the rendering technology. This setup is primarily suitable for
  19.401              development - it needs no special packaging, starts quickly and
  19.402 -            allows you to use classical HotSpot VM debuggers. A final 
  19.403 +            allows you to use classical HotSpot VM debuggers. A final
  19.404              artifact from the build is also a ZIP file which you can use
  19.405              and distribute to your users. Good for desktop applications.
  19.406          </p>
  19.407 -        
  19.408 +
  19.409          <p>
  19.410              <img src='resources/netbeans_logo.jpg' width="64"
  19.411                   height="64" align="right"/>
  19.412 @@ -409,17 +412,17 @@
  19.413              All the <a href="http://html.java.net">HTML for Java</a> libraries
  19.414              are packaged as <a href="http://wiki.apidesign.org/wiki/OSGi">OSGi</a>
  19.415              bundles and as such they can easily be run in NetBeans as well as
  19.416 -            in Eclipse. As a result one can use 
  19.417 +            in Eclipse. As a result one can use
  19.418              <a href="http://wiki.apidesign.org/wiki/OSGi">OSGi</a>
  19.419 -            and have a common module system for both platforms. In addition to that 
  19.420 +            and have a common module system for both platforms. In addition to that
  19.421              one can render using
  19.422              HTML and have a common UI in both platforms. In such case
  19.423              your application would be packaged as a set of
  19.424              <a href="http://wiki.apidesign.org/wiki/OSGi">OSGi</a> bundles.
  19.425 -            Read 
  19.426 +            Read
  19.427              <a href="http://wiki.apidesign.org/wiki/HTML">more</a>...
  19.428          </p>
  19.429 -        
  19.430 +
  19.431          <p>
  19.432              <img src='resources/chrome_logo.png' width="64"
  19.433                   height="64" align="left"/>
  19.434 @@ -429,7 +432,7 @@
  19.435                   height="64" align="left"/>
  19.436              <img src='resources/firefox_logo.png' width="64"
  19.437                   height="64" align="left"/>
  19.438 -            
  19.439 +
  19.440              There is more and more attempts to execute Java bytecode
  19.441              in a browser, without any special Java plugin installed.
  19.442              The <a href="http://html.java.net">HTML for Java</a> is
  19.443 @@ -437,16 +440,16 @@
  19.444              applications even on such restricted environments. It uses
  19.445              no reflection calls and that allows to statically pre-compile
  19.446              the applications into JavaScript. One of such environments
  19.447 -            is called <a href="http://wiki.apidesign.org/wiki/Bck2Brwsr">Bck2Brwsr</a>, 
  19.448 +            is called <a href="http://wiki.apidesign.org/wiki/Bck2Brwsr">Bck2Brwsr</a>,
  19.449              another <a href="http://wiki.apidesign.org/wiki/TeaVM">TeaVM</a>. Both support the
  19.450 -            {@link net.java.html.js.JavaScriptBody} annotation. Read 
  19.451 +            {@link net.java.html.js.JavaScriptBody} annotation. Read
  19.452              <a href="http://wiki.apidesign.org/wiki/Bck2BrwsrViaCLI">more</a> or play
  19.453 -            a minesweeper game packaged for your browser 
  19.454 +            a minesweeper game packaged for your browser
  19.455              (of course <a target="_blank"
  19.456                  href="http://source.apidesign.org/hg/html~demo/file/ea79b73d590a/minesweeper/src/main/java/org/apidesign/demo/minesweeper/MinesModel.java">
  19.457                  written</a> in Java):
  19.458          </p>
  19.459 -        
  19.460 +
  19.461          <script type="text/html" id="field">
  19.462              <table class="field">
  19.463                  <tbody>
  19.464 @@ -477,52 +480,53 @@
  19.465                   height="64" align="right"/>
  19.466              <img src='resources/android_logo.jpg' width="64"
  19.467                   height="64" align="right"/>
  19.468 -            
  19.469 -            Now when we have seen that the 
  19.470 -            <a href="http://html.java.net">HTML for Java</a> applications 
  19.471 +
  19.472 +            Now when we have seen that the
  19.473 +            <a href="http://html.java.net">HTML for Java</a> applications
  19.474              can run on any modern browser, we can ask whether they can also
  19.475 -            fit into a phone!? Yes, they can and especially to phones 
  19.476 +            fit into a phone!? Yes, they can and especially to phones
  19.477              that can execute Java code already! Just by changing your
  19.478              packaging you can create an APK file and deploy it to your
  19.479 -            Android phone. 
  19.480 +            Android phone.
  19.481              Read <a target="_blank" href="http://wiki.apidesign.org/wiki/DlvkBrwsr">more</a>...
  19.482              In case you'd like your application to reach out to second biggest
  19.483 -            group of smartphone users, don't despair: It 
  19.484 +            group of smartphone users, don't despair: It
  19.485              seems the set of devices that can execute
  19.486 -            <a href="http://html.java.net">HTML for Java</a> applications 
  19.487 -            has been extended to <em>iPads</em> and <em>iPhones</em>. Get the 
  19.488 +            <a href="http://html.java.net">HTML for Java</a> applications
  19.489 +            has been extended to <em>iPads</em> and <em>iPhones</em>. Get the
  19.490              <a target="_blank" href="http://wiki.apidesign.org/wiki/IBrwsr">details here</a>
  19.491              and deploy everywhere!
  19.492          </p>
  19.493          <p>
  19.494 -            Convinced it makes sense to use 
  19.495 +            Convinced it makes sense to use
  19.496              <a href="http://html.java.net">HTML for Java</a>
  19.497 -            APIs for writing applications that are 
  19.498 +            APIs for writing applications that are
  19.499              <em>written once, displayed anywhere</em>? Or do you have an
  19.500              environment which is not supported? In such case you can bring
  19.501              <a href="http://html.java.net">HTML for Java</a>
  19.502              to your environment yourself. Just implement your own
  19.503              {@link org.netbeans.html.boot.spi.Fn.Presenter}!
  19.504          </p>
  19.505 -        
  19.506 +
  19.507          <h2>Other Resources</h2>
  19.508 -        
  19.509 +
  19.510          <img src="net/java/html/json/doc-files/DukeHTML.png" width="256" height="184" alt="Duke and HTML5. Together at last!" align="right"/>
  19.511 -        
  19.512 +
  19.513          The javadoc for latest and previous versions is also available
  19.514          online:
  19.515          <ul>
  19.516              <li>Current <a target="_blank" href="http://bits.netbeans.org/html+java/dev/">development</a> version
  19.517 +            <li>Version <a target="_blank" href="http://bits.netbeans.org/html+java/1.1">1.1</a>
  19.518              <li>Version <a target="_blank" href="http://bits.netbeans.org/html+java/1.0">1.0</a>
  19.519 -            <li>Version <a target="_blank" href="http://bits.netbeans.org/html+java/0.9">0.9</a> 
  19.520 +            <li>Version <a target="_blank" href="http://bits.netbeans.org/html+java/0.9">0.9</a>
  19.521                  and historic ones (<a target="_blank" href="http://bits.netbeans.org/html+java/0.8.3">0.8.3</a>,
  19.522 -                <a target="_blank" href="http://bits.netbeans.org/html+java/0.8.2">0.8.2</a>, 
  19.523 +                <a target="_blank" href="http://bits.netbeans.org/html+java/0.8.2">0.8.2</a>,
  19.524                  <a target="_blank" href="http://bits.netbeans.org/html+java/0.8.1">0.8.1</a>,
  19.525                  <a target="_blank" href="http://bits.netbeans.org/html+java/0.8">0.8</a>, and
  19.526                  <a target="_blank" href="http://bits.netbeans.org/html+java/0.7.5">0.7.5</a>)
  19.527              </li>
  19.528          </ul>
  19.529 -        
  19.530 +
  19.531  <style type="text/css">
  19.532  table.field td {
  19.533      padding: 4px;
  19.534 @@ -541,6 +545,6 @@
  19.535  table.field td.DISCOVERED {
  19.536      background-color: #9DB2B1;
  19.537  }
  19.538 -</style>        
  19.539 +</style>
  19.540      </body>
  19.541  </html>