#5929: OnReceive method can take additional callback parameters
authorJaroslav Tulach <jtulach@netbeans.org>
Sat, 05 Apr 2014 09:06:59 +0200
changeset 650cbffa9025961
parent 649 b3caab5b24c7
child 651 ab2e8d07758b
#5929: OnReceive method can take additional callback parameters
json-tck/src/main/java/net/java/html/json/tests/JSONTest.java
json/src/main/java/org/apidesign/html/json/spi/Proto.java
json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java
json/src/test/java/net/java/html/json/ModelProcessorTest.java
json/src/test/java/org/netbeans/html/json/impl/EmployeeImpl.java
json/src/test/java/org/netbeans/html/json/impl/OnReceiveTest.java
src/main/javadoc/overview.html
     1.1 --- a/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java	Fri Apr 04 18:09:39 2014 +0200
     1.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java	Sat Apr 05 09:06:59 2014 +0200
     1.3 @@ -155,13 +155,13 @@
     1.4      private static BrwsrCtx onCallback;
     1.5      
     1.6      @OnReceive(url="{url}")
     1.7 -    static void fetch(Person p, JSONik model) {
     1.8 +    static void fetch(JSONik model, Person p) {
     1.9          model.setFetched(p);
    1.10          onCallback = BrwsrCtx.findDefault(model.getClass());
    1.11      }
    1.12  
    1.13      @OnReceive(url="{url}", onError = "setMessage")
    1.14 -    static void fetchArray(Person[] p, JSONik model) {
    1.15 +    static void fetchArray(JSONik model, Person[] p) {
    1.16          model.setFetchedCount(p.length);
    1.17          model.setFetched(p[0]);
    1.18          onCallback = BrwsrCtx.findDefault(model.getClass());
    1.19 @@ -173,7 +173,7 @@
    1.20      }
    1.21      
    1.22      @OnReceive(url="{url}")
    1.23 -    static void fetchPeople(People p, JSONik model) {
    1.24 +    static void fetchPeople(JSONik model, People p) {
    1.25          final int size = p.getInfo().size();
    1.26          if (size > 0) {
    1.27              model.setFetched(p.getInfo().get(0));
    1.28 @@ -182,7 +182,7 @@
    1.29      }
    1.30  
    1.31      @OnReceive(url="{url}")
    1.32 -    static void fetchPeopleAge(People p, JSONik model) {
    1.33 +    static void fetchPeopleAge(JSONik model, People p) {
    1.34          int sum = 0;
    1.35          for (int a : p.getAge()) {
    1.36              sum += a;
    1.37 @@ -215,7 +215,7 @@
    1.38      }
    1.39      
    1.40      @OnReceive(url="{url}?callme={me}", jsonp = "me")
    1.41 -    static void fetchViaJSONP(Person p, JSONik model) {
    1.42 +    static void fetchViaJSONP(JSONik model, Person p) {
    1.43          model.setFetched(p);
    1.44      }
    1.45      
    1.46 @@ -396,7 +396,7 @@
    1.47      }
    1.48      
    1.49      @OnReceive(url="{url}")
    1.50 -    static void fetchPeopleSex(People p, JSONik model) {
    1.51 +    static void fetchPeopleSex(JSONik model, People p) {
    1.52          model.setFetchedCount(1);
    1.53          model.getFetchedSex().addAll(p.getSex());
    1.54      }
     2.1 --- a/json/src/main/java/org/apidesign/html/json/spi/Proto.java	Fri Apr 04 18:09:39 2014 +0200
     2.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/Proto.java	Sat Apr 05 09:06:59 2014 +0200
     2.3 @@ -226,6 +226,17 @@
     2.4          return JSON.read(context, modelClass, data);
     2.5      }
     2.6  
     2.7 +    /** Initializes asynchronous JSON connection to specified URL. Delegates
     2.8 +     * to {@link #loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...) }
     2.9 +     * with no extra parameters.
    2.10 +     */
    2.11 +    public void loadJSON(final int index, 
    2.12 +        String urlBefore, String urlAfter, String method,
    2.13 +        final Object data
    2.14 +    ) {
    2.15 +        loadJSON(index, urlBefore, urlAfter, method, data, new Object[0]);
    2.16 +    }
    2.17 +    
    2.18      /** Initializes asynchronous JSON connection to specified URL. The 
    2.19       * method returns immediately and later does callback later.
    2.20       * 
    2.21 @@ -237,20 +248,23 @@
    2.22       * @param method method to use for connection to the server
    2.23       * @param data string, number or a {@link Model} generated class to send to
    2.24       *    the server when doing a query
    2.25 +     * @param params extra params to pass back when calling
    2.26 +     *   {@link Type#onMessage(java.lang.Object, int, int, java.lang.Object, java.lang.Object[])}
    2.27 +     * @since 0.8.1
    2.28       */
    2.29      public void loadJSON(final int index, 
    2.30          String urlBefore, String urlAfter, String method,
    2.31 -        final Object data
    2.32 +        final Object data, final Object... params
    2.33      ) {
    2.34          class Rcvr extends RcvrJSON {
    2.35              @Override
    2.36              protected void onMessage(MsgEvnt msg) {
    2.37 -                type.onMessage(obj, index, 1, msg.getValues());
    2.38 +                type.onMessage(obj, index, 1, msg.getValues(), params);
    2.39              }
    2.40  
    2.41              @Override
    2.42              protected void onError(MsgEvnt msg) {
    2.43 -                type.onMessage(obj, index, 2, msg.getException());
    2.44 +                type.onMessage(obj, index, 2, msg.getException(), params);
    2.45              }
    2.46          }
    2.47          JSON.loadJSON(context, new Rcvr(), urlBefore, urlAfter, method, data);
    2.48 @@ -546,7 +560,7 @@
    2.49  
    2.50          /** Called to report results of asynchronous over-the-wire 
    2.51           * communication. Result of calling {@link Proto#wsOpen(int, java.lang.String, java.lang.Object)}
    2.52 -         * or {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object)}.
    2.53 +         * or {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}.
    2.54           * 
    2.55           * @param model the instance of the model class
    2.56           * @param index index used during initiating the communication (via <code>loadJSON</code> or <code>wsOpen</code> calls)
    2.57 @@ -555,7 +569,28 @@
    2.58           * @param data <code>null</code> or string, number or a {@link Model} class
    2.59           *   obtained to the server as a response
    2.60           */
    2.61 -        protected abstract void onMessage(Model model, int index, int type, Object data);
    2.62 +        protected void onMessage(Model model, int index, int type, Object data) {
    2.63 +            onMessage(model, index, type, data, new Object[0]);
    2.64 +        }
    2.65 +        
    2.66 +        /** Called to report results of asynchronous over-the-wire 
    2.67 +         * communication. Result of calling {@link Proto#wsOpen(int, java.lang.String, java.lang.Object)}
    2.68 +         * or {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}.
    2.69 +         * 
    2.70 +         * @param model the instance of the model class
    2.71 +         * @param index index used during initiating the communication (via <code>loadJSON</code> or <code>wsOpen</code> calls)
    2.72 +         * @param type type of the message: 0 - onOpen, 1 - onMessage, 2 - onError, 3 - onClose -
    2.73 +         *   not all messages are applicable to all communication protocols (JSON has only 1 and 2).
    2.74 +         * @param data <code>null</code> or string, number or a {@link Model} class
    2.75 +         *   obtained to the server as a response
    2.76 +         * @param params extra parameters as passed for example to
    2.77 +         *   {@link Proto#loadJSON(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object, java.lang.Object...)}
    2.78 +         *   method
    2.79 +         * @since 0.8.1
    2.80 +         */
    2.81 +        protected void onMessage(Model model, int index, int type, Object data, Object[] params) {
    2.82 +            onMessage(model, index, type, data);
    2.83 +        }
    2.84  
    2.85          //
    2.86          // Various support methods the generated classes use
     3.1 --- a/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java	Fri Apr 04 18:09:39 2014 +0200
     3.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/ModelProcessor.java	Sat Apr 05 09:06:59 2014 +0200
     3.3 @@ -986,7 +986,7 @@
     3.4          Element clazz, StringWriter body, String className, 
     3.5          List<? extends Element> enclosedElements, StringBuilder inType
     3.6      ) {
     3.7 -        inType.append("  @Override public void onMessage(").append(className).append(" model, int index, int type, Object data) {\n");
     3.8 +        inType.append("  @Override public void onMessage(").append(className).append(" model, int index, int type, Object data, Object[] params) {\n");
     3.9          inType.append("    switch (index) {\n");
    3.10          int index = 0;
    3.11          for (Element m : enclosedElements) {
    3.12 @@ -1027,6 +1027,7 @@
    3.13              }
    3.14              int expectsList = 0;
    3.15              List<String> args = new ArrayList<String>();
    3.16 +            List<String> params = new ArrayList<String>();
    3.17              // first argument is model class
    3.18              {
    3.19                  TypeMirror type = e.getParameters().get(0).asType();
    3.20 @@ -1079,7 +1080,8 @@
    3.21                  }
    3.22              }
    3.23              String n = e.getSimpleName().toString();
    3.24 -            if ("WebSocket".equals(onR.method())) {
    3.25 +            final boolean isWebSocket = "WebSocket".equals(onR.method());
    3.26 +            if (isWebSocket) {
    3.27                  body.append("  /** Performs WebSocket communication. Call with <code>null</code> data parameter\n");
    3.28                  body.append("  * to open the connection (even if not required). Call with non-null data to\n");
    3.29                  body.append("  * send messages to server. Call again with <code>null</code> data to close the socket.\n");
    3.30 @@ -1111,17 +1113,34 @@
    3.31                  if (dataMirror != null) {
    3.32                      body.append(sep).append(dataMirror.toString()).append(" data");
    3.33                  }
    3.34 +                for (int i = 2; i < e.getParameters().size(); i++) {
    3.35 +                    if (isWebSocket) {
    3.36 +                        error("@OnReceive(method=\"WebSocket\") can only have two arguments", e);
    3.37 +                        return false;
    3.38 +                    }
    3.39 +                    
    3.40 +                    VariableElement ve = e.getParameters().get(i);
    3.41 +                    body.append(sep).append(ve.asType().toString()).append(" ").append(ve.getSimpleName());
    3.42 +                    final String tp = ve.asType().toString();
    3.43 +                    String btn = findBoxedType(tp);
    3.44 +                    if (btn == null) {
    3.45 +                        btn = tp;
    3.46 +                    }
    3.47 +                    args.add("(" + btn + ")params[" + (i - 2) + "]");
    3.48 +                    params.add(ve.getSimpleName().toString());
    3.49 +                    sep = ", ";
    3.50 +                }
    3.51              }
    3.52              body.append(") {\n");
    3.53              boolean webSocket = onR.method().equals("WebSocket");
    3.54              if (webSocket) {
    3.55 -                if (generateWSReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList != 0, modelClass, n, args, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
    3.56 +                if (generateWSReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList != 0, modelClass, n, args, params, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
    3.57                      return false;
    3.58                  }
    3.59                  body.append("  }\n");
    3.60                  body.append("  private Object ws_" + e.getSimpleName() + ";\n");
    3.61              } else {
    3.62 -                if (generateJSONReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList != 0, modelClass, n, args, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
    3.63 +                if (generateJSONReceiveBody(index++, body, inType, onR, e, clazz, className, expectsList != 0, modelClass, n, args, params, urlBefore, jsonpVarName, urlAfter, dataMirror)) {
    3.64                      return false;
    3.65                  }
    3.66                  body.append("  }\n");
    3.67 @@ -1133,7 +1152,7 @@
    3.68          return true;
    3.69      }
    3.70  
    3.71 -    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, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror) {
    3.72 +    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) {
    3.73          body.append(
    3.74              "    case " + index + ": {\n" +
    3.75              "      if (type == 2) { /* on error */\n" +
    3.76 @@ -1199,11 +1218,14 @@
    3.77          } else {
    3.78              method.append(", null, null");
    3.79          }
    3.80 +        for (String a : params) {
    3.81 +            method.append(", ").append(a);
    3.82 +        }
    3.83          method.append(");\n");
    3.84          return false;
    3.85      }
    3.86      
    3.87 -    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, StringBuilder urlBefore, String jsonpVarName, StringBuilder urlAfter, String dataMirror) {
    3.88 +    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) {
    3.89          body.append(
    3.90              "    case " + index + ": {\n" +
    3.91              "      if (type == 0) { /* on open */\n" +
    3.92 @@ -1212,7 +1234,7 @@
    3.93              String sep = "";
    3.94              for (String arg : args) {
    3.95                  body.append(sep);
    3.96 -                if (arg.startsWith("arr")) {
    3.97 +                if (arg.startsWith("arr") || arg.startsWith("java.util.Array")) {
    3.98                      body.append("null");
    3.99                  } else {
   3.100                      body.append(arg);
   3.101 @@ -1284,7 +1306,11 @@
   3.102          method.append("= proto.wsOpen(" + index + ", ");
   3.103          method.append(urlBefore).append(", data);\n");
   3.104          method.append("    } else {\n");
   3.105 -        method.append("      proto.wsSend(this.ws_").append(e.getSimpleName()).append(", ").append(urlBefore).append(", data);\n");
   3.106 +        method.append("      proto.wsSend(this.ws_").append(e.getSimpleName()).append(", ").append(urlBefore).append(", data");
   3.107 +        for (String a : params) {
   3.108 +            method.append(", ").append(a);
   3.109 +        }
   3.110 +        method.append(");\n");
   3.111          method.append("    }\n");
   3.112          return false;
   3.113      }
     4.1 --- a/json/src/test/java/net/java/html/json/ModelProcessorTest.java	Fri Apr 04 18:09:39 2014 +0200
     4.2 +++ b/json/src/test/java/net/java/html/json/ModelProcessorTest.java	Sat Apr 05 09:06:59 2014 +0200
     4.3 @@ -341,6 +341,21 @@
     4.4          res.assertError("Cannot have the name");
     4.5      }
     4.6      
     4.7 +    @Test public void onWebSocketJustTwoArgs() throws IOException {
     4.8 +        Compile res = Compile.create("", "package x;\n"
     4.9 +            + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
    4.10 +            + "  @net.java.html.json.Property(name=\"x\", type=String.class)\n"
    4.11 +            + "})\n"
    4.12 +            + "class UseOnReceive {\n"
    4.13 +            + "  @net.java.html.json.OnReceive(url=\"http://nowhere.com\", method=\"WebSocket\")\n"
    4.14 +            + "  static void onMessage(MyModel model, String value, int arg) {\n"
    4.15 +            + "  }\n"
    4.16 +            + "}\n"
    4.17 +        );
    4.18 +        res.assertErrors();
    4.19 +        res.assertError("only have two arg");
    4.20 +    }
    4.21 +    
    4.22      @Test public void onErrorWouldHaveToBeStatic() throws IOException {
    4.23          Compile res = Compile.create("", "package x;\n"
    4.24              + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
     5.1 --- a/json/src/test/java/org/netbeans/html/json/impl/EmployeeImpl.java	Fri Apr 04 18:09:39 2014 +0200
     5.2 +++ b/json/src/test/java/org/netbeans/html/json/impl/EmployeeImpl.java	Sat Apr 05 09:06:59 2014 +0200
     5.3 @@ -54,7 +54,8 @@
     5.4   */
     5.5  @Model(className = "Employee", properties = {
     5.6      @Property(name = "person", type = Person.class),
     5.7 -    @Property(name = "employer", type = Employer.class)
     5.8 +    @Property(name = "employer", type = Employer.class),
     5.9 +    @Property(name = "call", type = Call.class)
    5.10  })
    5.11  public class EmployeeImpl {
    5.12      @OnReceive(url = "some/url")
    5.13 @@ -62,8 +63,39 @@
    5.14          e.setPerson(p);
    5.15      }
    5.16  
    5.17 +    private static void callChangePers(Employee e) {
    5.18 +        Person per = new Person();
    5.19 +        e.changePersonalities(10, 3.14, "Ahoj", per);
    5.20 +        e.updatePersonalities("kuk", new Person(), 1, 2, "3", new Person());
    5.21 +        e.socketPersonalities("where", null);
    5.22 +    }
    5.23 +
    5.24      @OnReceive(url = "some/other/url")
    5.25 -    static void changePersonalities(Employee e, List<Person> p) {
    5.26 +    static void changePersonalities(Employee e, List<Person> data, int i, double d, String s, Person o) {
    5.27 +        e.setCall(new Call(i, d, s, o, data.toArray(new Person[0])));
    5.28 +    }
    5.29 +
    5.30 +    @OnReceive(url = "{url}", method = "PUT", data = Person.class)
    5.31 +    static void updatePersonalities(Employee e, List<Person> p, int i, double d, String s, Person o) {
    5.32          e.setPerson(p.get(0));
    5.33      }
    5.34 +
    5.35 +    @OnReceive(url = "{url}", method = "WebSocket", data = Person.class)
    5.36 +    static void socketPersonalities(Employee e, List<Person> p) {
    5.37 +        e.setPerson(p.get(0));
    5.38 +    }
    5.39 +    @OnReceive(url = "{url}", method = "WebSocket", data = Person.class)
    5.40 +    static void socketArrayPersonalities(Employee e, Person[] p) {
    5.41 +        e.setPerson(p[0]);
    5.42 +    }
    5.43 +    
    5.44 +    @Model(className="Call", properties = {
    5.45 +        @Property(name = "i", type=int.class),
    5.46 +        @Property(name = "d", type=double.class),
    5.47 +        @Property(name = "s", type=String.class),
    5.48 +        @Property(name = "p", type=Person.class),
    5.49 +        @Property(name = "data", type=Person.class, array = true)
    5.50 +    })
    5.51 +    static class CallModel {
    5.52 +    }
    5.53  }
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/json/src/test/java/org/netbeans/html/json/impl/OnReceiveTest.java	Sat Apr 05 09:06:59 2014 +0200
     6.3 @@ -0,0 +1,112 @@
     6.4 +/**
     6.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     6.6 + *
     6.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     6.8 + *
     6.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    6.10 + * Other names may be trademarks of their respective owners.
    6.11 + *
    6.12 + * The contents of this file are subject to the terms of either the GNU
    6.13 + * General Public License Version 2 only ("GPL") or the Common
    6.14 + * Development and Distribution License("CDDL") (collectively, the
    6.15 + * "License"). You may not use this file except in compliance with the
    6.16 + * License. You can obtain a copy of the License at
    6.17 + * http://www.netbeans.org/cddl-gplv2.html
    6.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    6.19 + * specific language governing permissions and limitations under the
    6.20 + * License.  When distributing the software, include this License Header
    6.21 + * Notice in each file and include the License file at
    6.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    6.23 + * particular file as subject to the "Classpath" exception as provided
    6.24 + * by Oracle in the GPL Version 2 section of the License file that
    6.25 + * accompanied this code. If applicable, add the following below the
    6.26 + * License Header, with the fields enclosed by brackets [] replaced by
    6.27 + * your own identifying information:
    6.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    6.29 + *
    6.30 + * Contributor(s):
    6.31 + *
    6.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    6.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    6.34 + *
    6.35 + * If you wish your version of this file to be governed by only the CDDL
    6.36 + * or only the GPL Version 2, indicate your decision by adding
    6.37 + * "[Contributor] elects to include this software in this distribution
    6.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    6.39 + * single choice of license, a recipient has the option to distribute
    6.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    6.41 + * to extend the choice of license to its licensees as provided above.
    6.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    6.43 + * Version 2 license, then the option applies only if the new code is
    6.44 + * made subject to such option by the copyright holder.
    6.45 + */
    6.46 +package org.netbeans.html.json.impl;
    6.47 +
    6.48 +import java.io.IOException;
    6.49 +import java.io.InputStream;
    6.50 +import java.util.HashMap;
    6.51 +import java.util.Map;
    6.52 +import net.java.html.BrwsrCtx;
    6.53 +import net.java.html.json.Models;
    6.54 +import net.java.html.json.Person;
    6.55 +import org.apidesign.html.context.spi.Contexts;
    6.56 +import org.apidesign.html.json.spi.JSONCall;
    6.57 +import org.apidesign.html.json.spi.Transfer;
    6.58 +import static org.testng.Assert.*;
    6.59 +import org.testng.annotations.Test;
    6.60 +
    6.61 +/**
    6.62 + *
    6.63 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    6.64 + */
    6.65 +public class OnReceiveTest {
    6.66 +    @Test public void performJSONCall() {
    6.67 +        MockTrans mt = new MockTrans();
    6.68 +        BrwsrCtx ctx = Contexts.newBuilder().register(Transfer.class, mt, 1).build();
    6.69 +        
    6.70 +        Employee e = Models.bind(new Employee(), ctx);
    6.71 +        e.setCall(null);
    6.72 +        Person p = new Person();
    6.73 +        
    6.74 +        mt.result = new HashMap<String, String>();
    6.75 +        mt.result.put("firstName", "Jarda");
    6.76 +        mt.result.put("lastName", "Tulach");
    6.77 +        e.changePersonalities(1, 2.0, "3", p);
    6.78 +        final Call c = e.getCall();
    6.79 +        assertNotNull(c, "A call has been made");
    6.80 +        assertEquals(c.getI(), 1);
    6.81 +        assertEquals(c.getD(), 2.0);
    6.82 +        assertEquals(c.getS(), "3");
    6.83 +        assertEquals(c.getP(), p);
    6.84 +        assertEquals(c.getData().size(), 1, "One result sent over wire");
    6.85 +        assertEquals(c.getData().get(0).getFirstName(), "Jarda");
    6.86 +        assertEquals(c.getData().get(0).getLastName(), "Tulach");
    6.87 +    }
    6.88 +
    6.89 +    
    6.90 +    public static class MockTrans implements Transfer {
    6.91 +        Map<String,String> result;
    6.92 +        
    6.93 +        @Override
    6.94 +        public void extract(Object obj, String[] props, Object[] values) {
    6.95 +            assertTrue(obj instanceof Map, "It is a map: " + obj);
    6.96 +            Map<?,?> mt = (Map<?,?>) obj;
    6.97 +            for (int i = 0; i < props.length; i++) {
    6.98 +                values[i] = mt.get(props[i]);
    6.99 +            }
   6.100 +        }
   6.101 +
   6.102 +        @Override
   6.103 +        public Object toJSON(InputStream is) throws IOException {
   6.104 +            throw new IOException();
   6.105 +        }
   6.106 +
   6.107 +        @Override
   6.108 +        public void loadJSON(JSONCall call) {
   6.109 +            Object r = result;
   6.110 +            assertNotNull(r, "We need a reply!");
   6.111 +            result = null;
   6.112 +            call.notifySuccess(r);
   6.113 +        }
   6.114 +    }
   6.115 +}
     7.1 --- a/src/main/javadoc/overview.html	Fri Apr 04 18:09:39 2014 +0200
     7.2 +++ b/src/main/javadoc/overview.html	Sat Apr 05 09:06:59 2014 +0200
     7.3 @@ -78,6 +78,12 @@
     7.4          <h3>What's New in Version 0.8?</h3>
     7.5          
     7.6          <p>
     7.7 +            The first argument of method annotated by
     7.8 +            {@link net.java.html.json.OnReceive} annotation has to
     7.9 +            be the associated {@link net.java.html.json.Model model class}.
    7.10 +        </p>
    7.11 +        
    7.12 +        <p>
    7.13              {@link net.java.html.json.OnReceive} annotation now accepts
    7.14              {@link java.util.List} of data values as second argument
    7.15              (previously required an array).