Rony G. Flatscher
2018-01-15 20:56:51 UTC
Finally having gained enough time to start rewriting the reflection part for a bridge between a
scripting language (ooRexx) and Java 9.
From past discussions on this list my view upon accessing members in superclasses that are protected
in Java 9 is possible as such protected members are regarded to be public (they are accessible) from
their subclasses.
To test against the new module system I have created three simple modules (only what is relevant to
this problem is given):
* "mod_A": defines a package "mtest1" with an abstract public class "Class01A" that implements an
interface "I01A", its "module-info.java" reads:
"module mod_A { exports mtest1; }"
* "mod_B": defines a package "mtest2" with a public "Class02A" which extends "mtest1.Class01A" and
implements an interface "I02A" which extends "mtest1.I01A", its "module-info.java" reads:
"module mod_B { requires mod_A; exports mtest2 to mod_C; }
* "mod_C": defines a package "mtest2" with a public "Class03A" which extends "mtest2.Class02A",
its "module-info.java" reads:
"module mod_C { exports mtest3; requires mod_B; }"
The code doing the reflection resides in the unnamed module for the time being (it eventually will
be part of a module).
Running the script code is done against the following Java settings:
-cp "%CLASSPATH%" --module-path F:\java9modules\out --add-modules mod_A,mod_B,mod_C
In the first round reflecting Fields is used as a testbed. The reflection code at this stage is able
to successfully skip over the closed "mod_B" module and arrive at "mod_A" classes. However,
reflecting for "Class03A" instance is not able to access the defined *protected* static field
"myClassName" in the superclass "Class01A" (the String value of that static field is:
"class-mtest1.Class01A")!
The debug output with the trailing stack trace for the runtime error is:
about to load class [mtest3.Class03A]
loaded, clz~toString: [class mtest3.Class03A]
[***@4973813a] package: [package mtest3] module: [module mod_C]
***@6fb0d3ed -> reflect(rru):
***@16f7c8c1, field values:
---> rajo =[***@24a35978]
invocationType=[GET_FIELD_VALUE]
reflectionType=[REFLECT_FIELD]
bStrict =[false]
beanName =[***@1563da5]
bean =[***@1563da5] instanceof Class? [false]
beanClz =[class mtest3.Class03A]
memberName =[MYCLASSNAME]
rexxArgs[] =[[Ljava.lang.String;@df27fae], rexxArgs.length =[3]: [GETFIELDVALUE,
***@1563da5, MYCLASSNAME]
tmpRexxArgs[] =[[Ljava.lang.String;@704921a5], tmpRexxArgs.length=[0]: []
funcArgs[] =[[Ljava.lang.Object;@727803de], funcArgs.length =[0]: []
bReturnJSO =[false]
bTryCoercions =[true]
<---
\\// RexxReflectJava9.reflectField: (1) in tmpClz.getSuperclass() loop:
tmpClz=[mtest2.Class02A], BEFORE isExported()
\\// RexxReflectJava9.reflectField: package of tmpClz not EXPORTED, hence SKIPPING tmpClz:
[mtest2.Class02A]
\\// RexxReflectJava9.reflectField: now checking --->
tmpClz=[mtest1.Class01A]
\\// RexxReflectJava9.reflectField: (1) in tmpClz.getSuperclass() loop:
tmpClz=[mtest1.Class01A], BEFORE isExported()
//\\ RexxReflectJava9.reflectField: (2) in tmpClz.getSuperclass() loop:
tmpClz=[mtest1.Class01A], AFTER isExported()
RexxReflectJava9.processField(), arrived: -> [GET_FIELD_VALUE], tmpField=[protected static
java.lang.String mtest1.Class01A.myClassName]: field=[MYCLASSNAME] in
object=[***@1563da5]
RexxReflectJava9.processField(): => [GET_FIELD_VALUE]: found field=[MYCLASSNAME] in
object=[***@1563da5/***@1563da5]
oops GET-operation: tmpField "myClassName" caused exception "java.lang.IllegalAccessException:
class org.rexxla.bsf.engines.rexx.RexxReflectJava9 cannot access a member of class
mtest1.Class01A (in module mod_A) with modifiers "protected static""
java.lang.reflect.InaccessibleObjectException: Unable to make field protected static
java.lang.String mtest1.Class01A.myClassName accessible: module mod_A does not "opens mtest1"
to unnamed module @16022d9d
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(Unknown Source)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(Unknown Source)
at java.base/java.lang.reflect.Field.checkCanSetAccessible(Unknown Source)
at java.base/java.lang.reflect.Field.setAccessible(Unknown Source)
at org.rexxla.bsf.engines.rexx.RexxReflectJava9.processField(RexxReflectJava9.java:294)
at org.rexxla.bsf.engines.rexx.RexxReflectJava9.reflectField(RexxReflectJava9.java:113)
at org.rexxla.bsf.engines.rexx.RexxReflectJava9.reflect(RexxReflectJava9.java:59)
at org.rexxla.bsf.engines.rexx.RexxAndJava.javaCallBSF(RexxAndJava.java:3247)
at org.rexxla.bsf.engines.rexx.RexxAndJava.javaCallBSF(RexxAndJava.java:4163)
at org.rexxla.bsf.engines.rexx.RexxAndJava.jniRexxRunProgram(Native Method)
at org.rexxla.bsf.engines.rexx.RexxEngine.apply(RexxEngine.java:1153)
at org.rexxla.bsf.RexxDispatcher.main(RexxDispatcher.java:158)
Doing the comparable operation - accessing a field named "myClassName" in Java code from
"mtest3.Class03a" (in the main method) succeeds!
Here is the output of running "mtest3.Class3A" for comparison:
F:\java9modules>java --module-path out -m mod_C/mtest3.Class03A
class mtest3.Class03A.main() ...
getMyClassNameStatic()=[class-mtest1.Class01A]
myClassName =[class-mtest1.Class01A]
getMyName1() =[from Class01A (static)]
o.getMyName2() =[from Class02A (instance)]
o.myClassName =[class-mtest1.Class01A]
So the question is, how can I reflectively access "mtest1.Class01A" static protected field
"myClassName" from "mtest3.Class03a" in Java 9?
The Java code for "mtest1.Class01A" (in "mod_A"), "mtest2.Class02A" (in "mod_B") and
"mtest3.Class03A" (in "mod_C") is given below.
---rony
"mod_A":
package mtest1;
abstract public class Class01A implements I01A
{
protected static String myClassName = "class-mtest1.Class01A";
protected static String myName1 = "from Class01A (static)";
protected String myName2 = "from Class01A (instance)";
public static void main (String args[]) {
System.err.println(Class01A.class+".main() ...");
}
public static String getMyClassNameStatic() // static method in interface
{
return myClassName;
}
abstract public String getMyClassName() ; // default method in interface
static protected String getMyName1()
{
return myName1;
}
abstract protected String getMyName2();
}
"mod_B":
package mtest2;
public class Class02A extends mtest1.Class01A implements I02A
{
public static String myName1 = "from Class02A (static)";
public String myName2 = "from Class02A (instance)";
public static void main (String args[]) {
System.err.println(Class02A.class+".main() ...");
System.err.println(" getMyClassNameStatic()=["+ getMyClassNameStatic()+"]");
System.err.println(" getMyName1() =["+ getMyName1() +"]");
Class02A o=new Class02A();
System.err.println(" o.getMyName2() =["+o.getMyName2() +"]");
System.err.println(" o.getMyClassName() =["+o.getMyClassName() +"]");
}
public String getMyClassName()
{
return myClassName;
}
protected String getMyName2()
{
return myName2;
}
}
"mod_C":
package mtest3;
public class Class03A extends mtest2.Class02A
{
public static void main (String args[]) {
System.err.println(Class03A.class+".main() ...");
System.err.println(" getMyClassNameStatic()=["+ getMyClassNameStatic()+"]");
System.err.println(" myClassName =["+ myClassName +"]");
System.err.println(" getMyName1() =["+ getMyName1() +"]");
Class03A o=new Class03A();
System.err.println(" o.getMyName2() =["+o.getMyName2() +"]");
System.err.println(" o.myClassName =["+o.myClassName +"]");
}
}
for completeness the Interface classes:
"mod_A":
package mtest1;
public interface I01A
{
static public String getMyClassNameStatic() // static method in interface
{
System.err.println("\t<<<(static public getMyClassNameStatic() method from
["+mtest1.I01A.class+"])>>>");
return "interface-mtest1.I01A"; // myClassName;
}
default public String getMyClassName() // default method in interface
{
System.err.println("\t<<<(default public getMyClassName() method from
["+mtest1.I01A.class+"])>>>");
return "interface-mtest1.I01A"; // myClassName;
}
}
"mod_B":
package mtest2;
public interface I02A extends mtest1.I01A
{
static public String getMyClassNameStatic() // static method in interface
{
System.err.println("\t<<<(static public getMyClassNameStatic() method from
["+mtest2.I02A.class+"])>>>");
return "interface-mtest2.I02A"; // myClassName;
}
default public String getMyClassName() // default method in interface
{
System.err.println("\t<<<(default public getMyClassName() method from
["+mtest2.I02A.class+"])>>>");
return "interface-mtest2.I02A"; // myClassName;
}
}
scripting language (ooRexx) and Java 9.
From past discussions on this list my view upon accessing members in superclasses that are protected
in Java 9 is possible as such protected members are regarded to be public (they are accessible) from
their subclasses.
To test against the new module system I have created three simple modules (only what is relevant to
this problem is given):
* "mod_A": defines a package "mtest1" with an abstract public class "Class01A" that implements an
interface "I01A", its "module-info.java" reads:
"module mod_A { exports mtest1; }"
* "mod_B": defines a package "mtest2" with a public "Class02A" which extends "mtest1.Class01A" and
implements an interface "I02A" which extends "mtest1.I01A", its "module-info.java" reads:
"module mod_B { requires mod_A; exports mtest2 to mod_C; }
* "mod_C": defines a package "mtest2" with a public "Class03A" which extends "mtest2.Class02A",
its "module-info.java" reads:
"module mod_C { exports mtest3; requires mod_B; }"
The code doing the reflection resides in the unnamed module for the time being (it eventually will
be part of a module).
Running the script code is done against the following Java settings:
-cp "%CLASSPATH%" --module-path F:\java9modules\out --add-modules mod_A,mod_B,mod_C
In the first round reflecting Fields is used as a testbed. The reflection code at this stage is able
to successfully skip over the closed "mod_B" module and arrive at "mod_A" classes. However,
reflecting for "Class03A" instance is not able to access the defined *protected* static field
"myClassName" in the superclass "Class01A" (the String value of that static field is:
"class-mtest1.Class01A")!
The debug output with the trailing stack trace for the runtime error is:
about to load class [mtest3.Class03A]
loaded, clz~toString: [class mtest3.Class03A]
[***@4973813a] package: [package mtest3] module: [module mod_C]
***@6fb0d3ed -> reflect(rru):
***@16f7c8c1, field values:
---> rajo =[***@24a35978]
invocationType=[GET_FIELD_VALUE]
reflectionType=[REFLECT_FIELD]
bStrict =[false]
beanName =[***@1563da5]
bean =[***@1563da5] instanceof Class? [false]
beanClz =[class mtest3.Class03A]
memberName =[MYCLASSNAME]
rexxArgs[] =[[Ljava.lang.String;@df27fae], rexxArgs.length =[3]: [GETFIELDVALUE,
***@1563da5, MYCLASSNAME]
tmpRexxArgs[] =[[Ljava.lang.String;@704921a5], tmpRexxArgs.length=[0]: []
funcArgs[] =[[Ljava.lang.Object;@727803de], funcArgs.length =[0]: []
bReturnJSO =[false]
bTryCoercions =[true]
<---
\\// RexxReflectJava9.reflectField: (1) in tmpClz.getSuperclass() loop:
tmpClz=[mtest2.Class02A], BEFORE isExported()
\\// RexxReflectJava9.reflectField: package of tmpClz not EXPORTED, hence SKIPPING tmpClz:
[mtest2.Class02A]
\\// RexxReflectJava9.reflectField: now checking --->
tmpClz=[mtest1.Class01A]
\\// RexxReflectJava9.reflectField: (1) in tmpClz.getSuperclass() loop:
tmpClz=[mtest1.Class01A], BEFORE isExported()
//\\ RexxReflectJava9.reflectField: (2) in tmpClz.getSuperclass() loop:
tmpClz=[mtest1.Class01A], AFTER isExported()
RexxReflectJava9.processField(), arrived: -> [GET_FIELD_VALUE], tmpField=[protected static
java.lang.String mtest1.Class01A.myClassName]: field=[MYCLASSNAME] in
object=[***@1563da5]
RexxReflectJava9.processField(): => [GET_FIELD_VALUE]: found field=[MYCLASSNAME] in
object=[***@1563da5/***@1563da5]
oops GET-operation: tmpField "myClassName" caused exception "java.lang.IllegalAccessException:
class org.rexxla.bsf.engines.rexx.RexxReflectJava9 cannot access a member of class
mtest1.Class01A (in module mod_A) with modifiers "protected static""
java.lang.reflect.InaccessibleObjectException: Unable to make field protected static
java.lang.String mtest1.Class01A.myClassName accessible: module mod_A does not "opens mtest1"
to unnamed module @16022d9d
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(Unknown Source)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(Unknown Source)
at java.base/java.lang.reflect.Field.checkCanSetAccessible(Unknown Source)
at java.base/java.lang.reflect.Field.setAccessible(Unknown Source)
at org.rexxla.bsf.engines.rexx.RexxReflectJava9.processField(RexxReflectJava9.java:294)
at org.rexxla.bsf.engines.rexx.RexxReflectJava9.reflectField(RexxReflectJava9.java:113)
at org.rexxla.bsf.engines.rexx.RexxReflectJava9.reflect(RexxReflectJava9.java:59)
at org.rexxla.bsf.engines.rexx.RexxAndJava.javaCallBSF(RexxAndJava.java:3247)
at org.rexxla.bsf.engines.rexx.RexxAndJava.javaCallBSF(RexxAndJava.java:4163)
at org.rexxla.bsf.engines.rexx.RexxAndJava.jniRexxRunProgram(Native Method)
at org.rexxla.bsf.engines.rexx.RexxEngine.apply(RexxEngine.java:1153)
at org.rexxla.bsf.RexxDispatcher.main(RexxDispatcher.java:158)
Doing the comparable operation - accessing a field named "myClassName" in Java code from
"mtest3.Class03a" (in the main method) succeeds!
Here is the output of running "mtest3.Class3A" for comparison:
F:\java9modules>java --module-path out -m mod_C/mtest3.Class03A
class mtest3.Class03A.main() ...
getMyClassNameStatic()=[class-mtest1.Class01A]
myClassName =[class-mtest1.Class01A]
getMyName1() =[from Class01A (static)]
o.getMyName2() =[from Class02A (instance)]
o.myClassName =[class-mtest1.Class01A]
So the question is, how can I reflectively access "mtest1.Class01A" static protected field
"myClassName" from "mtest3.Class03a" in Java 9?
The Java code for "mtest1.Class01A" (in "mod_A"), "mtest2.Class02A" (in "mod_B") and
"mtest3.Class03A" (in "mod_C") is given below.
---rony
"mod_A":
package mtest1;
abstract public class Class01A implements I01A
{
protected static String myClassName = "class-mtest1.Class01A";
protected static String myName1 = "from Class01A (static)";
protected String myName2 = "from Class01A (instance)";
public static void main (String args[]) {
System.err.println(Class01A.class+".main() ...");
}
public static String getMyClassNameStatic() // static method in interface
{
return myClassName;
}
abstract public String getMyClassName() ; // default method in interface
static protected String getMyName1()
{
return myName1;
}
abstract protected String getMyName2();
}
"mod_B":
package mtest2;
public class Class02A extends mtest1.Class01A implements I02A
{
public static String myName1 = "from Class02A (static)";
public String myName2 = "from Class02A (instance)";
public static void main (String args[]) {
System.err.println(Class02A.class+".main() ...");
System.err.println(" getMyClassNameStatic()=["+ getMyClassNameStatic()+"]");
System.err.println(" getMyName1() =["+ getMyName1() +"]");
Class02A o=new Class02A();
System.err.println(" o.getMyName2() =["+o.getMyName2() +"]");
System.err.println(" o.getMyClassName() =["+o.getMyClassName() +"]");
}
public String getMyClassName()
{
return myClassName;
}
protected String getMyName2()
{
return myName2;
}
}
"mod_C":
package mtest3;
public class Class03A extends mtest2.Class02A
{
public static void main (String args[]) {
System.err.println(Class03A.class+".main() ...");
System.err.println(" getMyClassNameStatic()=["+ getMyClassNameStatic()+"]");
System.err.println(" myClassName =["+ myClassName +"]");
System.err.println(" getMyName1() =["+ getMyName1() +"]");
Class03A o=new Class03A();
System.err.println(" o.getMyName2() =["+o.getMyName2() +"]");
System.err.println(" o.myClassName =["+o.myClassName +"]");
}
}
for completeness the Interface classes:
"mod_A":
package mtest1;
public interface I01A
{
static public String getMyClassNameStatic() // static method in interface
{
System.err.println("\t<<<(static public getMyClassNameStatic() method from
["+mtest1.I01A.class+"])>>>");
return "interface-mtest1.I01A"; // myClassName;
}
default public String getMyClassName() // default method in interface
{
System.err.println("\t<<<(default public getMyClassName() method from
["+mtest1.I01A.class+"])>>>");
return "interface-mtest1.I01A"; // myClassName;
}
}
"mod_B":
package mtest2;
public interface I02A extends mtest1.I01A
{
static public String getMyClassNameStatic() // static method in interface
{
System.err.println("\t<<<(static public getMyClassNameStatic() method from
["+mtest2.I02A.class+"])>>>");
return "interface-mtest2.I02A"; // myClassName;
}
default public String getMyClassName() // default method in interface
{
System.err.println("\t<<<(default public getMyClassName() method from
["+mtest2.I02A.class+"])>>>");
return "interface-mtest2.I02A"; // myClassName;
}
}