[ 
https://issues.apache.org/jira/browse/LANG-1685?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18037424#comment-18037424
 ] 

Gary D. Gregory commented on LANG-1685:
---------------------------------------

One simple answer would be: The ReflectionToStringBuilder code is designed to 
use certain reflection features, so you must enable those features.

If you are a call site of ReflectionToStringBuilder, you can call toString() 
instead, but that's a code change. You'll lose the feature and get completely 
different behavior, of course. But you are in control; you decide what your app 
or library needs.

If we magically and dynamically disable the internals of 
ReflectionToStringBuilder to not use these features under certain conditions, 
and use toString() instead, then that's going to be bug reports, for somebody, 
probably us as well, with different behavior depending on what Java version is 
running.

What worries me is changing behavior without signaling something to the user. 
If we do changes at all, should the result string include a message like "This 
is a broken string because this feature is not enabled on the JVM command 
line"?)

If you use an app stack or library where Lang comes in through a transitive 
dependency, then you can't change code. You can enable reflection, though. If 
we change the behavior, there are going to be bad surprises, I suspect.

Maybe this is a case where we use a system property that says "Disable advanced 
reflection features in ReflectionToStringBuilder". IOW, it's an opt-in.

Looking for comments and opinions.

> [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)

Reply via email to