changeset 10476:351df411f9eb jdk-9+169

Merge
author kcr
date Thu, 04 May 2017 08:45:51 -0700
parents 734b706c510a 7237e032862b
children 344e91ef79de 6e6860b1f95f 52fb8496133d 5414dfc162e8
files
diffstat 138 files changed, 9348 insertions(+), 234 deletions(-) [+]
line wrap: on
line diff
--- a/build.gradle	Mon May 01 00:00:37 2017 -0700
+++ b/build.gradle	Thu May 04 08:45:51 2017 -0700
@@ -263,6 +263,9 @@
 ext.TESTRUNARGSFILE = "testrun.args"
 ext.TESTJAVAPOLICYFILE = 'test.java.policy'
 
+// the file containing "extra" --add-exports
+ext.EXTRAADDEXPORTS = 'buildSrc/addExports'
+
 ext.MODULESOURCEPATH = "modulesourcepath.args"
 
 // These variables indicate what platform is running the build. Is
@@ -858,15 +861,53 @@
         addExportsFile.eachLine { line ->
             line = line.trim()
             if (!(line.startsWith("#") || line.equals(""))) {
-                // one line arguments are a bit easier to debug
-                //if (line.startsWith('--add-exports ')) {
-                //    line = line.replaceFirst('--add-exports ', '--add-exports=')
-                //}
-                line = line.replace('--add-exports *', '--add-exports=')
                 ae += line.split(' ')
             }
         }
-        p.ext.addExports  = ae.flatten()
+        p.ext.testAddExports  = ae.flatten()
+    }
+
+    // read in the temporary addExports file EXTRAADDEXPORTS)
+    //
+    // These extra --add-exports will be used in two places and so we
+    // create/modify two items:
+    // p.testAddExports - add the extra items so they are included in test builds
+    //
+    // p.extraAddExports - for use in any other place where we don't automatically update
+    //    for example any non modular, non 'test' compile, any compile that does not
+    //    use a module-source-path that includes the dependent modules
+    //
+    // Note that we don't modify the modular build (main, shims) because they use
+    // module-info directly, and we don't want to cover up any missing items there.
+    //
+    if (!rootProject.hasProperty("EXTRA_ADDEXPORTS_ARGS")) {
+        List<String> extraAddExportsList = []
+        String fullae = ""
+        File tmpaddExportsFile = new File(rootProject.projectDir, EXTRAADDEXPORTS)
+        if (tmpaddExportsFile.exists()) {
+            String nl = System.getProperty("line.separator")
+            tmpaddExportsFile.eachLine { line ->
+                line = line.trim()
+                fullae += line + nl
+                if (!(line.startsWith("#") || line.equals(""))) {
+                    extraAddExportsList += line.split(' ')
+                }
+            }
+        }
+        // This string is used in the creation of the build/*.args files
+        // so we preserve comments
+        if (!extraAddExportsList.isEmpty()) {
+            rootProject.ext.EXTRA_ADDEXPORTS_STRING = fullae
+        }
+        rootProject.ext.EXTRA_ADDEXPORTS_ARGS = extraAddExportsList
+    }
+
+    // use this variable, because it shows we have a non empty addition
+    if (rootProject.hasProperty("EXTRA_ADDEXPORTS_STRING")) {
+        p.ext.extraAddExports = EXTRA_ADDEXPORTS_ARGS.flatten()
+        if (p.hasProperty("testAddExports")) {
+            p.testAddExports += EXTRA_ADDEXPORTS_ARGS.flatten()
+        }
     }
 }
 
@@ -3201,7 +3242,13 @@
 
         // Source sets for standalone test apps (used for launcher tests)
         testapp1
+
+        // Modular applications
         testapp2
+        testapp3
+        testapp4
+        testapp5
+        testapp6
     }
 
     project.ext.buildModule = false
@@ -3287,28 +3334,51 @@
         include("pkg2/**")
     }
 
-    def List<String> testApp2SourceDirs = []
-    project.sourceSets.testapp2.java.srcDirs.each { dir ->
-        testApp2SourceDirs += dir
-    }
-    compileTestapp2Java.options.compilerArgs.addAll([
-        '-implicit:none',
-        '--module-source-path', testApp2SourceDirs.join(File.pathSeparator)
-        ] )
-
     task createTestApps() {
         dependsOn(createTestapp1Jar1)
         dependsOn(createTestapp1Jar2)
-        dependsOn(compileTestapp2Java)
     }
     test.dependsOn(createTestApps);
 
+    def modtestapps = [ "testapp2", "testapp3", "testapp4", "testapp5", "testapp6"  ]
+    modtestapps.each { testapp ->
+        def testappCapital = testapp.capitalize()
+        def copyTestAppTask = task("copy${testappCapital}", type: Copy) {
+            from project.sourceSets."${testapp}".output.classesDir
+            from project.sourceSets."${testapp}".output.resourcesDir
+            into "${project.buildDir}/modules/${testapp}"
+        }
+
+        def List<String> testAppSourceDirs = []
+        project.sourceSets."${testapp}".java.srcDirs.each { dir ->
+            testAppSourceDirs += dir
+        }
+        def testappCompileTasks = project.getTasksByName("compile${testappCapital}Java", true);
+        def testappResourceTasks = project.getTasksByName("process${testappCapital}Resources", true);
+        testappCompileTasks.each { appCompileTask ->
+            appCompileTask.options.compilerArgs.addAll([
+                '-implicit:none',
+                '--module-source-path', testAppSourceDirs.join(File.pathSeparator)
+                ] )
+
+            copyTestAppTask.dependsOn(appCompileTask)
+        }
+        testappResourceTasks.each { appResourceTask ->
+            copyTestAppTask.dependsOn(appResourceTask)
+        }
+
+        createTestApps.dependsOn(copyTestAppTask)
+    }
+
     test {
         enabled = IS_FULL_TEST
 
         // Properties passed to launcher tests
-        systemProperties 'launchertest.testapp1.jar': "build/testapp1/$testapp1JarName"
-        systemProperties 'launchertest.testapp2.module.path': project.sourceSets.testapp2.output.classesDir
+        systemProperty "launchertest.testapp1.jar", "build/testapp1/$testapp1JarName"
+        modtestapps.each { testapp ->
+            systemProperty "launchertest.${testapp}.module.path",
+                    "${project.buildDir}/modules/${testapp}"
+        }
 
         // Properties passed to test.util.Util
         systemProperties 'worker.debug': IS_WORKER_DEBUG
@@ -3424,9 +3494,19 @@
         project.test.jvmArgs += testPatchModuleArgs
     }
 
-    if (project.hasProperty('addExports')) {
-        project.compileTestJava.options.compilerArgs.addAll(addExports);
-        project.test.jvmArgs += addExports
+    /* Note: we should not have to add extraAddExports to the normal
+     * modular compile, as it contains all of the module-info files.
+     * In fact doing so might cover up a module-info issue.
+     * so we don't do it, and I will leave this commented out
+     * block as a reminder of this fact.
+    if (project.hasProperty('extraAddExports')) {
+        project.compileJava.options.compilerArgs.addAll(extraAddExports);
+    }
+    */
+
+    if (project.hasProperty('testAddExports')) {
+        project.compileTestJava.options.compilerArgs.addAll(testAddExports);
+        project.test.jvmArgs += testAddExports
     }
 
     if (rootProject.hasProperty("EXTRA_TEST_ARGS") && project.hasProperty('test')) {
@@ -3513,6 +3593,7 @@
     }
     options.addBooleanOption("XDignore.symbol.file").setValue(true);
     options.addBooleanOption("Xdoclint:${DOC_LINT}").setValue(IS_DOC_LINT);
+    options.addBooleanOption("html5").setValue(true);
     options.addBooleanOption("javafx").setValue(true);
     options.addBooleanOption("use").setValue(true);
 
@@ -3921,6 +4002,7 @@
     def buildRunArgsTask = task("buildRunArgs$t.capital",
             group: "Build", dependsOn: buildModulesTask) {
         outputs.file(runArgsFile);
+        inputs.file(EXTRAADDEXPORTS);
         doLast() {
             List<String>libpath = []
             List<String>modpath = []
@@ -3933,6 +4015,11 @@
 
             writeRunArgsFile(runArgsFile, computeLibraryPath(true), modpath)
             writeRunArgsFile(compileArgsFile, null, modpath)
+
+            if (rootProject.hasProperty("EXTRA_ADDEXPORTS_STRING")) {
+                runArgsFile << EXTRA_ADDEXPORTS_STRING
+                compileArgsFile << EXTRA_ADDEXPORTS_STRING
+            }
         }
     }
     buildModules.dependsOn(buildRunArgsTask)
@@ -4272,6 +4359,7 @@
         outputs.file(testCompileArgsFile)
         outputs.file(testJavaPolicyFile)
         outputs.file(runJavaPolicyFile)
+        inputs.file(EXTRAADDEXPORTS);
 
         doLast() {
             rootProject.buildDir.mkdir()
@@ -4318,6 +4406,11 @@
 
             writeRunArgsFile(testCompileArgsFile, null, modpath)
             writeRunArgsFile(testRunArgsFile, computeLibraryPath(true), modpath)
+
+            if (rootProject.hasProperty("EXTRA_ADDEXPORTS_STRING")) {
+                testCompileArgsFile << EXTRA_ADDEXPORTS_STRING
+                testRunArgsFile << EXTRA_ADDEXPORTS_STRING
+            }
         }
     }
     sdk.dependsOn(testArgFiles)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/buildSrc/addExports	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,15 @@
+# buildSrc/addExports: temporary --add-exports
+#
+# After a promoted jdk build includes the module-info changes associated
+# with a particular fix, we might eventually remove the --add-exports from
+# this file for that fix, after bumping the minimum jdk to that build.
+#
+# ----------------------------------------------------------------------
+#
+# Qualified exports for JDK-8177566
+--add-exports=javafx.base/com.sun.javafx.reflect=javafx.fxml
+--add-exports=javafx.base/com.sun.javafx.reflect=javafx.web
+#
+# ----------------------------------------------------------------------
+#
+# end temporary --add-exports
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/javafx.base/src/main/java/com/sun/javafx/property/MethodHelper.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.property;
+
+import com.sun.javafx.reflect.MethodUtil;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import sun.reflect.misc.ReflectUtil;
+
+/**
+ * Utility class to wrap method invocation.
+ */
+public class MethodHelper {
+    private static final boolean logAccessErrors
+            = AccessController.doPrivileged((PrivilegedAction<Boolean>) ()
+                    -> Boolean.getBoolean("sun.reflect.debugModuleAccessChecks"));
+
+    private static final Module trampolineModule = MethodUtil.getTrampolineModule();
+
+    public static Object invoke(Method m, Object obj, Object[] params)
+            throws InvocationTargetException, IllegalAccessException {
+
+        // Check that the class in question is in a package that is open to
+        // this module (or exported unconditionally). If so, then we will open
+        // the containing package to the unnamed trampoline module. If not,
+        // we will throw an IllegalAccessException in order to generate a
+        // clearer error message.
+        final Class<?> clazz = m.getDeclaringClass();
+        final String packageName = clazz.getPackage().getName();
+        final Module module = clazz.getModule();
+        final Module thisModule = MethodHelper.class.getModule();
+        try {
+            // Verify that the module being called either exports the package
+            // in question unconditionally or opens the package in question to
+            // this module.
+            if (!module.isExported(packageName)) {
+                if (!module.isOpen(packageName, thisModule)) {
+                    throw new IllegalAccessException(
+                            "module " + thisModule.getName()
+                            + " cannot access class " + clazz.getName()
+                            + " (in module " + module.getName()
+                            + ") because module " + module.getName()
+                            + " does not open " + packageName
+                            + " to " + thisModule.getName());
+                }
+                if (!module.isOpen(packageName, trampolineModule)) {
+                    ReflectUtil.checkPackageAccess(packageName);
+                    module.addOpens(packageName, trampolineModule);
+                }
+            }
+        } catch (IllegalAccessException ex) {
+            if (logAccessErrors) {
+                ex.printStackTrace(System.err);
+            }
+            throw ex;
+        }
+
+        return MethodUtil.invoke(m, obj, params);
+    }
+
+    // Utility class, do not instantiate
+    private MethodHelper() {
+    }
+
+}
--- a/modules/javafx.base/src/main/java/com/sun/javafx/property/PropertyReference.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.base/src/main/java/com/sun/javafx/property/PropertyReference.java	Thu May 04 08:45:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -33,7 +33,6 @@
 
 import javafx.beans.property.ReadOnlyProperty;
 
