Java Platform Module System

alex_ber
alex_ber
Sep 4, 2020 · 6 min read

setAccessible is broken?

If you’ve ever written code using Core Reflection API, you wrote something like this:

Field field=...
field.setAccessible(true);

or

Method method=...
method.setAccessible(true);

This code works fine till JDK 8 . In JDK 9 incompatible change was made. Quote from JavaDoc of setAccessible() method. This quote is done from recent JDK 14, but it is (almost) the same was in JDK 9:

public void setAccessible​(boolean flag)

Set the accessible flag for this reflected object to the indicated boolean value. A value of true indicates that the reflected object should suppress checks for Java language access control when it is used. A value of false indicates that the reflected object should enforce checks for Java language access control when it is used, with the variation noted in the class description.

This method may be used by a caller in class C to enable access to a member of declaring class D if any of the following hold:

C and D are in the same module.

The member is public and D is public in a package that the module containing D exports to at least the module containing C.

The member is protected static, D is public in a package that the module containing D exports to at least the module containing C, and C is a subclass of D.

D is in a package that the module containing D opens to at least the module containing C. All packages in unnamed and open modules are open to all modules and so this method always succeeds when D is in an unnamed or open module.

This method cannot be used to enable access to private members, members with default (package) access, protected instance members, or protected constructors when the declaring class is in a different module to the caller and the package containing the declaring class is not open to the caller’s module.

If there is a security manager, its checkPermission method is first called with a ReflectPermission("suppressAccessChecks") permission.

https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/reflect/AccessibleObject.html#setAccessible(boolean)

Even more, new method was added to Field/Method:

public final boolean trySetAccessible()

Set the accessible flag for this reflected object to true if possible. This method sets the accessible flag, as if by invoking setAccessible(true), and returns the possibly-updated value for the accessible flag. If access cannot be enabled, i.e. the checks or Java language access control cannot be suppressed, this method returns false (as opposed to setAccessible(true) throwing InaccessibleObjectException when it fails).

This method is a no-op if the accessible flag for this reflected object is true.

For example, a caller can invoke trySetAccessible on a Method object for a private instance method p.T::privateMethod to suppress the checks for Java language access control when the Method is invoked. If p.T class is in a different module to the caller and package p is open to at least the caller's module, the code below successfully sets the accessible flag to true.

p.T obj = ....; // instance of p.T
:
Method m = p.T.class.getDeclaredMethod("privateMethod");
if (m.trySetAccessible()) {
m.invoke(obj);
} else {
// package p is not opened to the caller to access private member of T
...
}

In the last minute of JDK 9 release the “big kill switch” was added as temporary solution for 1 release:

Mark Reinhold has a new proposal for JDK 9: to allow illegal reflective access from code on the class path by default in JDK 9 and to disallow it in a future release. He wrote in a message to the OpenJDK mailing list that “the strong encapsulation of JDK-internal APIs has triggered many worried expressions of concern that code that works on JDK 8 today will not work on JDK 9 tomorrow, yet no advance warning of this change was given in JDK 8.”

To help the ecosystem “migrate to the modular Java platform at a more relaxed pace,” Reinhold proposed to allow illegal reflective access from code on the class path by default in JDK 9 and to disallow it in a future release

The Chief Architect of the Java Platform Group at Oracle explained that the existing “big kill switch” of the ‘–permit-illegal-access‘ option will become the default behavior of the JDK 9 run-time system but emphasized that there won’t be as many warnings. He also added that the current behavior of JDK 9 [where illegal reflective-access operations from code on the class path are not permitted] will become the default in a future release and nothing will change at compile time.

Reinhold wrote that the recently-introduced `–permit-illegal-access` option will be replaced by a more general option, `–illegal-access`.

If adopted [today we know, it was, indeed, adopted], the proposal will require some changes to JEP 260, “Encapsulate Most Internal APIs”. Although APIs that are internal to the JDK will still be strongly encapsulated from the standpoint of code in modules [whether those modules are automatic or explicit] they won’t appear to be encapsulated at run time from the standpoint of code on the class path.

Furthermore, when `deny` becomes the default mode, Reinhold expects `permit` to remain supported for at least one release, so that developers can continue to migrate their code.

https://jaxenter.com/jdk-9-replace-permit-illegal-access-134180.html

Indeed, –permit-illegal-access was changed to –illegal-access.

As for now, the last released JDK is 14. And the “big kill switch” is still in place.

Quote from documentation of --illegal-access for JDK-14:

-illegal-access=parameter

When present at run time, --illegal-access= takes a keyword parameter to specify a mode of operation:

Note: This option will be removed in a future release.

* permit: This mode opens each package in each module in the run-time image to code in all unnamed modules ( such as code on the class path), if that package existed in JDK 8. This enables both static access, (for example, by compiled bytecode, and deep reflective access) through the platform's various reflection APIs. The first reflective-access operation to any such package causes a warning to be issued. However, no warnings are issued after the first occurrence. This single warning describes how to enable further warnings. This mode is the default for the current JDK but will change in a future release.

* warn: This mode is identical to permit except that a warning message is issued for each illegal reflective-access operation.

* debug: This mode is identical to warn except that both a warning message and a stack trace are issued for each illegal reflective-access operation.

* deny: This mode disables all illegal-access operations except for those enabled by other command-line options, such as --add-opens. This mode will become the default in a future release.

The default mode, --illegal-access=permit, is intended to make you aware of code on the class path that reflectively accesses any JDK-internal APIs at least once. To learn about all such accesses, you can use the warn or the debug modes. For each library or framework on the class path that requires illegal access, you have two options:

If the component’s maintainers have already released a fixed version that no longer uses JDK-internal APIs then you can consider upgrading to that version.

If the component still needs to be fixed, then you can contact its maintainers and ask them to replace their use of JDK-internal APIs with the proper exported APIs.

If you must continue to use a component that requires illegal access, then you can eliminate the warning messages by using one or more --add-opens options to open only those internal packages to which access is required.

To verify that your application is ready for a future version of the JDK, run it with --illegal-access=deny along with any necessary --add-opens options. Any remaining illegal-access errors will most likely be due to static references from compiled code to JDK-internal APIs. You can identify those by running the jdeps tool with the --jdk-internals option. For performance reasons, the current JDK does not issue warnings for illegal static-access operations.

https://docs.oracle.com/en/java/javase/14/docs/specs/man/java.html

Personally, I think weeking Core Reflection API was big design mistake. On Java conferences many people, including myself, still says, that they are using JDK 8. And when people that uses JDK 9 and later are asked whether they used modules, the majority of them still uses classpath.With this “big kill switch” in place user code that Java developer is writting can access JDK internals. So, effectively, modules “strong encapsulation” have failed big time.

P.S. Many popular libraries in maven central not works on JDK 9 and later with modules.

The Startup

Get smarter at building your thing. Join The Startup’s +800K followers.

 by the author.

alex_ber

Written by

alex_ber

Senior Software Engineer at Pursway

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +800K followers.

alex_ber

Written by

alex_ber

Senior Software Engineer at Pursway

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +800K followers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store