BrwsrCtx.execute(Runnable) and using it whenever one mangles with @Model classes BrwsrCtxExec
authorJaroslav Tulach <jtulach@netbeans.org>
Mon, 03 Mar 2014 23:30:02 +0100
branchBrwsrCtxExec
changeset 569245637e6d8db
parent 568 f0b64a5d6517
child 571 b55e41ff3fd9
BrwsrCtx.execute(Runnable) and using it whenever one mangles with @Model classes
boot-fx/src/test/java/org/netbeans/html/boot/fx/BootstrapTest.java
boot-fx/src/test/java/org/netbeans/html/boot/fx/TestingProvider.java
boot/pom.xml
boot/src/main/java/net/java/html/boot/BrowserBuilder.java
context/pom.xml
context/src/main/java/net/java/html/BrwsrCtx.java
context/src/test/java/net/java/html/BrwsrCtxTest.java
json-tck/src/main/java/net/java/html/json/tests/JSONTest.java
json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java
json-tck/src/main/java/net/java/html/json/tests/PairModel.java
json/src/main/java/org/apidesign/html/json/spi/FunctionBinding.java
json/src/main/java/org/netbeans/html/json/impl/JSON.java
json/src/test/java/net/java/html/json/OperationTest.java
     1.1 --- a/boot-fx/src/test/java/org/netbeans/html/boot/fx/BootstrapTest.java	Mon Mar 03 15:14:46 2014 +0100
     1.2 +++ b/boot-fx/src/test/java/org/netbeans/html/boot/fx/BootstrapTest.java	Mon Mar 03 23:30:02 2014 +0100
     1.3 @@ -47,10 +47,16 @@
     1.4  import java.util.ArrayList;
     1.5  import java.util.List;
     1.6  import java.util.concurrent.Executors;
     1.7 +import net.java.html.BrwsrCtx;
     1.8  import net.java.html.boot.BrowserBuilder;
     1.9  import org.apidesign.html.boot.spi.Fn;
    1.10 +import org.apidesign.html.context.spi.Contexts;
    1.11  import org.apidesign.html.json.tck.KOTest;
    1.12 +import org.openide.util.lookup.ServiceProvider;
    1.13  import org.testng.Assert;
    1.14 +import static org.testng.Assert.assertNotSame;
    1.15 +import static org.testng.Assert.assertSame;
    1.16 +import static org.testng.Assert.assertTrue;
    1.17  import org.testng.annotations.Factory;
    1.18  
    1.19  /**
    1.20 @@ -106,6 +112,11 @@
    1.21      }
    1.22      
    1.23      public static void initialized() throws Exception {
    1.24 +        BrwsrCtx b1 = BrwsrCtx.findDefault(BootstrapTest.class);
    1.25 +        TestingProvider.assertCalled("Our context created");
    1.26 +        assertNotSame(b1, BrwsrCtx.EMPTY, "Browser context is not empty");
    1.27 +        BrwsrCtx b2 = BrwsrCtx.findDefault(BootstrapTest.class);
    1.28 +        assertSame(b1, b2, "Browser context remains stable");
    1.29          Assert.assertSame(
    1.30              BootstrapTest.class.getClassLoader(),
    1.31              ClassLoader.getSystemClassLoader(),
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/boot-fx/src/test/java/org/netbeans/html/boot/fx/TestingProvider.java	Mon Mar 03 23:30:02 2014 +0100
     2.3 @@ -0,0 +1,65 @@
     2.4 +/**
     2.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     2.6 + *
     2.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     2.8 + *
     2.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    2.10 + * Other names may be trademarks of their respective owners.
    2.11 + *
    2.12 + * The contents of this file are subject to the terms of either the GNU
    2.13 + * General Public License Version 2 only ("GPL") or the Common
    2.14 + * Development and Distribution License("CDDL") (collectively, the
    2.15 + * "License"). You may not use this file except in compliance with the
    2.16 + * License. You can obtain a copy of the License at
    2.17 + * http://www.netbeans.org/cddl-gplv2.html
    2.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    2.19 + * specific language governing permissions and limitations under the
    2.20 + * License.  When distributing the software, include this License Header
    2.21 + * Notice in each file and include the License file at
    2.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    2.23 + * particular file as subject to the "Classpath" exception as provided
    2.24 + * by Oracle in the GPL Version 2 section of the License file that
    2.25 + * accompanied this code. If applicable, add the following below the
    2.26 + * License Header, with the fields enclosed by brackets [] replaced by
    2.27 + * your own identifying information:
    2.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    2.29 + *
    2.30 + * Contributor(s):
    2.31 + *
    2.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    2.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    2.34 + *
    2.35 + * If you wish your version of this file to be governed by only the CDDL
    2.36 + * or only the GPL Version 2, indicate your decision by adding
    2.37 + * "[Contributor] elects to include this software in this distribution
    2.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    2.39 + * single choice of license, a recipient has the option to distribute
    2.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    2.41 + * to extend the choice of license to its licensees as provided above.
    2.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    2.43 + * Version 2 license, then the option applies only if the new code is
    2.44 + * made subject to such option by the copyright holder.
    2.45 + */
    2.46 +package org.netbeans.html.boot.fx;
    2.47 +
    2.48 +import org.apidesign.html.context.spi.Contexts;
    2.49 +import org.openide.util.lookup.ServiceProvider;
    2.50 +import static org.testng.Assert.assertTrue;
    2.51 +
    2.52 +/**
    2.53 + *
    2.54 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    2.55 + */
    2.56 +@ServiceProvider(service = Contexts.Provider.class)
    2.57 +public final class TestingProvider implements Contexts.Provider {
    2.58 +    
    2.59 +    static void assertCalled(String msg) {
    2.60 +        assertTrue(Boolean.getBoolean(TestingProvider.class.getName()), msg);
    2.61 +    }
    2.62 +
    2.63 +    @Override
    2.64 +    public void fillContext(Contexts.Builder context, Class<?> requestor) {
    2.65 +        System.setProperty(TestingProvider.class.getName(), "true");
    2.66 +    }
    2.67 +    
    2.68 +}
     3.1 --- a/boot/pom.xml	Mon Mar 03 15:14:46 2014 +0100
     3.2 +++ b/boot/pom.xml	Mon Mar 03 23:30:02 2014 +0100
     3.3 @@ -48,6 +48,12 @@
     3.4        <artifactId>org-openide-util-lookup</artifactId>
     3.5        <scope>provided</scope>
     3.6      </dependency>
     3.7 +    <dependency>
     3.8 +      <groupId>org.netbeans.html</groupId>
     3.9 +      <artifactId>net.java.html</artifactId>
    3.10 +      <version>${project.version}</version>
    3.11 +      <type>jar</type>
    3.12 +    </dependency>
    3.13    </dependencies>
    3.14      <description>Builder to launch your Java/HTML based application.</description>
    3.15  </project>
     4.1 --- a/boot/src/main/java/net/java/html/boot/BrowserBuilder.java	Mon Mar 03 15:14:46 2014 +0100
     4.2 +++ b/boot/src/main/java/net/java/html/boot/BrowserBuilder.java	Mon Mar 03 23:30:02 2014 +0100
     4.3 @@ -53,6 +53,7 @@
     4.4  import java.util.ServiceLoader;
     4.5  import java.util.logging.Level;
     4.6  import java.util.logging.Logger;
     4.7 +import net.java.html.BrwsrCtx;
     4.8  import net.java.html.js.JavaScriptBody;
     4.9  import org.apidesign.html.boot.spi.Fn;
    4.10  import org.netbeans.html.boot.impl.FindResources;
    4.11 @@ -235,14 +236,21 @@
    4.12  
    4.13          final Fn.Presenter currentP = dfnr;
    4.14          class OnPageLoad implements Runnable {
    4.15 +            Class<?> newClazz;
    4.16              @Override
    4.17              public void run() {
    4.18                  try {
    4.19 -                    Thread.currentThread().setContextClassLoader(loader);
    4.20 -                    Class<?> newClazz = Class.forName(clazz.getName(), true, loader);
    4.21 -                    if (browserClass != null) {
    4.22 -                        browserClass[0] = newClazz;
    4.23 +                    if (newClazz == null) {
    4.24 +                        Thread.currentThread().setContextClassLoader(loader);
    4.25 +                        newClazz = Class.forName(clazz.getName(), true, loader);
    4.26 +                        if (browserClass != null) {
    4.27 +                            browserClass[0] = newClazz;
    4.28 +                        }
    4.29 +                        BrwsrCtx c = BrwsrCtx.findDefault(newClazz);
    4.30 +                        c.execute(this);
    4.31 +                        return;
    4.32                      }
    4.33 +                    
    4.34                      if (onLoad != null) {
    4.35                          onLoad.run();
    4.36                      }
     5.1 --- a/context/pom.xml	Mon Mar 03 15:14:46 2014 +0100
     5.2 +++ b/context/pom.xml	Mon Mar 03 23:30:02 2014 +0100
     5.3 @@ -30,6 +30,12 @@
     5.4        </plugins>
     5.5    </build>
     5.6    <dependencies>
     5.7 +    <dependency>
     5.8 +      <groupId>org.testng</groupId>
     5.9 +      <artifactId>testng</artifactId>
    5.10 +      <version>6.8.1</version>
    5.11 +      <scope>test</scope>
    5.12 +    </dependency>
    5.13    </dependencies>
    5.14      <description>Representation of an HTML page context a Java program operates in.</description>
    5.15  </project>
     6.1 --- a/context/src/main/java/net/java/html/BrwsrCtx.java	Mon Mar 03 15:14:46 2014 +0100
     6.2 +++ b/context/src/main/java/net/java/html/BrwsrCtx.java	Mon Mar 03 23:30:02 2014 +0100
     6.3 @@ -83,6 +83,9 @@
     6.4       */
     6.5      public static final BrwsrCtx EMPTY = Contexts.newBuilder().build();
     6.6      
     6.7 +    /** currently {@link #execute(java.lang.Runnable) activated context} */
     6.8 +    private static final ThreadLocal<BrwsrCtx> CURRENT = new ThreadLocal<BrwsrCtx>();
     6.9 +    
    6.10      /** Seeks for the default context that is associated with the requesting
    6.11       * class. If no suitable context is found, a warning message is
    6.12       * printed and {@link #EMPTY} context is returned.
    6.13 @@ -91,6 +94,11 @@
    6.14       * @return appropriate context for the request
    6.15       */
    6.16      public static BrwsrCtx findDefault(Class<?> requestor) {
    6.17 +        BrwsrCtx brwsr = CURRENT.get();
    6.18 +        if (brwsr != null) {
    6.19 +            return brwsr;
    6.20 +        }
    6.21 +        
    6.22          org.apidesign.html.context.spi.Contexts.Builder cb = Contexts.newBuilder();
    6.23          boolean found = false;
    6.24          
    6.25 @@ -130,5 +138,21 @@
    6.26          }
    6.27          return cb.build();
    6.28      }
    6.29 -    
    6.30 +
    6.31 +    /** Runs provided code in the context of this {@link BrwsrCtx}.
    6.32 +     * While the <code>exec</code> is running, the {@link #findDefault(java.lang.Class)}
    6.33 +     * method returns <code>this</code>.
    6.34 +     * 
    6.35 +     * @param exec the code to execute
    6.36 +     * @since 0.7.6
    6.37 +     */
    6.38 +    public final void execute(Runnable exec) {
    6.39 +        BrwsrCtx prev = CURRENT.get();
    6.40 +        try {
    6.41 +            CURRENT.set(this);
    6.42 +            exec.run();
    6.43 +        } finally {
    6.44 +            CURRENT.set(prev);
    6.45 +        }
    6.46 +    }
    6.47  }
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/context/src/test/java/net/java/html/BrwsrCtxTest.java	Mon Mar 03 23:30:02 2014 +0100
     7.3 @@ -0,0 +1,75 @@
     7.4 +/**
     7.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     7.6 + *
     7.7 + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved.
     7.8 + *
     7.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    7.10 + * Other names may be trademarks of their respective owners.
    7.11 + *
    7.12 + * The contents of this file are subject to the terms of either the GNU
    7.13 + * General Public License Version 2 only ("GPL") or the Common
    7.14 + * Development and Distribution License("CDDL") (collectively, the
    7.15 + * "License"). You may not use this file except in compliance with the
    7.16 + * License. You can obtain a copy of the License at
    7.17 + * http://www.netbeans.org/cddl-gplv2.html
    7.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    7.19 + * specific language governing permissions and limitations under the
    7.20 + * License.  When distributing the software, include this License Header
    7.21 + * Notice in each file and include the License file at
    7.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    7.23 + * particular file as subject to the "Classpath" exception as provided
    7.24 + * by Oracle in the GPL Version 2 section of the License file that
    7.25 + * accompanied this code. If applicable, add the following below the
    7.26 + * License Header, with the fields enclosed by brackets [] replaced by
    7.27 + * your own identifying information:
    7.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    7.29 + *
    7.30 + * Contributor(s):
    7.31 + *
    7.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    7.33 + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved.
    7.34 + *
    7.35 + * If you wish your version of this file to be governed by only the CDDL
    7.36 + * or only the GPL Version 2, indicate your decision by adding
    7.37 + * "[Contributor] elects to include this software in this distribution
    7.38 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    7.39 + * single choice of license, a recipient has the option to distribute
    7.40 + * your version of this file under either the CDDL, the GPL Version 2 or
    7.41 + * to extend the choice of license to its licensees as provided above.
    7.42 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    7.43 + * Version 2 license, then the option applies only if the new code is
    7.44 + * made subject to such option by the copyright holder.
    7.45 + */
    7.46 +package net.java.html;
    7.47 +
    7.48 +import org.apidesign.html.context.spi.Contexts;
    7.49 +import static org.testng.Assert.*;
    7.50 +
    7.51 +/**
    7.52 + *
    7.53 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    7.54 + */
    7.55 +public class BrwsrCtxTest {
    7.56 +    
    7.57 +    public BrwsrCtxTest() {
    7.58 +    }
    7.59 +
    7.60 +
    7.61 +    @org.testng.annotations.Test
    7.62 +    public void canSetAssociateCtx() {
    7.63 +        final BrwsrCtx ctx = Contexts.newBuilder().build();
    7.64 +        final boolean[] arr = { false };
    7.65 +        
    7.66 +        assertNotSame(BrwsrCtx.findDefault(BrwsrCtxTest.class), ctx, "Not associated yet");
    7.67 +        ctx.execute(new Runnable() {
    7.68 +            @Override public void run() {
    7.69 +                assertSame(BrwsrCtx.findDefault(BrwsrCtxTest.class), ctx, "Once same");
    7.70 +                assertSame(BrwsrCtx.findDefault(BrwsrCtxTest.class), ctx, "2nd same");
    7.71 +                arr[0] = true;
    7.72 +            }
    7.73 +        });
    7.74 +        assertNotSame(BrwsrCtx.findDefault(BrwsrCtxTest.class), ctx, "Not associated again");
    7.75 +        assertTrue(arr[0], "Runnable was executed");
    7.76 +    }
    7.77 +    
    7.78 +}
    7.79 \ No newline at end of file
     8.1 --- a/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java	Mon Mar 03 15:14:46 2014 +0100
     8.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/JSONTest.java	Mon Mar 03 23:30:02 2014 +0100
     8.3 @@ -71,6 +71,7 @@
     8.4      @ModelOperation static void assignFetched(JSONik m, Person p) {
     8.5          m.setFetched(p);
     8.6      }
     8.7 +    private BrwsrCtx ctx;
     8.8      
     8.9      @KOTest public void toJSONInABrowser() throws Throwable {
    8.10          Person p = Models.bind(new Person(), newContext());
    8.11 @@ -150,16 +151,20 @@
    8.12          assert p2.getFirstName().equals(p.getFirstName()) : 
    8.13              "Should be the same: " + p.getFirstName() + " != " + p2.getFirstName();
    8.14      }
    8.15 +
    8.16 +    private static BrwsrCtx onCallback;
    8.17      
    8.18      @OnReceive(url="{url}")
    8.19      static void fetch(Person p, JSONik model) {
    8.20          model.setFetched(p);
    8.21 +        onCallback = BrwsrCtx.findDefault(model.getClass());
    8.22      }
    8.23  
    8.24      @OnReceive(url="{url}", onError = "setMessage")
    8.25      static void fetchArray(Person[] p, JSONik model) {
    8.26          model.setFetchedCount(p.length);
    8.27          model.setFetched(p[0]);
    8.28 +        onCallback = BrwsrCtx.findDefault(model.getClass());
    8.29      }
    8.30      
    8.31      static void setMessage(JSONik m, Exception t) {
    8.32 @@ -191,7 +196,7 @@
    8.33                  JSONTest.class, "{'firstName': 'Sitar', 'sex': 'MALE'}",
    8.34                  "application/json"
    8.35              );
    8.36 -            js = Models.bind(new JSONik(), newContext());
    8.37 +            js = Models.bind(new JSONik(), ctx = newContext());
    8.38              js.applyBindings();
    8.39  
    8.40              js.setFetched(null);
    8.41 @@ -205,6 +210,8 @@
    8.42          
    8.43          assert "Sitar".equals(p.getFirstName()) : "Expecting Sitar: " + p.getFirstName();
    8.44          assert Sex.MALE.equals(p.getSex()) : "Expecting MALE: " + p.getSex();
    8.45 +        
    8.46 +        assert ctx == onCallback;
    8.47      }
    8.48      
    8.49      @OnReceive(url="{url}?callme={me}", jsonp = "me")
     9.1 --- a/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java	Mon Mar 03 15:14:46 2014 +0100
     9.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/KnockoutTest.java	Mon Mar 03 23:30:02 2014 +0100
     9.3 @@ -51,6 +51,7 @@
     9.4  import net.java.html.json.Property;
     9.5  import org.apidesign.html.json.tck.KOTest;
     9.6  import org.apidesign.html.json.tck.KnockoutTCK;
     9.7 +import static org.testng.Assert.assertSame;
     9.8  
     9.9  /**
    9.10   *
    9.11 @@ -215,13 +216,16 @@
    9.12              + "</div>\n"
    9.13          );
    9.14          try {
    9.15 -            Pair m = Models.bind(new Pair(null, null, new Pair("First", "Last", null)), newContext());
    9.16 +            final BrwsrCtx ctx = newContext();
    9.17 +            Pair m = Models.bind(new Pair(null, null, new Pair("First", "Last", null)), ctx);
    9.18              m.applyBindings();
    9.19  
    9.20              int cnt = countChildren("ul");
    9.21              assert cnt == 2 : "Two children now, but was " + cnt;
    9.22  
    9.23              triggerChildClick("ul", 1);
    9.24 +            
    9.25 +            assertSame(PairModel.ctx, ctx, "Context remains the same");
    9.26  
    9.27              assert "Last".equals(m.getFirstName()) : "We got callback from 2nd child " + m.getFirstName();
    9.28          } finally {
    10.1 --- a/json-tck/src/main/java/net/java/html/json/tests/PairModel.java	Mon Mar 03 15:14:46 2014 +0100
    10.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/PairModel.java	Mon Mar 03 23:30:02 2014 +0100
    10.3 @@ -44,6 +44,7 @@
    10.4  
    10.5  import java.util.Arrays;
    10.6  import java.util.List;
    10.7 +import net.java.html.BrwsrCtx;
    10.8  import net.java.html.json.ComputedProperty;
    10.9  import net.java.html.json.Function;
   10.10  import net.java.html.json.Model;
   10.11 @@ -59,6 +60,8 @@
   10.12      @Property(name = "next", type = Pair.class)
   10.13  })
   10.14  class PairModel {
   10.15 +    static BrwsrCtx ctx;
   10.16 +    
   10.17      @ComputedProperty 
   10.18      static List<String> bothNames(String firstName, String lastName) {
   10.19          return Arrays.asList(firstName, lastName);
   10.20 @@ -71,6 +74,7 @@
   10.21      
   10.22      @Function 
   10.23      static void assignFirstName(Pair m, String data) {
   10.24 +        ctx = BrwsrCtx.findDefault(Pair.class);
   10.25          m.setFirstName(data);
   10.26      }
   10.27  }
    11.1 --- a/json/src/main/java/org/apidesign/html/json/spi/FunctionBinding.java	Mon Mar 03 15:14:46 2014 +0100
    11.2 +++ b/json/src/main/java/org/apidesign/html/json/spi/FunctionBinding.java	Mon Mar 03 23:30:02 2014 +0100
    11.3 @@ -42,6 +42,7 @@
    11.4   */
    11.5  package org.apidesign.html.json.spi;
    11.6  
    11.7 +import net.java.html.BrwsrCtx;
    11.8  import net.java.html.json.Function;
    11.9  import net.java.html.json.Model;
   11.10  
   11.11 @@ -91,12 +92,19 @@
   11.12          }
   11.13  
   11.14          @Override
   11.15 -        public void call(Object data, Object ev) {
   11.16 -            try {
   11.17 -                access.call(model, index, data, ev);
   11.18 -            } catch (Throwable ex) {
   11.19 -                ex.printStackTrace();
   11.20 +        public void call(final Object data, final Object ev) {
   11.21 +            BrwsrCtx ctx = access.protoFor(model).getContext();
   11.22 +            class Dispatch implements Runnable {
   11.23 +                @Override
   11.24 +                public void run() {
   11.25 +                    try {
   11.26 +                        access.call(model, index, data, ev);
   11.27 +                    } catch (Throwable ex) {
   11.28 +                        ex.printStackTrace();
   11.29 +                    }
   11.30 +                }
   11.31              }
   11.32 +            ctx.execute(new Dispatch());
   11.33          }
   11.34      }
   11.35  }
    12.1 --- a/json/src/main/java/org/netbeans/html/json/impl/JSON.java	Mon Mar 03 15:14:46 2014 +0100
    12.2 +++ b/json/src/main/java/org/netbeans/html/json/impl/JSON.java	Mon Mar 03 23:30:02 2014 +0100
    12.3 @@ -80,8 +80,14 @@
    12.4          return t == null ? EmptyTech.EMPTY : t;
    12.5      }
    12.6      
    12.7 -    public static void runInBrowser(BrwsrCtx c, Runnable runnable) {
    12.8 -        findTechnology(c).runSafe(runnable);
    12.9 +    public static void runInBrowser(final BrwsrCtx c, final Runnable runnable) {
   12.10 +        class Wrap implements Runnable {
   12.11 +            @Override
   12.12 +            public void run() {
   12.13 +                c.execute(runnable);
   12.14 +            }
   12.15 +        }
   12.16 +        findTechnology(c).runSafe(new Wrap());
   12.17      }
   12.18      
   12.19      public static void extract(BrwsrCtx c, Object value, String[] props, Object[] values) {
   12.20 @@ -492,7 +498,7 @@
   12.21          }
   12.22  
   12.23          @Override
   12.24 -        public synchronized void runSafe(Runnable r) {
   12.25 +        public void runSafe(Runnable r) {
   12.26              r.run();
   12.27          }
   12.28  
    13.1 --- a/json/src/test/java/net/java/html/json/OperationTest.java	Mon Mar 03 15:14:46 2014 +0100
    13.2 +++ b/json/src/test/java/net/java/html/json/OperationTest.java	Mon Mar 03 23:30:02 2014 +0100
    13.3 @@ -42,7 +42,10 @@
    13.4   */
    13.5  package net.java.html.json;
    13.6  
    13.7 +import net.java.html.BrwsrCtx;
    13.8 +import org.apidesign.html.context.spi.Contexts;
    13.9  import static org.testng.Assert.assertEquals;
   13.10 +import static org.testng.Assert.assertSame;
   13.11  import org.testng.annotations.Test;
   13.12  
   13.13  /**
   13.14 @@ -53,13 +56,15 @@
   13.15      @Property(name = "names", type = String.class, array = true)
   13.16  })
   13.17  public class OperationTest {
   13.18 -    @ModelOperation static void add(OpModel m, String name) {
   13.19 +    @ModelOperation static void add(OpModel m, String name, BrwsrCtx exp) {
   13.20 +        assertSame(BrwsrCtx.findDefault(OpModel.class), exp, "Context is passed in");
   13.21          m.getNames().add(name);
   13.22      }
   13.23  
   13.24      @Test public void addOneToTheModel() {
   13.25 -        OpModel m = new OpModel("One");
   13.26 -        m.add("Second");
   13.27 +        BrwsrCtx ctx = Contexts.newBuilder().build();
   13.28 +        OpModel m = Models.bind(new OpModel("One"), ctx);
   13.29 +        m.add("Second", ctx);
   13.30          assertEquals(m.getNames().size(), 2, "Both are there: " + m.getNames());
   13.31      }
   13.32  }