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
-~----------~----~----~----~------~----~------~--~---

Reply via email to