Before I get to your specific points, let me clarify that this informational 
JEP is a summary of *past* work and decisions (except maybe the FFM/JNI 
restriction) that, though already delivered or announced, their full motivation 
was never canonised in JEP form. Everything it describes has either already 
been delivered or serves as a reminder of upcoming work that was announced long 
ago. It contains no change one way or another to the path to integrity that 
Java has been on for the past five years and, in particular, no change to the 
use of --add-opens/add-exports compared to JDK 17. In general, this JEP 
describes very, very little beyond what JDK 17 already delivered, which is why 
it is designated "informational".

> On 8 May 2023, at 09:41, Dan Heidinga <heidi...@redhat.com> wrote:
> 
> 
> I agree the old regime worked.  It worked well and enabled Java to flourish 
> as a stable base for applications built on top of the runtime. And many of 
> those applications have chosen to "violate integrity" to achieve business 
> goals. Enforcing more constraints on the ecosystem to make JDK development / 
> maintenance easier isn't necessarily a winning strategy for the applications 
> built on top of the runtime.  Especially given we have existing tools - such 
> as marking specific classes as "unmodifiable" [0] - that would allow the VM 
> to enforce invariants on critical implementation classes that are ported from 
> C++ to Java and could be extended to protect the runtime further.


First, we are not enforcing any more constraints in that area than those that 
have already been delivered in JDK 17. Second, making the maintenance of the 
JDK easier is not a direct motivation for better integrity (although it had 
contributed some urgency to it). Rather, a part of the motivation is to make 
the platform’s evolution possible at all. The old regime worked when Java was 
either young or relatively stagnant, and stopped working when that situation 
changed, quite visibly so when non-portable code caused significant migration 
issues from 8 to 9+.

Now, the issue is not so much applications but libraries. In the 'Strong 
Encapsulation by Default' section, we tried to reflect what we've learned over 
the last 20+ years about why Java libraries break encapsulation. Perhaps we 
didn't speak plainly enough, so we'll do that now: We sympathize with libraries 
that use public elements of com.sun.* and sun.*, but we do not sympathize with 
libraries that use non-public elements of java.*. Some libraries were forced to 
use APIs in com.sun.* and sun.* because there was no standard API available 
when they were written (e.g. using the com.sun.net.ssl.internal.ssl package 
prior to JDK 1.4, when javax.net.ssl was introduced.) Those libraries are now 
legacy, usually unmaintained, and will require --add-opens/exports forever. 
That's OK -- they delivered business value by relying on JDK internals, and 
will continue to deliver business value (with the right options) because we 
don’t remove stuff *just* to make maintenance easier. In contrast, other 
libraries chose to use non-public elements of standard APIs -- often in 
java.lang, java.io, and java.util -- because it was convenient to rely on those 
JDK internals. The most common --add-opens option we see is for java.lang! The 
second most common is for java.util, to make unmodifiable collections 
modifiable. 

This depth of access to internal parts of external classes makes the evolution 
of the platform impossible without constantly breaking applications that depend 
on such libraries. Those applications didn’t *choose* to become non-portable. 
Thanks to --add-opens, at least they are now aware of the risks. True, even 
those encapsulation violations allowed libraries to deliver value to their 
users, but they also took away value by making those applications non-portable, 
and the end result on the ecosystem as a whole was a tragedy of the commons: 
every individual party felt justified, but the entire ecosystem ossified as a 
result.

> 
> Can you speak further to the "new deployments" and why integrity constraints 
> are critical to them?

I’m referring to future modes of AOT compilation — including those that may 
never be implemented in HotSpot itself, but that Java should support — 
including “serverless” and perhaps even the browser. But bear in mind that Java 
has not been on a path to integrity because of any single motivation of the 
three mentioned in the JEP — maintainability, security, and performance — but 
because of the combination of all three.

> 
> That's a fair characterization.  I see this JEP draft as a necessary 
> foundational step towards the removal of the SecurityManager.  Without the 
> limitations being proposed by this JEP, there is nothing the runtime offers 
> to fill the gap produced by removing the SecurityManager.  I think it's worth 
> calling out that this JEP draft is an enabling step towards the complete 
> removal of the deprecated SecurityManager.

