Why do we need modularity in the first place? Lets get rid of this extraneous layer and make life simpler as Erlang's Joe Armstrong argues. Then again, maybe not.
The reason we need modules is that Object Oriented (OO) technology is starting to fail us. OO gave us a tremendous boost in productivity for 25 years but it is running out of steam for today's systems and especially for statically typed languages like Java. The reason is simply too much coupling. Using a single class from a library drags in its whole transitive dependency, to run even a simple program we have to download the internet. The independent evolution of these dependencies tend to create nightmares like JAR hell. After assemblers, subroutines/functions, modules, classes, and packages it is time for the next paradigm in software! A paradigm that must explicitly address the problem of too much coupling in Java systems.
Jigsaw and JBoss A7 claim to provide the solution to the dependency pains of Java by providing modularity since modularity provides encapsulation; we all know that encapsulation can reduce coupling. But do they? Sure, they make it much simpler to assemble a large class path because it becomes largely self assembling. However, they do not fundamentally change the way the class path works today. That is, they actually only provide a more convenient way towards JAR hell (Personally, I prefer a slightly different destination).
Worse, the hallmark of modularity, encapsulation, is severely compromised by the Jigsaw/JBoss model and Java's best practices.Virtually all Java code has become contaminated with class loading hacks to fight coupling. Java interfaces provided us with a tool to make clean decoupled class diagrams but at the same time created the problem of instance coupling: how to get the instance that implements that interface. In Java, instance coupling is generally achieved with dynamic class loading hacks. Unfortunately, most of those patterns need to violate the module's boundaries to get access to the client and implementation classes.
Once you see this problem (and it is not obvious), the solution is rather clear: we need a concept to handle the interaction between modules, a contract. Just like a class can implement one or more interfaces (the type contract), a module should be able to provide one or more services (the module contract). A module should depend on the services it needs, not on the JAR that holds those implementations. Exactly like a type should depend on an interface and not on an implementation class.
How to specify the service contract? An interface? The granularity of an interface is too small in almost all of the most trivial cases. In general a contract describes a collaboration between multiple actors, defined in interfaces, abstract or base classes, permissions, utility functions, etc. Like Jigsaw, could we (ab)use the virtual module as the "interface" of a module? Nope. That model has no representation in the type system and is therefore fundamentally unsafe. It also is wrong to restrict a module to providing a single service; remember why interfaces were added to Java? Only because we had single inheritance and it was felt that the 1:1 relation was too constraining.
The logical choice in Java is obviously the package. A Java package is already the Java module: it provides encapsulation, a namespace, is part of the Java language, is part of the type system, it has a name, can easily be versioned, and it has the proper granularity for a contract. A module should provide a number of packages and depend on a number of packages that specify contracts between the modules. Just like methods provide the contract between classes. Since the dependencies are expressed in packages, modules can be substituted.
Anybody that doubts package imports/exports are a bad idea should
consider that OSGi also supports the module-to-module dependency of Jigsaw/Maven/JBoss,
it can even separate types. Since each OSGi user has the choice, why do
you think that over time they use packages? Maybe because it works?
The need for package imports/exports is crystal clear when you really understand the violation of encapsulation caused by the interaction of the Jigsaw/JBoss programming model and popular Java patterns. Five years from now I might be able to say: "I told you so," but I still have hope that enough people will put in an effort to understand the looming problem to prevent another 5 years lost to the gains of true Java modularity.
Peter Kriens
Hi Peter,
ReplyDeleteinteresting post!
"we need a concept to handle the interaction between modules, a contract [...] How to specify the service contract? [...] A module should provide a number of packages and depend on a number of packages that specify contracts between the modules"
What is the difference between service contract and a contract based on packages? I think this is not clear in OSGi! For the moment there is no *real* service contract.
I think developers want to think in terms of services and service contracts between modules (as in a 'real' SOA), packages (e.g. interface and type dependencies) is what OSGi needs to get as most as possible *under the hood*! You can't solve the problems if you just rename them and pass them to the developers!
If anyone would implement a dynamic import resolution in the PDE editor this is worth of thousands of new specs, bnd tools or an OSGi IDE! Even though I compliment you for the OSGi IDE idea, as OSGi needs a referene IDE. Still somehow strange it's not Eclipse (PDE).
This is always just my private opinion and consciously offensive. If I'm wrong with anything, please correct me.