AccessibleObject.setAccessible() backward compatibility
mark.reinhold at oracle.com
mark.reinhold at oracle.com
Thu Sep 17 14:56:11 UTC 2015
2015/9/11 10:15 -0700, forax at univ-mlv.fr:
> There are two changes to setAccessible that make migration to the
> module universe hard.
>
> The first one is that setAccessible() is now declared final which is a
> binary incompatible change so it breaks Guava two projects of mine.
As Alan Bateman already noted on the jigsaw-dev list, this is a bug (now
fixed: http://hg.openjdk.java.net/jigsaw/jake/jdk/rev/9d521fd83608).
> The second change is that now setAccessible() is now CallerSensitive
> and throw an exception if the AccessibleObject is not visible from the
> caller class.
No, it throws an exception if the caller's module does not read the
module of the target's declaring type, or if the package of the target's
declaring type is not exported by its module (either to all modules, or
at least to the caller's module), regardless of whether or not the type
is public.
> While this change may improve the security, it's a
> backward incompatible change is not strictly required to support
> modules it's more an enhancement of the current security model and i
> don't think it's a good idea to mix it with the introduction of the
> module support.
This is not an enhancement of the security model per se (which would,
anyway, be beyond the scope of this JSR). This change is, rather, in
service of the strong-encapsulation goal (which does, of course, help
ensure security).
To truly support strong encapsulation would require taking setAccessible
away completely. I think that's desirable in the (very) long term, but
it would break too much existing code in the near term.
The present behavior of setAccessible is a compromise, one of several
possible choices. For types in unnamed modules (e.g., on the class path)
it behaves exactly as it does in SE 8, since types in unnamed modules are
always in exported packages. What a couple of early testers have run up
against is the fact that if a type is in an unexported package in a named
module (e.g., in a Java SE or JDK module) then setAccessible fails [1].
Some other consequences of this particular choice:
- If you want to use a framework that uses setAccessible upon members
of Java SE or JDK modules (e.g., some third-party serializers) then
you'll have to use the command-line -XaddExports option to grant them
the necessary access.
- If you put code into a module, and you want to use a framework that
uses setAccessible upon members of that module, then the module must
export the packages containing the declaring types of those members,
even if those types are not public. (Qualified exports can be used
to limit, by name, the modules to which such packages are exported.)
- The only way to really hide something in a module is to put it in
an unexported package (but at least there's one way to really hide
something!). If it's a package-private or private nested member in
an exported package then setAccessible can still be used to break
into it.
While working on the prototype we did consider some alternatives:
(a) Limit setAccessible to require the caller to have access to the
target member's declaring type, consistent with the rest of the
core reflection API. (The problem with this approach is that it
does not allow setAccessible to be used on package-private types.)
(b) Limit setAccessible so that both the caller and the target must be
in the same module. (This would break almost as much existing code
as taking setAccessible away completely.)
(c) Variants of (a) or (b) which add a way to grant specific modules,
via a command-line option, the power to use setAccessible as it
exists today.
(d) Do not change the behavior of setAccessible in SE 9, but deprecate
it with an eye toward removing it in a future release, after
supported (and safer!) replacements for known use cases have been
defined.
>From one of your later messages, I'm guessing that you'd be content with
this last option.
> More philosophically, every libraries that propose an
> abstraction that hide underlying dirts provides an escape hatch, the
> reflection API is one of such hatch of Java the language allowing to
> bypass the typechecker and the security sandbox. Trying to close the
> hatch will just make people to open holes in the nearby wall with
> hacks that are less secure and that may even compromise the integrity
> of the plateform.
Philosophically, I can't agree with this. If encapsulation is to mean
anything then it should not be possible to break it solely from within
the language itself. Enabling such powers via an external, second-class
mechanism such as a command-line option or a debugger is fine, but the
history of Java has shown that if it's easy to break encapsulation then
people will do so, increasing everyone's maintenance burdens over the
long haul.
- Mark
[1] It throws an exception that does not extend SecurityException, which
breaks some existing code in other ways, but that's a separate issue.
More information about the jpms-spec-experts
mailing list