-import sun.reflect.misc.MethodUtil;
 import sun.reflect.misc.ReflectUtil;
 
 /**
@@ -149,7 +148,7 @@
                     "Cannot write to readonly property " + name);
         assert setter != null;
         try {
-            MethodUtil.invoke(setter, bean, new Object[] {value});
+            MethodHelper.invoke(setter, bean, new Object[] {value});
         } catch (Exception ex) {
             throw new RuntimeException(ex);
         }
@@ -172,7 +171,7 @@
                     "Cannot read from unreadable property " + name);
         assert getter != null;
         try {
-            return (T)MethodUtil.invoke(getter, bean, (Object[])null);
+            return (T)MethodHelper.invoke(getter, bean, (Object[])null);
         } catch (Exception ex) {
             throw new RuntimeException(ex);
         }
@@ -195,7 +194,7 @@
             throw new IllegalStateException("Cannot get property " + name);
         assert propertyGetter != null;
         try {
-            return (ReadOnlyProperty<T>)MethodUtil.invoke(propertyGetter, bean, (Object[])null);
+            return (ReadOnlyProperty<T>)MethodHelper.invoke(propertyGetter, bean, (Object[])null);
         } catch (Exception ex) {
             throw new RuntimeException(ex);
         }
--- a/modules/javafx.base/src/main/java/com/sun/javafx/property/adapter/PropertyDescriptor.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.base/src/main/java/com/sun/javafx/property/adapter/PropertyDescriptor.java	Thu May 04 08:45:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
 
 package com.sun.javafx.property.adapter;
 
+import com.sun.javafx.property.MethodHelper;
 import javafx.beans.property.Property;
 import javafx.beans.property.adapter.ReadOnlyJavaBeanProperty;
 import javafx.beans.value.ChangeListener;
@@ -36,8 +37,6 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
-import sun.reflect.misc.MethodUtil;
-
 /**
  */
 public class PropertyDescriptor extends ReadOnlyPropertyDescriptor {
@@ -159,7 +158,7 @@
             } else if (!updating) {
                 updating = true;
                 try {
-                    MethodUtil.invoke(setter, bean, new Object[] {newValue});
+                    MethodHelper.invoke(setter, bean, new Object[] {newValue});
                     property.fireValueChangedEvent();
                 } catch (IllegalAccessException e) {
                     // ignore
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/javafx.base/src/main/java/com/sun/javafx/reflect/MethodUtil.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.reflect;
+
+import java.io.EOFException;
+import java.security.AllPermission;
+import java.security.AccessController;
+import java.security.PermissionCollection;
+import java.security.SecureClassLoader;
+import java.security.PrivilegedExceptionAction;
+import java.security.CodeSource;
+import java.io.InputStream;
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import sun.reflect.misc.ReflectUtil;
+
+
+class Trampoline {
+    static {
+        if (Trampoline.class.getClassLoader() == null) {
+            throw new Error(
+                "Trampoline must not be defined by the bootstrap classloader");
+        }
+        if (Trampoline.class.getClassLoader() == ClassLoader.getPlatformClassLoader()) {
+            throw new Error(
+                "Trampoline must not be defined by the platform classloader");
+        }
+    }
+
+    private static void ensureInvocableMethod(Method m)
+        throws InvocationTargetException
+    {
+        Class<?> clazz = m.getDeclaringClass();
+        if (clazz.equals(AccessController.class) ||
+            clazz.equals(Method.class) ||
+            clazz.getName().startsWith("java.lang.invoke."))
+            throw new InvocationTargetException(
+                new UnsupportedOperationException("invocation not supported"));
+    }
+
+    private static Object invoke(Method m, Object obj, Object[] params)
+        throws InvocationTargetException, IllegalAccessException
+    {
+        ensureInvocableMethod(m);
+        return m.invoke(obj, params);
+    }
+}
+
+/*
+ * Create a trampoline class.
+ */
+public final class MethodUtil extends SecureClassLoader {
+    private static final String MISC_PKG = "com.sun.javafx.reflect.";
+    private static final String TRAMPOLINE = MISC_PKG + "Trampoline";
+    private static final Method bounce = getTrampoline();
+
+    private MethodUtil() {
+        super();
+    }
+
+    /*public*/
+    static Method getMethod(Class<?> cls, String name, Class<?>[] args)
+        throws NoSuchMethodException {
+        ReflectUtil.checkPackageAccess(cls);
+        return cls.getMethod(name, args);
+    }
+
+    /*public*/
+    static Method[] getMethods(Class<?> cls) {
+        ReflectUtil.checkPackageAccess(cls);
+        return cls.getMethods();
+    }
+
+    /*
+     * Discover the public methods on public classes
+     * and interfaces accessible to any caller by calling
+     * Class.getMethods() and walking towards Object until
+     * we're done.
+     */
+    /*public*/
+    static Method[] getPublicMethods(Class<?> cls) {
+        // compatibility for update release
+        if (System.getSecurityManager() == null) {
+            return cls.getMethods();
+        }
+        Map<Signature, Method> sigs = new HashMap<Signature, Method>();
+        while (cls != null) {
+            boolean done = getInternalPublicMethods(cls, sigs);
+            if (done) {
+                break;
+            }
+            getInterfaceMethods(cls, sigs);
+            cls = cls.getSuperclass();
+        }
+        return sigs.values().toArray(new Method[sigs.size()]);
+    }
+
+    /*
+     * Process the immediate interfaces of this class or interface.
+     */
+    private static void getInterfaceMethods(Class<?> cls,
+                                            Map<Signature, Method> sigs) {
+        Class<?>[] intfs = cls.getInterfaces();
+        for (int i=0; i < intfs.length; i++) {
+            Class<?> intf = intfs[i];
+            boolean done = getInternalPublicMethods(intf, sigs);
+            if (!done) {
+                getInterfaceMethods(intf, sigs);
+            }
+        }
+    }
+
+    /*
+     *
+     * Process the methods in this class or interface
+     */
+    private static boolean getInternalPublicMethods(Class<?> cls,
+                                                    Map<Signature, Method> sigs) {
+        Method[] methods = null;
+        try {
+            /*
+             * This class or interface is non-public so we
+             * can't use any of it's methods. Go back and
+             * try again with a superclass or superinterface.
+             */
+            if (!Modifier.isPublic(cls.getModifiers())) {
+                return false;
+            }
+            if (!ReflectUtil.isPackageAccessible(cls)) {
+                return false;
+            }
+
+            methods = cls.getMethods();
+        } catch (SecurityException se) {
+            return false;
+        }
+
+        /*
+         * Check for inherited methods with non-public
+         * declaring classes. They might override and hide
+         * methods from their superclasses or
+         * superinterfaces.
+         */
+        boolean done = true;
+        for (int i=0; i < methods.length; i++) {
+            Class<?> dc = methods[i].getDeclaringClass();
+            if (!Modifier.isPublic(dc.getModifiers())) {
+                done = false;
+                break;
+            }
+        }
+
+        if (done) {
+            /*
+             * We're done. Spray all the methods into
+             * the list and then we're out of here.
+             */
+            for (int i=0; i < methods.length; i++) {
+                addMethod(sigs, methods[i]);
+            }
+        } else {
+            /*
+             * Simulate cls.getDeclaredMethods() by
+             * stripping away inherited methods.
+             */
+            for (int i=0; i < methods.length; i++) {
+                Class<?> dc = methods[i].getDeclaringClass();
+                if (cls.equals(dc)) {
+                    addMethod(sigs, methods[i]);
+                }
+            }
+        }
+        return done;
+    }
+
+    private static void addMethod(Map<Signature, Method> sigs, Method method) {
+        Signature signature = new Signature(method);
+        if (!sigs.containsKey(signature)) {
+            sigs.put(signature, method);
+        } else if (!method.getDeclaringClass().isInterface()){
+            /*
+             * Superclasses beat interfaces.
+             */
+            Method old = sigs.get(signature);
+            if (old.getDeclaringClass().isInterface()) {
+                sigs.put(signature, method);
+            }
+        }
+    }
+
+    /**
+     * A class that represents the unique elements of a method that will be a
+     * key in the method cache.
+     */
+    private static class Signature {
+        private final String methodName;
+        private final Class<?>[] argClasses;
+        private final int hashCode;
+
+        Signature(Method m) {
+            this.methodName = m.getName();
+            this.argClasses = m.getParameterTypes();
+            this.hashCode = methodName.hashCode() + Arrays.hashCode(argClasses);
+        }
+
+        @Override public int hashCode() {
+            return hashCode;
+        }
+
+        @Override public boolean equals(Object o2) {
+            if (this == o2) {
+                return true;
+            }
+            Signature that = (Signature)o2;
+            if (!(methodName.equals(that.methodName))) {
+                return false;
+            }
+            if (argClasses.length != that.argClasses.length) {
+                return false;
+            }
+            for (int i = 0; i < argClasses.length; i++) {
+                if (!(argClasses[i] == that.argClasses[i])) {
+                  return false;
+                }
+            }
+            return true;
+        }
+    }
+
+
+    /*
+     * Get the (unnamed) module of the trampoline class
+     */
+    public static Module getTrampolineModule() {
+        return bounce.getDeclaringClass().getModule();
+    }
+
+    /*
+     * Bounce through the trampoline.
+     */
+    public static Object invoke(Method m, Object obj, Object[] params)
+        throws InvocationTargetException, IllegalAccessException {
+        try {
+            return bounce.invoke(null, new Object[] {m, obj, params});
+        } catch (InvocationTargetException ie) {
+            Throwable t = ie.getCause();
+
+            if (t instanceof InvocationTargetException) {
+                throw (InvocationTargetException)t;
+            } else if (t instanceof IllegalAccessException) {
+                throw (IllegalAccessException)t;
+            } else if (t instanceof RuntimeException) {
+                throw (RuntimeException)t;
+            } else if (t instanceof Error) {
+                throw (Error)t;
+            } else {
+                throw new Error("Unexpected invocation error", t);
+            }
+        } catch (IllegalAccessException iae) {
+            // this can't happen
+            throw new Error("Unexpected invocation error", iae);
+        }
+    }
+
+    private static Method getTrampoline() {
+        try {
+            return AccessController.doPrivileged(
+                new PrivilegedExceptionAction<Method>() {
+                    public Method run() throws Exception {
+                        Class<?> t = getTrampolineClass();
+                        Class<?>[] types = {
+                            Method.class, Object.class, Object[].class
+                        };
+                        Method b = t.getDeclaredMethod("invoke", types);
+                        b.setAccessible(true);
+                        return b;
+                    }
+                });
+        } catch (Exception e) {
+            throw new InternalError("bouncer cannot be found", e);
+        }
+    }
+
+
+    protected synchronized Class<?> loadClass(String name, boolean resolve)
+        throws ClassNotFoundException
+    {
+        // First, check if the class has already been loaded
+        ReflectUtil.checkPackageAccess(name);
+        Class<?> c = findLoadedClass(name);
+        if (c == null) {
+            try {
+                c = findClass(name);
+            } catch (ClassNotFoundException e) {
+                // Fall through ...
+            }
+            if (c == null) {
+                c = getParent().loadClass(name);
+            }
+        }
+        if (resolve) {
+            resolveClass(c);
+        }
+        return c;
+    }
+
+
+    protected Class<?> findClass(final String name)
+        throws ClassNotFoundException
+    {
+        if (!name.startsWith(MISC_PKG)) {
+            throw new ClassNotFoundException(name);
+        }
+        String path = name.replace('.', '/').concat(".class");
+        try {
+            InputStream in = MethodUtil.class.getModule().getResourceAsStream(path);
+            if (in != null) {
+                try (in) {
+                    byte[] b = in.readAllBytes();
+                    return defineClass(name, b);
+                }
+            }
+        } catch (IOException e) {
+            throw new ClassNotFoundException(name, e);
+        }
+
+        throw new ClassNotFoundException(name);
+    }
+
+
+    /*
+     * Define the proxy classes
+     */
+    private Class<?> defineClass(String name, byte[] b) throws IOException {
+        CodeSource cs = new CodeSource(null, (java.security.cert.Certificate[])null);
+        if (!name.equals(TRAMPOLINE)) {
+            throw new IOException("MethodUtil: bad name " + name);
+        }
+        return defineClass(name, b, 0, b.length, cs);
+    }
+
+    protected PermissionCollection getPermissions(CodeSource codesource)
+    {
+        PermissionCollection perms = super.getPermissions(codesource);
+        perms.add(new AllPermission());
+        return perms;
+    }
+
+    private static Class<?> getTrampolineClass() {
+        try {
+            return Class.forName(TRAMPOLINE, true, new MethodUtil());
+        } catch (ClassNotFoundException e) {
+        }
+        return null;
+    }
+
+}
--- a/modules/javafx.base/src/main/java/javafx/beans/binding/Bindings.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.base/src/main/java/javafx/beans/binding/Bindings.java	Thu May 04 08:45:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -97,6 +97,31 @@
  * Fluent API allows to be more specific about the returned type (see
  * {@link javafx.beans.binding.NumberExpression} for more details about implicit
  * casting.
+ * </p>
+ * <p><a id="DeployAppAsModule"></a><b>Deploying an Application as a Module</b></p>
+ * <p>
+ * If any class used in a select-binding (see the various {@code select*}
+ * methods) is in a named module, then it must be reflectively accessible to the
+ * {@code javafx.base} module.
+ * A class is reflectively accessible if the module
+ * {@link Module#isOpen(String,Module) opens} the containing package to at
+ * least the {@code javafx.base} module.
+ * </p>
+ * <p>
+ * For example, if {@code com.foo.MyClass} is in the {@code foo.app} module,
+ * the {@code module-info.java} might
+ * look like this:
+ * </p>
+ *
+<pre>{@code module foo.app {
+    opens com.foo to javafx.base;
+}}</pre>
+ *
+ * <p>
+ * Alternatively, a class is reflectively accessible if the module
+ * {@link Module#isExported(String) exports} the containing package
+ * unconditionally.
+ * </p>
  *
  * @see Binding
  * @see NumberBinding
@@ -403,9 +428,15 @@
      * be reached (due to {@code b} not having a {@code c} property,
      * {@code b} being {@code null}, or {@code c} not being the right type etc.).
      * <p>
-     * All classes and properties used in a select-binding have to be public.
-     *
+     * All classes and properties used in a select-binding have to be
+     * declared public.
+     * Additionally, if any class is in a named module, then it must be
+     * reflectively accessible to the {@code javafx.base} module (see
+     * <a href="#DeployAppAsModule">Deploying an Application as a Module</a>).
+     * </p>
+     * <p>
      * Note: since 8.0, JavaBeans properties are supported and might be in the chain.
+     * </p>
      *
      * @param <T> the type of the wrapped {@code Object}
      * @param root
@@ -424,9 +455,15 @@
      * be reached (due to {@code b} not having a {@code c} property,
      * {@code b} being {@code null}, or {@code c} not being a {@code Number} etc.).
      * <p>
-     * All classes and properties used in a select-binding have to be public.
-     *
+     * All classes and properties used in a select-binding have to be
+     * declared public.
+     * Additionally, if any class is in a named module, then it must be
+     * reflectively accessible to the {@code javafx.base} module (see
+     * <a href="#DeployAppAsModule">Deploying an Application as a Module</a>).
+     * </p>
+     * <p>
      * Note: since 8.0, JavaBeans properties are supported and might be in the chain.
+     * </p>
      *
      * @param root
      *            The root {@link javafx.beans.value.ObservableValue}
@@ -444,9 +481,15 @@
      * be reached (due to {@code b} not having a {@code c} property,
      * {@code b} being {@code null}, or {@code c} not being a {@code Number} etc.).
      * <p>
-     * All classes and properties used in a select-binding have to be public.
-     *
+     * All classes and properties used in a select-binding have to be
+     * declared public.
+     * Additionally, if any class is in a named module, then it must be
+     * reflectively accessible to the {@code javafx.base} module (see
+     * <a href="#DeployAppAsModule">Deploying an Application as a Module</a>).
+     * </p>
+     * <p>
      * Note: since 8.0, JavaBeans properties are supported and might be in the chain.
+     * </p>
      *
      * @param root
      *            The root {@link javafx.beans.value.ObservableValue}
@@ -464,9 +507,15 @@
      * be reached (due to {@code b} not having a {@code c} property,
      * {@code b} being {@code null}, or {@code c} not being a {@code Number} etc.).
      * <p>
-     * All classes and properties used in a select-binding have to be public.
-     *
+     * All classes and properties used in a select-binding have to be
+     * declared public.
+     * Additionally, if any class is in a named module, then it must be
+     * reflectively accessible to the {@code javafx.base} module (see
+     * <a href="#DeployAppAsModule">Deploying an Application as a Module</a>).
+     * </p>
+     * <p>
      * Note: since 8.0, JavaBeans properties are supported and might be in the chain.
+     * </p>
      *
      * @param root
      *            The root {@link javafx.beans.value.ObservableValue}
@@ -484,9 +533,15 @@
      * be reached (due to {@code b} not having a {@code c} property,
      * {@code b} being {@code null}, or {@code c} not being a {@code Number} etc.).
      * <p>
-     * All classes and properties used in a select-binding have to be public.
-     *
+     * All classes and properties used in a select-binding have to be
+     * declared public.
+     * Additionally, if any class is in a named module, then it must be
+     * reflectively accessible to the {@code javafx.base} module (see
+     * <a href="#DeployAppAsModule">Deploying an Application as a Module</a>).
+     * </p>
+     * <p>
      * Note: since 8.0, JavaBeans properties are supported and might be in the chain.
+     * </p>
      *
      * @param root
      *            The root {@link javafx.beans.value.ObservableValue}
@@ -504,9 +559,15 @@
      * be reached (due to {@code b} not having a {@code c} property,
      * {@code b} being {@code null}, or {@code c} not being a {@code boolean} etc.).
      * <p>
-     * All classes and properties used in a select-binding have to be public.
-     *
+     * All classes and properties used in a select-binding have to be
+     * declared public.
+     * Additionally, if any class is in a named module, then it must be
+     * reflectively accessible to the {@code javafx.base} module (see
+     * <a href="#DeployAppAsModule">Deploying an Application as a Module</a>).
+     * </p>
+     * <p>
      * Note: since 8.0, JavaBeans properties are supported and might be in the chain.
+     * </p>
      *
      * @param root
      *            The root {@link javafx.beans.value.ObservableValue}
@@ -524,9 +585,15 @@
      * be reached (due to {@code b} not having a {@code c} property,
      * {@code b} being {@code null}, or {@code c} not being a {@code String} etc.).
      * <p>
-     * All classes and properties used in a select-binding have to be public.
-     *
+     * All classes and properties used in a select-binding have to be
+     * declared public.
+     * Additionally, if any class is in a named module, then it must be
+     * reflectively accessible to the {@code javafx.base} module (see
+     * <a href="#DeployAppAsModule">Deploying an Application as a Module</a>).
+     * </p>
+     * <p>
      * Note: since 8.0, JavaBeans properties are supported and might be in the chain.
+     * </p>
      *
      * @param root
      *            The root {@link javafx.beans.value.ObservableValue}
@@ -544,10 +611,17 @@
      * be reached (due to {@code b} not having a {@code c} property,
      * {@code b} being {@code null}, or {@code c} not being the right type etc.).
      * <p>
-     * All classes and properties used in a select-binding have to be public.
-     *
+     * All classes and properties used in a select-binding have to be
+     * declared public.
+     * Additionally, if any class is in a named module, then it must be
+     * reflectively accessible to the {@code javafx.base} module (see
+     * <a href="#DeployAppAsModule">Deploying an Application as a Module</a>).
+     * </p>
+     *
+     * <p>
      * If root has JavaFX properties, this call is equivalent to {@link #select(javafx.beans.value.ObservableValue, java.lang.String[])},
      * with the {@code root} and {@code step[0]} being substituted with the relevant property object.
+     * </p>
      *
      * @param <T> the type of the wrapped {@code Object}
      * @param root
@@ -568,10 +642,17 @@
      * be reached (due to {@code b} not having a {@code c} property,
      * {@code b} being {@code null}, or {@code c} not being a {@code Number} etc.).
      * <p>
-     * All classes and properties used in a select-binding have to be public.
-     *
+     * All classes and properties used in a select-binding have to be
+     * declared public.
+     * Additionally, if any class is in a named module, then it must be
+     * reflectively accessible to the {@code javafx.base} module (see
+     * <a href="#DeployAppAsModule">Deploying an Application as a Module</a>).
+     * </p>
+     *
+     * <p>
      * If root has JavaFX properties, this call is equivalent to {@link #selectDouble(javafx.beans.value.ObservableValue, java.lang.String[])},
      * with the {@code root} and {@code step[0]} being substituted with the relevant property object.
+     * </p>
      *
      * @param root
      *            The root bean.
@@ -591,10 +672,17 @@
      * be reached (due to {@code b} not having a {@code c} property,
      * {@code b} being {@code null}, or {@code c} not being a {@code Number} etc.).
      * <p>
-     * All classes and properties used in a select-binding have to be public.
-     *
+     * All classes and properties used in a select-binding have to be
+     * declared public.
+     * Additionally, if any class is in a named module, then it must be
+     * reflectively accessible to the {@code javafx.base} module (see
+     * <a href="#DeployAppAsModule">Deploying an Application as a Module</a>).
+     * </p>
+     *
+     * <p>
      * If root has JavaFX properties, this call is equivalent to {@link #selectFloat(javafx.beans.value.ObservableValue, java.lang.String[])},
      * with the {@code root} and {@code step[0]} being substituted with the relevant property object.
+     * </p>
      *
      * @param root
      *            The root bean.
@@ -614,10 +702,17 @@
      * be reached (due to {@code b} not having a {@code c} property,
      * {@code b} being {@code null}, or {@code c} not being a {@code Number} etc.).
      * <p>
-     * All classes and properties used in a select-binding have to be public.
-     *
+     * All classes and properties used in a select-binding have to be
+     * declared public.
+     * Additionally, if any class is in a named module, then it must be
+     * reflectively accessible to the {@code javafx.base} module (see
+     * <a href="#DeployAppAsModule">Deploying an Application as a Module</a>).
+     * </p>
+     *
+     * <p>
      * If root has JavaFX properties, this call is equivalent to {@link #selectInteger(javafx.beans.value.ObservableValue, java.lang.String[])},
      * with the {@code root} and {@code step[0]} being substituted with the relevant property object.
+     * </p>
      *
      * @param root
      *            The root bean.
@@ -637,10 +732,17 @@
      * be reached (due to {@code b} not having a {@code c} property,
      * {@code b} being {@code null}, or {@code c} not being a {@code Number} etc.).
      * <p>
-     * All classes and properties used in a select-binding have to be public.
-     *
+     * All classes and properties used in a select-binding have to be
+     * declared public.
+     * Additionally, if any class is in a named module, then it must be
+     * reflectively accessible to the {@code javafx.base} module (see
+     * <a href="#DeployAppAsModule">Deploying an Application as a Module</a>).
+     * </p>
+     *
+     * <p>
      * If root has JavaFX properties, this call is equivalent to {@link #selectLong(javafx.beans.value.ObservableValue, java.lang.String[])},
      * with the {@code root} and {@code step[0]} being substituted with the relevant property object.
+     * </p>
      *
      * @param root
      *            The root bean.
@@ -660,10 +762,17 @@
      * be reached (due to {@code b} not having a {@code c} property,
      * {@code b} being {@code null}, or {@code c} not being a {@code boolean} etc.).
      * <p>
-     * All classes and properties used in a select-binding have to be public.
-     *
+     * All classes and properties used in a select-binding have to be
+     * declared public.
+     * Additionally, if any class is in a named module, then it must be
+     * reflectively accessible to the {@code javafx.base} module (see
+     * <a href="#DeployAppAsModule">Deploying an Application as a Module</a>).
+     * </p>
+     *
+     * <p>
      * If root has JavaFX properties, this call is equivalent to {@link #selectBoolean(javafx.beans.value.ObservableValue, java.lang.String[])},
      * with the {@code root} and {@code step[0]} being substituted with the relevant property object.
+     * </p>
      *
      * @param root
      *            The root bean.
@@ -683,10 +792,17 @@
      * be reached (due to {@code b} not having a {@code c} property,
      * {@code b} being {@code null}, or {@code c} not being a {@code String} etc.).
      * <p>
-     * All classes and properties used in a select-binding have to be public.
-     *
+     * All classes and properties used in a select-binding have to be
+     * declared public.
+     * Additionally, if any class is in a named module, then it must be
+     * reflectively accessible to the {@code javafx.base} module (see
+     * <a href="#DeployAppAsModule">Deploying an Application as a Module</a>).
+     * </p>
+     *
+     * <p>
      * If root has JavaFX properties, this call is equivalent to {@link #selectString(javafx.beans.value.ObservableValue, java.lang.String[])},
      * with the {@code root} and {@code step[0]} being substituted with the relevant property object.
+     * </p>
      *
      * @param root
      *            The root bean.
--- a/modules/javafx.base/src/main/java/javafx/beans/property/adapter/JavaBeanBooleanProperty.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.base/src/main/java/javafx/beans/property/adapter/JavaBeanBooleanProperty.java	Thu May 04 08:45:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
 package javafx.beans.property.adapter;
 
 import com.sun.javafx.binding.ExpressionHelper;
+import com.sun.javafx.property.MethodHelper;
 import com.sun.javafx.property.adapter.Disposer;
 import com.sun.javafx.property.adapter.PropertyDescriptor;
 import javafx.beans.InvalidationListener;
@@ -40,16 +41,16 @@
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 
-import sun.reflect.misc.MethodUtil;
-
 /**
  * A {@code JavaBeanBooleanProperty} provides an adapter between a regular
  * Java Bean property of type {@code boolean} or {@code Boolean} and a JavaFX
  * {@code BooleanProperty}. It cannot be created directly, but a
  * {@link JavaBeanBooleanPropertyBuilder} has to be used.
  * <p>
- * As a minimum, the Java Bean must implement a getter and a setter for the
- * property. If the getter of an instance of this class is called, the property of
+ * As a minimum, the Java Bean class must implement a getter and a setter for the
+ * property.
+ * The class, as well as the getter and a setter methods, must be declared public.
+ * If the getter of an instance of this class is called, the property of
  * the Java Bean is returned. If the setter is called, the value will be passed
  * to the Java Bean property. If the Java Bean property is bound (i.e. it supports
  * PropertyChangeListeners), this {@code JavaBeanBooleanProperty} will be
@@ -58,6 +59,30 @@
  * is also constrained (i.e. it supports VetoableChangeListeners), this
  * {@code JavaBeanBooleanProperty} will reject changes, if it is bound to an
  * {@link javafx.beans.value.ObservableValue ObservableValue&lt;Boolean&gt;}.
+ * </p>
+ * <p><b>Deploying an Application as a Module</b></p>
+ * <p>
+ * If the Java Bean class is in a named module, then it must be reflectively
+ * accessible to the {@code javafx.base} module.
+ * A class is reflectively accessible if the module
+ * {@link Module#isOpen(String,Module) opens} the containing package to at
+ * least the {@code javafx.base} module.
+ * </p>
+ * <p>
+ * For example, if {@code com.foo.MyBeanClass} is in the {@code foo.app} module,
+ * the {@code module-info.java} might
+ * look like this:
+ * </p>
+ *
+<pre>{@code module foo.app {
+    opens com.foo to javafx.base;
+}}</pre>
+ *
+ * <p>
+ * Alternatively, a class is reflectively accessible if the module
+ * {@link Module#isExported(String) exports} the containing package
+ * unconditionally.
+ * </p>
  *
  * @see javafx.beans.property.BooleanProperty
  * @see JavaBeanBooleanPropertyBuilder
@@ -91,7 +116,7 @@
     public boolean get() {
         return AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> {
             try {
-                return (Boolean)MethodUtil.invoke(descriptor.getGetter(), getBean(), (Object[])null);
+                return (Boolean)MethodHelper.invoke(descriptor.getGetter(), getBean(), (Object[])null);
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
             } catch (InvocationTargetException e) {
@@ -115,7 +140,7 @@
 
         AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
             try {
-                MethodUtil.invoke(descriptor.getSetter(), getBean(), new Object[] {value});
+                MethodHelper.invoke(descriptor.getSetter(), getBean(), new Object[] {value});
                 ExpressionHelper.fireValueChangedEvent(helper);
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
--- a/modules/javafx.base/src/main/java/javafx/beans/property/adapter/JavaBeanDoubleProperty.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.base/src/main/java/javafx/beans/property/adapter/JavaBeanDoubleProperty.java	Thu May 04 08:45:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
 package javafx.beans.property.adapter;
 
 import com.sun.javafx.binding.ExpressionHelper;
+import com.sun.javafx.property.MethodHelper;
 import com.sun.javafx.property.adapter.Disposer;
 import com.sun.javafx.property.adapter.PropertyDescriptor;
 import javafx.beans.InvalidationListener;
@@ -40,16 +41,16 @@
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 
-import sun.reflect.misc.MethodUtil;
-
 /**
  * A {@code JavaBeanDoubleProperty} provides an adapter between a regular
  * Java Bean property of type {@code double} or {@code Double} and a JavaFX
  * {@code DoubleProperty}. It cannot be created directly, but a
  * {@link JavaBeanDoublePropertyBuilder} has to be used.
  * <p>
- * As a minimum, the Java Bean must implement a getter and a setter for the
- * property. If the getter of an instance of this class is called, the property of
+ * As a minimum, the Java Bean class must implement a getter and a setter for the
+ * property.
+ * The class, as well as the getter and a setter methods, must be declared public.
+ * If the getter of an instance of this class is called, the property of
  * the Java Bean is returned. If the setter is called, the value will be passed
  * to the Java Bean property. If the Java Bean property is bound (i.e. it supports
  * PropertyChangeListeners), this {@code JavaBeanDoubleProperty} will be
@@ -58,6 +59,30 @@
  * is also constrained (i.e. it supports VetoableChangeListeners), this
  * {@code JavaBeanDoubleProperty} will reject changes, if it is bound to an
  * {@link javafx.beans.value.ObservableValue ObservableValue&lt;Double&gt;}.
+ * </p>
+ * <p><b>Deploying an Application as a Module</b></p>
+ * <p>
+ * If the Java Bean class is in a named module, then it must be reflectively
+ * accessible to the {@code javafx.base} module.
+ * A class is reflectively accessible if the module
+ * {@link Module#isOpen(String,Module) opens} the containing package to at
+ * least the {@code javafx.base} module.
+ * </p>
+ * <p>
+ * For example, if {@code com.foo.MyBeanClass} is in the {@code foo.app} module,
+ * the {@code module-info.java} might
+ * look like this:
+ * </p>
+ *
+<pre>{@code module foo.app {
+    opens com.foo to javafx.base;
+}}</pre>
+ *
+ * <p>
+ * Alternatively, a class is reflectively accessible if the module
+ * {@link Module#isExported(String) exports} the containing package
+ * unconditionally.
+ * </p>
  *
  * @see javafx.beans.property.DoubleProperty
  * @see JavaBeanDoublePropertyBuilder
@@ -91,7 +116,7 @@
     public double get() {
         return AccessController.doPrivileged((PrivilegedAction<Double>) () -> {
             try {
-                return ((Number)MethodUtil.invoke(
+                return ((Number)MethodHelper.invoke(
                     descriptor.getGetter(), getBean(), (Object[])null)).doubleValue();
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
@@ -116,7 +141,7 @@
 
         AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
             try {
-                MethodUtil.invoke(descriptor.getSetter(), getBean(), new Object[] {value});
+                MethodHelper.invoke(descriptor.getSetter(), getBean(), new Object[] {value});
                 ExpressionHelper.fireValueChangedEvent(helper);
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
--- a/modules/javafx.base/src/main/java/javafx/beans/property/adapter/JavaBeanFloatProperty.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.base/src/main/java/javafx/beans/property/adapter/JavaBeanFloatProperty.java	Thu May 04 08:45:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
 package javafx.beans.property.adapter;
 
 import com.sun.javafx.binding.ExpressionHelper;
+import com.sun.javafx.property.MethodHelper;
 import com.sun.javafx.property.adapter.Disposer;
 import com.sun.javafx.property.adapter.PropertyDescriptor;
 import javafx.beans.InvalidationListener;
@@ -40,16 +41,16 @@
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 
-import sun.reflect.misc.MethodUtil;
-
 /**
  * A {@code JavaBeanFloatProperty} provides an adapter between a regular
  * Java Bean property of type {@code float} or {@code Float} and a JavaFX
  * {@code FloatProperty}. It cannot be created directly, but a
  * {@link JavaBeanFloatPropertyBuilder} has to be used.
  * <p>
- * As a minimum, the Java Bean must implement a getter and a setter for the
- * property. If the getter of an instance of this class is called, the property of
+ * As a minimum, the Java Bean class must implement a getter and a setter for the
+ * property.
+ * The class, as well as the getter and a setter methods, must be declared public.
+ * If the getter of an instance of this class is called, the property of
  * the Java Bean is returned. If the setter is called, the value will be passed
  * to the Java Bean property. If the Java Bean property is bound (i.e. it supports
  * PropertyChangeListeners), this {@code JavaBeanFloatProperty} will be
@@ -58,6 +59,30 @@
  * is also constrained (i.e. it supports VetoableChangeListeners), this
  * {@code JavaBeanFloatProperty} will reject changes, if it is bound to an
  * {@link javafx.beans.value.ObservableValue ObservableValue&lt;Float&gt;}.
+ * </p>
+ * <p><b>Deploying an Application as a Module</b></p>
+ * <p>
+ * If the Java Bean class is in a named module, then it must be reflectively
+ * accessible to the {@code javafx.base} module.
+ * A class is reflectively accessible if the module
+ * {@link Module#isOpen(String,Module) opens} the containing package to at
+ * least the {@code javafx.base} module.
+ * </p>
+ * <p>
+ * For example, if {@code com.foo.MyBeanClass} is in the {@code foo.app} module,
+ * the {@code module-info.java} might
+ * look like this:
+ * </p>
+ *
+<pre>{@code module foo.app {
+    opens com.foo to javafx.base;
+}}</pre>
+ *
+ * <p>
+ * Alternatively, a class is reflectively accessible if the module
+ * {@link Module#isExported(String) exports} the containing package
+ * unconditionally.
+ * </p>
  *
  * @see javafx.beans.property.FloatProperty
  * @see JavaBeanFloatPropertyBuilder
@@ -91,7 +116,7 @@
     public float get() {
         return AccessController.doPrivileged((PrivilegedAction<Float>) () -> {
             try {
-                return ((Number)MethodUtil.invoke(
+                return ((Number)MethodHelper.invoke(
                     descriptor.getGetter(), getBean(), (Object[])null)).floatValue();
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
@@ -115,7 +140,7 @@
         }
         AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
             try {
-                MethodUtil.invoke(descriptor.getSetter(), getBean(), new Object[] {value});
+                MethodHelper.invoke(descriptor.getSetter(), getBean(), new Object[] {value});
                 ExpressionHelper.fireValueChangedEvent(helper);
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
--- a/modules/javafx.base/src/main/java/javafx/beans/property/adapter/JavaBeanIntegerProperty.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.base/src/main/java/javafx/beans/property/adapter/JavaBeanIntegerProperty.java	Thu May 04 08:45:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
 package javafx.beans.property.adapter;
 
 import com.sun.javafx.binding.ExpressionHelper;
+import com.sun.javafx.property.MethodHelper;
 import com.sun.javafx.property.adapter.Disposer;
 import com.sun.javafx.property.adapter.PropertyDescriptor;
 import javafx.beans.InvalidationListener;
@@ -40,16 +41,16 @@
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 
-import sun.reflect.misc.MethodUtil;
-
 /**
  * A {@code JavaBeanIntegerProperty} provides an adapter between a regular
  * Java Bean property of type {@code int} or {@code Integer} and a JavaFX
  * {@code IntegerProperty}. It cannot be created directly, but a
  * {@link JavaBeanIntegerPropertyBuilder} has to be used.
  * <p>
- * As a minimum, the Java Bean must implement a getter and a setter for the
- * property. If the getter of an instance of this class is called, the property of
+ * As a minimum, the Java Bean class must implement a getter and a setter for the
+ * property.
+ * The class, as well as the getter and a setter methods, must be declared public.
+ * If the getter of an instance of this class is called, the property of
  * the Java Bean is returned. If the setter is called, the value will be passed
  * to the Java Bean property. If the Java Bean property is bound (i.e. it supports
  * PropertyChangeListeners), this {@code JavaBeanIntegerProperty} will be
@@ -58,6 +59,30 @@
  * is also constrained (i.e. it supports VetoableChangeListeners), this
  * {@code JavaBeanIntegerProperty} will reject changes, if it is bound to an
  * {@link javafx.beans.value.ObservableValue ObservableValue&lt;Integer&gt;}.
+ * </p>
+ * <p><b>Deploying an Application as a Module</b></p>
+ * <p>
+ * If the Java Bean class is in a named module, then it must be reflectively
+ * accessible to the {@code javafx.base} module.
+ * A class is reflectively accessible if the module
+ * {@link Module#isOpen(String,Module) opens} the containing package to at
+ * least the {@code javafx.base} module.
+ * </p>
+ * <p>
+ * For example, if {@code com.foo.MyBeanClass} is in the {@code foo.app} module,
+ * the {@code module-info.java} might
+ * look like this:
+ * </p>
+ *
+<pre>{@code module foo.app {
+    opens com.foo to javafx.base;
+}}</pre>
+ *
+ * <p>
+ * Alternatively, a class is reflectively accessible if the module
+ * {@link Module#isExported(String) exports} the containing package
+ * unconditionally.
+ * </p>
  *
  * @see javafx.beans.property.IntegerProperty
  * @see JavaBeanIntegerPropertyBuilder
@@ -91,7 +116,7 @@
     public int get() {
         return AccessController.doPrivileged((PrivilegedAction<Integer>) () -> {
             try {
-                return ((Number)MethodUtil.invoke(
+                return ((Number)MethodHelper.invoke(
                     descriptor.getGetter(), getBean(), (Object[])null)).intValue();
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
@@ -115,7 +140,7 @@
         }
         AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
             try {
-                MethodUtil.invoke(descriptor.getSetter(), getBean(), new Object[] {value});
+                MethodHelper.invoke(descriptor.getSetter(), getBean(), new Object[] {value});
                 ExpressionHelper.fireValueChangedEvent(helper);
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
--- a/modules/javafx.base/src/main/java/javafx/beans/property/adapter/JavaBeanLongProperty.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.base/src/main/java/javafx/beans/property/adapter/JavaBeanLongProperty.java	Thu May 04 08:45:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
 package javafx.beans.property.adapter;
 
 import com.sun.javafx.binding.ExpressionHelper;
+import com.sun.javafx.property.MethodHelper;
 import com.sun.javafx.property.adapter.Disposer;
 import com.sun.javafx.property.adapter.PropertyDescriptor;
 import javafx.beans.InvalidationListener;
@@ -40,16 +41,16 @@
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 
-import sun.reflect.misc.MethodUtil;
-
 /**
  * A {@code JavaBeanLongProperty} provides an adapter between a regular
  * Java Bean property of type {@code long} or {@code Long} and a JavaFX
  * {@code LongProperty}. It cannot be created directly, but a
  * {@link JavaBeanLongPropertyBuilder} has to be used.
  * <p>
- * As a minimum, the Java Bean must implement a getter and a setter for the
- * property. If the getter of an instance of this class is called, the property of
+ * As a minimum, the Java Bean class must implement a getter and a setter for the
+ * property.
+ * The class, as well as the getter and a setter methods, must be declared public.
+ * If the getter of an instance of this class is called, the property of
  * the Java Bean is returned. If the setter is called, the value will be passed
  * to the Java Bean property. If the Java Bean property is bound (i.e. it supports
  * PropertyChangeListeners), this {@code JavaBeanLongProperty} will be
@@ -58,6 +59,30 @@
  * is also constrained (i.e. it supports VetoableChangeListeners), this
  * {@code JavaBeanLongProperty} will reject changes, if it is bound to an
  * {@link javafx.beans.value.ObservableValue ObservableValue&lt;Long&gt;}.
+ * </p>
+ * <p><b>Deploying an Application as a Module</b></p>
+ * <p>
+ * If the Java Bean class is in a named module, then it must be reflectively
+ * accessible to the {@code javafx.base} module.
+ * A class is reflectively accessible if the module
+ * {@link Module#isOpen(String,Module) opens} the containing package to at
+ * least the {@code javafx.base} module.
+ * </p>
+ * <p>
+ * For example, if {@code com.foo.MyBeanClass} is in the {@code foo.app} module,
+ * the {@code module-info.java} might
+ * look like this:
+ * </p>
+ *
+<pre>{@code module foo.app {
+    opens com.foo to javafx.base;
+}}</pre>
+ *
+ * <p>
+ * Alternatively, a class is reflectively accessible if the module
+ * {@link Module#isExported(String) exports} the containing package
+ * unconditionally.
+ * </p>
  *
  * @see javafx.beans.property.LongProperty
  * @see JavaBeanLongPropertyBuilder
@@ -91,7 +116,7 @@
     public long get() {
         return AccessController.doPrivileged((PrivilegedAction<Long>) () -> {
             try {
-                return ((Number)MethodUtil.invoke(
+                return ((Number)MethodHelper.invoke(
                     descriptor.getGetter(), getBean(), (Object[])null)).longValue();
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
@@ -115,7 +140,7 @@
         }
         AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
             try {
-                MethodUtil.invoke(descriptor.getSetter(), getBean(), new Object[] {value});
+                MethodHelper.invoke(descriptor.getSetter(), getBean(), new Object[] {value});
                 ExpressionHelper.fireValueChangedEvent(helper);
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
--- a/modules/javafx.base/src/main/java/javafx/beans/property/adapter/JavaBeanObjectProperty.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.base/src/main/java/javafx/beans/property/adapter/JavaBeanObjectProperty.java	Thu May 04 08:45:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
 package javafx.beans.property.adapter;
 
 import com.sun.javafx.binding.ExpressionHelper;
+import com.sun.javafx.property.MethodHelper;
 import com.sun.javafx.property.adapter.Disposer;
 import com.sun.javafx.property.adapter.PropertyDescriptor;
 import javafx.beans.InvalidationListener;
@@ -40,16 +41,16 @@
 import java.security.AccessControlContext;
 import java.security.PrivilegedAction;
 
-import sun.reflect.misc.MethodUtil;
-
 /**
  * A {@code JavaBeanObjectProperty} provides an adapter between a regular
  * Java Bean property of type {@code T} and a JavaFX
  * {@code ObjectProperty<T>}. It cannot be created directly, but a
  * {@link JavaBeanObjectPropertyBuilder} has to be used.
  * <p>
- * As a minimum, the Java Bean must implement a getter and a setter for the
- * property. If the getter of an instance of this class is called, the property of
+ * As a minimum, the Java Bean class must implement a getter and a setter for the
+ * property.
+ * The class, as well as the getter and a setter methods, must be declared public.
+ * If the getter of an instance of this class is called, the property of
  * the Java Bean is returned. If the setter is called, the value will be passed
  * to the Java Bean property. If the Java Bean property is bound (i.e. it supports
  * PropertyChangeListeners), this {@code JavaBeanObjectProperty} will be
@@ -58,6 +59,30 @@
  * is also constrained (i.e. it supports VetoableChangeListeners), this
  * {@code JavaBeanObjectProperty} will reject changes, if it is bound to an
  * {@link javafx.beans.value.ObservableValue ObservableValue&lt;Object&gt;}.
+ * </p>
+ * <p><b>Deploying an Application as a Module</b></p>
+ * <p>
+ * If the Java Bean class is in a named module, then it must be reflectively
+ * accessible to the {@code javafx.base} module.
+ * A class is reflectively accessible if the module
+ * {@link Module#isOpen(String,Module) opens} the containing package to at
+ * least the {@code javafx.base} module.
+ * </p>
+ * <p>
+ * For example, if {@code com.foo.MyBeanClass} is in the {@code foo.app} module,
+ * the {@code module-info.java} might
+ * look like this:
+ * </p>
+ *
+<pre>{@code module foo.app {
+    opens com.foo to javafx.base;
+}}</pre>
+ *
+ * <p>
+ * Alternatively, a class is reflectively accessible if the module
+ * {@link Module#isExported(String) exports} the containing package
+ * unconditionally.
+ * </p>
  *
  * @see javafx.beans.property.ObjectProperty
  * @see JavaBeanObjectPropertyBuilder
@@ -94,7 +119,7 @@
     public T get() {
         return AccessController.doPrivileged((PrivilegedAction<T>) () -> {
             try {
-                return (T)MethodUtil.invoke(descriptor.getGetter(), getBean(), (Object[])null);
+                return (T)MethodHelper.invoke(descriptor.getGetter(), getBean(), (Object[])null);
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
             } catch (InvocationTargetException e) {
@@ -118,7 +143,7 @@
 
         AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
             try {
-                MethodUtil.invoke(descriptor.getSetter(), getBean(), new Object[] {value});
+                MethodHelper.invoke(descriptor.getSetter(), getBean(), new Object[] {value});
                 ExpressionHelper.fireValueChangedEvent(helper);
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
--- a/modules/javafx.base/src/main/java/javafx/beans/property/adapter/JavaBeanStringProperty.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.base/src/main/java/javafx/beans/property/adapter/JavaBeanStringProperty.java	Thu May 04 08:45:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
 package javafx.beans.property.adapter;
 
 import com.sun.javafx.binding.ExpressionHelper;
+import com.sun.javafx.property.MethodHelper;
 import com.sun.javafx.property.adapter.Disposer;
 import com.sun.javafx.property.adapter.PropertyDescriptor;
 import javafx.beans.InvalidationListener;
@@ -40,16 +41,16 @@
 import java.security.AccessControlContext;
 import java.security.PrivilegedAction;
 
-import sun.reflect.misc.MethodUtil;
-
 /**
  * A {@code JavaBeanStringProperty} provides an adapter between a regular
  * Java Bean property of type {@code String} and a JavaFX
  * {@code StringProperty}. It cannot be created directly, but a
  * {@link JavaBeanStringPropertyBuilder} has to be used.
  * <p>
- * As a minimum, the Java Bean must implement a getter and a setter for the
- * property. If the getter of an instance of this class is called, the property of
+ * As a minimum, the Java Bean class must implement a getter and a setter for the
+ * property.
+ * The class, as well as the getter and a setter methods, must be declared public.
+ * If the getter of an instance of this class is called, the property of
  * the Java Bean is returned. If the setter is called, the value will be passed
  * to the Java Bean property. If the Java Bean property is bound (i.e. it supports
  * PropertyChangeListeners), this {@code JavaBeanStringProperty} will be
@@ -58,6 +59,30 @@
  * is also constrained (i.e. it supports VetoableChangeListeners), this
  * {@code JavaBeanStringProperty} will reject changes, if it is bound to an
  * {@link javafx.beans.value.ObservableValue ObservableValue&lt;String&gt;}.
+ * </p>
+ * <p><b>Deploying an Application as a Module</b></p>
+ * <p>
+ * If the Java Bean class is in a named module, then it must be reflectively
+ * accessible to the {@code javafx.base} module.
+ * A class is reflectively accessible if the module
+ * {@link Module#isOpen(String,Module) opens} the containing package to at
+ * least the {@code javafx.base} module.
+ * </p>
+ * <p>
+ * For example, if {@code com.foo.MyBeanClass} is in the {@code foo.app} module,
+ * the {@code module-info.java} might
+ * look like this:
+ * </p>
+ *
+<pre>{@code module foo.app {
+    opens com.foo to javafx.base;
+}}</pre>
+ *
+ * <p>
+ * Alternatively, a class is reflectively accessible if the module
+ * {@link Module#isExported(String) exports} the containing package
+ * unconditionally.
+ * </p>
  *
  * @see javafx.beans.property.StringProperty
  * @see JavaBeanStringPropertyBuilder
@@ -91,7 +116,7 @@
     public String get() {
         return AccessController.doPrivileged((PrivilegedAction<String>) () -> {
             try {
-                return (String)MethodUtil.invoke(descriptor.getGetter(), getBean(), (Object[])null);
+                return (String)MethodHelper.invoke(descriptor.getGetter(), getBean(), (Object[])null);
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
             } catch (InvocationTargetException e) {
@@ -114,7 +139,7 @@
         }
         AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
             try {
-                MethodUtil.invoke(descriptor.getSetter(), getBean(), new Object[] {value});
+                MethodHelper.invoke(descriptor.getSetter(), getBean(), new Object[] {value});
                 ExpressionHelper.fireValueChangedEvent(helper);
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
--- a/modules/javafx.base/src/main/java/javafx/beans/property/adapter/ReadOnlyJavaBeanBooleanProperty.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.base/src/main/java/javafx/beans/property/adapter/ReadOnlyJavaBeanBooleanProperty.java	Thu May 04 08:45:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
 
 package javafx.beans.property.adapter;
 
+import com.sun.javafx.property.MethodHelper;
 import com.sun.javafx.property.adapter.Disposer;
 import com.sun.javafx.property.adapter.ReadOnlyPropertyDescriptor;
 import javafx.beans.property.ReadOnlyBooleanPropertyBase;
@@ -36,20 +37,44 @@
 import java.security.AccessControlContext;
 import java.security.PrivilegedAction;
 
-import sun.reflect.misc.MethodUtil;
-
 /**
  * A {@code ReadOnlyJavaBeanBooleanProperty} provides an adapter between a regular
  * read only Java Bean property of type {@code boolean} or {@code Boolean} and a JavaFX
  * {@code ReadOnlyBooleanProperty}. It cannot be created directly, but a
  * {@link ReadOnlyJavaBeanBooleanPropertyBuilder} has to be used.
  * <p>
- * As a minimum, the Java Bean must implement a getter for the
- * property. If the getter of an instance of this class is called, the property of
+ * As a minimum, the Java Bean class must implement a getter for the
+ * property.
+ * The class, as well as the getter method, must be declared public.
+ * If the getter of an instance of this class is called, the property of
  * the Java Bean is returned. If the Java Bean property is bound (i.e. it supports
  * PropertyChangeListeners), this {@code ReadOnlyJavaBeanBooleanProperty} will be
  * aware of changes in the Java Bean. Otherwise it can be notified about
  * changes by calling {@link #fireValueChangedEvent()}.
+ * </p>
+ * <p><b>Deploying an Application as a Module</b></p>
+ * <p>
+ * If the Java Bean class is in a named module, then it must be reflectively
+ * accessible to the {@code javafx.base} module.
+ * A class is reflectively accessible if the module
+ * {@link Module#isOpen(String,Module) opens} the containing package to at
+ * least the {@code javafx.base} module.
+ * </p>
+ * <p>
+ * For example, if {@code com.foo.MyBeanClass} is in the {@code foo.app} module,
+ * the {@code module-info.java} might
+ * look like this:
+ * </p>
+ *
+<pre>{@code module foo.app {
+    opens com.foo to javafx.base;
+}}</pre>
+ *
+ * <p>
+ * Alternatively, a class is reflectively accessible if the module
+ * {@link Module#isExported(String) exports} the containing package
+ * unconditionally.
+ * </p>
  *
  * @see javafx.beans.property.ReadOnlyBooleanProperty
  * @see ReadOnlyJavaBeanBooleanPropertyBuilder
@@ -80,7 +105,7 @@
     public boolean get() {
         return AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> {
             try {
-                return (Boolean)MethodUtil.invoke(descriptor.getGetter(), getBean(), (Object[])null);
+                return (Boolean)MethodHelper.invoke(descriptor.getGetter(), getBean(), (Object[])null);
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
             } catch (InvocationTargetException e) {
--- a/modules/javafx.base/src/main/java/javafx/beans/property/adapter/ReadOnlyJavaBeanDoubleProperty.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.base/src/main/java/javafx/beans/property/adapter/ReadOnlyJavaBeanDoubleProperty.java	Thu May 04 08:45:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
 
 package javafx.beans.property.adapter;
 
+import com.sun.javafx.property.MethodHelper;
 import com.sun.javafx.property.adapter.Disposer;
 import com.sun.javafx.property.adapter.ReadOnlyPropertyDescriptor;
 import javafx.beans.property.ReadOnlyDoublePropertyBase;
@@ -36,20 +37,44 @@
 import java.security.AccessControlContext;
 import java.security.PrivilegedAction;
 
-import sun.reflect.misc.MethodUtil;
-
 /**
  * A {@code ReadOnlyJavaBeanDoubleProperty} provides an adapter between a regular
  * read only Java Bean property of type {@code double} or {@code Double} and a JavaFX
  * {@code ReadOnlyDoubleProperty}. It cannot be created directly, but a
  * {@link ReadOnlyJavaBeanDoublePropertyBuilder} has to be used.
  * <p>
- * As a minimum, the Java Bean must implement a getter for the
- * property. If the getter of an instance of this class is called, the property of
+ * As a minimum, the Java Bean class must implement a getter for the
+ * property.
+ * The class, as well as the getter method, must be declared public.
+ * If the getter of an instance of this class is called, the property of
  * the Java Bean is returned. If the Java Bean property is bound (i.e. it supports
  * PropertyChangeListeners), this {@code ReadOnlyJavaBeanDoubleProperty} will be
  * aware of changes in the Java Bean. Otherwise it can be notified about
  * changes by calling {@link #fireValueChangedEvent()}.
+ * </p>
+ * <p><b>Deploying an Application as a Module</b></p>
+ * <p>
+ * If the Java Bean class is in a named module, then it must be reflectively
+ * accessible to the {@code javafx.base} module.
+ * A class is reflectively accessible if the module
+ * {@link Module#isOpen(String,Module) opens} the containing package to at
+ * least the {@code javafx.base} module.
+ * </p>
+ * <p>
+ * For example, if {@code com.foo.MyBeanClass} is in the {@code foo.app} module,
+ * the {@code module-info.java} might
+ * look like this:
+ * </p>
+ *
+<pre>{@code module foo.app {
+    opens com.foo to javafx.base;
+}}</pre>
+ *
+ * <p>
+ * Alternatively, a class is reflectively accessible if the module
+ * {@link Module#isExported(String) exports} the containing package
+ * unconditionally.
+ * </p>
  *
  * @see javafx.beans.property.ReadOnlyDoubleProperty
  * @see ReadOnlyJavaBeanDoublePropertyBuilder
@@ -80,7 +105,7 @@
     public double get() {
         return AccessController.doPrivileged((PrivilegedAction<Double>) () -> {
             try {
-                return ((Number)MethodUtil.invoke(
+                return ((Number)MethodHelper.invoke(
                     descriptor.getGetter(), getBean(), (Object[])null)).doubleValue();
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
--- a/modules/javafx.base/src/main/java/javafx/beans/property/adapter/ReadOnlyJavaBeanFloatProperty.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.base/src/main/java/javafx/beans/property/adapter/ReadOnlyJavaBeanFloatProperty.java	Thu May 04 08:45:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
 
 package javafx.beans.property.adapter;
 
+import com.sun.javafx.property.MethodHelper;
 import com.sun.javafx.property.adapter.Disposer;
 import com.sun.javafx.property.adapter.ReadOnlyPropertyDescriptor;
 import javafx.beans.property.ReadOnlyFloatPropertyBase;
@@ -36,20 +37,44 @@
 import java.security.AccessControlContext;
 import java.security.PrivilegedAction;
 
-import sun.reflect.misc.MethodUtil;
-
 /**
  * A {@code ReadOnlyJavaBeanFloatProperty} provides an adapter between a regular
  * read only Java Bean property of type {@code float} or {@code Float} and a JavaFX
  * {@code ReadOnlyFloatProperty}. It cannot be created directly, but a
  * {@link ReadOnlyJavaBeanFloatPropertyBuilder} has to be used.
  * <p>
- * As a minimum, the Java Bean must implement a getter for the
- * property. If the getter of an instance of this class is called, the property of
+ * As a minimum, the Java Bean class must implement a getter for the
+ * property.
+ * The class, as well as the getter method, must be declared public.
+ * If the getter of an instance of this class is called, the property of
  * the Java Bean is returned. If the Java Bean property is bound (i.e. it supports
  * PropertyChangeListeners), this {@code ReadOnlyJavaBeanFloatProperty} will be
  * aware of changes in the Java Bean. Otherwise it can be notified about
  * changes by calling {@link #fireValueChangedEvent()}.
+ * </p>
+ * <p><b>Deploying an Application as a Module</b></p>
+ * <p>
+ * If the Java Bean class is in a named module, then it must be reflectively
+ * accessible to the {@code javafx.base} module.
+ * A class is reflectively accessible if the module
+ * {@link Module#isOpen(String,Module) opens} the containing package to at
+ * least the {@code javafx.base} module.
+ * </p>
+ * <p>
+ * For example, if {@code com.foo.MyBeanClass} is in the {@code foo.app} module,
+ * the {@code module-info.java} might
+ * look like this:
+ * </p>
+ *
+<pre>{@code module foo.app {
+    opens com.foo to javafx.base;
+}}</pre>
+ *
+ * <p>
+ * Alternatively, a class is reflectively accessible if the module
+ * {@link Module#isExported(String) exports} the containing package
+ * unconditionally.
+ * </p>
  *
  * @see javafx.beans.property.ReadOnlyFloatProperty
  * @see ReadOnlyJavaBeanFloatPropertyBuilder
@@ -80,7 +105,7 @@
     public float get() {
         return AccessController.doPrivileged((PrivilegedAction<Float>) () -> {
             try {
-                return ((Number)MethodUtil.invoke(
+                return ((Number)MethodHelper.invoke(
                     descriptor.getGetter(), getBean(), (Object[])null)).floatValue();
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
--- a/modules/javafx.base/src/main/java/javafx/beans/property/adapter/ReadOnlyJavaBeanIntegerProperty.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.base/src/main/java/javafx/beans/property/adapter/ReadOnlyJavaBeanIntegerProperty.java	Thu May 04 08:45:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
 
 package javafx.beans.property.adapter;
 
+import com.sun.javafx.property.MethodHelper;
 import com.sun.javafx.property.adapter.Disposer;
 import com.sun.javafx.property.adapter.ReadOnlyPropertyDescriptor;
 import javafx.beans.property.ReadOnlyIntegerPropertyBase;
@@ -36,20 +37,44 @@
 import java.security.AccessControlContext;
 import java.security.PrivilegedAction;
 
-import sun.reflect.misc.MethodUtil;
-
 /**
  * A {@code ReadOnlyJavaBeanIntegerProperty} provides an adapter between a regular
  * read only Java Bean property of type {@code int} or {@code Integer} and a JavaFX
  * {@code ReadOnlyIntegerProperty}. It cannot be created directly, but a
  * {@link ReadOnlyJavaBeanIntegerPropertyBuilder} has to be used.
  * <p>
- * As a minimum, the Java Bean must implement a getter for the
- * property. If the getter of an instance of this class is called, the property of
+ * As a minimum, the Java Bean class must implement a getter for the
+ * property.
+ * The class, as well as the getter method, must be declared public.
+ * If the getter of an instance of this class is called, the property of
  * the Java Bean is returned. If the Java Bean property is bound (i.e. it supports
  * PropertyChangeListeners), this {@code ReadOnlyJavaBeanIntegerProperty} will be
  * aware of changes in the Java Bean. Otherwise it can be notified about
  * changes by calling {@link #fireValueChangedEvent()}.
+ * </p>
+ * <p><b>Deploying an Application as a Module</b></p>
+ * <p>
+ * If the Java Bean class is in a named module, then it must be reflectively
+ * accessible to the {@code javafx.base} module.
+ * A class is reflectively accessible if the module
+ * {@link Module#isOpen(String,Module) opens} the containing package to at
+ * least the {@code javafx.base} module.
+ * </p>
+ * <p>
+ * For example, if {@code com.foo.MyBeanClass} is in the {@code foo.app} module,
+ * the {@code module-info.java} might
+ * look like this:
+ * </p>
+ *
+<pre>{@code module foo.app {
+    opens com.foo to javafx.base;
+}}</pre>
+ *
+ * <p>
+ * Alternatively, a class is reflectively accessible if the module
+ * {@link Module#isExported(String) exports} the containing package
+ * unconditionally.
+ * </p>
  *
  * @see javafx.beans.property.ReadOnlyIntegerProperty
  * @see ReadOnlyJavaBeanIntegerPropertyBuilder
@@ -80,7 +105,7 @@
     public int get() {
         return AccessController.doPrivileged((PrivilegedAction<Integer>) () -> {
             try {
-                return ((Number)MethodUtil.invoke(
+                return ((Number)MethodHelper.invoke(
                     descriptor.getGetter(), getBean(), (Object[])null)).intValue();
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
--- a/modules/javafx.base/src/main/java/javafx/beans/property/adapter/ReadOnlyJavaBeanLongProperty.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.base/src/main/java/javafx/beans/property/adapter/ReadOnlyJavaBeanLongProperty.java	Thu May 04 08:45:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
 
 package javafx.beans.property.adapter;
 
+import com.sun.javafx.property.MethodHelper;
 import com.sun.javafx.property.adapter.Disposer;
 import com.sun.javafx.property.adapter.ReadOnlyPropertyDescriptor;
 import javafx.beans.property.ReadOnlyLongPropertyBase;
@@ -36,20 +37,44 @@
 import java.security.AccessControlContext;
 import java.security.PrivilegedAction;
 
-import sun.reflect.misc.MethodUtil;
-
 /**
  * A {@code ReadOnlyJavaBeanLongProperty} provides an adapter between a regular
  * read only Java Bean property of type {@code long} or {@code Long} and a JavaFX
  * {@code ReadOnlyLongProperty}. It cannot be created directly, but a
  * {@link ReadOnlyJavaBeanLongPropertyBuilder} has to be used.
  * <p>
- * As a minimum, the Java Bean must implement a getter for the
- * property. If the getter of an instance of this class is called, the property of
+ * As a minimum, the Java Bean class must implement a getter for the
+ * property.
+ * The class, as well as the getter method, must be declared public.
+ * If the getter of an instance of this class is called, the property of
  * the Java Bean is returned. If the Java Bean property is bound (i.e. it supports
  * PropertyChangeListeners), this {@code ReadOnlyJavaBeanLongProperty} will be
  * aware of changes in the Java Bean. Otherwise it can be notified about
  * changes by calling {@link #fireValueChangedEvent()}.
+ * </p>
+ * <p><b>Deploying an Application as a Module</b></p>
+ * <p>
+ * If the Java Bean class is in a named module, then it must be reflectively
+ * accessible to the {@code javafx.base} module.
+ * A class is reflectively accessible if the module
+ * {@link Module#isOpen(String,Module) opens} the containing package to at
+ * least the {@code javafx.base} module.
+ * </p>
+ * <p>
+ * For example, if {@code com.foo.MyBeanClass} is in the {@code foo.app} module,
+ * the {@code module-info.java} might
+ * look like this:
+ * </p>
+ *
+<pre>{@code module foo.app {
+    opens com.foo to javafx.base;
+}}</pre>
+ *
+ * <p>
+ * Alternatively, a class is reflectively accessible if the module
+ * {@link Module#isExported(String) exports} the containing package
+ * unconditionally.
+ * </p>
  *
  * @see javafx.beans.property.ReadOnlyLongProperty
  * @see ReadOnlyJavaBeanLongPropertyBuilder
@@ -80,7 +105,7 @@
     public long get() {
         return AccessController.doPrivileged((PrivilegedAction<Long>) () -> {
             try {
-                return ((Number)MethodUtil.invoke(
+                return ((Number)MethodHelper.invoke(
                     descriptor.getGetter(), getBean(), (Object[])null)).longValue();
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
--- a/modules/javafx.base/src/main/java/javafx/beans/property/adapter/ReadOnlyJavaBeanObjectProperty.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.base/src/main/java/javafx/beans/property/adapter/ReadOnlyJavaBeanObjectProperty.java	Thu May 04 08:45:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
 
 package javafx.beans.property.adapter;
 
+import com.sun.javafx.property.MethodHelper;
 import com.sun.javafx.property.adapter.Disposer;
 import com.sun.javafx.property.adapter.ReadOnlyPropertyDescriptor;
 import javafx.beans.property.ReadOnlyObjectPropertyBase;
@@ -36,20 +37,44 @@
 import java.security.AccessControlContext;
 import java.security.PrivilegedAction;
 
-import sun.reflect.misc.MethodUtil;
-
 /**
  * A {@code ReadOnlyJavaBeanObjectProperty} provides an adapter between a regular
  * read only Java Bean property of {@code T} and a JavaFX
  * {@code ReadOnlyObjectProperty}. It cannot be created directly, but a
  * {@link ReadOnlyJavaBeanObjectPropertyBuilder} has to be used.
  * <p>
- * As a minimum, the Java Bean must implement a getter for the
- * property. If the getter of an instance of this class is called, the property of
+ * As a minimum, the Java Bean class must implement a getter for the
+ * property.
+ * The class, as well as the getter method, must be declared public.
+ * If the getter of an instance of this class is called, the property of
  * the Java Bean is returned. If the Java Bean property is bound (i.e. it supports
  * PropertyChangeListeners), this {@code ReadOnlyJavaBeanObjectProperty} will be
  * aware of changes in the Java Bean. Otherwise it can be notified about
  * changes by calling {@link #fireValueChangedEvent()}.
+ * </p>
+ * <p><b>Deploying an Application as a Module</b></p>
+ * <p>
+ * If the Java Bean class is in a named module, then it must be reflectively
+ * accessible to the {@code javafx.base} module.
+ * A class is reflectively accessible if the module
+ * {@link Module#isOpen(String,Module) opens} the containing package to at
+ * least the {@code javafx.base} module.
+ * </p>
+ * <p>
+ * For example, if {@code com.foo.MyBeanClass} is in the {@code foo.app} module,
+ * the {@code module-info.java} might
+ * look like this:
+ * </p>
+ *
+<pre>{@code module foo.app {
+    opens com.foo to javafx.base;
+}}</pre>
+ *
+ * <p>
+ * Alternatively, a class is reflectively accessible if the module
+ * {@link Module#isExported(String) exports} the containing package
+ * unconditionally.
+ * </p>
  *
  * @see javafx.beans.property.ReadOnlyObjectProperty
  * @see ReadOnlyJavaBeanObjectPropertyBuilder
@@ -82,7 +107,7 @@
     public T get() {
         return AccessController.doPrivileged((PrivilegedAction<T>) () -> {
             try {
-                return (T)MethodUtil.invoke(descriptor.getGetter(), getBean(), (Object[])null);
+                return (T)MethodHelper.invoke(descriptor.getGetter(), getBean(), (Object[])null);
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
             } catch (InvocationTargetException e) {
--- a/modules/javafx.base/src/main/java/javafx/beans/property/adapter/ReadOnlyJavaBeanStringProperty.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.base/src/main/java/javafx/beans/property/adapter/ReadOnlyJavaBeanStringProperty.java	Thu May 04 08:45:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
 
 package javafx.beans.property.adapter;
 
+import com.sun.javafx.property.MethodHelper;
 import com.sun.javafx.property.adapter.Disposer;
 import com.sun.javafx.property.adapter.ReadOnlyPropertyDescriptor;
 import javafx.beans.property.ReadOnlyStringPropertyBase;
@@ -36,20 +37,44 @@
 import java.security.AccessControlContext;
 import java.security.PrivilegedAction;
 
-import sun.reflect.misc.MethodUtil;
-
 /**
  * A {@code ReadOnlyJavaBeanStringProperty} provides an adapter between a regular
  * read only Java Bean property of type {@code String} and a JavaFX
  * {@code ReadOnlyStringProperty}. It cannot be created directly, but a
  * {@link ReadOnlyJavaBeanStringPropertyBuilder} has to be used.
  * <p>
- * As a minimum, the Java Bean must implement a getter for the
- * property. If the getter of an instance of this class is called, the property of
+ * As a minimum, the Java Bean class must implement a getter for the
+ * property.
+ * The class, as well as the getter method, must be declared public.
+ * If the getter of an instance of this class is called, the property of
  * the Java Bean is returned. If the Java Bean property is bound (i.e. it supports
  * PropertyChangeListeners), this {@code ReadOnlyJavaBeanStringProperty} will be
  * aware of changes in the Java Bean. Otherwise it can be notified about
  * changes by calling {@link #fireValueChangedEvent()}.
+ * </p>
+ * <p><b>Deploying an Application as a Module</b></p>
+ * <p>
+ * If the Java Bean class is in a named module, then it must be reflectively
+ * accessible to the {@code javafx.base} module.
+ * A class is reflectively accessible if the module
+ * {@link Module#isOpen(String,Module) opens} the containing package to at
+ * least the {@code javafx.base} module.
+ * </p>
+ * <p>
+ * For example, if {@code com.foo.MyBeanClass} is in the {@code foo.app} module,
+ * the {@code module-info.java} might
+ * look like this:
+ * </p>
+ *
+<pre>{@code module foo.app {
+    opens com.foo to javafx.base;
+}}</pre>
+ *
+ * <p>
+ * Alternatively, a class is reflectively accessible if the module
+ * {@link Module#isExported(String) exports} the containing package
+ * unconditionally.
+ * </p>
  *
  * @see javafx.beans.property.ReadOnlyStringProperty
  * @see ReadOnlyJavaBeanStringPropertyBuilder
@@ -80,7 +105,7 @@
     public String get() {
         return AccessController.doPrivileged((PrivilegedAction<String>) () -> {
             try {
-                return (String)MethodUtil.invoke(descriptor.getGetter(), getBean(), (Object[])null);
+                return (String)MethodHelper.invoke(descriptor.getGetter(), getBean(), (Object[])null);
             } catch (IllegalAccessException e) {
                 throw new UndeclaredThrowableException(e);
             } catch (InvocationTargetException e) {
--- a/modules/javafx.base/src/main/java/module-info.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.base/src/main/java/module-info.java	Thu May 04 08:45:51 2017 -0700
@@ -68,6 +68,9 @@
         javafx.graphics;
     exports com.sun.javafx.property to
         javafx.controls;
+    exports com.sun.javafx.reflect to
+        javafx.fxml,
+        javafx.web;
     exports com.sun.javafx.runtime to
         javafx.graphics;
 }
--- a/modules/javafx.controls/src/main/java/javafx/scene/control/cell/PropertyValueFactory.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.controls/src/main/java/javafx/scene/control/cell/PropertyValueFactory.java	Thu May 04 08:45:51 2017 -0700
@@ -52,29 +52,37 @@
  * firstNameCol.setCellValueFactory(new PropertyValueFactory&lt;Person,String&gt;("firstName"));
  * </code></pre>
  *
- * In this example, the "firstName" string is used as a reference to an assumed
- * <code>firstNameProperty()</code> method in the <code>Person</code> class type
- * (which is the class type of the TableView
- * {@link TableView#itemsProperty() items} list). Additionally, this method must
- * return a {@link Property} instance. If a method meeting these requirements
- * is found, then the {@link TableCell} is populated with this
- * {@literal ObservableValue<T>}.
- * In addition, the TableView will automatically add an observer to the
- * returned value, such that any changes fired will be observed by the TableView,
- * resulting in the cell immediately updating.
- *
- * <p>If no method matching this pattern exists, there is fall-through support
- * for attempting to call get&lt;property&gt;() or is&lt;property&gt;() (that is,
- * <code>getFirstName()</code> or <code>isFirstName()</code> in the example
- * above). If a  method matching this pattern exists, the value returned from this method
- * is wrapped in a {@link ReadOnlyObjectWrapper} and returned to the TableCell.
- * However, in this situation, this means that the TableCell will not be able
- * to observe the ObservableValue for changes (as is the case in the first
- * approach above).
+ * <p>
+ * In this example, {@code Person} is the class type of the {@code TableView}
+ * {@link TableView#itemsProperty() items} list.
+ * The class {@code Person} must be declared public.
+ * {@code PropertyValueFactory} uses the constructor argument,
+ * {@code "firstName"}, to assume that {@code Person} has a public method
+ * {@code firstNameProperty} with no formal parameters and a return type of
+ * {@code ObservableValue<String>}.
+ * </p>
+ * <p>
+ * If such a method exists, then it is invoked, and additionally assumed
+ * to return an instance of {@code Property<String>}. The return value is used
+ * to populate the {@link TableCell}. In addition, the {@code TableView} adds
+ * an observer to the return value, such that any changes fired will be observed
+ * by the {@code TableView}, resulting in the cell immediately updating.
+ * </p>
+ * <p>
+ * If no such method exists, then {@code PropertyValueFactory}
+ * assumes that {@code Person} has a public method {@code getFirstName} or
+ * {@code isFirstName} with no formal parameters and a return type of
+ * {@code String}. If such a method exists, then it is invoked, and its return
+ * value is wrapped in a {@link ReadOnlyObjectWrapper}
+ * and returned to the {@code TableCell}. In this situation,
+ * the {@code TableCell} will not be able to observe changes to the property,
+ * unlike in the first approach above.
+ * </p>
  *
  * <p>For reference (and as noted in the TableColumn
  * {@link TableColumn#cellValueFactory cell value factory} documentation), the
  * long form of the code above would be the following:
+ * </p>
  *
  * <pre><code>
  * TableColumn&lt;Person,String&gt; firstNameCol = new TableColumn&lt;Person,String&gt;("First Name");
@@ -87,6 +95,32 @@
  * }
  * </code></pre>
  *
+ * <p><b>Deploying an Application as a Module</b></p>
+ * <p>
+ * If the referenced class is in a named module, then it must be reflectively
+ * accessible to the {@code javafx.base} module.
+ * A class is reflectively accessible if the module
+ * {@link Module#isOpen(String,Module) opens} the containing package to at
+ * least the {@code javafx.base} module.
+ * Otherwise the {@link #call call(TableColumn.CellDataFeatures)} method
+ * will log a warning and return {@code null}.
+ * </p>
+ * <p>
+ * For example, if the {@code Person} class is in the {@code com.foo} package
+ * in the {@code foo.app} module, the {@code module-info.java} might
+ * look like this:
+ * </p>
+ *
+<pre>{@code module foo.app {
+    opens com.foo to javafx.base;
+}}</pre>
+ *
+ * <p>
+ * Alternatively, a class is reflectively accessible if the module
+ * {@link Module#isExported(String) exports} the containing package
+ * unconditionally.
+ * </p>
+ *
  * @see TableColumn
  * @see TableView
  * @see TableCell
@@ -143,20 +177,23 @@
                 this.propertyRef = new PropertyReference<T>(rowData.getClass(), getProperty());
             }
 
-            if (propertyRef.hasProperty()) {
-                return propertyRef.getProperty(rowData);
-            } else {
-                T value = propertyRef.get(rowData);
-                return new ReadOnlyObjectWrapper<T>(value);
+            if (propertyRef != null) {
+                if (propertyRef.hasProperty()) {
+                    return propertyRef.getProperty(rowData);
+                } else {
+                    T value = propertyRef.get(rowData);
+                    return new ReadOnlyObjectWrapper<T>(value);
+                }
             }
-        } catch (IllegalStateException e) {
+        } catch (RuntimeException e) {
             // log the warning and move on
             final PlatformLogger logger = Logging.getControlsLogger();
             if (logger.isLoggable(Level.WARNING)) {
-               logger.finest("Can not retrieve property '" + getProperty() +
+               logger.warning("Can not retrieve property '" + getProperty() +
                         "' in PropertyValueFactory: " + this +
                         " with provided class type: " + rowData.getClass(), e);
             }
+            propertyRef = null;
         }
 
         return null;
--- a/modules/javafx.controls/src/main/java/javafx/scene/control/cell/TreeItemPropertyValueFactory.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.controls/src/main/java/javafx/scene/control/cell/TreeItemPropertyValueFactory.java	Thu May 04 08:45:51 2017 -0700
@@ -30,8 +30,10 @@
 import javafx.beans.property.ReadOnlyObjectWrapper;
 import javafx.beans.value.ObservableValue;
 import javafx.scene.control.TreeItem;
+import javafx.scene.control.TreeTableCell;
 import javafx.scene.control.TreeTableColumn;
 import javafx.scene.control.TreeTableColumn.CellDataFeatures;
+import javafx.scene.control.TreeTableView;
 import javafx.util.Callback;
 import com.sun.javafx.property.PropertyReference;
 import com.sun.javafx.scene.control.Logging;
@@ -50,28 +52,38 @@
  * firstNameCol.setCellValueFactory(new TreeItemPropertyValueFactory&lt;Person,String&gt;("firstName"));
  * </code></pre>
  *
- * In this example, the "firstName" string is used as a reference to an assumed
- * <code>firstNameProperty()</code> method in the <code>Person</code> class type
- * (which is the class type of the TreeTableView). Additionally, this method must
- * return a {@link Property} instance. If a method meeting these requirements
- * is found, then the {@link javafx.scene.control.TreeTableCell} is populated
- * with this {@literal ObservableValue<T>}.
- * In addition, the TreeTableView will automatically add an observer to the
- * returned value, such that any changes fired will be observed by the TreeTableView,
- * resulting in the cell immediately updating.
- *
- * <p>If no method matching this pattern exists, there is fall-through support
- * for attempting to call get&lt;property&gt;() or is&lt;property&gt;() (that is,
- * <code>getFirstName()</code> or <code>isFirstName()</code> in the example
- * above). If a  method matching this pattern exists, the value returned from this method
- * is wrapped in a {@link ReadOnlyObjectWrapper} and returned to the TreeTableCell.
- * However, in this situation, this means that the TreeTableCell will not be able
- * to observe the ObservableValue for changes (as is the case in the first
- * approach above).
+ * <p>
+ * In this example, {@code Person} is the class type of the {@link TreeItem}
+ * instances used in the {@link TreeTableView}.
+ * The class {@code Person} must be declared public.
+ * {@code TreeItemPropertyValueFactory} uses the constructor argument,
+ * {@code "firstName"}, to assume that {@code Person} has a public method
+ * {@code firstNameProperty} with no formal parameters and a return type of
+ * {@code ObservableValue<String>}.
+ * </p>
+ * <p>
+ * If such a method exists, then it is invoked, and additionally assumed
+ * to return an instance of {@code Property<String>}. The return value is used
+ * to populate the {@link TreeTableCell}. In addition, the {@code TreeTableView}
+ * adds an observer to the return value, such that any changes fired will be
+ * observed by the {@code TreeTableView}, resulting in the cell immediately
+ * updating.
+ * </p>
+ * <p>
+ * If no such method exists, then {@code TreeItemPropertyValueFactory}
+ * assumes that {@code Person} has a public method {@code getFirstName} or
+ * {@code isFirstName} with no formal parameters and a return type of
+ * {@code String}. If such a method exists, then it is invoked, and its return
+ * value is wrapped in a {@link ReadOnlyObjectWrapper}
+ * and returned to the {@code TreeTableCell}. In this situation,
+ * the {@code TreeTableCell} will not be able to observe changes to the property,
+ * unlike in the first approach above.
+ * </p>
  *
  * <p>For reference (and as noted in the TreeTableColumn
  * {@link TreeTableColumn#cellValueFactory cell value factory} documentation), the
  * long form of the code above would be the following:
+ * </p>
  *
  * <pre><code>
  * TreeTableColumn&lt;Person,String&gt; firstNameCol = new TreeTableColumn&lt;Person,String&gt;("First Name");
@@ -88,9 +100,35 @@
  * }
  * </code></pre>
  *
+ * <p><b>Deploying an Application as a Module</b></p>
+ * <p>
+ * If the referenced class is in a named module, then it must be reflectively
+ * accessible to the {@code javafx.base} module.
+ * A class is reflectively accessible if the module
+ * {@link Module#isOpen(String,Module) opens} the containing package to at
+ * least the {@code javafx.base} module.
+ * Otherwise the {@link #call call(TreeTableColumn.CellDataFeatures)} method
+ * will log a warning and return {@code null}.
+ * </p>
+ * <p>
+ * For example, if the {@code Person} class is in the {@code com.foo} package
+ * in the {@code foo.app} module, the {@code module-info.java} might
+ * look like this:
+ * </p>
+ *
+<pre>{@code module foo.app {
+    opens com.foo to javafx.base;
+}}</pre>
+ *
+ * <p>
+ * Alternatively, a class is reflectively accessible if the module
+ * {@link Module#isExported(String) exports} the containing package
+ * unconditionally.
+ * </p>
+ *
  * @see TreeTableColumn
- * @see javafx.scene.control.TreeTableView
- * @see javafx.scene.control.TreeTableCell
+ * @see TreeTableView
+ * @see TreeTableCell
  * @see PropertyValueFactory
  * @see MapValueFactory
  * @since JavaFX 8.0
@@ -143,23 +181,26 @@
                 this.propertyRef = new PropertyReference<T>(rowData.getClass(), getProperty());
             }
 
-            return propertyRef.getProperty(rowData);
-        } catch (IllegalStateException e) {
+            if (propertyRef != null) {
+                return propertyRef.getProperty(rowData);
+            }
+        } catch (RuntimeException e) {
             try {
                 // attempt to just get the value
                 T value = propertyRef.get(rowData);
                 return new ReadOnlyObjectWrapper<T>(value);
-            } catch (IllegalStateException e2) {
+            } catch (RuntimeException e2) {
                 // fall through to logged exception below
             }
 
             // log the warning and move on
             final PlatformLogger logger = Logging.getControlsLogger();
             if (logger.isLoggable(Level.WARNING)) {
-               logger.finest("Can not retrieve property '" + getProperty() +
+               logger.warning("Can not retrieve property '" + getProperty() +
                         "' in TreeItemPropertyValueFactory: " + this +
                         " with provided class type: " + rowData.getClass(), e);
             }
+            propertyRef = null;
         }
 
         return null;
--- a/modules/javafx.fxml/src/main/docs/javafx/fxml/doc-files/introduction_to_fxml.html	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.fxml/src/main/docs/javafx/fxml/doc-files/introduction_to_fxml.html	Thu May 04 08:45:51 2017 -0700
@@ -15,7 +15,7 @@
 </div>
 
 <h1>Introduction to FXML</h1>
-<p class="subtitle">Last updated: 3/3/2017</p>
+<p class="subtitle">Last updated: 01 May 2017</p>
 
 <h2>Contents</h2>
 <ul class="contents">
@@ -89,6 +89,7 @@
     <li><a href="#custom_components">Custom Components</a></li>
     </ul>
 </li>
+<li><a href="#deploy_as_module">Deploying an Application as a Module</a></li>
 </ul>
 
 <h2><a name="overview">Overview</a></h2>
@@ -886,9 +887,9 @@
 class="code">javafx.fxml.FXML</span> annotation can be used. This
 annotation marks a protected or private class member as accessible
 to FXML.
-If the class being annotated is part of a named module, the
+If the class being annotated is in a named module, the
 module containing that class must <span class="code">open</span>
-the containing package to
+the containing package to at least
 the <span class="code">javafx.fxml</span> module.</assert></span>
 
 <p>For example, the controllers from the previous examples could be rewritten as follows:</p>
@@ -1045,6 +1046,27 @@
 &lt;/HBox&gt;
 </pre>
 
+<h2><a name="deploy_as_module">Deploying an Application as a Module</a></h2>
+<p>If <span class="code">FXMLLoader</span> is used to load types in a named
+module, the application must ensure that all types that are referenced in the
+FXML files, including the controller class and any custom <span class="code">Node</span>
+classes, are reflectively accessible to the <span class="code">javafx.fxml</span>
+module. A type is reflectively accessible if the module
+<span class="code">opens</span> the containing package to at least the
+<span class="code">javafx.fxml</span> module.
+</p>
+
+<p>For example, if <span class="code">com.foo.MyController</span> is in the
+<span class="code">foo.app</span> module, the
+<span class="code">module-info.java</span> might look like this:
+</p>
+<pre><span class="code">module foo.app {
+    opens com.foo to javafx.fxml;
+}</span></pre>
+
+<p>Alternatively, a type is reflectively accessible if the module
+<span class="code">exports</span> the containing package unconditionally.
+</p>
 <hr>
 <p>
 <font size="-1"><a href="http://bugreport.java.com/bugreport/" target="_blank">Submit a bug or feature</a><br>For further API reference and developer documentation, see <a href="http://download.java.net/java/jdk9/docs/index.html" target="_blank">Java SE Documentation</a>. That documentation contains more detailed, developer-targeted descriptions, with conceptual overviews, definitions of terms, workarounds, and working code examples.<br> <a href="http://download.java.net/java/jdk9/docs/legal/cpyr.html" target="_blank">Copyright</a> &copy; 2008, 2017, Oracle and/or its affiliates. All rights reserved.<br><b>DRAFT 9-ea</b></font>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/javafx.fxml/src/main/java/com/sun/javafx/fxml/MethodHelper.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.fxml;
+
+import com.sun.javafx.reflect.MethodUtil;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import sun.reflect.misc.ReflectUtil;
+
+/**
+ * Utility class to wrap method invocation.
+ */
+public class MethodHelper {
+    private static final boolean logAccessErrors
+            = AccessController.doPrivileged((PrivilegedAction<Boolean>) ()
+                    -> Boolean.getBoolean("sun.reflect.debugModuleAccessChecks"));
+
+    private static final Module trampolineModule = MethodUtil.getTrampolineModule();
+
+    public static Object invoke(Method m, Object obj, Object[] params)
+            throws InvocationTargetException, IllegalAccessException {
+
+        // Check that the class in question is in a package that is open to
+        // this module (or exported unconditionally). If so, then we will open
+        // the containing package to the unnamed trampoline module. If not,
+        // we will throw an IllegalAccessException in order to generate a
+        // clearer error message.
+        final Class<?> clazz = m.getDeclaringClass();
+        final String packageName = clazz.getPackage().getName();
+        final Module module = clazz.getModule();
+        final Module thisModule = MethodHelper.class.getModule();
+        try {
+            // Verify that the module being called either exports the package
+            // in question unconditionally or opens the package in question to
+            // this module.
+            if (!module.isExported(packageName)) {
+                if (!module.isOpen(packageName, thisModule)) {
+                    throw new IllegalAccessException(
+                            "module " + thisModule.getName()
+                            + " cannot access class " + clazz.getName()
+                            + " (in module " + module.getName()
+                            + ") because module " + module.getName()
+                            + " does not open " + packageName
+                            + " to " + thisModule.getName());
+                }
+                if (!module.isOpen(packageName, trampolineModule)) {
+                    ReflectUtil.checkPackageAccess(packageName);
+                    module.addOpens(packageName, trampolineModule);
+                }
+            }
+        } catch (IllegalAccessException ex) {
+            if (logAccessErrors) {
+                ex.printStackTrace(System.err);
+            }
+            throw ex;
+        }
+
+        return MethodUtil.invoke(m, obj, params);
+    }
+
+    // Utility class, do not instantiate
+    private MethodHelper() {
+    }
+
+}
--- a/modules/javafx.fxml/src/main/java/com/sun/javafx/fxml/ModuleHelper.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.fxml/src/main/java/com/sun/javafx/fxml/ModuleHelper.java	Thu May 04 08:45:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -30,7 +30,6 @@
 import java.lang.reflect.Method;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
-import sun.reflect.misc.MethodUtil;
 
 public class ModuleHelper {
     private static final Method getModuleMethod;
@@ -96,7 +95,7 @@
             System.out.println("m = " + m);
         }
         if (methodModule != thisModule) {
-            return MethodUtil.invoke(m, obj, params);
+            return MethodHelper.invoke(m, obj, params);
         } else {
             return m.invoke(obj, params);
         }
--- a/modules/javafx.fxml/src/main/java/javafx/fxml/FXML.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.fxml/src/main/java/javafx/fxml/FXML.java	Thu May 04 08:45:51 2017 -0700
@@ -32,6 +32,8 @@
 
 /**
  * Annotation that tags a field or method as accessible to markup.
+ * <p><b>Deploying an Application as a Module</b></p>
+ * <p>
  * If the object being annotated is in a named module then it must
  * be reflectively accessible to the {@code javafx.fxml} module.
  * Otherwise, the {@link FXMLLoader} will fail with an
@@ -40,12 +42,24 @@
  * An object is reflectively accessible if the module containing that
  * object {@link Module#isOpen(String,Module) opens} the containing package to
  * at least the
- * {@code javafx.fxml} module, either in its {@code module-info.class} or
- * by calling {@link Module#addOpens}.
- * An object is also reflectively accessible if it is declared as a public
- * member, is in a public class, and the module containing that class
- * {@link Module#isExported(String,Module) exports}
- * the containing package to at least the {@code javafx.fxml} module.
+ * {@code javafx.fxml} module.
+ * </p>
+ * <p>
+ * For example, if the object being annotated is in the {@code com.foo}
+ * package in the {@code foo.app} module, the {@code module-info.java} might
+ * look like this:
+ * </p>
+ *
+<pre>{@code module foo.app {
+    opens com.foo to javafx.fxml;
+}}</pre>
+ *
+ * <p>
+ * Alternatively, an object is reflectively accessible if it is declared
+ * as a public member, is in a public class, and the module containing that
+ * class {@link Module#isExported(String) exports} the containing
+ * package unconditionally.
+ * </p>
  *
  * @since JavaFX 2.0
  */
--- a/modules/javafx.fxml/src/main/java/javafx/fxml/FXMLLoader.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.fxml/src/main/java/javafx/fxml/FXMLLoader.java	Thu May 04 08:45:51 2017 -0700
@@ -83,6 +83,7 @@
 import com.sun.javafx.fxml.expression.KeyPath;
 import static com.sun.javafx.FXPermissions.MODIFY_FXML_CLASS_LOADER_PERMISSION;
 import com.sun.javafx.fxml.FXMLLoaderHelper;
+import com.sun.javafx.fxml.MethodHelper;
 import java.net.MalformedURLException;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
@@ -95,6 +96,10 @@
 
 /**
  * Loads an object hierarchy from an XML document.
+ * For more information, see the
+ * <a href="doc-files/introduction_to_fxml.html">Introduction to FXML</a>
+ * document.
+ *
  * @since JavaFX 2.0
  */
 public class FXMLLoader {
@@ -995,7 +1000,7 @@
                 }
 
                 try {
-                    value = MethodUtil.invoke(factoryMethod, null, new Object [] {});
+                    value = MethodHelper.invoke(factoryMethod, null, new Object [] {});
                 } catch (IllegalAccessException exception) {
                     throw constructLoadException(exception);
                 } catch (InvocationTargetException exception) {
@@ -1774,9 +1779,9 @@
         public void invoke(Object... params) {
             try {
                 if (type != SupportedType.PARAMETERLESS) {
-                    MethodUtil.invoke(method, controller, params);
+                    MethodHelper.invoke(method, controller, params);
                 } else {
-                    MethodUtil.invoke(method, controller, new Object[] {});
+                    MethodHelper.invoke(method, controller, new Object[] {});
                 }
             } catch (InvocationTargetException exception) {
                 throw new RuntimeException(exception);
@@ -2583,10 +2588,9 @@
 
                     if (initializeMethod != null) {
                         try {
-                            MethodUtil.invoke(initializeMethod, controller, new Object [] {});
+                            MethodHelper.invoke(initializeMethod, controller, new Object [] {});
                         } catch (IllegalAccessException exception) {
-                            // TODO Throw when Initializable is deprecated/removed
-                            // throw constructLoadException(exception);
+                            throw constructLoadException(exception);
                         } catch (InvocationTargetException exception) {
                             throw constructLoadException(exception);
                         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/javafx.web/src/main/java/com/sun/webkit/MethodHelper.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.webkit;
+
+import com.sun.javafx.reflect.MethodUtil;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import sun.reflect.misc.ReflectUtil;
+
+/**
+ * Utility class to wrap method invocation.
+ */
+public class MethodHelper {
+    private static final boolean logAccessErrors
+            = AccessController.doPrivileged((PrivilegedAction<Boolean>) ()
+                    -> Boolean.getBoolean("sun.reflect.debugModuleAccessChecks"));
+
+    private static final Module trampolineModule = MethodUtil.getTrampolineModule();
+
+    public static Object invoke(Method m, Object obj, Object[] params)
+            throws InvocationTargetException, IllegalAccessException {
+
+        // Check that the class in question is in a package that is open to
+        // this module (or exported unconditionally). If so, then we will open
+        // the containing package to the unnamed trampoline module. If not,
+        // we will throw an IllegalAccessException in order to generate a
+        // clearer error message.
+        final Class<?> clazz = m.getDeclaringClass();
+        final String packageName = clazz.getPackage().getName();
+        final Module module = clazz.getModule();
+        final Module thisModule = MethodHelper.class.getModule();
+        try {
+            // Verify that the module being called either exports the package
+            // in question unconditionally or opens the package in question to
+            // this module.
+            if (!module.isExported(packageName)) {
+                if (!module.isOpen(packageName, thisModule)) {
+                    throw new IllegalAccessException(
+                            "module " + thisModule.getName()
+                            + " cannot access class " + clazz.getName()
+                            + " (in module " + module.getName()
+                            + ") because module " + module.getName()
+                            + " does not open " + packageName
+                            + " to " + thisModule.getName());
+                }
+                if (!module.isOpen(packageName, trampolineModule)) {
+                    ReflectUtil.checkPackageAccess(packageName);
+                    module.addOpens(packageName, trampolineModule);
+                }
+            }
+        } catch (IllegalAccessException ex) {
+            if (logAccessErrors) {
+                ex.printStackTrace(System.err);
+            }
+            throw ex;
+        }
+
+        return MethodUtil.invoke(m, obj, params);
+    }
+
+    // Utility class, do not instantiate
+    private MethodHelper() {
+    }
+
+}
--- a/modules/javafx.web/src/main/java/com/sun/webkit/Utilities.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.web/src/main/java/com/sun/webkit/Utilities.java	Thu May 04 08:45:51 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -33,7 +33,6 @@
 import java.security.PrivilegedExceptionAction;
 import java.util.HashMap;
 import java.util.Map;
-import sun.reflect.misc.MethodUtil;
 
 public abstract class Utilities {
 
@@ -91,7 +90,8 @@
                                                AccessControlContext acc)
     throws Throwable {
         try {
-            return AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> MethodUtil.invoke(method, instance, args), acc);
+            return AccessController.doPrivileged((PrivilegedExceptionAction<Object>)
+                    () -> MethodHelper.invoke(method, instance, args), acc);
         } catch (PrivilegedActionException ex) {
             Throwable cause = ex.getCause();
             if (cause == null)
@@ -103,4 +103,3 @@
         }
     }
 }
-
--- a/modules/javafx.web/src/main/java/javafx/scene/web/WebEngine.java	Mon May 01 00:00:37 2017 -0700
+++ b/modules/javafx.web/src/main/java/javafx/scene/web/WebEngine.java	Thu May 04 08:45:51 2017 -0700
@@ -297,6 +297,36 @@
  * <code><var>receiver</var>["<var>method_name</var>(<var>param_type1</var>,...,<var>param_typeN</var>)"](<var>arg1</var>,...,<var>argN</var>)</code>
  * </pre>
  *
+ * <p>
+ * The Java class and method must both be declared public.
+ * </p>
+ *
+ * <p><b>Deploying an Application as a Module</b></p>
+ * <p>
+ * If any Java class passed to JavaScript is in a named module, then it must
+ * be reflectively accessible to the {@code javafx.web} module.
+ * A class is reflectively accessible if the module
+ * {@link Module#isOpen(String,Module) opens} the containing package to at
+ * least the {@code javafx.web} module.
+ * Otherwise, the method will not be called, and no error or
+ * warning will be produced.
+ * </p>
+ * <p>
+ * For example, if {@code com.foo.MyClass} is in the {@code foo.app} module,
+ * the {@code module-info.java} might
+ * look like this:
+ * </p>
+ *
+<pre>{@code module foo.app {
+    opens com.foo to javafx.web;
+}}</pre>
+ *
+ * <p>
+ * Alternatively, a class is reflectively accessible if the module
+ * {@link Module#isExported(String) exports} the containing package
+ * unconditionally.
+ * </p>
+ *
  * <p><b>Threading</b></p>
  * <p>{@code WebEngine} objects must be created and accessed solely from the
  * JavaFX Application thread. This rule also applies to any DOM and JavaScript
--- a/netbeans/systemTests/nbproject/build-impl.xml	Mon May 01 00:00:37 2017 -0700
+++ b/netbeans/systemTests/nbproject/build-impl.xml	Thu May 04 08:45:51 2017 -0700
@@ -162,6 +162,11 @@
                 <available file="${test.resources.dir}"/>
                 <available file="${test.java2.dir}"/>
                 <available file="${test.mymod.dir}"/>
+                <available file="${test.mymod2.dir}"/>
+                <available file="${test.mymod3.dir}"/>
+                <available file="${test.mymod4.dir}"/>
+                <available file="${test.mymod5.dir}"/>
+                <available file="${test.mymod6.dir}"/>
             </or>
         </condition>
         <condition property="have.sources">
@@ -255,6 +260,11 @@
         <fail unless="test.resources.dir">Must set test.resources.dir</fail>
         <fail unless="test.java2.dir">Must set test.java2.dir</fail>
         <fail unless="test.mymod.dir">Must set test.mymod.dir</fail>
+        <fail unless="test.mymod2.dir">Must set test.mymod2.dir</fail>
+        <fail unless="test.mymod3.dir">Must set test.mymod3.dir</fail>
+        <fail unless="test.mymod4.dir">Must set test.mymod4.dir</fail>
+        <fail unless="test.mymod5.dir">Must set test.mymod5.dir</fail>
+        <fail unless="test.mymod6.dir">Must set test.mymod6.dir</fail>
         <fail unless="build.dir">Must set build.dir</fail>
         <fail unless="dist.dir">Must set dist.dir</fail>
         <fail unless="build.classes.dir">Must set build.classes.dir</fail>
@@ -448,6 +458,21 @@
                         <fileset dir="${test.mymod.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
                             <filename name="@{testincludes}"/>
                         </fileset>
+                        <fileset dir="${test.mymod2.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
+                            <filename name="@{testincludes}"/>
+                        </fileset>
+                        <fileset dir="${test.mymod3.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
+                            <filename name="@{testincludes}"/>
+                        </fileset>
+                        <fileset dir="${test.mymod4.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
+                            <filename name="@{testincludes}"/>
+                        </fileset>
+                        <fileset dir="${test.mymod5.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
+                            <filename name="@{testincludes}"/>
+                        </fileset>
+                        <fileset dir="${test.mymod6.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
+                            <filename name="@{testincludes}"/>
+                        </fileset>
                         <fileset dir="${build.test.classes.dir}" excludes="@{excludes},${excludes},${test.binaryexcludes}" includes="${test.binaryincludes}">
                             <filename name="${test.binarytestincludes}"/>
                         </fileset>
@@ -489,6 +514,21 @@
                     <fileset dir="${test.mymod.dir}" excludes="@{excludes},**/*.xml,${excludes}" includes="@{includes}">
                         <filename name="@{testincludes}"/>
                     </fileset>
+                    <fileset dir="${test.mymod2.dir}" excludes="@{excludes},**/*.xml,${excludes}" includes="@{includes}">
+                        <filename name="@{testincludes}"/>
+                    </fileset>
+                    <fileset dir="${test.mymod3.dir}" excludes="@{excludes},**/*.xml,${excludes}" includes="@{includes}">
+                        <filename name="@{testincludes}"/>
+                    </fileset>
+                    <fileset dir="${test.mymod4.dir}" excludes="@{excludes},**/*.xml,${excludes}" includes="@{includes}">
+                        <filename name="@{testincludes}"/>
+                    </fileset>
+                    <fileset dir="${test.mymod5.dir}" excludes="@{excludes},**/*.xml,${excludes}" includes="@{includes}">
+                        <filename name="@{testincludes}"/>
+                    </fileset>
+                    <fileset dir="${test.mymod6.dir}" excludes="@{excludes},**/*.xml,${excludes}" includes="@{includes}">
+                        <filename name="@{testincludes}"/>
+                    </fileset>
                 </union>
                 <taskdef classname="org.testng.TestNGAntTask" classpath="${run.test.classpath}" name="testng"/>
                 <testng classfilesetref="test.set" failureProperty="tests.failed" jvm="${platform.java}" listeners="org.testng.reporters.VerboseReporter" methods="${testng.methods.arg}" mode="${testng.mode}" outputdir="${build.test.results.dir}" suitename="systemTests" testname="TestNG tests" workingDir="${work.dir}">
@@ -610,6 +650,21 @@
                         <fileset dir="${test.mymod.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
                             <filename name="@{testincludes}"/>
                         </fileset>
+                        <fileset dir="${test.mymod2.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
+                            <filename name="@{testincludes}"/>
+                        </fileset>
+                        <fileset dir="${test.mymod3.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
+                            <filename name="@{testincludes}"/>
+                        </fileset>
+                        <fileset dir="${test.mymod4.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
+                            <filename name="@{testincludes}"/>
+                        </fileset>
+                        <fileset dir="${test.mymod5.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
+                            <filename name="@{testincludes}"/>
+                        </fileset>
+                        <fileset dir="${test.mymod6.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
+                            <filename name="@{testincludes}"/>
+                        </fileset>
                         <fileset dir="${build.test.classes.dir}" excludes="@{excludes},${excludes},${test.binaryexcludes}" includes="${test.binaryincludes}">
                             <filename name="${test.binarytestincludes}"/>
                         </fileset>
@@ -1357,15 +1412,20 @@
         <!-- You can override this target in the ../build.xml file. -->
     </target>
     <target if="do.depend.true" name="-compile-test-depend">
-        <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.java.dir}:${test.resources.dir}:${test.java2.dir}:${test.mymod.dir}"/>
+        <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.java.dir}:${test.resources.dir}:${test.java2.dir}:${test.mymod.dir}:${test.mymod2.dir}:${test.mymod3.dir}:${test.mymod4.dir}:${test.mymod5.dir}:${test.mymod6.dir}"/>
     </target>
     <target depends="init,deps-jar,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
-        <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" processorpath="${javac.test.processorpath}" srcdir="${test.java.dir}:${test.resources.dir}:${test.java2.dir}:${test.mymod.dir}"/>
+        <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" processorpath="${javac.test.processorpath}" srcdir="${test.java.dir}:${test.resources.dir}:${test.java2.dir}:${test.mymod.dir}:${test.mymod2.dir}:${test.mymod3.dir}:${test.mymod4.dir}:${test.mymod5.dir}:${test.mymod6.dir}"/>
         <copy todir="${build.test.classes.dir}">
             <fileset dir="${test.java.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
             <fileset dir="${test.resources.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
             <fileset dir="${test.java2.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
             <fileset dir="${test.mymod.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+            <fileset dir="${test.mymod2.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+            <fileset dir="${test.mymod3.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+            <fileset dir="${test.mymod4.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+            <fileset dir="${test.mymod5.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+            <fileset dir="${test.mymod6.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
         </copy>
     </target>
     <target name="-post-compile-test">
@@ -1380,12 +1440,17 @@
     <target depends="init,deps-jar,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
         <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
         <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
-        <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" processorpath="${javac.test.processorpath}" sourcepath="${test.java.dir}:${test.resources.dir}:${test.java2.dir}:${test.mymod.dir}" srcdir="${test.java.dir}:${test.resources.dir}:${test.java2.dir}:${test.mymod.dir}"/>
+        <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" processorpath="${javac.test.processorpath}" sourcepath="${test.java.dir}:${test.resources.dir}:${test.java2.dir}:${test.mymod.dir}:${test.mymod2.dir}:${test.mymod3.dir}:${test.mymod4.dir}:${test.mymod5.dir}:${test.mymod6.dir}" srcdir="${test.java.dir}:${test.resources.dir}:${test.java2.dir}:${test.mymod.dir}:${test.mymod2.dir}:${test.mymod3.dir}:${test.mymod4.dir}:${test.mymod5.dir}:${test.mymod6.dir}"/>
         <copy todir="${build.test.classes.dir}">
             <fileset dir="${test.java.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
             <fileset dir="${test.resources.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
             <fileset dir="${test.java2.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
             <fileset dir="${test.mymod.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+            <fileset dir="${test.mymod2.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+            <fileset dir="${test.mymod3.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+            <fileset dir="${test.mymod4.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+            <fileset dir="${test.mymod5.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
+            <fileset dir="${test.mymod6.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
         </copy>
     </target>
     <target name="-post-compile-test-single">
--- a/netbeans/systemTests/nbproject/genfiles.properties	Mon May 01 00:00:37 2017 -0700
+++ b/netbeans/systemTests/nbproject/genfiles.properties	Thu May 04 08:45:51 2017 -0700
@@ -1,8 +1,8 @@
-build.xml.data.CRC32=793bdab3
+build.xml.data.CRC32=ab8120e0
 build.xml.script.CRC32=78341dd5
 build.xml.stylesheet.CRC32=8064a381@1.74.2.48
 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
 # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
-nbproject/build-impl.xml.data.CRC32=793bdab3
-nbproject/build-impl.xml.script.CRC32=9587cd26
+nbproject/build-impl.xml.data.CRC32=ab8120e0
+nbproject/build-impl.xml.script.CRC32=3df47464
 nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.74.2.48
--- a/netbeans/systemTests/nbproject/project.properties	Mon May 01 00:00:37 2017 -0700
+++ b/netbeans/systemTests/nbproject/project.properties	Thu May 04 08:45:51 2017 -0700
@@ -30,6 +30,11 @@
 file.reference.classes-test=../../modules/javafx.base/build/classes/test/
 file.reference.classes-test-1=../../modules/javafx.graphics/build/classes/test/
 file.reference.java-mymod=../../tests/system/src/testapp2/java/mymod
+file.reference.java-mymod-1=../../tests/system/src/testapp3/java/mymod
+file.reference.java-mymod-2=../../tests/system/src/testapp4/java/mymod
+file.reference.java-mymod-3=../../tests/system/src/testapp5/java/mymod
+file.reference.java-mymod-4=../../tests/system/src/testapp6/java/mymod
+file.reference.resources-mymod=../../tests/system/src/testapp6/resources/mymod
 file.reference.test-java=../../tests/system/src/test/java
 file.reference.test-resources=../../tests/system/src/test/resources
 file.reference.testapp1-java=../../tests/system/src/testapp1/java
@@ -104,4 +109,9 @@
 test.java.dir=${file.reference.test-java}
 test.java2.dir=${file.reference.testapp1-java}
 test.mymod.dir=${file.reference.java-mymod}
+test.mymod2.dir=${file.reference.java-mymod-1}
+test.mymod3.dir=${file.reference.java-mymod-2}
+test.mymod4.dir=${file.reference.java-mymod-3}
+test.mymod5.dir=${file.reference.java-mymod-4}
+test.mymod6.dir=${file.reference.resources-mymod}
 test.resources.dir=${file.reference.test-resources}
--- a/netbeans/systemTests/nbproject/project.xml	Mon May 01 00:00:37 2017 -0700
+++ b/netbeans/systemTests/nbproject/project.xml	Thu May 04 08:45:51 2017 -0700
@@ -11,6 +11,11 @@
                 <root id="test.resources.dir"/>
                 <root id="test.java2.dir"/>
                 <root id="test.mymod.dir"/>
+                <root id="test.mymod2.dir"/>
+                <root id="test.mymod3.dir"/>
+                <root id="test.mymod4.dir"/>
+                <root id="test.mymod5.dir"/>
+                <root id="test.mymod6.dir"/>
             </test-roots>
         </data>
         <references xmlns="http://www.netbeans.org/ns/ant-project-references/1">
--- a/tests/system/src/test/java/test/launchertest/ModuleLauncherTest.java	Mon May 01 00:00:37 2017 -0700
+++ b/tests/system/src/test/java/test/launchertest/ModuleLauncherTest.java	Thu May 04 08:45:51 2017 -0700
@@ -37,11 +37,16 @@
  */
 public class ModuleLauncherTest {
 
-    private final String modulePath = System.getProperty("launchertest.testapp2.module.path");
-    private final String moduleName = "mymod";
+    private static final String modulePath2 = System.getProperty("launchertest.testapp2.module.path");
+    private static final String modulePath3 = System.getProperty("launchertest.testapp3.module.path");
+    private static final String modulePath4 = System.getProperty("launchertest.testapp4.module.path");
+    private static final String modulePath5 = System.getProperty("launchertest.testapp5.module.path");
+    private static final String modulePath6 = System.getProperty("launchertest.testapp6.module.path");
+    private static final String moduleName = "mymod";
+
     private final int testExitCode = ERROR_NONE;
 
-    private void doTestLaunchModule(String testAppName) throws Exception {
+    private void doTestLaunchModule(String modulePath, String testAppName) throws Exception {
         assertNotNull(testAppName);
         String mpArg = "--module-path=" + modulePath;
         String moduleAppName = "--module=" + moduleName + "/" + testAppName;
@@ -80,6 +85,10 @@
                 throw new AssertionFailedError(testAppName
                         + ": Toolkit is running but should not be");
 
+            case ERROR_ASSERTION_FAILURE:
+                throw new AssertionFailedError(testAppName
+                + ": Assertion failure in test application");
+
             case ERROR_UNEXPECTED_EXCEPTION:
                 throw new AssertionFailedError(testAppName
                 + ": unexpected exception");
@@ -93,17 +102,167 @@
 
     @Test (timeout=15000)
     public void testLaunchModule() throws Exception {
-        doTestLaunchModule("testapp.TestApp");
+        doTestLaunchModule(modulePath2, "testapp.TestApp");
     }
 
     @Test (timeout=15000)
     public void testLaunchModuleNoMain() throws Exception {
-        doTestLaunchModule("testapp.TestAppNoMain");
+        doTestLaunchModule(modulePath2, "testapp.TestAppNoMain");
     }
 
     @Test (timeout=15000)
     public void testLaunchModuleNotApplication() throws Exception {
-        doTestLaunchModule("testapp.TestNotApplication");
+        doTestLaunchModule(modulePath2, "testapp.TestNotApplication");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleTableViewUnexported() throws Exception {
+        doTestLaunchModule(modulePath3, "myapp3.AppTableViewUnexported");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleTableViewExported() throws Exception {
+        doTestLaunchModule(modulePath3, "myapp3.AppTableViewExported");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleTableViewQualExported() throws Exception {
+        doTestLaunchModule(modulePath3, "myapp3.AppTableViewQualExported");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleTableViewOpened() throws Exception {
+        doTestLaunchModule(modulePath3, "myapp3.AppTableViewOpened");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleTableViewQualOpened() throws Exception {
+        doTestLaunchModule(modulePath3, "myapp3.AppTableViewQualOpened");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleTreeTableViewUnexported() throws Exception {
+        doTestLaunchModule(modulePath3, "myapp3.AppTreeTableViewUnexported");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleTreeTableViewExported() throws Exception {
+        doTestLaunchModule(modulePath3, "myapp3.AppTreeTableViewExported");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleTreeTableViewQualExported() throws Exception {
+        doTestLaunchModule(modulePath3, "myapp3.AppTreeTableViewQualExported");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleTreeTableViewOpened() throws Exception {
+        doTestLaunchModule(modulePath3, "myapp3.AppTreeTableViewOpened");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleTreeTableViewQualOpened() throws Exception {
+        doTestLaunchModule(modulePath3, "myapp3.AppTreeTableViewQualOpened");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleBeansUnexported() throws Exception {
+        doTestLaunchModule(modulePath4, "myapp4.AppBeansUnexported");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleBeansExported() throws Exception {
+        doTestLaunchModule(modulePath4, "myapp4.AppBeansExported");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleBeansQualExported() throws Exception {
+        doTestLaunchModule(modulePath4, "myapp4.AppBeansQualExported");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleBeansOpened() throws Exception {
+        doTestLaunchModule(modulePath4, "myapp4.AppBeansOpened");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleBeansQualOpened() throws Exception {
+        doTestLaunchModule(modulePath4, "myapp4.AppBeansQualOpened");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleBindingsUnexported() throws Exception {
+        doTestLaunchModule(modulePath4, "myapp4.AppBindingsUnexported");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleBindingsExported() throws Exception {
+        doTestLaunchModule(modulePath4, "myapp4.AppBindingsExported");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleBindingsQualExported() throws Exception {
+        doTestLaunchModule(modulePath4, "myapp4.AppBindingsQualExported");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleBindingsOpened() throws Exception {
+        doTestLaunchModule(modulePath4, "myapp4.AppBindingsOpened");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleBindingsQualOpened() throws Exception {
+        doTestLaunchModule(modulePath4, "myapp4.AppBindingsQualOpened");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleJSCallbackUnexported() throws Exception {
+        doTestLaunchModule(modulePath5, "myapp5.AppJSCallbackUnexported");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleJSCallbackExported() throws Exception {
+        doTestLaunchModule(modulePath5, "myapp5.AppJSCallbackExported");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleJSCallbackQualExported() throws Exception {
+        doTestLaunchModule(modulePath5, "myapp5.AppJSCallbackQualExported");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleJSCallbackOpened() throws Exception {
+        doTestLaunchModule(modulePath5, "myapp5.AppJSCallbackOpened");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleJSCallbackQualOpened() throws Exception {
+        doTestLaunchModule(modulePath5, "myapp5.AppJSCallbackQualOpened");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleFXMLUnexported() throws Exception {
+        doTestLaunchModule(modulePath6, "myapp6.AppFXMLUnexported");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleFXMLExported() throws Exception {
+        doTestLaunchModule(modulePath6, "myapp6.AppFXMLExported");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleFXMLQualExported() throws Exception {
+        doTestLaunchModule(modulePath6, "myapp6.AppFXMLQualExported");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleFXMLOpened() throws Exception {
+        doTestLaunchModule(modulePath6, "myapp6.AppFXMLOpened");
+    }
+
+    @Test (timeout=15000)
+    public void testModuleFXMLQualOpened() throws Exception {
+        doTestLaunchModule(modulePath6, "myapp6.AppFXMLQualOpened");
     }
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp3/java/mymod/module-info.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module mymod {
+    requires javafx.controls;
+    requires java.logging;
+
+    exports myapp3;
+
+    // myapp3.pkg1 is deliberately not listed
+    exports myapp3.pkg2;
+    exports myapp3.pkg3 to javafx.base;
+    opens myapp3.pkg4;
+    opens myapp3.pkg5 to javafx.base;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp3/java/mymod/myapp3/AppTableViewExported.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp3;
+
+import java.util.Locale;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import javafx.animation.KeyFrame;
+import javafx.animation.Timeline;
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.scene.layout.StackPane;
+import javafx.stage.Stage;
+import javafx.util.Duration;
+import myapp3.pkg2.MyData;
+
+import static myapp3.Constants.*;
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppTableViewExported extends Application {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            System.err.println("ERROR: caught unexpected exception: " + t);
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    private Logger logger;
+    private Handler logHandler;
+
+    private void initLogger() {
+        Locale.setDefault(Locale.US);
+
+        // Initialize Logger
+        logHandler = new Handler() {
+            @Override
+            public void publish(LogRecord record) {
+                final Throwable t = record.getThrown();
+                // detect any Throwable:
+                if (t != null) {
+                    System.err.println("ERROR: unexpected exception was logged: " + record.getMessage());
+                    t.printStackTrace();
+                    System.exit(ERROR_UNEXPECTED_EXCEPTION);
+                }
+            }
+
+            @Override
+            public void flush() {
+            }
+
+            @Override
+            public void close() {
+            }
+        };
+
+        logger = Logger.getLogger("javafx.scene.control");
+        logger.addHandler(logHandler);
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        initLogger();
+
+        try {
+            StackPane root = new StackPane();
+            Scene scene = new Scene(root);
+            TableView<MyData> tableView = new TableView<>();
+
+            // Name column
+            TableColumn<MyData, String> nameCol = new TableColumn<>();
+            nameCol.setText("Name");
+            nameCol.setCellValueFactory(new PropertyValueFactory<>("name"));
+
+            // Value column
+            TableColumn<MyData, Integer> valueCol = new TableColumn<>();
+            valueCol.setText("Value");
+            valueCol.setCellValueFactory(new PropertyValueFactory<>("value"));
+
+            tableView.getColumns().addAll(nameCol, valueCol);
+
+            tableView.getItems().add(new MyData("Row A", 1));
+            tableView.getItems().add(new MyData("Row B", 2));
+            tableView.getItems().add(new MyData("Row C", 3));
+
+            root.getChildren().add(tableView);
+
+            stage.setScene(scene);
+            stage.show();
+
+            // Hide the stage after the specified amount of time
+            KeyFrame kf = new KeyFrame(Duration.millis(SHOWTIME), e -> stage.hide());
+            Timeline timeline = new Timeline(kf);
+            timeline.play();
+        } catch (Error | Exception ex) {
+            System.err.println("ERROR: caught unexpected exception: " + ex);
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    @Override public void stop() {
+        System.exit(ERROR_NONE);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp3/java/mymod/myapp3/AppTableViewOpened.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp3;
+
+import java.util.Locale;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import javafx.animation.KeyFrame;
+import javafx.animation.Timeline;
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.scene.layout.StackPane;
+import javafx.stage.Stage;
+import javafx.util.Duration;
+import myapp3.pkg4.MyData;
+
+import static myapp3.Constants.*;
+
+// This logic is copied from AppTableViewExported.
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppTableViewOpened extends Application {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            System.err.println("ERROR: caught unexpected exception: " + t);
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    private Logger logger;
+    private Handler logHandler;
+
+    private void initLogger() {
+        Locale.setDefault(Locale.US);
+
+        // Initialize Logger
+        logHandler = new Handler() {
+            @Override
+            public void publish(LogRecord record) {
+                final Throwable t = record.getThrown();
+                // detect any Throwable:
+                if (t != null) {
+                    System.err.println("ERROR: unexpected exception was logged: " + record.getMessage());
+                    t.printStackTrace();
+                    System.exit(ERROR_UNEXPECTED_EXCEPTION);
+                }
+            }
+
+            @Override
+            public void flush() {
+            }
+
+            @Override
+            public void close() {
+            }
+        };
+
+        logger = Logger.getLogger("javafx.scene.control");
+        logger.addHandler(logHandler);
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        initLogger();
+
+        try {
+            StackPane root = new StackPane();
+            Scene scene = new Scene(root);
+            TableView<MyData> tableView = new TableView<>();
+
+            // Name column
+            TableColumn<MyData, String> nameCol = new TableColumn<>();
+            nameCol.setText("Name");
+            nameCol.setCellValueFactory(new PropertyValueFactory<>("name"));
+
+            // Value column
+            TableColumn<MyData, Integer> valueCol = new TableColumn<>();
+            valueCol.setText("Value");
+            valueCol.setCellValueFactory(new PropertyValueFactory<>("value"));
+
+            tableView.getColumns().addAll(nameCol, valueCol);
+
+            tableView.getItems().add(new MyData("Row A", 1));
+            tableView.getItems().add(new MyData("Row B", 2));
+            tableView.getItems().add(new MyData("Row C", 3));
+
+            root.getChildren().add(tableView);
+
+            stage.setScene(scene);
+            stage.show();
+
+            // Hide the stage after the specified amount of time
+            KeyFrame kf = new KeyFrame(Duration.millis(SHOWTIME), e -> stage.hide());
+            Timeline timeline = new Timeline(kf);
+            timeline.play();
+        } catch (Error | Exception ex) {
+            System.err.println("ERROR: caught unexpected exception: " + ex);
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    @Override public void stop() {
+        System.exit(ERROR_NONE);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp3/java/mymod/myapp3/AppTableViewQualExported.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp3;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import javafx.animation.KeyFrame;
+import javafx.animation.Timeline;
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.scene.layout.StackPane;
+import javafx.stage.Stage;
+import javafx.util.Duration;
+import myapp3.pkg3.MyData;
+
+import static myapp3.Constants.*;
+
+// This logic is copied from AppTableViewUnexported.
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppTableViewQualExported extends Application {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            System.err.println("ERROR: caught unexpected exception: " + t);
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    private Logger logger;
+    private Handler logHandler;
+    private final List<Throwable> errs = new ArrayList<>();
+
+    private void initLogger() {
+        Locale.setDefault(Locale.US);
+
+        // Initialize Logger
+        logHandler = new Handler() {
+            @Override
+            public void publish(LogRecord record) {
+                final Throwable t = record.getThrown();
+                // detect any Throwable:
+                if (t != null) {
+                    errs.add(t);
+                }
+            }
+
+            @Override
+            public void flush() {
+            }
+
+            @Override
+            public void close() {
+            }
+        };
+
+        logger = Logger.getLogger("javafx.scene.control");
+        logger.addHandler(logHandler);
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        initLogger();
+
+        try {
+            StackPane root = new StackPane();
+            Scene scene = new Scene(root);
+            TableView<MyData> tableView = new TableView<>();
+
+            // Name column
+            TableColumn<MyData, String> nameCol = new TableColumn<>();
+            nameCol.setText("Name");
+            nameCol.setCellValueFactory(new PropertyValueFactory<>("name"));
+
+            // Value column
+            TableColumn<MyData, Integer> valueCol = new TableColumn<>();
+            valueCol.setText("Value");
+            valueCol.setCellValueFactory(new PropertyValueFactory<>("value"));
+
+            tableView.getColumns().addAll(nameCol, valueCol);
+
+            tableView.getItems().add(new MyData("Row A", 1));
+            tableView.getItems().add(new MyData("Row B", 2));
+            tableView.getItems().add(new MyData("Row C", 3));
+
+            root.getChildren().add(tableView);
+
+            stage.setScene(scene);
+            System.err.println("The following two WARNING messages are expected:");
+            stage.show();
+
+            // Hide the stage after the specified amount of time
+            KeyFrame kf = new KeyFrame(Duration.millis(SHOWTIME), e -> stage.hide());
+            Timeline timeline = new Timeline(kf);
+            timeline.play();
+        } catch (Error | Exception ex) {
+            System.err.println("ERROR: caught unexpected exception: " + ex);
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    private void fail(String message, Throwable t) {
+        if (message != null) {
+            System.err.print(message + ": ");
+        }
+        if (t != null) {
+            System.err.println(t);
+            t.printStackTrace();
+        } else {
+            System.err.println();
+        }
+        System.exit(ERROR_ASSERTION_FAILURE);
+    }
+
+    @Override public void stop() {
+        final int expectedExceptions = 2; // One for each PropertyValueFactory
+
+        if (errs.isEmpty()) {
+            fail("ERROR: did not get the expected exception", null);
+        }
+
+        if (expectedExceptions != errs.size()) {
+            fail("ERROR: expected " + expectedExceptions + " exceptions, got: " + errs.size(), null);
+        }
+
+        for (Throwable t : errs) {
+            if (! (t instanceof RuntimeException)) {
+                fail("ERROR: unexpected exception: ", t);
+            }
+
+            RuntimeException ex = (RuntimeException) t;
+            Throwable cause = ex.getCause();
+            if (! (cause instanceof IllegalAccessException)) {
+                fail("ERROR: unexpected cause: ", ex);
+            }
+
+            String message = cause.getMessage();
+            if (message == null) {
+                fail("ERROR: detail message of cause is null", ex);
+            }
+
+            boolean badMessage = false;
+            if (!message.contains(" cannot access class ")) badMessage = true;
+            if (!message.contains(" does not open ")) badMessage = true;
+            if (!message.endsWith(" to javafx.base")) badMessage = true;
+            if (badMessage) {
+                fail("ERROR: detail message not formatted correctly", ex);
+            }
+        }
+
+        // We got the expected exception, exit normally
+        System.exit(ERROR_NONE);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp3/java/mymod/myapp3/AppTableViewQualOpened.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp3;
+
+import java.util.Locale;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import javafx.animation.KeyFrame;
+import javafx.animation.Timeline;
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.scene.layout.StackPane;
+import javafx.stage.Stage;
+import javafx.util.Duration;
+import myapp3.pkg5.MyData;
+
+import static myapp3.Constants.*;
+
+// This logic is copied from AppTableViewExported.
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppTableViewQualOpened extends Application {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            System.err.println("ERROR: caught unexpected exception: " + t);
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    private Logger logger;
+    private Handler logHandler;
+
+    private void initLogger() {
+        Locale.setDefault(Locale.US);
+
+        // Initialize Logger
+        logHandler = new Handler() {
+            @Override
+            public void publish(LogRecord record) {
+                final Throwable t = record.getThrown();
+                // detect any Throwable:
+                if (t != null) {
+                    System.err.println("ERROR: unexpected exception was logged: " + record.getMessage());
+                    t.printStackTrace();
+                    System.exit(ERROR_UNEXPECTED_EXCEPTION);
+                }
+            }
+
+            @Override
+            public void flush() {
+            }
+
+            @Override
+            public void close() {
+            }
+        };
+
+        logger = Logger.getLogger("javafx.scene.control");
+        logger.addHandler(logHandler);
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        initLogger();
+
+        try {
+            StackPane root = new StackPane();
+            Scene scene = new Scene(root);
+            TableView<MyData> tableView = new TableView<>();
+
+            // Name column
+            TableColumn<MyData, String> nameCol = new TableColumn<>();
+            nameCol.setText("Name");
+            nameCol.setCellValueFactory(new PropertyValueFactory<>("name"));
+
+            // Value column
+            TableColumn<MyData, Integer> valueCol = new TableColumn<>();
+            valueCol.setText("Value");
+            valueCol.setCellValueFactory(new PropertyValueFactory<>("value"));
+
+            tableView.getColumns().addAll(nameCol, valueCol);
+
+            tableView.getItems().add(new MyData("Row A", 1));
+            tableView.getItems().add(new MyData("Row B", 2));
+            tableView.getItems().add(new MyData("Row C", 3));
+
+            root.getChildren().add(tableView);
+
+            stage.setScene(scene);
+            stage.show();
+
+            // Hide the stage after the specified amount of time
+            KeyFrame kf = new KeyFrame(Duration.millis(SHOWTIME), e -> stage.hide());
+            Timeline timeline = new Timeline(kf);
+            timeline.play();
+        } catch (Error | Exception ex) {
+            System.err.println("ERROR: caught unexpected exception: " + ex);
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    @Override public void stop() {
+        System.exit(ERROR_NONE);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp3/java/mymod/myapp3/AppTableViewUnexported.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp3;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import javafx.animation.KeyFrame;
+import javafx.animation.Timeline;
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.scene.layout.StackPane;
+import javafx.stage.Stage;
+import javafx.util.Duration;
+import myapp3.pkg1.MyData;
+
+import static myapp3.Constants.*;
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppTableViewUnexported extends Application {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            System.err.println("ERROR: caught unexpected exception: " + t);
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    private Logger logger;
+    private Handler logHandler;
+    private final List<Throwable> errs = new ArrayList<>();
+
+    private void initLogger() {
+        Locale.setDefault(Locale.US);
+
+        // Initialize Logger
+        logHandler = new Handler() {
+            @Override
+            public void publish(LogRecord record) {
+                final Throwable t = record.getThrown();
+                // detect any Throwable:
+                if (t != null) {
+                    errs.add(t);
+                }
+            }
+
+            @Override
+            public void flush() {
+            }
+
+            @Override
+            public void close() {
+            }
+        };
+
+        logger = Logger.getLogger("javafx.scene.control");
+        logger.addHandler(logHandler);
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        initLogger();
+
+        try {
+            StackPane root = new StackPane();
+            Scene scene = new Scene(root);
+            TableView<MyData> tableView = new TableView<>();
+
+            // Name column
+            TableColumn<MyData, String> nameCol = new TableColumn<>();
+            nameCol.setText("Name");
+            nameCol.setCellValueFactory(new PropertyValueFactory<>("name"));
+
+            // Value column
+            TableColumn<MyData, Integer> valueCol = new TableColumn<>();
+            valueCol.setText("Value");
+            valueCol.setCellValueFactory(new PropertyValueFactory<>("value"));
+
+            tableView.getColumns().addAll(nameCol, valueCol);
+
+            tableView.getItems().add(new MyData("Row A", 1));
+            tableView.getItems().add(new MyData("Row B", 2));
+            tableView.getItems().add(new MyData("Row C", 3));
+
+            root.getChildren().add(tableView);
+
+            stage.setScene(scene);
+            System.err.println("The following two WARNING messages are expected:");
+            stage.show();
+
+            // Hide the stage after the specified amount of time
+            KeyFrame kf = new KeyFrame(Duration.millis(SHOWTIME), e -> stage.hide());
+            Timeline timeline = new Timeline(kf);
+            timeline.play();
+        } catch (Error | Exception ex) {
+            System.err.println("ERROR: caught unexpected exception: " + ex);
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    private void fail(String message, Throwable t) {
+        if (message != null) {
+            System.err.print(message + ": ");
+        }
+        if (t != null) {
+            System.err.println(t);
+            t.printStackTrace();
+        } else {
+            System.err.println();
+        }
+        System.exit(ERROR_ASSERTION_FAILURE);
+    }
+
+    @Override public void stop() {
+        final int expectedExceptions = 2; // One for each PropertyValueFactory
+
+        if (errs.isEmpty()) {
+            fail("ERROR: did not get the expected exception", null);
+        }
+
+        if (expectedExceptions != errs.size()) {
+            fail("ERROR: expected " + expectedExceptions + " exceptions, got: " + errs.size(), null);
+        }
+
+        for (Throwable t : errs) {
+            if (! (t instanceof RuntimeException)) {
+                fail("ERROR: unexpected exception: ", t);
+            }
+
+            RuntimeException ex = (RuntimeException) t;
+            Throwable cause = ex.getCause();
+            if (! (cause instanceof IllegalAccessException)) {
+                fail("ERROR: unexpected cause: ", ex);
+            }
+
+            String message = cause.getMessage();
+            if (message == null) {
+                fail("ERROR: detail message of cause is null", ex);
+            }
+
+            boolean badMessage = false;
+            if (!message.contains(" cannot access class ")) badMessage = true;
+            if (!message.contains(" does not open ")) badMessage = true;
+            if (!message.endsWith(" to javafx.base")) badMessage = true;
+            if (badMessage) {
+                fail("ERROR: detail message not formatted correctly", ex);
+            }
+        }
+
+        // We got the expected exception, exit normally
+        System.exit(ERROR_NONE);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp3/java/mymod/myapp3/AppTreeTableViewExported.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp3;
+
+import java.util.Locale;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import javafx.animation.KeyFrame;
+import javafx.animation.Timeline;
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.control.TreeItem;
+import javafx.scene.control.TreeTableColumn;
+import javafx.scene.control.TreeTableView;
+import javafx.scene.control.cell.TreeItemPropertyValueFactory;
+import javafx.scene.layout.StackPane;
+import javafx.stage.Stage;
+import javafx.util.Duration;
+import myapp3.pkg2.MyData;
+
+import static myapp3.Constants.*;
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppTreeTableViewExported extends Application {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            System.err.println("ERROR: caught unexpected exception: " + t);
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    private Logger logger;
+    private Handler logHandler;
+
+    private void initLogger() {
+        Locale.setDefault(Locale.US);
+
+        // Initialize Logger
+        logHandler = new Handler() {
+            @Override
+            public void publish(LogRecord record) {
+                final Throwable t = record.getThrown();
+                // detect any Throwable:
+                if (t != null) {
+                    System.err.println("ERROR: unexpected exception was logged: " + record.getMessage());
+                    t.printStackTrace();
+                    System.exit(ERROR_UNEXPECTED_EXCEPTION);
+                }
+            }
+
+            @Override
+            public void flush() {
+            }
+
+            @Override
+            public void close() {
+            }
+        };
+
+        logger = Logger.getLogger("javafx.scene.control");
+        logger.addHandler(logHandler);
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        initLogger();
+
+        try {
+            StackPane root = new StackPane();
+            Scene scene = new Scene(root);
+            TreeTableView<MyData> treeTableView = new TreeTableView<>();
+
+            // Name column
+            TreeTableColumn<MyData, String> nameCol = new TreeTableColumn<>();
+            nameCol.setText("Name");
+            nameCol.setPrefWidth(150);
+            nameCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
+
+            // Value column
+            TreeTableColumn<MyData, Integer> valueCol = new TreeTableColumn<>();
+            valueCol.setText("Value");
+            valueCol.setPrefWidth(100);
+            valueCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("value"));
+
+            treeTableView.getColumns().addAll(nameCol, valueCol);
+
+            TreeItem<MyData> treeRoot = new TreeItem<>(new MyData("Row A", 1));
+            treeRoot.setExpanded(true);
+            treeTableView.setRoot(treeRoot);
+
+            TreeItem<MyData> item1 = new TreeItem<>(new MyData("Row B", 2));
+            TreeItem<MyData> item2 = new TreeItem<>(new MyData("Row C", 3));
+            treeRoot.getChildren().addAll(item1, item2);
+
+            root.getChildren().add(treeTableView);
+
+            stage.setScene(scene);
+            stage.show();
+
+            // Hide the stage after the specified amount of time
+            KeyFrame kf = new KeyFrame(Duration.millis(SHOWTIME), e -> stage.hide());
+            Timeline timeline = new Timeline(kf);
+            timeline.play();
+        } catch (Error | Exception ex) {
+            System.err.println("ERROR: caught unexpected exception: " + ex);
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    @Override public void stop() {
+        System.exit(ERROR_NONE);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp3/java/mymod/myapp3/AppTreeTableViewOpened.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp3;
+
+import java.util.Locale;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import javafx.animation.KeyFrame;
+import javafx.animation.Timeline;
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.control.TreeItem;
+import javafx.scene.control.TreeTableColumn;
+import javafx.scene.control.TreeTableView;
+import javafx.scene.control.cell.TreeItemPropertyValueFactory;
+import javafx.scene.layout.StackPane;
+import javafx.stage.Stage;
+import javafx.util.Duration;
+import myapp3.pkg4.MyData;
+
+import static myapp3.Constants.*;
+
+// This logic is copied from AppTreeTableViewExported.
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppTreeTableViewOpened extends Application {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            System.err.println("ERROR: caught unexpected exception: " + t);
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    private Logger logger;
+    private Handler logHandler;
+
+    private void initLogger() {
+        Locale.setDefault(Locale.US);
+
+        // Initialize Logger
+        logHandler = new Handler() {
+            @Override
+            public void publish(LogRecord record) {
+                final Throwable t = record.getThrown();
+                // detect any Throwable:
+                if (t != null) {
+                    System.err.println("ERROR: unexpected exception was logged: " + record.getMessage());
+                    t.printStackTrace();
+                    System.exit(ERROR_UNEXPECTED_EXCEPTION);
+                }
+            }
+
+            @Override
+            public void flush() {
+            }
+
+            @Override
+            public void close() {
+            }
+        };
+
+        logger = Logger.getLogger("javafx.scene.control");
+        logger.addHandler(logHandler);
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        initLogger();
+
+        try {
+            StackPane root = new StackPane();
+            Scene scene = new Scene(root);
+            TreeTableView<MyData> treeTableView = new TreeTableView<>();
+
+            // Name column
+            TreeTableColumn<MyData, String> nameCol = new TreeTableColumn<>();
+            nameCol.setText("Name");
+            nameCol.setPrefWidth(150);
+            nameCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
+
+            // Value column
+            TreeTableColumn<MyData, Integer> valueCol = new TreeTableColumn<>();
+            valueCol.setText("Value");
+            valueCol.setPrefWidth(100);
+            valueCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("value"));
+
+            treeTableView.getColumns().addAll(nameCol, valueCol);
+
+            TreeItem<MyData> treeRoot = new TreeItem<>(new MyData("Row A", 1));
+            treeRoot.setExpanded(true);
+            treeTableView.setRoot(treeRoot);
+
+            TreeItem<MyData> item1 = new TreeItem<>(new MyData("Row B", 2));
+            TreeItem<MyData> item2 = new TreeItem<>(new MyData("Row C", 3));
+            treeRoot.getChildren().addAll(item1, item2);
+
+            root.getChildren().add(treeTableView);
+
+            stage.setScene(scene);
+            stage.show();
+
+            // Hide the stage after the specified amount of time
+            KeyFrame kf = new KeyFrame(Duration.millis(SHOWTIME), e -> stage.hide());
+            Timeline timeline = new Timeline(kf);
+            timeline.play();
+        } catch (Error | Exception ex) {
+            System.err.println("ERROR: caught unexpected exception: " + ex);
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    @Override public void stop() {
+        System.exit(ERROR_NONE);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp3/java/mymod/myapp3/AppTreeTableViewQualExported.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp3;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import javafx.animation.KeyFrame;
+import javafx.animation.Timeline;
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.control.TreeItem;
+import javafx.scene.control.TreeTableColumn;
+import javafx.scene.control.TreeTableView;
+import javafx.scene.control.cell.TreeItemPropertyValueFactory;
+import javafx.scene.layout.StackPane;
+import javafx.stage.Stage;
+import javafx.util.Duration;
+import myapp3.pkg3.MyData;
+
+import static myapp3.Constants.*;
+
+// This logic is copied from AppTreeTableViewUnexported.
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppTreeTableViewQualExported extends Application {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    private Logger logger;
+    private Handler logHandler;
+    private final List<Throwable> errs = new ArrayList<>();
+
+    private void initLogger() {
+        Locale.setDefault(Locale.US);
+
+        // Initialize Logger
+        logHandler = new Handler() {
+            @Override
+            public void publish(LogRecord record) {
+                final Throwable t = record.getThrown();
+                // detect any Throwable:
+                if (t != null) {
+                    errs.add(t);
+                }
+            }
+
+            @Override
+            public void flush() {
+            }
+
+            @Override
+            public void close() {
+            }
+        };
+
+        logger = Logger.getLogger("javafx.scene.control");
+        logger.addHandler(logHandler);
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        initLogger();
+
+        try {
+            StackPane root = new StackPane();
+            Scene scene = new Scene(root);
+            TreeTableView<MyData> treeTableView = new TreeTableView<>();
+
+            // Name column
+            TreeTableColumn<MyData, String> nameCol = new TreeTableColumn<>();
+            nameCol.setText("Name");
+            nameCol.setPrefWidth(150);
+            nameCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
+
+            // Value column
+            TreeTableColumn<MyData, Integer> valueCol = new TreeTableColumn<>();
+            valueCol.setText("Value");
+            valueCol.setPrefWidth(100);
+            valueCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("value"));
+
+            treeTableView.getColumns().addAll(nameCol, valueCol);
+
+            TreeItem<MyData> treeRoot = new TreeItem<>(new MyData("Row A", 1));
+            treeRoot.setExpanded(true);
+            treeTableView.setRoot(treeRoot);
+
+            TreeItem<MyData> item1 = new TreeItem<>(new MyData("Row B", 2));
+            TreeItem<MyData> item2 = new TreeItem<>(new MyData("Row C", 3));
+            treeRoot.getChildren().addAll(item1, item2);
+
+            root.getChildren().add(treeTableView);
+
+            stage.setScene(scene);
+            System.err.println("The following two WARNING messages are expected:");
+            stage.show();
+
+            // Hide the stage after the specified amount of time
+            KeyFrame kf = new KeyFrame(Duration.millis(SHOWTIME), e -> stage.hide());
+            Timeline timeline = new Timeline(kf);
+            timeline.play();
+        } catch (Error | Exception ex) {
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    private void fail(String message, Throwable t) {
+        if (message != null) {
+            System.err.print(message + ": ");
+        }
+        if (t != null) {
+            System.err.println(t);
+            t.printStackTrace();
+        } else {
+            System.err.println();
+        }
+        System.exit(ERROR_ASSERTION_FAILURE);
+    }
+
+    @Override public void stop() {
+        final int expectedExceptions = 2; // One for each PropertyValueFactory
+
+        if (errs.isEmpty()) {
+            fail("ERROR: did not get the expected exception", null);
+        }
+
+        if (expectedExceptions != errs.size()) {
+            fail("ERROR: expected " + expectedExceptions + " exceptions, got: " + errs.size(), null);
+        }
+
+        for (Throwable t : errs) {
+            if (! (t instanceof RuntimeException)) {
+                fail("ERROR: unexpeted exception: ", t);
+            }
+
+            RuntimeException ex = (RuntimeException) t;
+            Throwable cause = ex.getCause();
+            if (! (cause instanceof IllegalAccessException)) {
+                fail("ERROR: unexpeted cause: ", ex);
+            }
+
+            String message = cause.getMessage();
+            if (message == null) {
+                fail("ERROR: detail message of cause is null", ex);
+            }
+
+            boolean badMessage = false;
+            if (!message.contains(" cannot access class ")) badMessage = true;
+            if (!message.contains(" does not open ")) badMessage = true;
+            if (!message.endsWith(" to javafx.base")) badMessage = true;
+            if (badMessage) {
+                fail("ERROR: detail message not formatted correctly", ex);
+            }
+        }
+
+        // We got the expected exception, exit normally
+        System.exit(ERROR_NONE);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp3/java/mymod/myapp3/AppTreeTableViewQualOpened.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp3;
+
+import java.util.Locale;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import javafx.animation.KeyFrame;
+import javafx.animation.Timeline;
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.control.TreeItem;
+import javafx.scene.control.TreeTableColumn;
+import javafx.scene.control.TreeTableView;
+import javafx.scene.control.cell.TreeItemPropertyValueFactory;
+import javafx.scene.layout.StackPane;
+import javafx.stage.Stage;
+import javafx.util.Duration;
+import myapp3.pkg5.MyData;
+
+import static myapp3.Constants.*;
+
+// This logic is copied from AppTreeTableViewExported.
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppTreeTableViewQualOpened extends Application {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            System.err.println("ERROR: caught unexpected exception: " + t);
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    private Logger logger;
+    private Handler logHandler;
+
+    private void initLogger() {
+        Locale.setDefault(Locale.US);
+
+        // Initialize Logger
+        logHandler = new Handler() {
+            @Override
+            public void publish(LogRecord record) {
+                final Throwable t = record.getThrown();
+                // detect any Throwable:
+                if (t != null) {
+                    System.err.println("ERROR: unexpected exception was logged: " + record.getMessage());
+                    t.printStackTrace();
+                    System.exit(ERROR_UNEXPECTED_EXCEPTION);
+                }
+            }
+
+            @Override
+            public void flush() {
+            }
+
+            @Override
+            public void close() {
+            }
+        };
+
+        logger = Logger.getLogger("javafx.scene.control");
+        logger.addHandler(logHandler);
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        initLogger();
+
+        try {
+            StackPane root = new StackPane();
+            Scene scene = new Scene(root);
+            TreeTableView<MyData> treeTableView = new TreeTableView<>();
+
+            // Name column
+            TreeTableColumn<MyData, String> nameCol = new TreeTableColumn<>();
+            nameCol.setText("Name");
+            nameCol.setPrefWidth(150);
+            nameCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
+
+            // Value column
+            TreeTableColumn<MyData, Integer> valueCol = new TreeTableColumn<>();
+            valueCol.setText("Value");
+            valueCol.setPrefWidth(100);
+            valueCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("value"));
+
+            treeTableView.getColumns().addAll(nameCol, valueCol);
+
+            TreeItem<MyData> treeRoot = new TreeItem<>(new MyData("Row A", 1));
+            treeRoot.setExpanded(true);
+            treeTableView.setRoot(treeRoot);
+
+            TreeItem<MyData> item1 = new TreeItem<>(new MyData("Row B", 2));
+            TreeItem<MyData> item2 = new TreeItem<>(new MyData("Row C", 3));
+            treeRoot.getChildren().addAll(item1, item2);
+
+            root.getChildren().add(treeTableView);
+
+            stage.setScene(scene);
+            stage.show();
+
+            // Hide the stage after the specified amount of time
+            KeyFrame kf = new KeyFrame(Duration.millis(SHOWTIME), e -> stage.hide());
+            Timeline timeline = new Timeline(kf);
+            timeline.play();
+        } catch (Error | Exception ex) {
+            System.err.println("ERROR: caught unexpected exception: " + ex);
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    @Override public void stop() {
+        System.exit(ERROR_NONE);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp3/java/mymod/myapp3/AppTreeTableViewUnexported.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp3;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import javafx.animation.KeyFrame;
+import javafx.animation.Timeline;
+import javafx.application.Application;
+import javafx.scene.Scene;
+import javafx.scene.control.TreeItem;
+import javafx.scene.control.TreeTableColumn;
+import javafx.scene.control.TreeTableView;
+import javafx.scene.control.cell.TreeItemPropertyValueFactory;
+import javafx.scene.layout.StackPane;
+import javafx.stage.Stage;
+import javafx.util.Duration;
+import myapp3.pkg1.MyData;
+
+import static myapp3.Constants.*;
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppTreeTableViewUnexported extends Application {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    private Logger logger;
+    private Handler logHandler;
+    private final List<Throwable> errs = new ArrayList<>();
+
+    private void initLogger() {
+        Locale.setDefault(Locale.US);
+
+        // Initialize Logger
+        logHandler = new Handler() {
+            @Override
+            public void publish(LogRecord record) {
+                final Throwable t = record.getThrown();
+                // detect any Throwable:
+                if (t != null) {
+                    errs.add(t);
+                }
+            }
+
+            @Override
+            public void flush() {
+            }
+
+            @Override
+            public void close() {
+            }
+        };
+
+        logger = Logger.getLogger("javafx.scene.control");
+        logger.addHandler(logHandler);
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        initLogger();
+
+        try {
+            StackPane root = new StackPane();
+            Scene scene = new Scene(root);
+            TreeTableView<MyData> treeTableView = new TreeTableView<>();
+
+            // Name column
+            TreeTableColumn<MyData, String> nameCol = new TreeTableColumn<>();
+            nameCol.setText("Name");
+            nameCol.setPrefWidth(150);
+            nameCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
+
+            // Value column
+            TreeTableColumn<MyData, Integer> valueCol = new TreeTableColumn<>();
+            valueCol.setText("Value");
+            valueCol.setPrefWidth(100);
+            valueCol.setCellValueFactory(new TreeItemPropertyValueFactory<>("value"));
+
+            treeTableView.getColumns().addAll(nameCol, valueCol);
+
+            TreeItem<MyData> treeRoot = new TreeItem<>(new MyData("Row A", 1));
+            treeRoot.setExpanded(true);
+            treeTableView.setRoot(treeRoot);
+
+            TreeItem<MyData> item1 = new TreeItem<>(new MyData("Row B", 2));
+            TreeItem<MyData> item2 = new TreeItem<>(new MyData("Row C", 3));
+            treeRoot.getChildren().addAll(item1, item2);
+
+            root.getChildren().add(treeTableView);
+
+            stage.setScene(scene);
+            System.err.println("The following two WARNING messages are expected:");
+            stage.show();
+
+            // Hide the stage after the specified amount of time
+            KeyFrame kf = new KeyFrame(Duration.millis(SHOWTIME), e -> stage.hide());
+            Timeline timeline = new Timeline(kf);
+            timeline.play();
+        } catch (Error | Exception ex) {
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    private void fail(String message, Throwable t) {
+        if (message != null) {
+            System.err.print(message + ": ");
+        }
+        if (t != null) {
+            System.err.println(t);
+            t.printStackTrace();
+        } else {
+            System.err.println();
+        }
+        System.exit(ERROR_ASSERTION_FAILURE);
+    }
+
+    @Override public void stop() {
+        final int expectedExceptions = 2; // One for each PropertyValueFactory
+
+        if (errs.isEmpty()) {
+            fail("ERROR: did not get the expected exception", null);
+        }
+
+        if (expectedExceptions != errs.size()) {
+            fail("ERROR: expected " + expectedExceptions + " exceptions, got: " + errs.size(), null);
+        }
+
+        for (Throwable t : errs) {
+            if (! (t instanceof RuntimeException)) {
+                fail("ERROR: unexpeted exception: ", t);
+            }
+
+            RuntimeException ex = (RuntimeException) t;
+            Throwable cause = ex.getCause();
+            if (! (cause instanceof IllegalAccessException)) {
+                fail("ERROR: unexpeted cause: ", ex);
+            }
+
+            String message = cause.getMessage();
+            if (message == null) {
+                fail("ERROR: detail message of cause is null", ex);
+            }
+
+            boolean badMessage = false;
+            if (!message.contains(" cannot access class ")) badMessage = true;
+            if (!message.contains(" does not open ")) badMessage = true;
+            if (!message.endsWith(" to javafx.base")) badMessage = true;
+            if (badMessage) {
+                fail("ERROR: detail message not formatted correctly", ex);
+            }
+        }
+
+        // We got the expected exception, exit normally
+        System.exit(ERROR_NONE);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp3/java/mymod/myapp3/Constants.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp3;
+
+public class Constants {
+
+    public static final int SHOWTIME = 2500;
+
+    // NOTE: these constants must match those in test.launchertest.Constants
+    public static final int ERROR_NONE = 2;
+    public static final int ERROR_UNEXPECTED_EXCEPTION = 4;
+
+    public static final int ERROR_ASSERTION_FAILURE = 28;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp3/java/mymod/myapp3/pkg1/MyData.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp3.pkg1;
+
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+public class MyData {
+    private final SimpleStringProperty nameProp;
+    private final SimpleIntegerProperty valueProp;
+
+    public MyData(String name, int value) {
+        nameProp = new SimpleStringProperty(name);
+        valueProp = new SimpleIntegerProperty(value);
+    }
+
+    public StringProperty nameProperty() {
+        return nameProp;
+    }
+
+    public String getName() {
+        return nameProp.get();
+    }
+
+    public void setName(String n) {
+        nameProp.set(n);
+    }
+
+    public IntegerProperty valueProperty() {
+        return valueProp;
+    }
+
+    public Integer geValue() {
+        return valueProp.get();
+    }
+
+    public void setValue(int v) {
+        valueProp.set(v);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp3/java/mymod/myapp3/pkg2/MyData.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp3.pkg2;
+
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+public class MyData {
+    private final SimpleStringProperty nameProp;
+    private final SimpleIntegerProperty valueProp;
+
+    public MyData(String name, int value) {
+        nameProp = new SimpleStringProperty(name);
+        valueProp = new SimpleIntegerProperty(value);
+    }
+
+    public StringProperty nameProperty() {
+        return nameProp;
+    }
+
+    public String getName() {
+        return nameProp.get();
+    }
+
+    public void setName(String n) {
+        nameProp.set(n);
+    }
+
+    public IntegerProperty valueProperty() {
+        return valueProp;
+    }
+
+    public Integer geValue() {
+        return valueProp.get();
+    }
+
+    public void setValue(int v) {
+        valueProp.set(v);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp3/java/mymod/myapp3/pkg3/MyData.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp3.pkg3;
+
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+public class MyData {
+    private final SimpleStringProperty nameProp;
+    private final SimpleIntegerProperty valueProp;
+
+    public MyData(String name, int value) {
+        nameProp = new SimpleStringProperty(name);
+        valueProp = new SimpleIntegerProperty(value);
+    }
+
+    public StringProperty nameProperty() {
+        return nameProp;
+    }
+
+    public String getName() {
+        return nameProp.get();
+    }
+
+    public void setName(String n) {
+        nameProp.set(n);
+    }
+
+    public IntegerProperty valueProperty() {
+        return valueProp;
+    }
+
+    public Integer geValue() {
+        return valueProp.get();
+    }
+
+    public void setValue(int v) {
+        valueProp.set(v);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp3/java/mymod/myapp3/pkg4/MyData.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp3.pkg4;
+
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+public class MyData {
+    private final SimpleStringProperty nameProp;
+    private final SimpleIntegerProperty valueProp;
+
+    public MyData(String name, int value) {
+        nameProp = new SimpleStringProperty(name);
+        valueProp = new SimpleIntegerProperty(value);
+    }
+
+    public StringProperty nameProperty() {
+        return nameProp;
+    }
+
+    public String getName() {
+        return nameProp.get();
+    }
+
+    public void setName(String n) {
+        nameProp.set(n);
+    }
+
+    public IntegerProperty valueProperty() {
+        return valueProp;
+    }
+
+    public Integer geValue() {
+        return valueProp.get();
+    }
+
+    public void setValue(int v) {
+        valueProp.set(v);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp3/java/mymod/myapp3/pkg5/MyData.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp3.pkg5;
+
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+public class MyData {
+    private final SimpleStringProperty nameProp;
+    private final SimpleIntegerProperty valueProp;
+
+    public MyData(String name, int value) {
+        nameProp = new SimpleStringProperty(name);
+        valueProp = new SimpleIntegerProperty(value);
+    }
+
+    public StringProperty nameProperty() {
+        return nameProp;
+    }
+
+    public String getName() {
+        return nameProp.get();
+    }
+
+    public void setName(String n) {
+        nameProp.set(n);
+    }
+
+    public IntegerProperty valueProperty() {
+        return valueProp;
+    }
+
+    public Integer geValue() {
+        return valueProp.get();
+    }
+
+    public void setValue(int v) {
+        valueProp.set(v);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/module-info.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module mymod {
+    requires javafx.base;
+    requires java.logging;
+
+    exports myapp4;
+
+    // myapp4.pkg1 is deliberately not listed
+    exports myapp4.pkg2;
+    exports myapp4.pkg3 to javafx.base;
+    opens myapp4.pkg4;
+    opens myapp4.pkg5 to javafx.base;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/AppBeansExported.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4;
+
+import javafx.beans.property.adapter.JavaBeanDoubleProperty;
+import javafx.beans.property.adapter.JavaBeanDoublePropertyBuilder;
+import javafx.beans.property.adapter.JavaBeanObjectProperty;
+import javafx.beans.property.adapter.JavaBeanObjectPropertyBuilder;
+import javafx.beans.property.adapter.ReadOnlyJavaBeanStringProperty;
+import javafx.beans.property.adapter.ReadOnlyJavaBeanStringPropertyBuilder;
+import myapp4.pkg2.POJO;
+import myapp4.pkg2.RefClass;
+
+import static myapp4.Constants.*;
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppBeansExported {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            new AppBeansExported().doTest();
+            System.exit(ERROR_NONE);
+        } catch (Throwable t) {
+            t.printStackTrace(System.err);
+            System.exit(ERROR_ASSERTION_FAILURE);
+        }
+    }
+
+    private final double EPSILON = 1.0e-4;
+
+    private void assertEquals(double expected, double observed) {
+        if (Math.abs(expected - observed) > EPSILON) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    private void assertEquals(String expected, String observed) {
+        if (!expected.equals(observed)) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    private void assertSame(Object expected, Object observed) {
+        if (expected != observed) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    public void doTest() throws Exception {
+        String name = "test object";
+        double val = 1.2;
+        RefClass obj = new RefClass();
+
+        POJO bean = new POJO(name, val, obj);
+
+        JavaBeanDoubleProperty valProp = JavaBeanDoublePropertyBuilder.create()
+                .bean(bean)
+                .name("val")
+                .build();
+
+        double retVal = valProp.get();
+        assertEquals(val, retVal);
+
+        val = 2.5;
+        valProp.set(val);
+        retVal = valProp.get();
+        assertEquals(val, retVal);
+
+        JavaBeanObjectProperty<RefClass> objProp = JavaBeanObjectPropertyBuilder.create()
+                .bean(bean)
+                .name("obj")
+                .build();
+
+        RefClass retObj = objProp.get();
+        assertSame(obj, retObj);
+
+        obj = new RefClass();
+        objProp.set(obj);
+        retObj = objProp.get();
+        assertSame(obj, retObj);
+
+        ReadOnlyJavaBeanStringProperty namePropRO = ReadOnlyJavaBeanStringPropertyBuilder.create()
+                .bean(bean)
+                .name("name")
+                .build();
+
+        String retName = namePropRO.get();
+        assertEquals(name, retName);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/AppBeansOpened.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4;
+
+import javafx.beans.property.adapter.JavaBeanDoubleProperty;
+import javafx.beans.property.adapter.JavaBeanDoublePropertyBuilder;
+import javafx.beans.property.adapter.JavaBeanObjectProperty;
+import javafx.beans.property.adapter.JavaBeanObjectPropertyBuilder;
+import javafx.beans.property.adapter.ReadOnlyJavaBeanStringProperty;
+import javafx.beans.property.adapter.ReadOnlyJavaBeanStringPropertyBuilder;
+import myapp4.pkg4.POJO;
+import myapp4.pkg4.RefClass;
+
+import static myapp4.Constants.*;
+
+// This logic is copied from AppBeansExported.
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppBeansOpened {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            new AppBeansOpened().doTest();
+            System.exit(ERROR_NONE);
+        } catch (Throwable t) {
+            t.printStackTrace(System.err);
+            System.exit(ERROR_ASSERTION_FAILURE);
+        }
+    }
+
+    private final double EPSILON = 1.0e-4;
+
+    private void assertEquals(double expected, double observed) {
+        if (Math.abs(expected - observed) > EPSILON) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    private void assertEquals(String expected, String observed) {
+        if (!expected.equals(observed)) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    private void assertSame(Object expected, Object observed) {
+        if (expected != observed) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    public void doTest() throws Exception {
+        String name = "test object";
+        double val = 1.2;
+        RefClass obj = new RefClass();
+
+        POJO bean = new POJO(name, val, obj);
+
+        JavaBeanDoubleProperty valProp = JavaBeanDoublePropertyBuilder.create()
+                .bean(bean)
+                .name("val")
+                .build();
+
+        double retVal = valProp.get();
+        assertEquals(val, retVal);
+
+        val = 2.5;
+        valProp.set(val);
+        retVal = valProp.get();
+        assertEquals(val, retVal);
+
+        JavaBeanObjectProperty<RefClass> objProp = JavaBeanObjectPropertyBuilder.create()
+                .bean(bean)
+                .name("obj")
+                .build();
+
+        RefClass retObj = objProp.get();
+        assertSame(obj, retObj);
+
+        obj = new RefClass();
+        objProp.set(obj);
+        retObj = objProp.get();
+        assertSame(obj, retObj);
+
+        ReadOnlyJavaBeanStringProperty namePropRO = ReadOnlyJavaBeanStringPropertyBuilder.create()
+                .bean(bean)
+                .name("name")
+                .build();
+
+        String retName = namePropRO.get();
+        assertEquals(name, retName);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/AppBeansQualExported.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4;
+
+import java.lang.reflect.UndeclaredThrowableException;
+import javafx.beans.property.adapter.JavaBeanDoubleProperty;
+import javafx.beans.property.adapter.JavaBeanDoublePropertyBuilder;
+import javafx.beans.property.adapter.JavaBeanObjectProperty;
+import javafx.beans.property.adapter.JavaBeanObjectPropertyBuilder;
+import javafx.beans.property.adapter.ReadOnlyJavaBeanStringProperty;
+import javafx.beans.property.adapter.ReadOnlyJavaBeanStringPropertyBuilder;
+import myapp4.pkg3.POJO;
+import myapp4.pkg3.RefClass;
+
+import static myapp4.Constants.*;
+
+// This logic is copied from AppBeansUnexported.
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppBeansQualExported {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            new AppBeansQualExported().doTest();
+            System.exit(ERROR_NONE);
+        } catch (Throwable t) {
+            t.printStackTrace(System.err);
+            System.exit(ERROR_ASSERTION_FAILURE);
+        }
+    }
+
+    private void checkException(UndeclaredThrowableException ex) {
+        Throwable cause = ex.getCause();
+        if (! (cause instanceof IllegalAccessException)) {
+            System.err.println("ERROR: unexpected cause: " + cause);
+            throw ex;
+        }
+
+        String message = cause.getMessage();
+        if (message == null) {
+            System.err.println("ERROR: detail message of cause is null");
+            throw ex;
+        }
+
+        boolean badMessage = false;
+        if (!message.contains(" cannot access class ")) badMessage = true;
+        if (!message.contains(" does not open ")) badMessage = true;
+        if (!message.endsWith(" to javafx.base")) badMessage = true;
+        if (badMessage) {
+            System.err.println("ERROR: detail message not formatted correctly: " + message);
+            throw ex;
+        }
+    }
+
+    public void doTest() throws Exception {
+        String name = "test object";
+        double val = 1.2;
+        RefClass obj = new RefClass();
+
+        POJO bean = new POJO(name, val, obj);
+
+        JavaBeanDoubleProperty valProp = JavaBeanDoublePropertyBuilder.create()
+                .bean(bean)
+                .name("val")
+                .build();
+
+        try {
+            valProp.get();
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (UndeclaredThrowableException ex) {
+            checkException(ex);
+        }
+
+        val = 2.5;
+        try {
+            valProp.set(val);
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (UndeclaredThrowableException ex) {
+            checkException(ex);
+        }
+
+        JavaBeanObjectProperty<RefClass> objProp = JavaBeanObjectPropertyBuilder.create()
+                .bean(bean)
+                .name("obj")
+                .build();
+
+        try {
+            objProp.get();
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (UndeclaredThrowableException ex) {
+            checkException(ex);
+        }
+
+        obj = new RefClass();
+        try {
+            objProp.set(obj);
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (UndeclaredThrowableException ex) {
+            checkException(ex);
+        }
+
+        ReadOnlyJavaBeanStringProperty namePropRO = ReadOnlyJavaBeanStringPropertyBuilder.create()
+                .bean(bean)
+                .name("name")
+                .build();
+
+        try {
+            namePropRO.get();
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (UndeclaredThrowableException ex) {
+            checkException(ex);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/AppBeansQualOpened.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4;
+
+import javafx.beans.property.adapter.JavaBeanDoubleProperty;
+import javafx.beans.property.adapter.JavaBeanDoublePropertyBuilder;
+import javafx.beans.property.adapter.JavaBeanObjectProperty;
+import javafx.beans.property.adapter.JavaBeanObjectPropertyBuilder;
+import javafx.beans.property.adapter.ReadOnlyJavaBeanStringProperty;
+import javafx.beans.property.adapter.ReadOnlyJavaBeanStringPropertyBuilder;
+import myapp4.pkg5.POJO;
+import myapp4.pkg5.RefClass;
+
+import static myapp4.Constants.*;
+
+// This logic is copied from AppBeansExported.
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppBeansQualOpened {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            new AppBeansQualOpened().doTest();
+            System.exit(ERROR_NONE);
+        } catch (Throwable t) {
+            t.printStackTrace(System.err);
+            System.exit(ERROR_ASSERTION_FAILURE);
+        }
+    }
+
+    private final double EPSILON = 1.0e-4;
+
+    private void assertEquals(double expected, double observed) {
+        if (Math.abs(expected - observed) > EPSILON) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    private void assertEquals(String expected, String observed) {
+        if (!expected.equals(observed)) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    private void assertSame(Object expected, Object observed) {
+        if (expected != observed) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    public void doTest() throws Exception {
+        String name = "test object";
+        double val = 1.2;
+        RefClass obj = new RefClass();
+
+        POJO bean = new POJO(name, val, obj);
+
+        JavaBeanDoubleProperty valProp = JavaBeanDoublePropertyBuilder.create()
+                .bean(bean)
+                .name("val")
+                .build();
+
+        double retVal = valProp.get();
+        assertEquals(val, retVal);
+
+        val = 2.5;
+        valProp.set(val);
+        retVal = valProp.get();
+        assertEquals(val, retVal);
+
+        JavaBeanObjectProperty<RefClass> objProp = JavaBeanObjectPropertyBuilder.create()
+                .bean(bean)
+                .name("obj")
+                .build();
+
+        RefClass retObj = objProp.get();
+        assertSame(obj, retObj);
+
+        obj = new RefClass();
+        objProp.set(obj);
+        retObj = objProp.get();
+        assertSame(obj, retObj);
+
+        ReadOnlyJavaBeanStringProperty namePropRO = ReadOnlyJavaBeanStringPropertyBuilder.create()
+                .bean(bean)
+                .name("name")
+                .build();
+
+        String retName = namePropRO.get();
+        assertEquals(name, retName);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/AppBeansUnexported.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4;
+
+import java.lang.reflect.UndeclaredThrowableException;
+import javafx.beans.property.adapter.JavaBeanDoubleProperty;
+import javafx.beans.property.adapter.JavaBeanDoublePropertyBuilder;
+import javafx.beans.property.adapter.JavaBeanObjectProperty;
+import javafx.beans.property.adapter.JavaBeanObjectPropertyBuilder;
+import javafx.beans.property.adapter.ReadOnlyJavaBeanStringProperty;
+import javafx.beans.property.adapter.ReadOnlyJavaBeanStringPropertyBuilder;
+import myapp4.pkg1.POJO;
+import myapp4.pkg1.RefClass;
+
+import static myapp4.Constants.*;
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppBeansUnexported {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            new AppBeansUnexported().doTest();
+            System.exit(ERROR_NONE);
+        } catch (Throwable t) {
+            t.printStackTrace(System.err);
+            System.exit(ERROR_ASSERTION_FAILURE);
+        }
+    }
+
+    private void checkException(UndeclaredThrowableException ex) {
+        Throwable cause = ex.getCause();
+        if (! (cause instanceof IllegalAccessException)) {
+            System.err.println("ERROR: unexpected cause: " + cause);
+            throw ex;
+        }
+
+        String message = cause.getMessage();
+        if (message == null) {
+            System.err.println("ERROR: detail message of cause is null");
+            throw ex;
+        }
+
+        boolean badMessage = false;
+        if (!message.contains(" cannot access class ")) badMessage = true;
+        if (!message.contains(" does not open ")) badMessage = true;
+        if (!message.endsWith(" to javafx.base")) badMessage = true;
+        if (badMessage) {
+            System.err.println("ERROR: detail message not formatted correctly: " + message);
+            throw ex;
+        }
+    }
+
+    public void doTest() throws Exception {
+        String name = "test object";
+        double val = 1.2;
+        RefClass obj = new RefClass();
+
+        POJO bean = new POJO(name, val, obj);
+
+        JavaBeanDoubleProperty valProp = JavaBeanDoublePropertyBuilder.create()
+                .bean(bean)
+                .name("val")
+                .build();
+
+        try {
+            valProp.get();
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (UndeclaredThrowableException ex) {
+            checkException(ex);
+        }
+
+        val = 2.5;
+        try {
+            valProp.set(val);
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (UndeclaredThrowableException ex) {
+            checkException(ex);
+        }
+
+        JavaBeanObjectProperty<RefClass> objProp = JavaBeanObjectPropertyBuilder.create()
+                .bean(bean)
+                .name("obj")
+                .build();
+
+        try {
+            objProp.get();
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (UndeclaredThrowableException ex) {
+            checkException(ex);
+        }
+
+        obj = new RefClass();
+        try {
+            objProp.set(obj);
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (UndeclaredThrowableException ex) {
+            checkException(ex);
+        }
+
+        ReadOnlyJavaBeanStringProperty namePropRO = ReadOnlyJavaBeanStringPropertyBuilder.create()
+                .bean(bean)
+                .name("name")
+                .build();
+
+        try {
+            namePropRO.get();
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (UndeclaredThrowableException ex) {
+            checkException(ex);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/AppBindingsExported.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4;
+
+import javafx.beans.binding.Bindings;
+import javafx.beans.binding.DoubleBinding;
+import javafx.beans.binding.ObjectBinding;
+import myapp4.pkg2.MyProps;
+
+import static myapp4.Constants.*;
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppBindingsExported {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            new AppBindingsExported().doTest();
+            System.exit(ERROR_NONE);
+        } catch (Throwable t) {
+            t.printStackTrace(System.err);
+            System.exit(ERROR_ASSERTION_FAILURE);
+        }
+    }
+
+    private final double EPSILON = 1.0e-4;
+
+    private void assertEquals(double expected, double observed) {
+        if (Math.abs(expected - observed) > EPSILON) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    private void assertEquals(String expected, String observed) {
+        if (!expected.equals(observed)) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    private void assertSame(Object expected, Object observed) {
+        if (expected != observed) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    public void doTest() throws Exception {
+        MyProps root = new MyProps();
+        MyProps a = new MyProps();
+        MyProps b = new MyProps();
+
+        root.setNext(a);
+        a.setNext(b);
+        a.setFoo(1.2);
+        b.setFoo(2.3);
+
+        DoubleBinding binding1 = Bindings.selectDouble(root, "next", "foo");
+        assertEquals(1.2, binding1.get());
+        a.setFoo(3.4);
+        assertEquals(3.4, binding1.get());
+
+        ObjectBinding<MyProps> binding2 = Bindings.select(root, "next", "next");
+        assertEquals(2.3, binding2.get().getFoo());
+        b.setFoo(4.5);
+        assertEquals(4.5, binding2.get().getFoo());
+
+        RootProps root2 = new RootProps();
+        MyProps c = new MyProps();
+        MyProps d = new MyProps();
+
+        root2.setNext(c);
+        c.setNext(d);
+        c.setFoo(1.2);
+        d.setFoo(2.3);
+
+        DoubleBinding binding3 = Bindings.selectDouble(root2, "next", "foo");
+        assertEquals(1.2, binding3.get());
+        c.setFoo(3.4);
+        assertEquals(3.4, binding3.get());
+
+        ObjectBinding<MyProps> binding4 = Bindings.select(root2, "next", "next");
+        assertEquals(2.3, binding4.get().getFoo());
+        d.setFoo(4.5);
+        assertEquals(4.5, binding4.get().getFoo());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/AppBindingsOpened.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4;
+
+import javafx.beans.binding.Bindings;
+import javafx.beans.binding.DoubleBinding;
+import javafx.beans.binding.ObjectBinding;
+import myapp4.pkg4.MyProps;
+
+import static myapp4.Constants.*;
+
+// This logic is copied from AppBindingsExported.
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppBindingsOpened {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            new AppBindingsOpened().doTest();
+            System.exit(ERROR_NONE);
+        } catch (Throwable t) {
+            t.printStackTrace(System.err);
+            System.exit(ERROR_ASSERTION_FAILURE);
+        }
+    }
+
+    private final double EPSILON = 1.0e-4;
+
+    private void assertEquals(double expected, double observed) {
+        if (Math.abs(expected - observed) > EPSILON) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    private void assertEquals(String expected, String observed) {
+        if (!expected.equals(observed)) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    private void assertSame(Object expected, Object observed) {
+        if (expected != observed) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    public void doTest() throws Exception {
+        MyProps root = new MyProps();
+        MyProps a = new MyProps();
+        MyProps b = new MyProps();
+
+        root.setNext(a);
+        a.setNext(b);
+        a.setFoo(1.2);
+        b.setFoo(2.3);
+
+        DoubleBinding binding1 = Bindings.selectDouble(root, "next", "foo");
+        assertEquals(1.2, binding1.get());
+        a.setFoo(3.4);
+        assertEquals(3.4, binding1.get());
+
+        ObjectBinding<MyProps> binding2 = Bindings.select(root, "next", "next");
+        assertEquals(2.3, binding2.get().getFoo());
+        b.setFoo(4.5);
+        assertEquals(4.5, binding2.get().getFoo());
+
+        RootProps root2 = new RootProps();
+        MyProps c = new MyProps();
+        MyProps d = new MyProps();
+
+        root2.setNext(c);
+        c.setNext(d);
+        c.setFoo(1.2);
+        d.setFoo(2.3);
+
+        DoubleBinding binding3 = Bindings.selectDouble(root2, "next", "foo");
+        assertEquals(1.2, binding3.get());
+        c.setFoo(3.4);
+        assertEquals(3.4, binding3.get());
+
+        ObjectBinding<MyProps> binding4 = Bindings.select(root2, "next", "next");
+        assertEquals(2.3, binding4.get().getFoo());
+        d.setFoo(4.5);
+        assertEquals(4.5, binding4.get().getFoo());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/AppBindingsQualExported.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4;
+
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import javafx.beans.binding.Bindings;
+import javafx.beans.binding.DoubleBinding;
+import javafx.beans.binding.ObjectBinding;
+import myapp4.pkg3.MyProps;
+
+import static myapp4.Constants.*;
+
+// This logic is copied from AppBindingsUnexported.
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppBindingsQualExported {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            new AppBindingsQualExported().doTest();
+            System.exit(ERROR_NONE);
+        } catch (Throwable t) {
+            t.printStackTrace(System.err);
+            System.exit(ERROR_ASSERTION_FAILURE);
+        }
+    }
+
+    private void checkException(RuntimeException ex) {
+        Throwable cause = ex.getCause();
+        if (! (cause instanceof IllegalAccessException)) {
+            System.err.println("ERROR: unexpected cause: " + cause);
+            throw ex;
+        }
+
+        String message = cause.getMessage();
+        if (message == null) {
+            System.err.println("ERROR: detail message of cause is null");
+            throw ex;
+        }
+
+        boolean badMessage = false;
+        if (!message.contains(" cannot access class ")) badMessage = true;
+        if (!message.contains(" does not open ")) badMessage = true;
+        if (!message.endsWith(" to javafx.base")) badMessage = true;
+        if (badMessage) {
+            System.err.println("ERROR: detail message not formatted correctly: " + message);
+            throw ex;
+        }
+    }
+
+    private final double EPSILON = 1.0e-4;
+
+    private void assertEquals(double expected, double observed) {
+        if (Math.abs(expected - observed) > EPSILON) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    private void assertEquals(String expected, String observed) {
+        if (!expected.equals(observed)) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    private void assertSame(Object expected, Object observed) {
+        if (expected != observed) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    private Logger logger;
+    private Handler logHandler;
+    private final List<Throwable> errs = new ArrayList<>();
+
+    private void initLogger() {
+        Locale.setDefault(Locale.US);
+
+        // Initialize Logger
+        logHandler = new Handler() {
+            @Override
+            public void publish(LogRecord record) {
+                final Throwable t = record.getThrown();
+                // detect any Throwable:
+                if (t != null) {
+                    errs.add(t);
+                }
+            }
+
+            @Override
+            public void flush() {
+            }
+
+            @Override
+            public void close() {
+            }
+        };
+
+        logger = Logger.getLogger("javafx.beans");
+        logger.addHandler(logHandler);
+    }
+
+    public void doTest() throws Exception {
+        initLogger();
+
+        MyProps root = new MyProps();
+        MyProps a = new MyProps();
+        MyProps b = new MyProps();
+
+        root.setNext(a);
+        a.setNext(b);
+        a.setFoo(1.2);
+        b.setFoo(2.3);
+
+        try {
+            Bindings.selectDouble(root, "next", "foo");
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (UndeclaredThrowableException ex) {
+            checkException(ex);
+        }
+
+        try {
+            Bindings.select(root, "next", "next");
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (UndeclaredThrowableException ex) {
+            checkException(ex);
+        }
+
+        RootProps root2 = new RootProps();
+        MyProps c = new MyProps();
+        MyProps d = new MyProps();
+
+        root2.setNext(c);
+        c.setNext(d);
+        c.setFoo(1.2);
+        d.setFoo(2.3);
+
+        // In this case, the binding will succeed; calling get() will return 0;
+        // the first time it is called it will log a warning.
+        DoubleBinding binding3 = Bindings.selectDouble(root2, "next", "foo");
+        assertEquals(0, binding3.get()); // This will log a warning
+        c.setFoo(3.4);
+        assertEquals(0, binding3.get()); // No warning here
+
+        // In this case, the binding will succeed; calling get() will return null;
+        // the first time it is called it will log a warning.
+        ObjectBinding<MyProps> binding4 = Bindings.select(root2, "next", "next");
+        assertSame(null, binding4.get()); // This will log a warning
+        assertSame(null, binding4.get()); // No warning here
+
+        // Assert that we got 2 warnings
+        final int expectedExceptions = 2; // First call to get for each binding
+
+        if (errs.isEmpty()) {
+            throw new AssertionError("ERROR: did not get the expected exception");
+        }
+
+        assertEquals(expectedExceptions, errs.size());
+
+        for (Throwable t : errs) {
+            if (!(t instanceof RuntimeException)) {
+                throw new AssertionError("ERROR: unexpected exception: ", t);
+            }
+            checkException((RuntimeException) t);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/AppBindingsQualOpened.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4;
+
+import javafx.beans.binding.Bindings;
+import javafx.beans.binding.DoubleBinding;
+import javafx.beans.binding.ObjectBinding;
+import myapp4.pkg5.MyProps;
+
+import static myapp4.Constants.*;
+
+// This logic is copied from AppBindingsExported.
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppBindingsQualOpened {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            new AppBindingsQualOpened().doTest();
+            System.exit(ERROR_NONE);
+        } catch (Throwable t) {
+            t.printStackTrace(System.err);
+            System.exit(ERROR_ASSERTION_FAILURE);
+        }
+    }
+
+    private final double EPSILON = 1.0e-4;
+
+    private void assertEquals(double expected, double observed) {
+        if (Math.abs(expected - observed) > EPSILON) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    private void assertEquals(String expected, String observed) {
+        if (!expected.equals(observed)) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    private void assertSame(Object expected, Object observed) {
+        if (expected != observed) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    public void doTest() throws Exception {
+        MyProps root = new MyProps();
+        MyProps a = new MyProps();
+        MyProps b = new MyProps();
+
+        root.setNext(a);
+        a.setNext(b);
+        a.setFoo(1.2);
+        b.setFoo(2.3);
+
+        DoubleBinding binding1 = Bindings.selectDouble(root, "next", "foo");
+        assertEquals(1.2, binding1.get());
+        a.setFoo(3.4);
+        assertEquals(3.4, binding1.get());
+
+        ObjectBinding<MyProps> binding2 = Bindings.select(root, "next", "next");
+        assertEquals(2.3, binding2.get().getFoo());
+        b.setFoo(4.5);
+        assertEquals(4.5, binding2.get().getFoo());
+
+        RootProps root2 = new RootProps();
+        MyProps c = new MyProps();
+        MyProps d = new MyProps();
+
+        root2.setNext(c);
+        c.setNext(d);
+        c.setFoo(1.2);
+        d.setFoo(2.3);
+
+        DoubleBinding binding3 = Bindings.selectDouble(root2, "next", "foo");
+        assertEquals(1.2, binding3.get());
+        c.setFoo(3.4);
+        assertEquals(3.4, binding3.get());
+
+        ObjectBinding<MyProps> binding4 = Bindings.select(root2, "next", "next");
+        assertEquals(2.3, binding4.get().getFoo());
+        d.setFoo(4.5);
+        assertEquals(4.5, binding4.get().getFoo());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/AppBindingsUnexported.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4;
+
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import javafx.beans.binding.Bindings;
+import javafx.beans.binding.DoubleBinding;
+import javafx.beans.binding.ObjectBinding;
+import myapp4.pkg1.MyProps;
+
+import static myapp4.Constants.*;
+
+/**
+ * Modular test application for testing JavaFX beans.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppBindingsUnexported {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            new AppBindingsUnexported().doTest();
+            System.exit(ERROR_NONE);
+        } catch (Throwable t) {
+            t.printStackTrace(System.err);
+            System.exit(ERROR_ASSERTION_FAILURE);
+        }
+    }
+
+    private void checkException(RuntimeException ex) {
+        Throwable cause = ex.getCause();
+        if (! (cause instanceof IllegalAccessException)) {
+            System.err.println("ERROR: unexpected cause: " + cause);
+            throw ex;
+        }
+
+        String message = cause.getMessage();
+        if (message == null) {
+            System.err.println("ERROR: detail message of cause is null");
+            throw ex;
+        }
+
+        boolean badMessage = false;
+        if (!message.contains(" cannot access class ")) badMessage = true;
+        if (!message.contains(" does not open ")) badMessage = true;
+        if (!message.endsWith(" to javafx.base")) badMessage = true;
+        if (badMessage) {
+            System.err.println("ERROR: detail message not formatted correctly: " + message);
+            throw ex;
+        }
+    }
+
+    private final double EPSILON = 1.0e-4;
+
+    private void assertEquals(double expected, double observed) {
+        if (Math.abs(expected - observed) > EPSILON) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    private void assertEquals(String expected, String observed) {
+        if (!expected.equals(observed)) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    private void assertSame(Object expected, Object observed) {
+        if (expected != observed) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    private Logger logger;
+    private Handler logHandler;
+    private final List<Throwable> errs = new ArrayList<>();
+
+    private void initLogger() {
+        Locale.setDefault(Locale.US);
+
+        // Initialize Logger
+        logHandler = new Handler() {
+            @Override
+            public void publish(LogRecord record) {
+                final Throwable t = record.getThrown();
+                // detect any Throwable:
+                if (t != null) {
+                    errs.add(t);
+                }
+            }
+
+            @Override
+            public void flush() {
+            }
+
+            @Override
+            public void close() {
+            }
+        };
+
+        logger = Logger.getLogger("javafx.beans");
+        logger.addHandler(logHandler);
+    }
+
+    public void doTest() throws Exception {
+        initLogger();
+
+        MyProps root = new MyProps();
+        MyProps a = new MyProps();
+        MyProps b = new MyProps();
+
+        root.setNext(a);
+        a.setNext(b);
+        a.setFoo(1.2);
+        b.setFoo(2.3);
+
+        try {
+            Bindings.selectDouble(root, "next", "foo");
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (UndeclaredThrowableException ex) {
+            checkException(ex);
+        }
+
+        try {
+            Bindings.select(root, "next", "next");
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (UndeclaredThrowableException ex) {
+            checkException(ex);
+        }
+
+        RootProps root2 = new RootProps();
+        MyProps c = new MyProps();
+        MyProps d = new MyProps();
+
+        root2.setNext(c);
+        c.setNext(d);
+        c.setFoo(1.2);
+        d.setFoo(2.3);
+
+        // In this case, the binding will succeed; calling get() will return 0;
+        // the first time it is called it will log a warning.
+        DoubleBinding binding3 = Bindings.selectDouble(root2, "next", "foo");
+        assertEquals(0, binding3.get()); // This will log a warning
+        c.setFoo(3.4);
+        assertEquals(0, binding3.get()); // No warning here
+
+        // In this case, the binding will succeed; calling get() will return null;
+        // the first time it is called it will log a warning.
+        ObjectBinding<MyProps> binding4 = Bindings.select(root2, "next", "next");
+        assertSame(null, binding4.get()); // This will log a warning
+        assertSame(null, binding4.get()); // No warning here
+
+        // Assert that we got 2 warnings
+        final int expectedExceptions = 2; // First call to get for each binding
+
+        if (errs.isEmpty()) {
+            throw new AssertionError("ERROR: did not get the expected exception");
+        }
+
+        assertEquals(expectedExceptions, errs.size());
+
+        for (Throwable t : errs) {
+            if (!(t instanceof RuntimeException)) {
+                throw new AssertionError("ERROR: unexpected exception: ", t);
+            }
+            checkException((RuntimeException) t);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/Constants.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4;
+
+public class Constants {
+
+    // NOTE: these constants must match those in test.launchertest.Constants
+    public static final int ERROR_NONE = 2;
+    public static final int ERROR_UNEXPECTED_EXCEPTION = 4;
+
+    public static final int ERROR_ASSERTION_FAILURE = 28;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/RootProps.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4;
+
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+
+public class RootProps {
+    private final ObjectProperty<Object> next = new SimpleObjectProperty<>();
+
+    public final ObjectProperty<Object> nextProperty() {
+        return next;
+    }
+
+    public final void setNext(Object next) {
+        this.next.set(next);
+    }
+
+    public final Object getNext() {
+        return next.get();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/pkg1/MyProps.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4.pkg1;
+
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.beans.property.SimpleObjectProperty;
+
+public class MyProps {
+    private final DoubleProperty foo = new SimpleDoubleProperty();
+
+    public final DoubleProperty fooProperty() {
+        return foo;
+    }
+
+    public final void setFoo(double foo) {
+        this.foo.set(foo);
+    }
+
+    public final double getFoo() {
+        return foo.get();
+    }
+
+    private final ObjectProperty<MyProps> next = new SimpleObjectProperty<>();
+
+    public final ObjectProperty<MyProps> nextProperty() {
+        return next;
+    }
+
+    public final void setNext(MyProps next) {
+        this.next.set(next);
+    }
+
+    public final MyProps getNext() {
+        return next.get();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/pkg1/POJO.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4.pkg1;
+
+public class POJO {
+    private final String name;
+    private double val;
+    private RefClass obj;
+
+    public final String getName() {
+        return name;
+    }
+
+    public final void setVal(double val) {
+        this.val = val;
+    }
+
+    public final double getVal() {
+        return val;
+    }
+
+    public final void setObj(RefClass obj) {
+        this.obj = obj;
+    }
+
+    public final RefClass getObj() {
+        return obj;
+    }
+
+    public POJO(String name, double val, RefClass obj) {
+        this.name = name;
+        this.val = val;
+        this.obj = obj;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/pkg1/RefClass.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4.pkg1;
+
+public class RefClass {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/pkg2/MyProps.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4.pkg2;
+
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.beans.property.SimpleObjectProperty;
+
+public class MyProps {
+    private final DoubleProperty foo = new SimpleDoubleProperty();
+
+    public final DoubleProperty fooProperty() {
+        return foo;
+    }
+
+    public final void setFoo(double foo) {
+        this.foo.set(foo);
+    }
+
+    public final double getFoo() {
+        return foo.get();
+    }
+
+    private final ObjectProperty<MyProps> next = new SimpleObjectProperty<>();
+
+    public final ObjectProperty<MyProps> nextProperty() {
+        return next;
+    }
+
+    public final void setNext(MyProps next) {
+        this.next.set(next);
+    }
+
+    public final MyProps getNext() {
+        return next.get();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/pkg2/POJO.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4.pkg2;
+
+public class POJO {
+    private final String name;
+    private double val;
+    private RefClass obj;
+
+    public final String getName() {
+        return name;
+    }
+
+    public final void setVal(double val) {
+        this.val = val;
+    }
+
+    public final double getVal() {
+        return val;
+    }
+
+    public final void setObj(RefClass obj) {
+        this.obj = obj;
+    }
+
+    public final RefClass getObj() {
+        return obj;
+    }
+
+    public POJO(String name, double val, RefClass obj) {
+        this.name = name;
+        this.val = val;
+        this.obj = obj;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/pkg2/RefClass.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4.pkg2;
+
+public class RefClass {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/pkg3/MyProps.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4.pkg3;
+
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.beans.property.SimpleObjectProperty;
+
+public class MyProps {
+    private final DoubleProperty foo = new SimpleDoubleProperty();
+
+    public final DoubleProperty fooProperty() {
+        return foo;
+    }
+
+    public final void setFoo(double foo) {
+        this.foo.set(foo);
+    }
+
+    public final double getFoo() {
+        return foo.get();
+    }
+
+    private final ObjectProperty<MyProps> next = new SimpleObjectProperty<>();
+
+    public final ObjectProperty<MyProps> nextProperty() {
+        return next;
+    }
+
+    public final void setNext(MyProps next) {
+        this.next.set(next);
+    }
+
+    public final MyProps getNext() {
+        return next.get();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/pkg3/POJO.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4.pkg3;
+
+public class POJO {
+    private final String name;
+    private double val;
+    private RefClass obj;
+
+    public final String getName() {
+        return name;
+    }
+
+    public final void setVal(double val) {
+        this.val = val;
+    }
+
+    public final double getVal() {
+        return val;
+    }
+
+    public final void setObj(RefClass obj) {
+        this.obj = obj;
+    }
+
+    public final RefClass getObj() {
+        return obj;
+    }
+
+    public POJO(String name, double val, RefClass obj) {
+        this.name = name;
+        this.val = val;
+        this.obj = obj;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/pkg3/RefClass.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4.pkg3;
+
+public class RefClass {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/pkg4/MyProps.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4.pkg4;
+
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.beans.property.SimpleObjectProperty;
+
+public class MyProps {
+    private final DoubleProperty foo = new SimpleDoubleProperty();
+
+    public final DoubleProperty fooProperty() {
+        return foo;
+    }
+
+    public final void setFoo(double foo) {
+        this.foo.set(foo);
+    }
+
+    public final double getFoo() {
+        return foo.get();
+    }
+
+    private final ObjectProperty<MyProps> next = new SimpleObjectProperty<>();
+
+    public final ObjectProperty<MyProps> nextProperty() {
+        return next;
+    }
+
+    public final void setNext(MyProps next) {
+        this.next.set(next);
+    }
+
+    public final MyProps getNext() {
+        return next.get();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/pkg4/POJO.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4.pkg4;
+
+public class POJO {
+    private final String name;
+    private double val;
+    private RefClass obj;
+
+    public final String getName() {
+        return name;
+    }
+
+    public final void setVal(double val) {
+        this.val = val;
+    }
+
+    public final double getVal() {
+        return val;
+    }
+
+    public final void setObj(RefClass obj) {
+        this.obj = obj;
+    }
+
+    public final RefClass getObj() {
+        return obj;
+    }
+
+    public POJO(String name, double val, RefClass obj) {
+        this.name = name;
+        this.val = val;
+        this.obj = obj;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/pkg4/RefClass.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4.pkg4;
+
+public class RefClass {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/pkg5/MyProps.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4.pkg5;
+
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.beans.property.SimpleObjectProperty;
+
+public class MyProps {
+    private final DoubleProperty foo = new SimpleDoubleProperty();
+
+    public final DoubleProperty fooProperty() {
+        return foo;
+    }
+
+    public final void setFoo(double foo) {
+        this.foo.set(foo);
+    }
+
+    public final double getFoo() {
+        return foo.get();
+    }
+
+    private final ObjectProperty<MyProps> next = new SimpleObjectProperty<>();
+
+    public final ObjectProperty<MyProps> nextProperty() {
+        return next;
+    }
+
+    public final void setNext(MyProps next) {
+        this.next.set(next);
+    }
+
+    public final MyProps getNext() {
+        return next.get();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/pkg5/POJO.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4.pkg5;
+
+public class POJO {
+    private final String name;
+    private double val;
+    private RefClass obj;
+
+    public final String getName() {
+        return name;
+    }
+
+    public final void setVal(double val) {
+        this.val = val;
+    }
+
+    public final double getVal() {
+        return val;
+    }
+
+    public final void setObj(RefClass obj) {
+        this.obj = obj;
+    }
+
+    public final RefClass getObj() {
+        return obj;
+    }
+
+    public POJO(String name, double val, RefClass obj) {
+        this.name = name;
+        this.val = val;
+        this.obj = obj;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp4/java/mymod/myapp4/pkg5/RefClass.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp4.pkg5;
+
+public class RefClass {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp5/java/mymod/module-info.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module mymod {
+    requires javafx.web;
+    requires jdk.jsobject;
+
+    exports myapp5;
+
+    // myapp5.pkg1 is deliberately not listed
+    exports myapp5.pkg2;
+    exports myapp5.pkg3 to javafx.web;
+    opens myapp5.pkg4;
+    opens myapp5.pkg5 to javafx.web;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp5/java/mymod/myapp5/AppJSCallbackExported.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp5;
+
+import javafx.application.Application;
+import javafx.concurrent.Worker;
+import javafx.scene.web.WebView;
+import javafx.stage.Stage;
+import netscape.javascript.JSObject;
+
+import myapp5.pkg2.MyCallback;
+
+import static myapp5.Constants.*;
+
+/**
+ * Modular test application for testing Javascript callback.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppJSCallbackExported extends Application {
+
+    private final MyCallback callback = new MyCallback();
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            System.err.println("ERROR: caught unexpected exception: " + t);
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        try {
+            final WebView webView = new WebView();
+            webView.getEngine().getLoadWorker().stateProperty().addListener((ov, o, n) -> {
+                if (n == Worker.State.SUCCEEDED) {
+                    try {
+                        final JSObject window = (JSObject) webView.getEngine().executeScript("window");
+                        Util.assertNotNull(window);
+                        window.setMember("javaCallback", callback);
+                        webView.getEngine().executeScript("document.getElementById(\"mybtn1\").click()");
+                        Util.assertEquals(1, callback.getCount());
+                        System.exit(ERROR_NONE);
+                    } catch (Throwable t) {
+                        t.printStackTrace(System.err);
+                        System.exit(ERROR_ASSERTION_FAILURE);
+                    }
+                }
+            });
+            webView.getEngine().loadContent(Util.content);
+        } catch (Error | Exception ex) {
+            System.err.println("ERROR: caught unexpected exception: " + ex);
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp5/java/mymod/myapp5/AppJSCallbackOpened.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp5;
+
+import javafx.application.Application;
+import javafx.concurrent.Worker;
+import javafx.scene.web.WebView;
+import javafx.stage.Stage;
+import netscape.javascript.JSObject;
+
+import myapp5.pkg4.MyCallback;
+
+import static myapp5.Constants.*;
+
+/**
+ * Modular test application for testing Javascript callback.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppJSCallbackOpened extends Application {
+
+    private final MyCallback callback = new MyCallback();
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            System.err.println("ERROR: caught unexpected exception: " + t);
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        try {
+            final WebView webView = new WebView();
+            webView.getEngine().getLoadWorker().stateProperty().addListener((ov, o, n) -> {
+                if (n == Worker.State.SUCCEEDED) {
+                    try {
+                        final JSObject window = (JSObject) webView.getEngine().executeScript("window");
+                        Util.assertNotNull(window);
+                        window.setMember("javaCallback", callback);
+                        webView.getEngine().executeScript("document.getElementById(\"mybtn1\").click()");
+                        Util.assertEquals(1, callback.getCount());
+                        System.exit(ERROR_NONE);
+                    } catch (Throwable t) {
+                        t.printStackTrace(System.err);
+                        System.exit(ERROR_ASSERTION_FAILURE);
+                    }
+                }
+            });
+            webView.getEngine().loadContent(Util.content);
+        } catch (Error | Exception ex) {
+            System.err.println("ERROR: caught unexpected exception: " + ex);
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp5/java/mymod/myapp5/AppJSCallbackQualExported.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp5;
+
+import javafx.application.Application;
+import javafx.concurrent.Worker;
+import javafx.scene.web.WebView;
+import javafx.stage.Stage;
+import netscape.javascript.JSObject;
+
+import myapp5.pkg3.MyCallback;
+
+import static myapp5.Constants.*;
+
+/**
+ * Modular test application for testing Javascript callback.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppJSCallbackQualExported extends Application {
+
+    private final MyCallback callback = new MyCallback();
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            System.err.println("ERROR: caught unexpected exception: " + t);
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        try {
+            final WebView webView = new WebView();
+            webView.getEngine().getLoadWorker().stateProperty().addListener((ov, o, n) -> {
+                if (n == Worker.State.SUCCEEDED) {
+                    try {
+                        final JSObject window = (JSObject) webView.getEngine().executeScript("window");
+                        Util.assertNotNull(window);
+                        window.setMember("javaCallback", callback);
+                        webView.getEngine().executeScript("document.getElementById(\"mybtn1\").click()");
+                        Util.assertEquals(0, callback.getCount());
+                        System.exit(ERROR_NONE);
+                    } catch (Throwable t) {
+                        t.printStackTrace(System.err);
+                        System.exit(ERROR_ASSERTION_FAILURE);
+                    }
+                }
+            });
+            webView.getEngine().loadContent(Util.content);
+        } catch (Error | Exception ex) {
+            System.err.println("ERROR: caught unexpected exception: " + ex);
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp5/java/mymod/myapp5/AppJSCallbackQualOpened.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp5;
+
+import javafx.application.Application;
+import javafx.concurrent.Worker;
+import javafx.scene.web.WebView;
+import javafx.stage.Stage;
+import netscape.javascript.JSObject;
+
+import myapp5.pkg5.MyCallback;
+
+import static myapp5.Constants.*;
+
+/**
+ * Modular test application for testing Javascript callback.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppJSCallbackQualOpened extends Application {
+
+    private final MyCallback callback = new MyCallback();
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            System.err.println("ERROR: caught unexpected exception: " + t);
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        try {
+            final WebView webView = new WebView();
+            webView.getEngine().getLoadWorker().stateProperty().addListener((ov, o, n) -> {
+                if (n == Worker.State.SUCCEEDED) {
+                    try {
+                        final JSObject window = (JSObject) webView.getEngine().executeScript("window");
+                        Util.assertNotNull(window);
+                        window.setMember("javaCallback", callback);
+                        webView.getEngine().executeScript("document.getElementById(\"mybtn1\").click()");
+                        Util.assertEquals(1, callback.getCount());
+                        System.exit(ERROR_NONE);
+                    } catch (Throwable t) {
+                        t.printStackTrace(System.err);
+                        System.exit(ERROR_ASSERTION_FAILURE);
+                    }
+                }
+            });
+            webView.getEngine().loadContent(Util.content);
+        } catch (Error | Exception ex) {
+            System.err.println("ERROR: caught unexpected exception: " + ex);
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp5/java/mymod/myapp5/AppJSCallbackUnexported.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp5;
+
+import javafx.application.Application;
+import javafx.concurrent.Worker;
+import javafx.scene.web.WebView;
+import javafx.stage.Stage;
+import netscape.javascript.JSObject;
+
+import myapp5.pkg1.MyCallback;
+
+import static myapp5.Constants.*;
+
+/**
+ * Modular test application for testing Javascript callback.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppJSCallbackUnexported extends Application {
+
+    private final MyCallback callback = new MyCallback();
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            System.err.println("ERROR: caught unexpected exception: " + t);
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    @Override
+    public void start(Stage stage) throws Exception {
+        try {
+            final WebView webView = new WebView();
+            webView.getEngine().getLoadWorker().stateProperty().addListener((ov, o, n) -> {
+                if (n == Worker.State.SUCCEEDED) {
+                    try {
+                        final JSObject window = (JSObject) webView.getEngine().executeScript("window");
+                        Util.assertNotNull(window);
+                        window.setMember("javaCallback", callback);
+                        webView.getEngine().executeScript("document.getElementById(\"mybtn1\").click()");
+                        Util.assertEquals(0, callback.getCount());
+                        System.exit(ERROR_NONE);
+                    } catch (Throwable t) {
+                        t.printStackTrace(System.err);
+                        System.exit(ERROR_ASSERTION_FAILURE);
+                    }
+                }
+            });
+            webView.getEngine().loadContent(Util.content);
+        } catch (Error | Exception ex) {
+            System.err.println("ERROR: caught unexpected exception: " + ex);
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp5/java/mymod/myapp5/Constants.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp5;
+
+public class Constants {
+
+    // NOTE: these constants must match those in test.launchertest.Constants
+    public static final int ERROR_NONE = 2;
+    public static final int ERROR_UNEXPECTED_EXCEPTION = 4;
+
+    public static final int ERROR_ASSERTION_FAILURE = 28;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp5/java/mymod/myapp5/Util.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp5;
+
+public class Util {
+
+    public static final String content = "<!DOCTYPE html>"
+            + "<html>"
+            + "<head>"
+            + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">"
+            + "</head>"
+            + "<button id=\"mybtn1\" type=\"button\" onClick=\"javaCallback.hello()\">Hello</button>"
+            + "</html>";
+
+    public static void assertNotNull(Object o) {
+        if (o == null) {
+            throw new AssertionError("Expected non-null object, but was null");
+        }
+    }
+
+    public static void assertEquals(int expected, int observed) {
+        if (expected != observed) {
+            throw new AssertionError("expected:<" + expected + "> but was:<" + observed + ">");
+        }
+    }
+
+    private Util() {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp5/java/mymod/myapp5/pkg1/MyCallback.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp5.pkg1;
+
+public class MyCallback {
+    private int count = 0;
+
+    public int getCount() {
+        return count;
+    }
+
+    public void hello() {
+        ++count;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp5/java/mymod/myapp5/pkg2/MyCallback.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp5.pkg2;
+
+public class MyCallback {
+    private int count = 0;
+
+    public int getCount() {
+        return count;
+    }
+
+    public void hello() {
+        ++count;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp5/java/mymod/myapp5/pkg3/MyCallback.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp5.pkg3;
+
+public class MyCallback {
+    private int count = 0;
+
+    public int getCount() {
+        return count;
+    }
+
+    public void hello() {
+        ++count;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp5/java/mymod/myapp5/pkg4/MyCallback.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp5.pkg4;
+
+public class MyCallback {
+    private int count = 0;
+
+    public int getCount() {
+        return count;
+    }
+
+    public void hello() {
+        ++count;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp5/java/mymod/myapp5/pkg5/MyCallback.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp5.pkg5;
+
+public class MyCallback {
+    private int count = 0;
+
+    public int getCount() {
+        return count;
+    }
+
+    public void hello() {
+        ++count;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp6/java/mymod/module-info.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module mymod {
+    requires javafx.fxml;
+    requires javafx.graphics;
+
+    exports myapp6;
+
+    // myapp6.pkg1 is deliberately not listed
+    exports myapp6.pkg2;
+    exports myapp6.pkg3 to javafx.fxml;
+    opens myapp6.pkg4;
+    opens myapp6.pkg5 to javafx.fxml;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp6/java/mymod/myapp6/AppFXMLExported.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp6;
+
+import java.io.IOException;
+import java.net.URL;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXMLLoader;
+import javafx.fxml.LoadException;
+import javafx.scene.Node;
+import javafx.scene.layout.StackPane;
+import javafx.scene.text.Text;
+import javafx.stage.Stage;
+import myapp6.pkg2.AnnotatedController;
+import myapp6.pkg2.CustomNode;
+import myapp6.pkg2.SimpleController;
+
+import static myapp6.Constants.*;
+
+/**
+ * Modular test application for testing FXML.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppFXMLExported extends Application {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            System.err.println("ERROR: caught unexpected exception: " + t);
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    // Load test FXML file -- no controller
+    private void doTestNone() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestNone");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        Node fxmlRoot = loader.load();
+
+        // Verify that the root node is a StackPane with the expected ID
+        Util.assertNotNull(fxmlRoot);
+        Util.assertTrue("fxmlRoot is not instance of StackPane", fxmlRoot instanceof StackPane);
+        Util.assertEquals("RootTestNone", fxmlRoot.getId());
+    }
+
+    // Load test FXML file with reference to CustomNode -- no controller
+    private void doTestCustomNode() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestCustomNode");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        Node fxmlRoot = loader.load();
+
+        // Verify that the root node is a StackPane with the expected ID
+        Util.assertNotNull(fxmlRoot);
+        Util.assertTrue("fxmlRoot is not instance of StackPane", fxmlRoot instanceof StackPane);
+        Util.assertEquals("RootTestCustomNode", fxmlRoot.getId());
+
+        // Verify that there is at least one child node
+        ObservableList<Node> children = ((StackPane) fxmlRoot).getChildren();
+        Util.assertNotNull(children);
+        Util.assertFalse("fxmlRoot has no children", children.isEmpty());
+
+        // Verify that the first child is our CustomNode
+        Node child = children.get(0);
+        Util.assertNotNull(child);
+        Util.assertTrue("child is not instance of CustomNode", child instanceof CustomNode);
+        String name = ((CustomNode) child).getName();
+        Util.assertEquals("Duke", name);
+    }
+
+    // Load test FXML file -- SimpleController
+    private void doTestSimple() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestSimple");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        Node fxmlRoot = loader.load();
+
+        // Verify that the controller was constructed and its initialize method called
+        SimpleController controller = loader.getController();
+        Util.assertNotNull(controller);
+        Util.assertTrue("Controller not initialized", controller.isInitialized());
+
+        // Verify that the root node is a StackPane with the expected ID
+        Util.assertNotNull(fxmlRoot);
+        Util.assertTrue("fxmlRoot is not instance of StackPane", fxmlRoot instanceof StackPane);
+        Util.assertEquals("RootTestSimple", fxmlRoot.getId());
+    }
+
+    // Load test FXML file -- AnnotatedController
+    private void doTestAnnotated() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestAnnotated");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        try {
+            Node fxmlRoot = loader.load();
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (LoadException ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    @Override
+    public void start(Stage stage) {
+        try {
+            doTestNone();
+            doTestCustomNode();
+            doTestSimple();
+            doTestAnnotated();
+        } catch (AssertionError ex) {
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_ASSERTION_FAILURE);
+        } catch (Error | Exception ex) {
+            System.err.println("ERROR: caught unexpected exception: " + ex);
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+        Platform.exit();
+    }
+
+    @Override public void stop() {
+        System.exit(ERROR_NONE);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp6/java/mymod/myapp6/AppFXMLOpened.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp6;
+
+import java.io.IOException;
+import java.net.URL;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Node;
+import javafx.scene.layout.StackPane;
+import javafx.scene.text.Text;
+import javafx.stage.Stage;
+import myapp6.pkg4.AnnotatedController;
+import myapp6.pkg4.CustomNode;
+import myapp6.pkg4.SimpleController;
+
+import static myapp6.Constants.*;
+
+/**
+ * Modular test application for testing FXML.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppFXMLOpened extends Application {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            System.err.println("ERROR: caught unexpected exception: " + t);
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    // Load test FXML file -- no controller
+    private void doTestNone() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestNone");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        Node fxmlRoot = loader.load();
+
+        // Verify that the root node is a StackPane with the expected ID
+        Util.assertNotNull(fxmlRoot);
+        Util.assertTrue("fxmlRoot is not instance of StackPane", fxmlRoot instanceof StackPane);
+        Util.assertEquals("RootTestNone", fxmlRoot.getId());
+    }
+
+    // Load test FXML file with reference to CustomNode -- no controller
+    private void doTestCustomNode() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestCustomNode");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        Node fxmlRoot = loader.load();
+
+        // Verify that the root node is a StackPane with the expected ID
+        Util.assertNotNull(fxmlRoot);
+        Util.assertTrue("fxmlRoot is not instance of StackPane", fxmlRoot instanceof StackPane);
+        Util.assertEquals("RootTestCustomNode", fxmlRoot.getId());
+
+        // Verify that there is at least one child node
+        ObservableList<Node> children = ((StackPane) fxmlRoot).getChildren();
+        Util.assertNotNull(children);
+        Util.assertFalse("fxmlRoot has no children", children.isEmpty());
+
+        // Verify that the first child is our CustomNode
+        Node child = children.get(0);
+        Util.assertNotNull(child);
+        Util.assertTrue("child is not instance of CustomNode", child instanceof CustomNode);
+        String name = ((CustomNode) child).getName();
+        Util.assertEquals("Duke", name);
+    }
+
+    // Load test FXML file -- SimpleController
+    private void doTestSimple() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestSimple");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        Node fxmlRoot = loader.load();
+
+        // Verify that the controller was constructed and its initialize method called
+        SimpleController controller = loader.getController();
+        Util.assertNotNull(controller);
+        Util.assertTrue("Controller not initialized", controller.isInitialized());
+
+        // Verify that the root node is a StackPane with the expected ID
+        Util.assertNotNull(fxmlRoot);
+        Util.assertTrue("fxmlRoot is not instance of StackPane", fxmlRoot instanceof StackPane);
+        Util.assertEquals("RootTestSimple", fxmlRoot.getId());
+    }
+
+    // Load test FXML file -- AnnotatedController
+    private void doTestAnnotated() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestAnnotated");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        Node fxmlRoot = loader.load();
+
+        // Verify that the controller was constructed and its initialize method called
+        AnnotatedController controller = loader.getController();
+        Util.assertNotNull(controller);
+        Util.assertTrue("Controller not initialized", controller.isInitialized());
+
+        // Verify that the root node is a StackPane with the expected ID
+        Util.assertNotNull(fxmlRoot);
+        Util.assertTrue("fxmlRoot is not instance of StackPane", fxmlRoot instanceof StackPane);
+        Util.assertEquals("RootTestAnnotated", fxmlRoot.getId());
+
+        // Verify that the root node and text node can be retrieved from the controller
+        StackPane cRoot = controller.getRoot();
+        Util.assertNotNull(cRoot);
+        Util.assertSame(fxmlRoot, cRoot);
+        Text cText = controller.getTheText();
+        Util.assertNotNull(cText);
+        Util.assertEquals("theText", cText.getId());
+    }
+
+    @Override
+    public void start(Stage stage) {
+        try {
+            doTestNone();
+            doTestCustomNode();
+            doTestSimple();
+            doTestAnnotated();
+        } catch (AssertionError ex) {
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_ASSERTION_FAILURE);
+        } catch (Error | Exception ex) {
+            System.err.println("ERROR: caught unexpected exception: " + ex);
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+        Platform.exit();
+    }
+
+    @Override public void stop() {
+        System.exit(ERROR_NONE);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp6/java/mymod/myapp6/AppFXMLQualExported.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp6;
+
+import java.io.IOException;
+import java.net.URL;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXMLLoader;
+import javafx.fxml.LoadException;
+import javafx.scene.Node;
+import javafx.scene.layout.StackPane;
+import javafx.scene.text.Text;
+import javafx.stage.Stage;
+import myapp6.pkg3.AnnotatedController;
+import myapp6.pkg3.CustomNode;
+import myapp6.pkg3.SimpleController;
+
+import static myapp6.Constants.*;
+
+/**
+ * Modular test application for testing FXML.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppFXMLQualExported extends Application {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            System.err.println("ERROR: caught unexpected exception: " + t);
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    // Load test FXML file -- no controller
+    private void doTestNone() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestNone");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        Node fxmlRoot = loader.load();
+
+        // Verify that the root node is a StackPane with the expected ID
+        Util.assertNotNull(fxmlRoot);
+        Util.assertTrue("fxmlRoot is not instance of StackPane", fxmlRoot instanceof StackPane);
+        Util.assertEquals("RootTestNone", fxmlRoot.getId());
+    }
+
+    // Load test FXML file with reference to CustomNode -- no controller
+    private void doTestCustomNode() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestCustomNode");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        try {
+            Node fxmlRoot = loader.load();
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (LoadException ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    // Load test FXML file -- SimpleController
+    private void doTestSimple() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestSimple");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        try {
+            Node fxmlRoot = loader.load();
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (LoadException ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    // Load test FXML file -- AnnotatedController
+    private void doTestAnnotated() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestAnnotated");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        try {
+            Node fxmlRoot = loader.load();
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (LoadException ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    @Override
+    public void start(Stage stage) {
+        try {
+            doTestNone();
+            doTestCustomNode();
+            doTestSimple();
+            doTestAnnotated();
+        } catch (AssertionError ex) {
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_ASSERTION_FAILURE);
+        } catch (Error | Exception ex) {
+            System.err.println("ERROR: caught unexpected exception: " + ex);
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+        Platform.exit();
+    }
+
+    @Override public void stop() {
+        System.exit(ERROR_NONE);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp6/java/mymod/myapp6/AppFXMLQualOpened.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp6;
+
+import java.io.IOException;
+import java.net.URL;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Node;
+import javafx.scene.layout.StackPane;
+import javafx.scene.text.Text;
+import javafx.stage.Stage;
+import myapp6.pkg5.AnnotatedController;
+import myapp6.pkg5.CustomNode;
+import myapp6.pkg5.SimpleController;
+
+import static myapp6.Constants.*;
+
+/**
+ * Modular test application for testing FXML.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppFXMLQualOpened extends Application {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            System.err.println("ERROR: caught unexpected exception: " + t);
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    // Load test FXML file -- no controller
+    private void doTestNone() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestNone");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        Node fxmlRoot = loader.load();
+
+        // Verify that the root node is a StackPane with the expected ID
+        Util.assertNotNull(fxmlRoot);
+        Util.assertTrue("fxmlRoot is not instance of StackPane", fxmlRoot instanceof StackPane);
+        Util.assertEquals("RootTestNone", fxmlRoot.getId());
+    }
+
+    // Load test FXML file with reference to CustomNode -- no controller
+    private void doTestCustomNode() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestCustomNode");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        Node fxmlRoot = loader.load();
+
+        // Verify that the root node is a StackPane with the expected ID
+        Util.assertNotNull(fxmlRoot);
+        Util.assertTrue("fxmlRoot is not instance of StackPane", fxmlRoot instanceof StackPane);
+        Util.assertEquals("RootTestCustomNode", fxmlRoot.getId());
+
+        // Verify that there is at least one child node
+        ObservableList<Node> children = ((StackPane) fxmlRoot).getChildren();
+        Util.assertNotNull(children);
+        Util.assertFalse("fxmlRoot has no children", children.isEmpty());
+
+        // Verify that the first child is our CustomNode
+        Node child = children.get(0);
+        Util.assertNotNull(child);
+        Util.assertTrue("child is not instance of CustomNode", child instanceof CustomNode);
+        String name = ((CustomNode) child).getName();
+        Util.assertEquals("Duke", name);
+    }
+
+    // Load test FXML file -- SimpleController
+    private void doTestSimple() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestSimple");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        Node fxmlRoot = loader.load();
+
+        // Verify that the controller was constructed and its initialize method called
+        SimpleController controller = loader.getController();
+        Util.assertNotNull(controller);
+        Util.assertTrue("Controller not initialized", controller.isInitialized());
+
+        // Verify that the root node is a StackPane with the expected ID
+        Util.assertNotNull(fxmlRoot);
+        Util.assertTrue("fxmlRoot is not instance of StackPane", fxmlRoot instanceof StackPane);
+        Util.assertEquals("RootTestSimple", fxmlRoot.getId());
+    }
+
+    // Load test FXML file -- AnnotatedController
+    private void doTestAnnotated() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestAnnotated");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        Node fxmlRoot = loader.load();
+
+        // Verify that the controller was constructed and its initialize method called
+        AnnotatedController controller = loader.getController();
+        Util.assertNotNull(controller);
+        Util.assertTrue("Controller not initialized", controller.isInitialized());
+
+        // Verify that the root node is a StackPane with the expected ID
+        Util.assertNotNull(fxmlRoot);
+        Util.assertTrue("fxmlRoot is not instance of StackPane", fxmlRoot instanceof StackPane);
+        Util.assertEquals("RootTestAnnotated", fxmlRoot.getId());
+
+        // Verify that the root node and text node can be retrieved from the controller
+        StackPane cRoot = controller.getRoot();
+        Util.assertNotNull(cRoot);
+        Util.assertSame(fxmlRoot, cRoot);
+        Text cText = controller.getTheText();
+        Util.assertNotNull(cText);
+        Util.assertEquals("theText", cText.getId());
+    }
+
+    @Override
+    public void start(Stage stage) {
+        try {
+            doTestNone();
+            doTestCustomNode();
+            doTestSimple();
+            doTestAnnotated();
+        } catch (AssertionError ex) {
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_ASSERTION_FAILURE);
+        } catch (Error | Exception ex) {
+            System.err.println("ERROR: caught unexpected exception: " + ex);
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+        Platform.exit();
+    }
+
+    @Override public void stop() {
+        System.exit(ERROR_NONE);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp6/java/mymod/myapp6/AppFXMLUnexported.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp6;
+
+import java.io.IOException;
+import java.net.URL;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXMLLoader;
+import javafx.fxml.LoadException;
+import javafx.scene.Node;
+import javafx.scene.layout.StackPane;
+import javafx.scene.text.Text;
+import javafx.stage.Stage;
+import myapp6.pkg1.AnnotatedController;
+import myapp6.pkg1.CustomNode;
+import myapp6.pkg1.SimpleController;
+
+import static myapp6.Constants.*;
+
+/**
+ * Modular test application for testing FXML.
+ * This is launched by ModuleLauncherTest.
+ */
+public class AppFXMLUnexported extends Application {
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Application.launch(args);
+        } catch (Throwable t) {
+            System.err.println("ERROR: caught unexpected exception: " + t);
+            t.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+    }
+
+    // Load test FXML file -- no controller
+    private void doTestNone() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestNone");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        Node fxmlRoot = loader.load();
+
+        // Verify that the root node is a StackPane with the expected ID
+        Util.assertNotNull(fxmlRoot);
+        Util.assertTrue("fxmlRoot is not instance of StackPane", fxmlRoot instanceof StackPane);
+        Util.assertEquals("RootTestNone", fxmlRoot.getId());
+    }
+
+    // Load test FXML file with reference to CustomNode -- no controller
+    private void doTestCustomNode() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestCustomNode");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        try {
+            Node fxmlRoot = loader.load();
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (LoadException ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    // Load test FXML file -- SimpleController
+    private void doTestSimple() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestSimple");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        try {
+            Node fxmlRoot = loader.load();
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (LoadException ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    // Load test FXML file -- AnnotatedController
+    private void doTestAnnotated() throws IOException {
+        final URL fxmlURL = Util.getURL(SimpleController.class, "TestAnnotated");
+
+        FXMLLoader loader = new FXMLLoader(fxmlURL);
+        try {
+            Node fxmlRoot = loader.load();
+            throw new AssertionError("ERROR: did not get the expected exception");
+        } catch (LoadException ex) {
+            ex.printStackTrace();
+        }
+    }
+
+    @Override
+    public void start(Stage stage) {
+        try {
+            doTestNone();
+            doTestCustomNode();
+            doTestSimple();
+            doTestAnnotated();
+        } catch (AssertionError ex) {
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_ASSERTION_FAILURE);
+        } catch (Error | Exception ex) {
+            System.err.println("ERROR: caught unexpected exception: " + ex);
+            ex.printStackTrace(System.err);
+            System.exit(ERROR_UNEXPECTED_EXCEPTION);
+        }
+        Platform.exit();
+    }
+
+    @Override public void stop() {
+        System.exit(ERROR_NONE);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp6/java/mymod/myapp6/Constants.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp6;
+
+public class Constants {
+
+    public static final int SHOWTIME = 2500;
+
+    // NOTE: these constants must match those in test.launchertest.Constants
+    public static final int ERROR_NONE = 2;
+    public static final int ERROR_UNEXPECTED_EXCEPTION = 4;
+
+    public static final int ERROR_ASSERTION_FAILURE = 28;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/system/src/testapp6/java/mymod/myapp6/Util.java	Thu May 04 08:45:51 2017 -0700
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package myapp6;
+
+import java.net.URL;
+
+public class Util {
+
+    public static final URL getURL(Class clazz, String name) {
+        final String theName = name + ".fxml";