On Wed, 26 Mar 2025 20:00:21 GMT, Sergey Bylokhov <s...@openjdk.org> wrote:
>> Also, in the code above, it is not necessary to filter out private methods >> since iface.getMethods() returns only public > > The last question I have is about the module system when the interface is not > exported. I have tried to check various cases using the script below: #!/bin/bash rm -rf app/ bean/ classes/ bean.jar app.jar mkdir -p bean/closed bean/exported app/app cat <<EOF > bean/module-info.java module bean { exports bean.exported; } EOF ######################## # non-exported classes ######################## cat <<EOF > bean/closed/ClosedI.java package bean.closed; public interface ClosedI { default int getBoo() { return 0; } default void setBoo(int a) { } } EOF cat <<EOF > bean/closed/ClosedP.java package bean.closed; public class ClosedP { public int getToo() { return 0; } public void setToo(int a) { } } EOF cat <<EOF > bean/closed/ClosedNonPublic.java package bean.closed; class ClosedNonPublic { public int getNoo() { return 0; } public void setNoo(int a) { } } EOF ######################## # exported classes ######################## cat <<EOF > bean/exported/OpenClass.java package bean.exported; import bean.closed.ClosedI; public class OpenClass implements OpenI, ClosedI { } EOF cat <<EOF > bean/exported/ClassCP.java package bean.exported; import bean.closed.ClosedP; public class ClassCP extends ClosedP { } EOF cat <<EOF > bean/exported/OpenP.java package bean.exported; public class OpenP { public int getZoo() { return 0; } public void setZoo(int a) { } } EOF cat <<EOF > bean/exported/ClassOP.java package bean.exported; import bean.exported.OpenP; public class ClassOP extends OpenP { } EOF cat <<EOF > bean/exported/OpenI.java package bean.exported; public interface OpenI { default int getFoo() { return 0; } default void setFoo(int a) { } } EOF ######################## # main app ######################## cat <<EOF > app/module-info.java module app { requires bean; requires java.desktop; exports app; } EOF cat <<EOF > app/app/TestBean.java package app; import bean.exported.ClassCP; import bean.exported.ClassOP; import bean.exported.OpenClass; import java.beans.*; import java.io.IOException; import java.lang.reflect.Method; import java.util.Arrays; public final class TestBean { public static void main(String[] args) throws Exception { System.out.println("\n*** Check exported class implementing two interfaces: exported and closed ***"); test(OpenClass.class); System.out.println("\n*** Check exported class extended from an exported class ***"); test(ClassOP.class); System.out.println("\n*** Check exported class extended from a closed class ***"); test(ClassCP.class); System.out.println("\n*** Check exported class via reflection ***"); test(Class.forName("bean.exported.OpenP")); System.out.println("\n*** Check closed class via reflection ***"); test(Class.forName("bean.closed.ClosedP")); System.out.println("\n*** Check closed non-public class via reflection ***"); test(Class.forName("bean.closed.ClosedNonPublic")); System.out.println("\n*** Check exported bean implementing two interfaces: exported and closed ***"); testBean(new OpenClass()); System.out.println("\n*** Check exported bean extended from an exported class ***"); testBean(new ClassOP()); System.out.println("\n*** Check exported bean extended from a closed class ***"); testBean(new ClassCP()); System.out.println("\n*** Check exported bean via JavaBeans instantiation ***"); testBean(createBean("bean.exported.OpenP")); System.out.println("\n*** Check closed bean via JavaBeans instantiation ***"); testBean(createBean("bean.closed.ClosedP")); System.out.println("\n*** Check closed non-public bean via JavaBeans instantiation ***"); testBean(createBean("bean.closed.ClosedNonPublic")); System.out.println("\n*** Check exported class via reflection ***"); testBean(Class.forName("bean.exported.OpenP").getDeclaredConstructor().newInstance()); //System.out.println("\n*** Check closed class via reflection ***"); //testBean(Class.forName("bean.closed.ClosedP").getDeclaredConstructor().newInstance()); //System.out.println("\n*** Check closed non-public class via reflection ***"); //testBean(Class.forName("bean.closed.ClosedNonPublic").getDeclaredConstructor().newInstance()); } private static void test(Class<?> beanClass) throws Exception { var info = java.beans.Introspector.getBeanInfo(beanClass, Object.class); System.out.println(info.getBeanDescriptor()); System.out.println("--- properties") for (var desc : info.getPropertyDescriptors()) { System.out.println(desc.getName()); System.out.println("\tRead: " + desc.getReadMethod()); System.out.println("\tWrite: " + desc.getWriteMethod()); } } private static void testBean(Object bean) throws Exception { if (bean == null) { return; } Class<?> beanClass = bean.getClass(); var info = java.beans.Introspector.getBeanInfo(beanClass, Object.class); System.out.println(info.getBeanDescriptor()); System.out.println("--- properties"); for (var desc : info.getPropertyDescriptors()) { System.out.println(desc.getName()); Method readMethod = desc.getReadMethod(); System.out.println("\tRead: " + readMethod); System.out.println("\tWrite: " + desc.getWriteMethod()); try { Object value = readMethod.invoke(bean); System.out.println("\tRead: " + readMethod.getName() + " -> " + value); } catch (Exception e) { System.out.println("\tRead: " + readMethod.getName() + " -> ERROR: " + e); } } } private static Object createBean(String className) { try { return Beans.instantiate(ClassLoader.getSystemClassLoader(), className); } catch (IOException | ClassNotFoundException e) { System.out.println("ERROR: Failed to instantiate bean: " + className); return null; } } } EOF javac -d classes bean/module-info.java bean/closed/*.java bean/exported/*.java jar --create --file bean.jar -C classes . rm -rf classes javac -d classes --module-path bean.jar app/module-info.java app/app/TestBean.java jar --create --file app.jar -C classes . rm -rf classes if [[ "$OSTYPE" == "cygwin" ]]; then SEP=";" else SEP=":" fi echo "Start..." java --module-path "bean.jar${SEP}app.jar" --module app/app.TestBean The new implementation of default methods in JavaBeans for non-exported interfaces behaves similarly to that for non-exported classes: For example, in the script above, we create an `OpenClass` that implements both exported and non-exported interfaces. The Introspector reports properties for "both interfaces", but if we attempt to use the property from the non-exported interface, we will get an exception. This is similar to the situation where we have a non-exported parent class for an exported class: `ClassCP`: the Introspector will report the property for the parent class, but it will not be possible to use it due to an exception at runtime. @AlanBateman I found that you worked on updating JavaBeans for modules, see [this commit](https://hg.openjdk.org/jigsaw/jake/jdk/rev/e8703be87031). Do you remember if the work was completed and if the current behavior described above in this message is correct? I'm curious because that commit aimed to skip introspecting non-exported classes/fields/methods, [link ](https://github.com/openjdk/jdk/blob/24833403b6b93ca464720f00de0e8bd5e1c140be/src/java.desktop/share/classes/com/sun/beans/finder/MethodFinder.java#L136)to the current code. @aivanov-jdk please take a look, probably I am missing something? ------------- PR Review Comment: https://git.openjdk.org/jdk/pull/23443#discussion_r2015416746