#256696: Making OSGi headers, enterprise OSGi ready
authorJaroslav Tulach <jtulach@netbeans.org>
Thu, 26 Nov 2015 20:59:51 +0100
changeset 1021c918924ad3c5
parent 1020 b5d5cbb44ce0
child 1022 2f6f1d20fa7a
#256696: Making OSGi headers, enterprise OSGi ready
boot-fx/pom.xml
boot/pom.xml
context/pom.xml
ko-felix-test/pom.xml
ko-felix-test/src/main/java/org/netbeans/html/ko/felix/test/KnockoutFelixTCKImpl.java
ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KOFx.java
ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KnockoutFelixAriesIT.java
ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KnockoutFelixIT.java
ko4j/pom.xml
src/main/javadoc/overview.html
     1.1 --- a/boot-fx/pom.xml	Sun Nov 22 21:18:35 2015 +0100
     1.2 +++ b/boot-fx/pom.xml	Thu Nov 26 20:59:51 2015 +0100
     1.3 @@ -21,6 +21,12 @@
     1.4            <plugin>
     1.5                <groupId>org.apache.felix</groupId>
     1.6                <artifactId>maven-bundle-plugin</artifactId>
     1.7 +              <configuration>
     1.8 +                  <instructions>
     1.9 +                      <Require-Capability>osgi.extender;resolution:=optional;filter:="(osgi.extender=osgi.serviceloader.registrar)"</Require-Capability>
    1.10 +                      <Provide-Capability>osgi.serviceloader;osgi.serviceloader=org.netbeans.html.boot.spi.Fn$Presenter</Provide-Capability>
    1.11 +                  </instructions>
    1.12 +              </configuration>
    1.13            </plugin>
    1.14            <plugin>
    1.15                <groupId>org.netbeans.html</groupId>
     2.1 --- a/boot/pom.xml	Sun Nov 22 21:18:35 2015 +0100
     2.2 +++ b/boot/pom.xml	Thu Nov 26 20:59:51 2015 +0100
     2.3 @@ -26,6 +26,7 @@
     2.4                        <Agent-Class>org.netbeans.html.boot.impl.JsAgent</Agent-Class>
     2.5                        <Premain-Class>org.netbeans.html.boot.impl.JsAgent</Premain-Class>
     2.6                        <Eclipse-BuddyPolicy>dependent</Eclipse-BuddyPolicy>
     2.7 +                      <Require-Capability>osgi.extender;resolution:=optional;filter:="(osgi.extender=osgi.serviceloader.processor)",osgi.serviceloader;filter:="(osgi.serviceloader=org.netbeans.html.boot.spi.Fn$Presenter)";cardinality:=multiple;resolution:=optional</Require-Capability>
     2.8                    </instructions>
     2.9                </configuration>
    2.10            </plugin>
     3.1 --- a/context/pom.xml	Sun Nov 22 21:18:35 2015 +0100
     3.2 +++ b/context/pom.xml	Thu Nov 26 20:59:51 2015 +0100
     3.3 @@ -24,6 +24,7 @@
     3.4                <configuration>
     3.5                    <instructions>
     3.6                        <Eclipse-BuddyPolicy>dependent</Eclipse-BuddyPolicy>
     3.7 +                      <Require-Capability>osgi.extender;resolution:=optional;filter:="(osgi.extender=osgi.serviceloader.processor)",osgi.serviceloader;filter:="(osgi.serviceloader=org.netbeans.html.context.spi.Contexts$Provider)";cardinality:=multiple;resolution:=optional</Require-Capability>
     3.8                    </instructions>
     3.9                </configuration>
    3.10            </plugin>
     4.1 --- a/ko-felix-test/pom.xml	Sun Nov 22 21:18:35 2015 +0100
     4.2 +++ b/ko-felix-test/pom.xml	Thu Nov 26 20:59:51 2015 +0100
     4.3 @@ -36,6 +36,7 @@
     4.4                      <additionalClasspathElements>
     4.5                          <additionalClasspathElement>${project.build.directory}/${project.build.finalName}.jar</additionalClasspathElement>
     4.6                      </additionalClasspathElements>
     4.7 +                    <forkMode>always</forkMode>
     4.8                  </configuration>
     4.9                  <executions>
    4.10                      <execution>
    4.11 @@ -125,5 +126,15 @@
    4.12              <groupId>org.apache.felix</groupId>
    4.13              <artifactId>org.apache.felix.framework</artifactId>
    4.14          </dependency>
    4.15 +        <dependency>
    4.16 +            <groupId>org.apache.aries.spifly</groupId>
    4.17 +            <artifactId>org.apache.aries.spifly.dynamic.bundle</artifactId>
    4.18 +            <version>1.0.4</version>
    4.19 +        </dependency>
    4.20 +        <dependency>
    4.21 +            <groupId>org.osgi</groupId>
    4.22 +            <artifactId>org.osgi.service.log</artifactId>
    4.23 +            <version>1.3.0</version>
    4.24 +        </dependency>
    4.25      </dependencies>
    4.26  </project>
    4.27 \ No newline at end of file
     5.1 --- a/ko-felix-test/src/main/java/org/netbeans/html/ko/felix/test/KnockoutFelixTCKImpl.java	Sun Nov 22 21:18:35 2015 +0100
     5.2 +++ b/ko-felix-test/src/main/java/org/netbeans/html/ko/felix/test/KnockoutFelixTCKImpl.java	Thu Nov 26 20:59:51 2015 +0100
     5.3 @@ -101,18 +101,20 @@
     5.4          return testClasses();
     5.5      }
     5.6      
     5.7 -    public static void start(URI server) throws Exception {
     5.8 +    public static void start(String callBackClass, URI server, final boolean useAllClassloader) throws Exception {
     5.9          final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(KnockoutFelixTCKImpl.class).
    5.10              loadPage(server.toString()).
    5.11 -            invoke("initialized");
    5.12 +            invoke("initialized", callBackClass);
    5.13  
    5.14          Executors.newSingleThreadExecutor().submit(new Runnable() {
    5.15              @Override
    5.16              public void run() {
    5.17                  try {
    5.18                      Bundle[] arr = FrameworkUtil.getBundle(BrowserBuilder.class).getBundleContext().getBundles();
    5.19 -                    final ClassLoader osgiClassLoader = new AllBundlesLoader(arr);
    5.20 -                    bb.classloader(osgiClassLoader);
    5.21 +                    if (useAllClassloader) {
    5.22 +                        final ClassLoader osgiClassLoader = new AllBundlesLoader(arr);
    5.23 +                        bb.classloader(osgiClassLoader);
    5.24 +                    }
    5.25                      bb.showAndWait();
    5.26                  } catch (Throwable t) {
    5.27                      t.printStackTrace();
    5.28 @@ -121,16 +123,14 @@
    5.29          });
    5.30      }
    5.31  
    5.32 -    public static void initialized() throws Exception {
    5.33 +    public static void initialized(String... args) throws Exception {
    5.34          Bundle bundle = FrameworkUtil.getBundle(KnockoutFelixTCKImpl.class);
    5.35          if (bundle == null) {
    5.36              throw new IllegalStateException(
    5.37                  "Should be loaded from a bundle. But was: " + KnockoutFelixTCKImpl.class.getClassLoader()
    5.38              );
    5.39          }
    5.40 -        Class<?> classpathClass = ClassLoader.getSystemClassLoader().loadClass(
    5.41 -            "org.netbeans.html.ko.felix.test.KnockoutFelixIT"
    5.42 -        );
    5.43 +        Class<?> classpathClass = ClassLoader.getSystemClassLoader().loadClass(args[0]);
    5.44          Method m = classpathClass.getMethod("initialized", Class.class, Object.class);
    5.45          browserContext = Fn.activePresenter();
    5.46          m.invoke(null, KnockoutFelixTCKImpl.class, browserContext);
     6.1 --- a/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KOFx.java	Sun Nov 22 21:18:35 2015 +0100
     6.2 +++ b/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KOFx.java	Thu Nov 26 20:59:51 2015 +0100
     6.3 @@ -60,8 +60,10 @@
     6.4      private Object result;
     6.5      private Object inst;
     6.6      private int count;
     6.7 +    private final Class<?> itClass;
     6.8  
     6.9 -    KOFx(Object p, Method m) {
    6.10 +    KOFx(Class<?> itClass, Object p, Method m) {
    6.11 +        this.itClass = itClass;
    6.12          this.p = p;
    6.13          this.m = m;
    6.14      }
    6.15 @@ -90,7 +92,7 @@
    6.16          boolean notify = true;
    6.17          Closeable a = null;
    6.18          try {
    6.19 -            a = KnockoutFelixIT.activateInOSGi(p);
    6.20 +            a = (Closeable) itClass.getMethod("activateInOSGi", Object.class).invoke(null, p);
    6.21              if (inst == null) {
    6.22                  inst = m.getDeclaringClass().newInstance();
    6.23              }
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KnockoutFelixAriesIT.java	Thu Nov 26 20:59:51 2015 +0100
     7.3 @@ -0,0 +1,258 @@
     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 org.netbeans.html.ko.felix.test;
    7.47 +
    7.48 +import java.io.Closeable;
    7.49 +import java.io.File;
    7.50 +import java.io.IOException;
    7.51 +import java.lang.annotation.Annotation;
    7.52 +import java.lang.reflect.Method;
    7.53 +import java.net.URI;
    7.54 +import java.util.ArrayList;
    7.55 +import java.util.HashMap;
    7.56 +import java.util.List;
    7.57 +import java.util.Map;
    7.58 +import java.util.ServiceLoader;
    7.59 +import java.util.concurrent.Callable;
    7.60 +import java.util.jar.JarFile;
    7.61 +import java.util.logging.Level;
    7.62 +import java.util.logging.Logger;
    7.63 +import org.netbeans.html.boot.spi.Fn;
    7.64 +import org.netbeans.html.json.tck.KOTest;
    7.65 +import org.netbeans.html.json.tck.KnockoutTCK;
    7.66 +import org.osgi.framework.Bundle;
    7.67 +import org.osgi.framework.BundleException;
    7.68 +import org.osgi.framework.Constants;
    7.69 +import org.osgi.framework.launch.Framework;
    7.70 +import org.osgi.framework.launch.FrameworkFactory;
    7.71 +import static org.testng.Assert.assertNotNull;
    7.72 +import static org.testng.Assert.assertTrue;
    7.73 +import static org.testng.Assert.fail;
    7.74 +import org.testng.annotations.AfterClass;
    7.75 +import org.testng.annotations.Factory;
    7.76 +
    7.77 +public class KnockoutFelixAriesIT {
    7.78 +    private static final Logger LOG = Logger.getLogger(KnockoutFelixAriesIT.class.getName());
    7.79 +    private static Framework framework;
    7.80 +    private static File dir;
    7.81 +    static Framework framework() throws Exception {
    7.82 +        if (framework != null) {
    7.83 +            return framework;
    7.84 +        }
    7.85 +        for (FrameworkFactory ff : ServiceLoader.load(FrameworkFactory.class)) {
    7.86 +            
    7.87 +            String basedir = System.getProperty("basedir");
    7.88 +            assertNotNull("basedir preperty provided", basedir);
    7.89 +            File target = new File(basedir, "target");
    7.90 +            dir = new File(target, "osgi-aries");
    7.91 +            dir.mkdirs();
    7.92 +            
    7.93 +            Map<String,String> config = new HashMap<String, String>();
    7.94 +            config.put(Constants.FRAMEWORK_STORAGE, dir.getPath());
    7.95 +            config.put(Constants.FRAMEWORK_STORAGE_CLEAN, "true");
    7.96 +            config.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, "sun.misc,"
    7.97 +                + "javafx.application,"
    7.98 +                + "javafx.beans,"
    7.99 +                + "javafx.beans.property,"
   7.100 +                + "javafx.beans.value,"
   7.101 +                + "javafx.collections,"
   7.102 +                + "javafx.concurrent,"
   7.103 +                + "javafx.event,"
   7.104 +                + "javafx.geometry,"
   7.105 +                + "javafx.scene,"
   7.106 +                + "javafx.scene.control,"
   7.107 +                + "javafx.scene.image,"
   7.108 +                + "javafx.scene.layout,"
   7.109 +                + "javafx.scene.text,"
   7.110 +                + "javafx.scene.web,"
   7.111 +                + "javafx.stage,"
   7.112 +                + "javafx.util,"
   7.113 +                + "netscape.javascript"
   7.114 +            );
   7.115 +            framework = ff.newFramework(config);
   7.116 +            framework.init();
   7.117 +            loadClassPathBundles(framework);
   7.118 +            framework.start();
   7.119 +            boolean ok = false;
   7.120 +            for (Bundle b : framework.getBundleContext().getBundles()) {
   7.121 +                try {
   7.122 +                    if (!b.getSymbolicName().equals("org.apache.aries.spifly.dynamic.bundle")) {
   7.123 +                        continue;
   7.124 +                    }
   7.125 +                    b.start();
   7.126 +                    ok = true;
   7.127 +                    LOG.log(Level.INFO, "Started {0}", b.getSymbolicName());
   7.128 +                } catch (BundleException ex) {
   7.129 +                    LOG.log(Level.WARNING, "Cannot start bundle " + b.getSymbolicName(), ex);
   7.130 +                }
   7.131 +            }
   7.132 +            assertTrue(ok, "Aries installed");
   7.133 +            for (Bundle b : framework.getBundleContext().getBundles()) {
   7.134 +                try {
   7.135 +                    if (b.getSymbolicName().contains("felix.framework")) {
   7.136 +                        continue;
   7.137 +                    }
   7.138 +                    if (b.getSymbolicName().contains("glassfish.grizzly")) {
   7.139 +                        continue;
   7.140 +                    }
   7.141 +                    b.start();
   7.142 +                    LOG.log(Level.INFO, "Started {0}", b.getSymbolicName());
   7.143 +                } catch (BundleException ex) {
   7.144 +                    LOG.log(Level.WARNING, "Cannot start bundle " + b.getSymbolicName(), ex);
   7.145 +                }
   7.146 +            }
   7.147 +            return framework;
   7.148 +        }
   7.149 +        fail("No OSGi framework in the path");
   7.150 +        return null;
   7.151 +    }
   7.152 +    
   7.153 +    @AfterClass public static void cleanUp() throws Exception {
   7.154 +        if (framework != null) framework.stop();
   7.155 +        clearUpDir(dir);
   7.156 +    }
   7.157 +    private static void clearUpDir(File dir) {
   7.158 +        if (dir.isDirectory()) {
   7.159 +            for (File f : dir.listFiles()) {
   7.160 +                clearUpDir(f);
   7.161 +            }
   7.162 +        }
   7.163 +        dir.delete();
   7.164 +    }
   7.165 +    
   7.166 +    
   7.167 +
   7.168 +    private static void loadClassPathBundles(Framework f) throws IOException, BundleException {
   7.169 +        for (String jar : System.getProperty("java.class.path").split(File.pathSeparator)) {
   7.170 +            File file = new File(jar);
   7.171 +            if (!file.isFile()) {
   7.172 +                LOG.info("Not loading " + file);
   7.173 +                continue;
   7.174 +            }
   7.175 +            JarFile jf = new JarFile(file);
   7.176 +            final String name = jf.getManifest().getMainAttributes().getValue("Bundle-SymbolicName");
   7.177 +            jf.close();
   7.178 +            if (name != null) {
   7.179 +                if (name.contains("org.eclipse.osgi")) {
   7.180 +                    throw new IllegalStateException("Found " + name + " !");
   7.181 +                }
   7.182 +                if (name.contains("felix.framework")) {
   7.183 +                    continue;
   7.184 +                }
   7.185 +                if (name.contains("testng")) {
   7.186 +                    continue;
   7.187 +                }
   7.188 +                final String path = "reference:" + file.toURI().toString();
   7.189 +                try {
   7.190 +                    Bundle b = f.getBundleContext().installBundle(path);
   7.191 +                } catch (BundleException ex) {
   7.192 +                    LOG.log(Level.WARNING, "Cannot install " + file, ex);
   7.193 +                }
   7.194 +            }
   7.195 +        }
   7.196 +    }
   7.197 +    
   7.198 +    private static Class<?> loadOSGiClass(Class<?> c) throws Exception {
   7.199 +        return KnockoutFelixTCKImpl.loadOSGiClass(c.getName(), KnockoutFelixAriesIT.framework().getBundleContext());
   7.200 +    }
   7.201 +    
   7.202 +    private static Class<?> browserClass;
   7.203 +    private static Object browserContext;
   7.204 +
   7.205 +    @Factory public static Object[] compatibilityTests() throws Exception {
   7.206 +        Class<?> tck = loadOSGiClass(KnockoutTCK.class);
   7.207 +        Class<?> peer = loadOSGiClass(KnockoutFelixTCKImpl.class);
   7.208 +        // initialize the TCK
   7.209 +        Callable<Class[]> inst = (Callable<Class[]>) peer.newInstance();
   7.210 +        
   7.211 +        Class[] arr = inst.call();
   7.212 +        for (int i = 0; i < arr.length; i++) {
   7.213 +            if (arr[i].getClassLoader() == ClassLoader.getSystemClassLoader()) {
   7.214 +                fail("Should be an OSGi class: " + arr[i]);
   7.215 +            }
   7.216 +        }
   7.217 +        
   7.218 +        URI uri = DynamicHTTP.initServer();
   7.219 +
   7.220 +        Method start = peer.getMethod("start", String.class, URI.class, boolean.class);
   7.221 +        start.invoke(null, KnockoutFelixAriesIT.class.getName(), uri, false);
   7.222 +        
   7.223 +        ClassLoader l = getClassLoader();
   7.224 +        List<Object> res = new ArrayList<Object>();
   7.225 +        for (int i = 0; i < arr.length; i++) {
   7.226 +            seekKOTests(arr[i], res);
   7.227 +        }
   7.228 +        return res.toArray();
   7.229 +    }
   7.230 +
   7.231 +    private static void seekKOTests(Class<?> c, List<Object> res) throws SecurityException, ClassNotFoundException {
   7.232 +        Class<? extends Annotation> koTest =
   7.233 +            c.getClassLoader().loadClass(KOTest.class.getName()).
   7.234 +            asSubclass(Annotation.class);
   7.235 +        for (Method m : c.getMethods()) {
   7.236 +            if (m.getAnnotation(koTest) != null) {
   7.237 +                res.add(new KOFx(KnockoutFelixAriesIT.class, browserContext, m));
   7.238 +            }
   7.239 +        }
   7.240 +    }
   7.241 +
   7.242 +    static synchronized ClassLoader getClassLoader() throws InterruptedException {
   7.243 +        while (browserClass == null) {
   7.244 +            KnockoutFelixAriesIT.class.wait();
   7.245 +        }
   7.246 +        return browserClass.getClassLoader();
   7.247 +    }
   7.248 +
   7.249 +    public static synchronized void initialized(Class<?> browserCls, Object presenter) throws Exception {
   7.250 +        browserClass = browserCls;
   7.251 +        browserContext = presenter;
   7.252 +        KnockoutFelixAriesIT.class.notifyAll();
   7.253 +    }
   7.254 +
   7.255 +    public static Closeable activateInOSGi(Object presenter) throws Exception {
   7.256 +        Class<?> presenterClass = loadOSGiClass(Fn.Presenter.class);
   7.257 +        Class<?> fnClass = loadOSGiClass(Fn.class);
   7.258 +        Method m = fnClass.getMethod("activate", presenterClass);
   7.259 +        return (Closeable) m.invoke(null, presenter);
   7.260 +    }
   7.261 +}
     8.1 --- a/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KnockoutFelixIT.java	Sun Nov 22 21:18:35 2015 +0100
     8.2 +++ b/ko-felix-test/src/test/java/org/netbeans/html/ko/felix/test/KnockoutFelixIT.java	Thu Nov 26 20:59:51 2015 +0100
     8.3 @@ -42,7 +42,6 @@
     8.4   */
     8.5  package org.netbeans.html.ko.felix.test;
     8.6  
     8.7 -import org.netbeans.html.ko.felix.test.KnockoutFelixTCKImpl;
     8.8  import java.io.Closeable;
     8.9  import java.io.File;
    8.10  import java.io.IOException;
    8.11 @@ -172,6 +171,9 @@
    8.12                  if (name.contains("testng")) {
    8.13                      continue;
    8.14                  }
    8.15 +                if (name.equals("org.apache.aries.spifly.dynamic.bundle")) {
    8.16 +                    continue;
    8.17 +                }
    8.18                  final String path = "reference:" + file.toURI().toString();
    8.19                  try {
    8.20                      Bundle b = f.getBundleContext().installBundle(path);
    8.21 @@ -204,10 +206,10 @@
    8.22          
    8.23          URI uri = DynamicHTTP.initServer();
    8.24  
    8.25 -        Method start = peer.getMethod("start", URI.class);
    8.26 -        start.invoke(null, uri);
    8.27 +        Method start = peer.getMethod("start", String.class, URI.class, boolean.class);
    8.28 +        start.invoke(null, KnockoutFelixIT.class.getName(), uri, true);
    8.29          
    8.30 -        ClassLoader l = getClassLoader();
    8.31 +        ClassLoader l = getClassLoader(null);
    8.32          List<Object> res = new ArrayList<Object>();
    8.33          for (int i = 0; i < arr.length; i++) {
    8.34              seekKOTests(arr[i], res);
    8.35 @@ -221,15 +223,18 @@
    8.36              asSubclass(Annotation.class);
    8.37          for (Method m : c.getMethods()) {
    8.38              if (m.getAnnotation(koTest) != null) {
    8.39 -                res.add(new KOFx(browserContext, m));
    8.40 +                res.add(new KOFx(KnockoutFelixIT.class, browserContext, m));
    8.41              }
    8.42          }
    8.43      }
    8.44  
    8.45 -    static synchronized ClassLoader getClassLoader() throws InterruptedException {
    8.46 +    static synchronized ClassLoader getClassLoader(Object[] presenter) throws InterruptedException {
    8.47          while (browserClass == null) {
    8.48              KnockoutFelixIT.class.wait();
    8.49          }
    8.50 +        if (presenter != null) {
    8.51 +            presenter[0] = browserContext;
    8.52 +        }
    8.53          return browserClass.getClassLoader();
    8.54      }
    8.55      
    8.56 @@ -239,7 +244,7 @@
    8.57          KnockoutFelixIT.class.notifyAll();
    8.58      }
    8.59  
    8.60 -    static Closeable activateInOSGi(Object presenter) throws Exception {
    8.61 +    public static Closeable activateInOSGi(Object presenter) throws Exception {
    8.62          Class<?> presenterClass = loadOSGiClass(Fn.Presenter.class);
    8.63          Class<?> fnClass = loadOSGiClass(Fn.class);
    8.64          Method m = fnClass.getMethod("activate", presenterClass);
     9.1 --- a/ko4j/pom.xml	Sun Nov 22 21:18:35 2015 +0100
     9.2 +++ b/ko4j/pom.xml	Thu Nov 26 20:59:51 2015 +0100
     9.3 @@ -22,6 +22,12 @@
     9.4            <plugin>
     9.5                <groupId>org.apache.felix</groupId>
     9.6                <artifactId>maven-bundle-plugin</artifactId>
     9.7 +              <configuration>
     9.8 +                  <instructions>
     9.9 +                      <Require-Capability>osgi.extender;resolution:=optional;filter:="(osgi.extender=osgi.serviceloader.registrar)"</Require-Capability>
    9.10 +                      <Provide-Capability>osgi.serviceloader;osgi.serviceloader=org.netbeans.html.context.spi.Contexts$Provider</Provide-Capability>
    9.11 +                  </instructions>
    9.12 +              </configuration>
    9.13            </plugin>
    9.14            <plugin>
    9.15                <groupId>org.netbeans.html</groupId>
    10.1 --- a/src/main/javadoc/overview.html	Sun Nov 22 21:18:35 2015 +0100
    10.2 +++ b/src/main/javadoc/overview.html	Thu Nov 26 20:59:51 2015 +0100
    10.3 @@ -115,6 +115,8 @@
    10.4    &lt;/configuration&gt;
    10.5  &lt;/plugin&gt;
    10.6  </pre>
    10.7 +        OSGi headers are now <a href="https://netbeans.org/bugzilla/show_bug.cgi?id=256696">
    10.8 +            enterprise OSGi ready</a>.
    10.9  
   10.10          <h3>What's Been Improved in Version 1.2.3?</h3>
   10.11