To clarify, if you have a plan on how to help the maintainability and security of your container with modules, then of course we’d be happy to help. But using a feature when you’re not clear on how to benefit from it is helpful to no one. I’ve seen this happen with virtual threads, too. If you use a feature just to tick a box, then you’re unlikely to see much value in it. You need to have a plan for how to enjoy it.
> On 17 Dec 2024, at 16:05, Ron Pressler <ron.press...@oracle.com> wrote: > > > >> On 17 Dec 2024, at 14:21, David Lloyd <david.ll...@redhat.com> wrote: >> >> >> >> You're confusing the idea of modular encapsulation with the idea of platform >> integrity. I'm talking specifically about the encapsulation of modules that >> are loaded by a custom ClassLoader. If the user defines their own version of >> a string class which purports to be immutable, and that class is defined >> within my ClassLoader, then yes, I can mutate it if I want to. There are >> many ways to do this. > > The “platform integrity” is, itself, enforced by the same module capabilities > available to libraries. The point is that modules give you the ability — > which didn’t exist previously — to enforce your own invariants, not that you > can *choose* not to enforce them. The choice not to enforce invariants is > still available, what’s new is the ability to choose to enforce them. > >> >> When I load a module in a class loader, I can basically do whatever I want >> with it, if I'm willing to do some tedious (and possibly >> performance-degrading) work. All I'm proposing is to make the work of doing >> *certain* things less tedious and performance degrading. > > Good, so because most programs will have just a single layer, we clearly have > to focus on not making many modules per layer be performance-degrading. > *That* seems to be the claimed core issue here. > >> >> However, to give a counter-example, `Module` has `addUses` as well. But if I >> want to call `addUses` on behalf of a module I've defined, there is no >> corresponding `ModuleLayer.Controller` method so I do have to define the >> extra class, and that's a bit silly. This is an example of an easy >> enhancement that would not affect the integrity of the platform, would not >> significantly increase maintenance burden (since the logic would be nearly >> identical to its sibling methods), and would be easy to achieve. > > But to me this sounds like a sunk-cost argument. If the choice is between > putting effort into supporting a rather baroque architecture, that itself is > justified by a performance problem, wouldn’t it be better to focus on the > performance problem, which could then support a much more common architecture > and make the more baroque one unnecessary? > > If you could show how the performance issue materialises, that would be very > helpful. > >> >> >> From our perspective, we started off with an open system, and the system has >> been made less and less open over time. Strictly speaking, the only >> "feature" here is fancy stack traces. Everything else arises out of adding >> restrictions. I'm not saying this to complain about modularity, but to >> remind you that we are presently only weakly incentivized to use modules at >> all, so dangling intangible "benefits" before me does not entice me much. We >> get more "features" by not using it. I'm essentially trying to be a team >> player here, but only out of a sense of cooperation. > > > But I’m not trying to entice you to use a feature which may or may not be > appropriate for your situation. I’m only presuming that since you seem > interested in using the feature, you’re already incentivised. > > Modules add powerful capabilities with very important benefits to > maintainability and security, and will likely add more benefits. These > benefits are available to users who wish to enjoy them. The feature doesn’t > exist to serve itself, and using it to tick a box if you don’t believe it > helps you certainly doesn’t help us. The JDK already enjoys these benefits. > >> >> I'm not sure yet. But I think so. > > I would advise against that. If there’s a problem with a more straightforward > architecture, let’s try and resolve that. > >> >> Imagine a layer of 1000 modules, packaged in JAR files. To load a single >> class from the layer, the layer must be defined. To do this, the graph must >> be resolved, and to accomplish that, the descriptors of every module must be >> created. To get the descriptors, each of the 1000 JAR files has to be opened >> and the bytes of the descriptors must be read from each one. Because of >> eager resolution, the load time of a module layer will always scale in >> linear proportion to the potential number of modules in the layer, no matter >> how optimized each step in the process is made to be. The only possible >> optimization strategies involve paring down the root set - something which >> requires ahead-of-time analysis which again would not be necessary if >> modules were loaded and linked lazily, like classes are. Loading modules on >> demand would solve this performance issue fairly decisively; it would also >> not forbid ahead-of-time assembly of a module graph if that is what the user >> wants. > > But the JDK has to support any reasonable number of modules in a single > layer, as that’s the common case, anyway. There is no need to hypothesise. > Either it works with acceptable performance or it doesn’t, and if it doesn’t, > that requires addressing. Indeed, there’s significant ongoing effort on > improving startup time, not just relative to modules’ current state, but > relative to that of all Java applications, modular or otherwise. > > — Ron