So, lately the buzz is all about jigsaw and I've repeatedly heard the posse sort of handwave over the difficulties with integrating modules; it's being seen as a panacea where a module system automatically means backwards compatibility is no longer a big concern and we can finally go ahead and fix some of the past mistakes made in the core libraries. I hope that didn't sound too offensive to the posse :) - tackling this topic would probably take too much time, but:
Let's say you're using two modules in your project, hibernate, and 'Yoyodyne data verification library' (from now on: yoyoverify). You update hibernate to the latest version, but the programmers who have built yoyoverify have long since left the company and you don't really feel like messing with it. Unfortunately, one of the updates is to ArrayList - the collections API has seen an overhaul (this is a hypothetical story!), where a bunch of methods have been added: .each, .filter, .sort, and a few other things have been added to java.util.List and its constituent parts. Whoops. Unless I misunderstand module systems, this is not going to work. hibernate routinely returns objects of type List, ArrayList, and other collections API classes, but so does yoyoverify. We could allow each module to have its own version of the collections API, but separate versions means that they aren't compatible with each other; I can't take a List produced by hibernate and hand it over to yoyoverify. In fact, what version of the collections API should your own code use? If it's the new one, then how do we even hand a List object to yoyoverify? Our notion of what a List is, doesn't mesh with yoyoverify's notion. A big aspect of why module systems even work right, is that the core library is not itself a module. So, objects like Sockets, Input/ OutputStreams, Dates, Calendars, Lists, Sets, Maps, Strings, Exceptions, Threads, Runnables, CharSets, Buffers, Properties, JButtons, MouseListeners and BigIntegers can all be passed back and forth without worrying about version incompatibilities. We can't -really- clean house in the core libraries without addressing this issue. But how to address it? I can't think of a simple solution. Just three complicated ones: 1) Any module is allowed to include code that can adapt an old version of one of its classes to a new one (like a copy constructor, where each field is provided in e.g. a HashMap or some such). Similarly it should offer a way to turn a new-version object into an old-version one. This works fine, though it has some memory and CPU overhead, for immutables, but not so much for mutables. Anytime either version (old or new) is changed, it should be reflected immediately in the new version. This is a hard problem to solve for methods (but annotations can help compile in an update call, AOP-style), but even harder for fields, which isn't entirely irrelevant: How would you fix java.awt.Point to be immutable, or at least work with setters? A classloader that rewrites the module that USES java.awt.Point to call methods instead of update fields? That would work, but how would we tell the module system classloader to do this? We'd need some way, as a newer API, to tell the module system to do special things when it encounters a module that is dependent on an old version of ourselves. Not easy stuff at all. 2) There's only one representation for any given class, instead of having both the new and old version like in solution #1, and instead there's a wrapper API that looks exactly like the older version, which actually translates old method calls into calls that have the same effect on the new object. However, many API changes you'd like to make aren't easily realized in this model. Again, java.awt.Point's fields is one example, but what if there's a sizable paradigm shift where you combine multiple separate classes into one, or split one bigger class up, or even more significant changes? 3) Anytime the module runtime realizes that it needs two different versions of the same library AND they need to interoperate, the new API is called on to modify classes loaded for the module targeted at the old API, so that it can fix calls. The amount of work that this bytecode changing fixer has to do can be significant, but at least it gives the option to the API developer to make as many changes as he likes, as long as he can find a way to rewrite old classes to adapt to the new model. I can easily see how you can rewrite java.awt.Point using old code, or adapt an old library's custom java.util.List subclass with .each, .filter, and .map methods, but this is not easy to test, ridiculously hard to write, and I fear that a module going through multiple rewrite stages because it is dependent on multiple older versions is just going to become so hard that most libraries backwards compatibility support is too buggy to be of use. Is there an easier way to solve this problem? Does OSGi try to solve it? Does OSGi have a mechanism whereby if two modules require different versions of the same library, *BUT*, they never send these objects to each other, its okay, but if they do send them to each other, OSGi gives up (preferably at start, possibly at runtime) stating irreconcilable version requirements? --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "The Java Posse" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/javaposse?hl=en -~----------~----~----~----~------~----~------~--~---