That’s something to consider, but I’m hesitant for two reasons. First, the JDK 
is not providing a security mechanism that controls access to, say, files to 
replace SM. Second, some already confuse integrity and security, and I wouldn’t 
want to confuse them further. The real relationship between integrity and 
security is through correctness. Integrity is required to support local 
reasoning, and local reasoning, in turn, is required to make assurance of 
certain correctness properties practically feasible (thanks to what we dubbed 
“integrity invariants”). It’s just that correctness is of particular importance 
to security because the stakes are high and because there is always an active 
effort to find exploitable bugs or holes in security-related code.

> 
> It's a great vision statement but the unfortunate reality is much messier.  
> Most programs - especially given the current adoption of modules - will need 
> --add-exports/add-opens until their dependencies are all fully modularized 
> and even then, if today's setAccessible use is any indication, will continue 
> to use those options.  Additionally, -javaagent is a key enabler of 
> Observability tooling.  I'd be surprised if only a minority of programs were 
> deployed with monitoring agents... in fact, I expect that given the 
> increasing emphasis on Observability, usage will increase, especially with 
> these tools needing to switch away from dynamic attach.


I don't see the relationship between modularisation and --add-opens, especially 
when it comes to opening JDK modules, and I also don’t see a direct 
relationship between --add-opens and setAccessible, either. setAccessible or 
equivalent Lookup operations can be used in the same module (including the 
unnamed module), for open modules, and passing Lookups as capability objects — 
all without --add-opens. As I wrote above, when using some legacy libraries, 
some applications may need to use --add-opens. But there is no need for modern 
Java applications or libraries to require --add-opens *in production* (perhaps 
aside from serialization, for which there’s a longer-term plan). I’d be 
interested to know what uses you see for --add-opens in libraries intended for 
production use that are written today.

As for agents, not only are they perfectly supported as ever, it may be 
interesting to run them at link time in certain future Leyden modes. The use of 
agents for tooling is more than just acceptable — it’s wonderful! It’s their 
use by libraries that’s somewhat concerning, but in the long term we may want 
to offer libraries some of the power of agents, only restricted by 
encapsulation boundaries.

> 
> As a member of the Valhalla EG, I can confidently state that many of the 
> Valhalla requirements come out of the underlying "vm physics" and need to 
> reflect those tradeoffs in a way that makes sense to developers who aren't 
> familiar with the ins-and-outs of the core runtime.  Valhalla still bets hard 
> on speculation - preferring to assume "this won't be null" for most values 
> rather than hard code that into the underlying runtime (see recent 
> discussions on removing the "Q" descriptor).

The way we expose a clear model of when certain optimisations can be made is an 
essential part of designing them. While HotSpot does and will continue to 
heavily depend on speculative optimisation, it will *also* continue to depend 
on hard contracts alongside it, as it has always done (not only in Valhalla but 
also in Records and in older areas, too, such as assuming that data is 
initialised to some workable value). There is no conflict between the two: 
Speculation and integrity have always worked hand-in-hand in Java.

> 
> Has there been any analysis on how common --add-opens actually is?  Or has 
> the use of setAccessible (as a proxy for --add-opens) been analyzed to 
> validate the assumptions here?  If that analysis could be shared it would 
> help to validate the assumptions being stated here.  I know we've examined 
> common corpuses as part of other JSRs to validate ie how widespread "_" was 
> used as variable name before restricting it.  Can the same be done here (if 
> it hasn't already)?

I don’t think setAccessible is a good proxy for add-opens because its use can 
well be “in policy”, i.e. with no violation of an encapsulation policy at all. 
We’ve not conducted any kind of rigorous survey on add-opens, but virtually all 
the anecdotes we encounter (from support etc.) are due to legacy libraries and 
technical debt. This JEP describes no change to add-opens, but I would love to 
see such a study conducted in a few years after more of the ecosystem is on 17+.

> 
> For applications that made the jump to a version > 9, the upgrade from 
> release to release has been (to my knowledge) fairly smooth apart from 
> dealing with --illegal-access=deny becoming mandatory.

That’s been my impression, too, which gives us confidence that strong 
encapsulation is working in the sense that it inflicted some pain “for the last 
time” in exchange for reducing regular update pain. I don’t think we can 
declare success just yet, but things are looking good so far.

— Ron

Reply via email to