<div dir="ltr"><div dir="ltr">On Wed, May 10, 2023 at 2:42 PM Ron Pressler <<a href="mailto:ron.pressler@oracle.com">ron.pressler@oracle.com</a>> wrote:<br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">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".<br></blockquote><div><br></div><div>I had read this JEP as a position statement on future directions given it refers to the preventing dynamic loading of agents (a future action) and restricting JNI (future) while also talking about strong encapsulation (past).</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> On 8 May 2023, at 09:41, Dan Heidinga <<a href="mailto:heidinga@redhat.com" target="_blank">heidinga@redhat.com</a>> wrote:<br>
> <br>
> <br>
> 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.<br>
<br>
<br>
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+.<br></blockquote><div><br></div><div>I think we need to ratchet back the rhetoric a bit here as statements like "make the platform’s evolution possible at all" vastly overstate the case. Integrity is good and brings benefits - we agree. Libraries grovelling through internals makes upgrading challenging but as a broad OpenJDK community, we are also claiming that upgrades from 9+ are generally easy (and I'd like to believe that uptake of JDK11 & 17 is proving that to be true). Locking the platform down further may be a benefit but our current state surely isn't preventing the platform's evolution.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
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, <a href="http://java.io" rel="noreferrer" target="_blank">java.io</a>, 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. <br>
<br>
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. </blockquote><div><br></div><div>Quite frankly, then those applications should break. Application developers will then need to decide if the benefits from those libraries outweigh the upgrade difficulties introduced.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Those applications didn’t *choose* to become non-portable. </blockquote><div><br></div><div>They kind of did when they *chose* those libraries.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">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.<br>
<br>
> <br>
> Can you speak further to the "new deployments" and why integrity constraints are critical to them?<br>
<br>
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. </blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> <br>
> 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.<br>
<br>
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. </blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> <br>
> 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.<br>
<br>
<br>
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.<br></blockquote><div><br></div><div>Most production applications are migrations - they previously accessed internals and now need --add-opens to allow their previous use of deep reflection (and setAccessible(true)) to continue to work. If you classify "modern Java applications" as those greenfield applications written without existing libraries, then I might agree with the "no need.... to require --add-opens".</div><div><br></div><div>As to the relationship between modularisation and --add-opens, as applications (or libraries) adopt modularity, the number of --add-opens *increases* at the boundaries between the newly made modules and the existing classpath libraries. Once everything is modularized, the usage will hopefully decrease (or disappear!).</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
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.<br></blockquote><div><br></div><div>This - and some of the Leyden-related statements - hint at a larger vision. Could you share some of that vision (or roadmap) with the broader community? It makes it much easier to get on board with where you're going if we can also see the intended destination. </div><div><br></div><div>--Dan</div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> <br>
> 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).<br>
<br>
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.<br>
<br>
> <br>
> 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)?<br>
<br>
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+.<br>
<br>
> <br>
> 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.<br>
<br>
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.<br>
<br>
— Ron<br>
</blockquote></div></div>