OnReceive can identify onError method
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Sat, 17 Aug 2013 10:23:40 +0200
changeset 245940d9fb9486c
parent 244 8d966fcb46c9
child 246 7bc1d2d09388
OnReceive can identify onError method
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/apidesign/html/json/impl/ModelProcessor.java
json/src/test/java/net/java/html/json/Compile.java
json/src/test/java/net/java/html/json/ModelProcessorTest.java
json/src/test/java/net/java/html/json/ModelTest.java
ko-fx/src/main/java/org/apidesign/html/kofx/LoadJSON.java
     1.1 --- a/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java	Fri Aug 16 21:20:42 2013 +0200
     1.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java	Sat Aug 17 10:23:40 2013 +0200
     1.3 @@ -73,12 +73,17 @@
     1.4          model.setFetched(p);
     1.5      }
     1.6  
     1.7 -    @OnReceive(url="{url}")
     1.8 +    @OnReceive(url="{url}", onError = "setMessage")
     1.9      static void fetchArray(Person[] p, JSONik model) {
    1.10          model.setFetchedCount(p.length);
    1.11          model.setFetched(p[0]);
    1.12      }
    1.13      
    1.14 +    static void setMessage(JSONik m, Exception t) {
    1.15 +        assert t != null;
    1.16 +        m.setFetchedResponse("Exception");
    1.17 +    }
    1.18 +    
    1.19      @OnReceive(url="{url}")
    1.20      static void fetchPeople(People p, JSONik model) {
    1.21          model.setFetchedCount(p.getInfo().size());
    1.22 @@ -353,6 +358,23 @@
    1.23          assert Sex.FEMALE.equals(p.getSex()) : "Expecting FEMALE: " + p.getSex();
    1.24      }
    1.25      
    1.26 +    @KOTest public void loadError() throws InterruptedException {
    1.27 +        if (js == null) {
    1.28 +            js = Models.bind(new JSONik(), newContext());
    1.29 +            js.applyBindings();
    1.30 +            js.setFetched(null);
    1.31 +            
    1.32 +            js.fetchArray("file:///unknown/url/to/query/xyz.txt");
    1.33 +        }
    1.34 +        
    1.35 +        
    1.36 +        if (js.getFetchedResponse() == null) {
    1.37 +            throw new InterruptedException();
    1.38 +        }
    1.39 +
    1.40 +        assert "Exception".equals(js.getFetchedResponse()) : js.getFetchedResponse();
    1.41 +    }
    1.42 +    
    1.43      @Model(className = "NameAndValue", properties = {
    1.44          @Property(name = "name", type = String.class),
    1.45          @Property(name = "value", type = long.class),
     2.1 --- a/json/src/main/java/net/java/html/json/OnReceive.java	Fri Aug 16 21:20:42 2013 +0200
     2.2 +++ b/json/src/main/java/net/java/html/json/OnReceive.java	Sat Aug 17 10:23:40 2013 +0200
     2.3 @@ -116,4 +116,15 @@
     2.4       * @since 0.3
     2.5       */
     2.6      String method() default "GET";
     2.7 +    
     2.8 +    /** Name of a method in this class which should be called in case of 
     2.9 +     * an error. The method has to be non-private and take one model and 
    2.10 +     * one {@link Exception} 
    2.11 +     * parameter. If this method is not specified, the exception is just
    2.12 +     * printed to console.
    2.13 +     * 
    2.14 +     * @return name of method in this class
    2.15 +     * @since 0.5
    2.16 +     */
    2.17 +    public String onError() default "";    
    2.18  }
     3.1 --- a/json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java	Fri Aug 16 21:20:42 2013 +0200
     3.2 +++ b/json/src/main/java/org/apidesign/html/json/impl/ModelProcessor.java	Sat Aug 17 10:23:40 2013 +0200
     3.3 @@ -985,7 +985,22 @@
     3.4                  "    class ProcessResult implements Runnable {\n" +
     3.5                  "      @Override\n" +
     3.6                  "      public void run() {\n" +
     3.7 -                "        Object value = result[0];\n");
     3.8 +                "        Object value = result[0];\n" +
     3.9 +                "        if (value instanceof Throwable) {\n");
    3.10 +            if (onR.onError().isEmpty()) {
    3.11 +                body.append(
    3.12 +                "          ((Throwable)value).printStackTrace();\n"
    3.13 +                );
    3.14 +            } else {
    3.15 +                if (!findOnError(e, ((TypeElement)clazz), onR.onError(), className)) {
    3.16 +                    return false;
    3.17 +                }
    3.18 +                body.append("          ").append(clazz.getSimpleName()).append(".").append(onR.onError()).append("(");
    3.19 +                body.append(className).append(".this, (Exception)value); return;\n");
    3.20 +            }
    3.21 +            body.append(
    3.22 +                "        }\n"
    3.23 +            );
    3.24              body.append(
    3.25                  "        " + modelClass + "[] arr;\n");
    3.26              body.append(
    3.27 @@ -1539,4 +1554,48 @@
    3.28          ResourceBundle rb = ResourceBundle.getBundle("org.apidesign.html.json.impl.Bundle");
    3.29          return Completions.of('"' + method + '"', rb.getString("MSG_Completion_" + method));
    3.30      }
    3.31 +    
    3.32 +    private boolean findOnError(ExecutableElement errElem, TypeElement te, String name, String className) {
    3.33 +        String err = null;
    3.34 +        METHODS:
    3.35 +        for (Element e : te.getEnclosedElements()) {
    3.36 +            if (e.getKind() != ElementKind.METHOD) {
    3.37 +                continue;
    3.38 +            }
    3.39 +            if (!e.getSimpleName().contentEquals(name)) {
    3.40 +                continue;
    3.41 +            }
    3.42 +            if (!e.getModifiers().contains(Modifier.STATIC)) {
    3.43 +                errElem = (ExecutableElement) e;
    3.44 +                err = "Would have to be static";
    3.45 +                continue;
    3.46 +            }
    3.47 +            ExecutableElement ee = (ExecutableElement) e;
    3.48 +            TypeMirror excType = processingEnv.getElementUtils().getTypeElement(Exception.class.getName()).asType();
    3.49 +            final List<? extends VariableElement> params = ee.getParameters();
    3.50 +            boolean error = false;
    3.51 +            if (params.size() != 2) {
    3.52 +                error = true;
    3.53 +            } else {
    3.54 +                if (!params.get(0).asType().toString().equals(className)) {
    3.55 +                    error = true;
    3.56 +                }
    3.57 +                if (!processingEnv.getTypeUtils().isAssignable(excType, params.get(1).asType())) {
    3.58 +                    error = true;
    3.59 +                }
    3.60 +            }
    3.61 +            if (error) {
    3.62 +                errElem = (ExecutableElement) e;
    3.63 +                err = "Error method first argument needs to be " + className + " and second Exception";
    3.64 +                continue;
    3.65 +            }
    3.66 +            return true;
    3.67 +        }
    3.68 +        if (err == null) {
    3.69 +            err = "Cannot find " + name + "(" + className + ", Exception) method in this class";
    3.70 +        }
    3.71 +        error(err, errElem);
    3.72 +        return false;
    3.73 +    }
    3.74 +    
    3.75  }
     4.1 --- a/json/src/test/java/net/java/html/json/Compile.java	Fri Aug 16 21:20:42 2013 +0200
     4.2 +++ b/json/src/test/java/net/java/html/json/Compile.java	Sat Aug 17 10:23:40 2013 +0200
     4.3 @@ -31,6 +31,7 @@
     4.4  import java.util.Arrays;
     4.5  import java.util.HashMap;
     4.6  import java.util.List;
     4.7 +import java.util.Locale;
     4.8  import java.util.Map;
     4.9  import java.util.regex.Matcher;
    4.10  import java.util.regex.Pattern;
    4.11 @@ -45,6 +46,8 @@
    4.12  import javax.tools.StandardJavaFileManager;
    4.13  import javax.tools.StandardLocation;
    4.14  import javax.tools.ToolProvider;
    4.15 +import static org.testng.Assert.assertFalse;
    4.16 +import static org.testng.Assert.fail;
    4.17  
    4.18  /**
    4.19   *
    4.20 @@ -241,4 +244,21 @@
    4.21          String fqn = "'" + pkg + '.' + cls + "'";
    4.22          return html.replace("'${fqn}'", fqn);
    4.23      }
    4.24 +    void assertErrors() {
    4.25 +        assertFalse(getErrors().isEmpty(), "There are supposed to be some errors");
    4.26 +    }
    4.27 +
    4.28 +    void assertError(String expMsg) {
    4.29 +        StringBuilder sb = new StringBuilder();
    4.30 +        sb.append("Can't find ").append(expMsg).append(" among:");
    4.31 +        for (Diagnostic<? extends JavaFileObject> e : errors) {
    4.32 +            String msg = e.getMessage(Locale.US);
    4.33 +            if (msg.contains(expMsg)) {
    4.34 +                return;
    4.35 +            }
    4.36 +            sb.append("\n");
    4.37 +            sb.append(msg);
    4.38 +        }
    4.39 +        fail(sb.toString());
    4.40 +    }
    4.41  }
     5.1 --- a/json/src/test/java/net/java/html/json/ModelProcessorTest.java	Fri Aug 16 21:20:42 2013 +0200
     5.2 +++ b/json/src/test/java/net/java/html/json/ModelProcessorTest.java	Sat Aug 17 10:23:40 2013 +0200
     5.3 @@ -241,4 +241,51 @@
     5.4          fail("Needs an error message about wrong method:\n" + c.getErrors());
     5.5          
     5.6      }
     5.7 +    
     5.8 +    @Test public void onErrorHasToExist() throws IOException {
     5.9 +        Compile res = Compile.create("", "package x;\n"
    5.10 +            + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
    5.11 +            + "  @net.java.html.json.Property(name=\"x\", type=String.class)\n"
    5.12 +            + "})\n"
    5.13 +            + "class UseOnReceive {\n"
    5.14 +            + "  @net.java.html.json.OnReceive(url=\"http://nowhere.com\", onError=\"doesNotExist\")\n"
    5.15 +            + "  static void onMessage(MyModel model, String value) {\n"
    5.16 +            + "  }\n"
    5.17 +            + "}\n"
    5.18 +        );
    5.19 +        res.assertErrors();
    5.20 +        res.assertError("not find doesNotExist");
    5.21 +    }
    5.22 +
    5.23 +    @Test public void onErrorWouldHaveToBeStatic() throws IOException {
    5.24 +        Compile res = Compile.create("", "package x;\n"
    5.25 +            + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
    5.26 +            + "  @net.java.html.json.Property(name=\"x\", type=String.class)\n"
    5.27 +            + "})\n"
    5.28 +            + "class UseOnReceive {\n"
    5.29 +            + "  @net.java.html.json.OnReceive(url=\"http://nowhere.com\", onError=\"notStatic\")\n"
    5.30 +            + "  static void onMessage(MyModel model, String value) {\n"
    5.31 +            + "  }\n"
    5.32 +            + "  void notStatic(Exception e) {}\n"
    5.33 +            + "}\n"
    5.34 +        );
    5.35 +        res.assertErrors();
    5.36 +        res.assertError("have to be static");
    5.37 +    }
    5.38 +
    5.39 +    @Test public void onErrorMustAcceptExceptionArgument() throws IOException {
    5.40 +        Compile res = Compile.create("", "package x;\n"
    5.41 +            + "@net.java.html.json.Model(className=\"MyModel\", properties= {\n"
    5.42 +            + "  @net.java.html.json.Property(name=\"x\", type=String.class)\n"
    5.43 +            + "})\n"
    5.44 +            + "class UseOnReceive {\n"
    5.45 +            + "  @net.java.html.json.OnReceive(url=\"http://nowhere.com\", onError=\"subclass\")\n"
    5.46 +            + "  static void onMessage(MyModel model, String value) {\n"
    5.47 +            + "  }\n"
    5.48 +            + "  static void subclass(java.io.IOException e) {}\n"
    5.49 +            + "}\n"
    5.50 +        );
    5.51 +        res.assertErrors();
    5.52 +        res.assertError("Error method first argument needs to be MyModel and second Exception");
    5.53 +    }
    5.54  }
     6.1 --- a/json/src/test/java/net/java/html/json/ModelTest.java	Fri Aug 16 21:20:42 2013 +0200
     6.2 +++ b/json/src/test/java/net/java/html/json/ModelTest.java	Sat Aug 17 10:23:40 2013 +0200
     6.3 @@ -164,12 +164,16 @@
     6.4          }
     6.5      }
     6.6      
     6.7 -    @OnReceive(url = "{protocol}://{host}?query={query}", data = Person.class)
     6.8 +    @OnReceive(url = "{protocol}://{host}?query={query}", data = Person.class, onError = "errorState")
     6.9      static void loadPeople(Modelik thiz, People p) {
    6.10          Modelik m = null;
    6.11          m.applyBindings();
    6.12          m.loadPeople("http", "apidesign.org", "query", new Person());
    6.13      }
    6.14 +    
    6.15 +    static void errorState(Modelik thiz, Exception ex) {
    6.16 +        
    6.17 +    }
    6.18  
    6.19      @OnReceive(url = "{protocol}://{host}?callback={back}&query={query}", jsonp = "back")
    6.20      static void loadPeopleViaJSONP(Modelik thiz, People p) {
     7.1 --- a/ko-fx/src/main/java/org/apidesign/html/kofx/LoadJSON.java	Fri Aug 16 21:20:42 2013 +0200
     7.2 +++ b/ko-fx/src/main/java/org/apidesign/html/kofx/LoadJSON.java	Sat Aug 17 10:23:40 2013 +0200
     7.3 @@ -172,7 +172,6 @@
     7.4              }
     7.5          } catch (IOException ex) {
     7.6              error = ex;
     7.7 -            LOG.log(Level.WARNING, "Cannot connect to " + url, ex);
     7.8          } finally {
     7.9              Platform.runLater(this);
    7.10          }