A way to opt out of access restrictions on non-exported members.
Reinier Zwitserloot
reinier at zwitserloot.com
Sun Nov 15 11:03:20 UTC 2015
TL;DR: There are rather a lot of libraries out there that access private
members via reflection. I posit that almost all such libraries break
without an easy way to fix the problem, if there is no way to use
reflection to access non-exported members.
setAccessible(true) doesn't let you opt out of module access rules. There
are ways to effectively 'read' everything (and if not, there's addRead),
but that still doesn't help if you need to reflectively access a member
that isn't exported. According to the questions at the end of the J1
session 'Advanced Modular Development (CON6821)', by Mark Reinhold, Alex
Buckley, and Alex Bateman, there is no way to in-process get around the
JVM-enforced access restriction.
Now, under normal circumstances you shouldn't be accessing such classes /
methods / fields / constructors in the first place, but that exact same
line of reasoning can be used to defend not including setAccessible,
getDeclaredMethod(s), getDeclaredField(s), and all the other ways the
reflection API can be used to access (in JDK1-JDK8, at least) protected,
package private, and private elements. Those things were never designed to
be accessed either, and yet, accessing private members of existing stuff
occurs in lots of libraries, usually for:
* Dependency injection (guice and company)
* Debugging tools (not all of these use JVMTI; sometimes you just have a
utility method that dumps some information to a log that includes private
members just because that seems like it'll help debug an exotic condition
that you can't quite put your finger on).
* Code automation tools (apache has a few utilities that return a useful
hashCode, which work by inspecting your instance's fields).
* Serialization libraries. I'm pretty sure a number of core JDK devs have
gone on record not liking the built in serialization mechanism
(java.io.Serializable and co), but third party solutions access private
members all the time to do their job.
* Workarounds. This one is a perhaps controversial, but as the saying goes,
java is a 'blue collar' programming language. Sometimes you use access to
private members to work around a bug or to get access to functionality that
optimally speaking should be done differently or should be accomodated with
an update to the library to make that part designed for public access.
What are such libraries to do? They could provide the user with ways to use
command line switches to hack in the required exports, but this is the kind
of hassle that is going to reflect badly on JDK9 and slow adoption rates. I
propose some mechanism to get around the restriction is added, with all due
warnings in the documentation that this should be considered a hacky
workaround. (interestingly, such commentary is not part of the javadoc on
.setAccessible!)
Alternatively, there are 3 ways this can go:
* The authors of these libraries, or the help forums associated with such
libraries, start espousing the principle that you should just export
everything from your modules, or 'things might break'. This doesn't sound
like a desirable outcome for the jigsaw project. It certainly doesn't sound
desirable to me.
* The authors of these libraries, or the help forums associated with this
libraries, start espousing the principle that upgrading to JDK9 is an
arduous slog, and that it is better to just stick with JDK8 for the
foreseeable future. Certainly, not a desirable outcome.
* The heavens part, ponies and rainbows for all, and everyone takes the
time to update their serialized data structures or whatnot to make sense in
a 'these are exported, these are not' world. I'm not sure this is even
possible; it would make sense to have a public interface, and then 2
(non-exported!) classes that are implementations of this. How would a
no-hassle serialization library ever get this done without a way to dodge
the rules? A module can export _TO_ a serialization library, but this
breaks encapsulation: Now the author of framework A needs to know that the
user of said framework so happens to have a desire to serialize the stuff
from framework A using serialization library X. In practice you'd have to
export everything to all the serialization frameworks out there, and now no
new framework can ever be created because all these libraries failed to
export their internals to them. In any case this boils down to 'just export
every package to this subset of tools', and that's the best case scenario,
which doesn't sound desirable either.
I guess option 4 is: "Surely it won't be that bad", but that's a bit of a
gamble to make, given that the stakes are serious lack of adoption of JDK9,
or widespread misconfiguration of exports clauses.
NB1: Note also that JDK9 has taken its time (and rightly so) to fix its
ball-o-twine dependency mess. However, once JDK9 is unleashed upon the
masses, a 'quick fix' to make libraries at least workable with JDK9
probably require such hacks. If it's not possible to do it, then libraries
won't be JDK9 ready for years. Perhaps other projects aren't as messy as
JDK pre-9 was, but let's not make that assumption.
NB2: The module system already has a larger impact on adoption (compared to
6, 7, or 8) simply because of the unavoidable differences inherent in what
jigsaw is doing (I bet a bunch of command line scripts are going to end up
breaking, for example, or at least will be obsolete). Adding more barriers
to adoption is something that should, in my opinion, by taken particularly
seriously for this release.
--Reinier Zwitserloot
More information about the jigsaw-dev
mailing list