Uploaded image for project: 'Commons Lang'
  1. Commons Lang
  2. LANG-1685

[JDK17] ToStringBuilder.reflectionToString fails with InaccessibleObjectException on java.lang classes

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Open
    • Major
    • Resolution: Unresolved
    • 3.12.0
    • None
    • lang.builder.*
    • None

    Description

      JDK17 prevents reflective access to java.lang classes by default.

      The following code fails on JDK17+

      System.out.println("boom = " + ToStringBuilder.reflectionToString(Set.of(123))); 

      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.

      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());
                      }
                  }
              }
          }
      };
       

      Attachments

        Issue Links

          Activity

            People

              Unassigned Unassigned
              davidconnard David Connard
              Votes:
              0 Vote for this issue
              Watchers:
              5 Start watching this issue

              Dates

                Created:
                Updated: