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 }