Better tools for adjusting to strong encapsulation

mark.reinhold at oracle.com mark.reinhold at oracle.com
Tue Mar 21 14:48:13 UTC 2017


By now we've heard quite a bit of feedback, both publicly and privately,
from developers who've migrated existing applications to JDK 9.  This can
be a non-trivial task, involving the iterative construction of a set of
command-line workarounds, since some popular libraries and frameworks use
the core reflection API to access internal APIs of the JDK.  On JDK 9
such components no longer work out-of-the-box because most internal APIs
are now strongly encapsulated in order to improve the integrity of the
platform.

In order to make it easier to adjust to the strong encapsulation of
internal APIs in JDK 9, in both the short term and over the long term,
we've introduced a temporary "big kill switch", described below.  This
switch will only be supported in JDK 9.  It's already available in the
current Jigsaw EA builds [1], and will shortly be merged into JDK 9.

Background
----------

Diagnosing the failures of libraries and frameworks that access internal
APIs of the JDK and then working around those failures is possible, but
tedious.  The initial symptom is usually an `IllegalAccessException` or
an `InaccessibleObjectException`.  The workaround is to use an
`--add-opens` command-line option whose arguments are based upon the
information shown in the exception message.  A large application that
uses several reflective components may suffer many such failures, so this
process may have to be repeated many times, resulting in a long list of
command-line options that will typically be recorded in a launch script
or an @-file.

These options are tedious to construct but, additionally, there's a
significant risk that once they're baked into a file they'll never be
removed, even long after the problems that they work around have been
resolved.  An application that needs some workarounds today might not
benefit as well as it could from strong encapsulation in the long term,
after all of the reflective components that it uses have been fixed to
replace their use of JDK-internal APIs with proper exported APIs.

To help the entire ecosystem migrate to the modular Java platform we want
to motivate such fixes as strongly as we possibly can, especially in the
near term.  We can do that by making it easier for you to get an existing
application to work while also ensuring that the run-time system reports
illegal reflective-access operations in a way that allows you to identify
which libraries and frameworks on your class path require illegal access
in order to work.  If a component's maintainers have already released a
new, fixed version that no longer requires illegal access then you can
consider upgrading to that version.  If, however, a component still needs
to be fixed then we encourage you to contact its maintainers and ask them
to stop using JDK-internal APIs [2].

With all this in mind we've introduced a "big kill switch", described
below, together with some new warning messages.  This switch will be
supported in JDK 9, in order to enable application migration and motivate
fixes to libraries and frameworks.  It will not be supported in JDK 10 or
later releases, in order to motivate fixes to libraries and frameworks
sooner rather than later.  If this switch were to be supported long-term
then that would tend to reduce the pressure on maintainers to fix their
components.

New command-line option: `--permit-illegal-access`
--------------------------------------------------

If this option is present at run time then it will permit illegal access
operations by code in any unnamed module (i.e., code on the class path)
to members of types in any named module via the standard reflective APIs
(`java.lang.reflect` and `java.lang.invoke`), including in particular
those that would otherwise result in an `IllegalAccessException` or an
`InaccessibleObjectException`.

            +----------------------------------------------+
            | This option will only be supported in JDK 9. |
            |         It will be removed in JDK 10.        |
            +----------------------------------------------+

This option does not permit illegal access operations by code in named
modules to members of types in other named modules; for that you must
use the `--add-opens` or `--add-exports` options.  Those options can be
combined with `--permit-illegal-access`.

Warnings of illegal reflective-access operations
------------------------------------------------

When an illegal reflective access operation succeeds due to the use of
the `--permit-illegal-access` option, or the use of an `--add-opens` or
`--add-exports` option, then a warning message of the following form is
written to the error stream:

    WARNING: Illegal access by $PERPETRATOR to $VICTIM (permitted by $OPTION)

where:

  - $PERPETRATOR is the fully-qualified name of the type containing
    the code that invoked the reflective operation in question plus
    the code source (i.e., JAR-file path), if available,

  - $VICTIM is a string that describes the member being accessed,
    including the fully-qualified name of the enclosing type, and

  - $OPTION is the name of the command-line option that enabled this
    access, when that can be determined, or the first one of those
    options if more than one option had that effect.

The run-time system attempts to suppress duplicate warnings for the same
$PERPETRATOR and $VICTIM, but it's not always practical to do so.

For deeper diagnosis you can request a stack trace on each such warning
by setting the system property `sun.reflect.debugModuleAccessChecks` to
the value `access`, though this detail might change.  (That property can
also be helpful to diagnose mysterious failures due to illegal-access
exceptions that are caught and suppressed.)

In addition to displaying a warning on each illegal access operation, the
run-time system also shows new initial warning messages at startup time.
If `--permit-illegal-access` is used then a warning reports the imminent
demise of that option in the next major release.  If either `--add-opens`
or `--add-exports` are used then a warning reports a count of each type
of option used (i.e., opens vs. exports).

Here are some examples of these messages, from running Jython on a very
recent Jigsaw build:

  $ java --permit-illegal-access -jar jython-standalone-2.7.0.jar
  WARNING: --permit-illegal-access will be removed in the next major release
  WARNING: Illegal access by jnr.posix.JavaLibCHelper (file:/tmp/jython-standalone-2.7.0.jar) to method sun.nio.ch.SelChImpl.getFD() (permitted by --permit-illegal-access)
  WARNING: Illegal access by jnr.posix.JavaLibCHelper (file:/tmp/jython-standalone-2.7.0.jar) to field sun.nio.ch.FileChannelImpl.fd (permitted by --permit-illegal-access)
  WARNING: Illegal access by jnr.posix.JavaLibCHelper (file:/tmp/jython-standalone-2.7.0.jar) to field java.io.FileDescriptor.fd (permitted by --permit-illegal-access)
  WARNING: Illegal access by org.python.core.PySystemState (file:/tmp/jython-standalone-2.7.0.jar) to method java.io.Console.encoding() (permitted by --permit-illegal-access)
  Jython 2.7.0 (default:9987c746f838, Apr 29 2015, 02:25:11) 
  [OpenJDK 64-Bit Server VM (Oracle Corporation)] on java9-internal
  Type "help", "copyright", "credits" or "license" for more information.
  >>> ^D
  $ 


[1] http://openjdk.java.net/projects/jigsaw/ea
[2] This will usually but not always be possible, since there are still a
    few critical internal APIs without exported replacements, per JEP 260
    (http://openjdk.java.net/jeps/260).


More information about the jigsaw-dev mailing list