[
https://issues.apache.org/jira/browse/LANG-1685?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18020437#comment-18020437
]
Iwauo Tajima commented on LANG-1685:
------------------------------------
I am facing the exact same issue while migrating legacy systems to JDK 21. I
believe this issue can be resolved by using the following helper method:
{code:java}
private boolean isAccessible(Class<?> targetClass) {
if (GET_MODULE == null) {
return true; // JDK 8 (no module system)
}
try {
Object targetModule = GET_MODULE.invoke(targetClass);
Object selfModule = GET_MODULE.invoke(getClass());
return (Boolean) IS_OPEN.invoke(
targetModule,
targetClass.getPackage().getName(),
selfModule);
} catch (IllegalAccessException | InvocationTargetException e) {
return false;
}
}
private static final Method GET_MODULE = initializeGetModuleMethod();
private static Method initializeGetModuleMethod() {
try {
return Class.class.getMethod("getModule");
} catch (NoSuchMethodException e) {
return null;
}
}
private static final Method IS_OPEN = initializeIsOpenMethod();
private static Method initializeIsOpenMethod() {
if (GET_MODULE == null || IS_OPEN == null) {
return null;
}
try {
Object module = GET_MODULE.invoke(Class.class);
Class<?> moduleClass = module.getClass();
return moduleClass.getMethod("isOpen", String.class, moduleClass);
} catch (NoSuchMethodException | InvocationTargetException |
IllegalAccessException e) {
return null;
}
}
{code}
* The code compiles on JDK 8 or newer.
* `isAccessible()` always returns `true` on JDK 8 (no module system).
* On JDK 9+, it returns the result of
`targetClass.getModule().isOpen(targetClass.getPackageName(),
this.getClass().getModule())`.
Using this helper, we can let *reflectionToString* fall back to
*Objects.toString()* when the target module is not open, while
retaining backward compatibility with older JDKs.
{code:java}
protected void appendFieldsIn(final Class<?> clazz) {
if (clazz.isArray()) {
reflectionAppendArray(getObject());
return;
}
if (!isAccessible(clazz)) {
appendToString(Objects.toString(getObject()));
return;
}
// … remaining logic …
}
{code}
I would be happy to submit a pull request if you think this solution would be
useful.
> [JDK17] ToStringBuilder.reflectionToString fails with
> InaccessibleObjectException on java.lang classes
> ------------------------------------------------------------------------------------------------------
>
> Key: LANG-1685
> URL: https://issues.apache.org/jira/browse/LANG-1685
> Project: Commons Lang
> Issue Type: Bug
> Components: lang.builder.*
> Affects Versions: 3.12.0
> Reporter: David Connard
> Priority: Major
>
> JDK17 prevents reflective access to java.lang classes by default.
> The following code fails on JDK17+
> {code:java}
> System.out.println("boom = " +
> ToStringBuilder.reflectionToString(Set.of(123))); {code}
> I understand that we can "--add-opens" (eg. as you've done for hbase builds
> in
> [https://github.com/jojochuang/hbase/commit/b909db7ca7c221308ad5aba1ea58317c77358b94)]
> ... but, ideally, that should not be a standard requirement to run an
> application that uses {{ToStringBuilder.reflectionToString()}} on JDK17+
> The following sample code appears to work for our use-case, albeit with some
> additional spurious output on the object. It catches the exception and just
> dumps a raw object toString() instead. You probably want to improve on this.
> {code:java}
> ReflectionToStringBuilder jdk17SafeToStringBuilder = new
> ReflectionToStringBuilder(obj) {
> protected void appendFieldsIn(final Class<?> clazz) {
> if (clazz.isArray()) {
> this.reflectionAppendArray(this.getObject());
> return;
> }
> // The elements in the returned array are not sorted and are not in
> any particular order.
> final Field[] fields = clazz.getDeclaredFields();
> Arrays.sort(fields, Comparator.comparing(Field::getName));
> try {
> // first, check that we can delve into the fields. With JDK17+,
> we cannot do this by default on
> // various JDK classes
> AccessibleObject.setAccessible(fields, true);
> } catch (InaccessibleObjectException ioEx) {
> // JDK 17 - prevents access to fields. We'll ignore this, and
> assume these have a decent toString() and not reflect into them
> this.appendToString(Objects.toString(obj));
> return;
> }
> for (final Field field : fields) {
> final String fieldName = field.getName();
> if (this.accept(field)) {
> try {
> // Warning: Field.get(Object) creates wrappers objects
> // for primitive types.
> final Object fieldValue = this.getValue(field);
> if (!isExcludeNullValues() || fieldValue != null) {
> this.append(fieldName, fieldValue,
> !field.isAnnotationPresent(ToStringSummary.class));
> }
> } catch (final IllegalAccessException ex) {
> //this can't happen. Would get a Security exception
> // instead
> //throw a runtime exception in case the impossible
> // happens.
> throw new InternalError("Unexpected
> IllegalAccessException: " + ex.getMessage());
> }
> }
> }
> }
> };
> {code}
--
This message was sent by Atlassian Jira
(v8.20.10#820010)