Mutable modules

Stephen Felts stephen.felts at oracle.com
Fri May 20 15:31:49 UTC 2016


I was on a project using OSGi where some of the applications relied on removing and replacing modules.
Generally they replaced applications modules at the top with no dependencies into the module.
For example, in one application that dealt with RFID, it was common for new RFID clients to be introduced or modifications required to existing RFID clients (the technology was changing very quickly).
The handling of these clients was done with modules that could be swapped in as necessary.
There were well defined interfaces with various implementations for the different clients.

-----Original Message-----
From: Neil Bartlett [mailto:njbartlett at gmail.com] 
Sent: Friday, May 20, 2016 11:20 AM
To: Alan Bateman
Cc: jigsaw-dev
Subject: Re: Mutable modules


> On 20 May 2016, at 15:12, Alan Bateman <Alan.Bateman at oracle.com> wrote:
> 
> On 18/05/2016 22:47, David M. Lloyd wrote:
>> 
>> I mean in *our* current concept of a module, we can add/remove/modify the contents of a module (its "class path") at run time.  It is up to the user to ensure that doing so makes sense.
> I don't think I can relate to the use case. As you probably know then ZIP files have historically had their central directory mapped into memory. Removing or replacing a file that is memory mapped will likely lead to processes accessing the mapped file to crash (SIGBUS usually). So if users are really doing such hairy things they would need a lot of insight into what is running and whether the file is opened before taking this risk.
> 

Mutable module graphs have an 18 year history in OSGi, so they are certainly practical, of course with certain limitations. The use cases are to allow runtime installation or upgrade of modules without shutting down an entire application. Such capabilities proved indispensable in OSGi’s original market of home gateways, and now in the IoT world.

They key point is that it is a cooperative mechanism. When a module is uninstalled in OSGi, a callback is sent to the module in question, and it is then responsible for safely cleaning up anything that it was doing. If it does so properly then the module, along with all of its loaded objects, their classes and the classloader, become available for garbage collection. When a module is updated, a new classloader is created for the new version. Therefore if a module fails to implement correct clean-up and is updated many times, you have a memory leak. This is inescapable… the JVM can no more force a module to unload then it can force a thread to stop, and we all know the dire consequences of calling Thread#stop().

In case of a dependency (module A imports a package from module B), when B is uninstalled then module A must be refreshed. This involves calling the shutdown callback on modules A and B, and then re-resolving A. That resolution will likely fail as it will not be able to import the required packaged (unless they are exported from somewhere else).

Obviously this model depends heavily on a 1-to-1 mapping of classloaders to modules, in common with the JBoss Module System as described previously by David Lloyd.

>> :
>> 
>> Our modules each correspond to their own class loader: so far so good, we can just have one Module per class loader.  Problem is that we support circularity, and also we support dependencies that go across module systems with isolated namespaces (basically, our module loaders are a higher order of the exact same concept of class loaders).
> If there are cyclic relationships between your modules then it will be problematic. Do you see much of this? If you've read Alex's JavaOne slides then you'll know that some of us like Kirk Knoernschild's book on Java Application Architecture and section "4.4 Cyclic Dependencies - the Death Knell" where he poses the question "Are Cycles Always Bad?". I don't want to say too much on this topic here as it is listed as an open issue on the JSR issues list.
> 

OSGi permits cyclical dependencies, but such cycles can only be resolved all-at-once. This is essentially why OSGi separates its resolution phase (i.e. solving all dependencies) from its activation phase. Modules involved in a cycle will not be GC’d until all of the modules in that cycle are uninstalled or refreshed.


>> 
>> Our modules support specifications including the content of the module ("resource loaders") and the dependencies of the module. At run time, custom ModuleLoader implementations can change the resource loader list and/or the dependency list at any time, causing the module to be relinked on the spot; the most useful aspect of this is the ability to incrementally deploy applications which may include circular dependencies. 
> Aside from cycles then what other use-cases do you have here? I read "change the resource loader list" to mean that the set of resources in the module changes, which is a bit weird if those resources are class files that have already been loaded. Maybe there is dynamic code generation with class bytes generated to the file system or into somewhere virtual? Or maybe these resources are something else, data files? I'm just trying to understand what you mean as we are using differently terminology.
> 
>> We also support delegating "fallback" class loading decisions to outside suppliers for dynamic class loading behavior (this was done to support a dynamic OSGi environment).  The ongoing integrity of the system is up to the party doing the relinking (the EE deployer or the OSGi resolver); most of the time it can reason about what is "safe" and what might cause system breakage (but still might be useful to do anyway).  These are the features we can't seem to support under Jigsaw, architecturally speaking.
> This sounds like class loader delegation to resolve types that are not in the module.
> 
>> 
>> Specifically this includes (but is not limited to) changing the package set associated with a JDK module at run time, something that this native code block makes impossible.  Also the ability to dynamically change module dependencies is an essential ingredient to making this work.
> Suppose that module m has package p and p.C has been loaded. Are you saying that you can drop package p from the module?
> 
> As things currently stand in JDK 9 then packages may be added to modules at runtime, the main use case is the dynamic proxy to a public interface in a non-exported packages. So I can relate to adding packages for code gen cases, I'm less sure about a module starting out as an XML API and suddenly changing into a JDBC driver. Do you really mean the same module instance?

Modules coming and going affects the state of the service registry. It’s true that an API module (say, one that exports a bunch of interfaces) does not often need to dynamically change, and when it does change it causes a substantial amount of churn in the dependency graph. But If a module that provides a service is dynamically updated then consumers of that service should immediately see the new service implementation. If the provider module does not export any packages then it cannot have an affect on the imports of other modules, so the resolution can be done entirely locally to that module.

We have found that the dynamics in the service registry are extremely valuable because service availability can be used to reflect the availability of the real-world resources that those services represent. So OSGi’s dynamics are about far more than just module updates. Indeed, my enterprise customers rarely perform runtime module updates but they do employ service dynamics heavily.


> 
>> 
>> In my view, architecturally speaking, most of the constraints imposed by the core module framework should be layer policy.  If the system's core module layer wants to maintain strict, static integrity, name constraints, version syntax and semantics, etc., that's fine, but why should all modules everywhere be forced to the same constraints?
> Using module names as an example, then it should be possible to develop a module that is deployed on the application module path or instantiated in a layer of modules that a container creates. The author of the module (that chooses the name) isn't going to know in advance how the module is deployed. I'm not even sure how such a module could be compiled or how anyone could depend on it when the characters or format can vary like this. I see there is an issue on the JSR issues list so I don't want to say any more on this topic.
> 
> 
>> There is no way that existing containers and class loading environments (other than, apparently, WebLogic) can conform to Jigsaw's constraints without losing functionality (and I'm trying hard to find ways to make it work).  This is where most of my raised issues are coming from.
> The module system imposes surprising few constraints.  If you are using your own class loaders then the delegation needs to respect module readability, something that should not be controversial.
> 
> However it is possible that you are still at the starting line because your have a dependency graph with cycles and/or modules that don't have names that can be expressed as a Java identifier, is that right?
> 

As a disclaimer, I am not involved with OSGi’s official effort to evaluate and integrate the Java 9 module system but I am interested in the subject. The biggest decision that needs to be made is whether to create a direct mapping of OSGi bundles to J9 modules, or to exist in parallel with it. To achieve the former, there seem to be four major problems that must be tackled:

1. Lack of extensibility in the module-info.java format for version, imports and other metadata; 2. No reflective access to non-exported packages — often needed for frameworks like dependency injectors; 3. No dependency cycles at the module level — this is not encouraged in OSGi but has never been forbidden because there was no need to. To forbid it now would be a backwards-incompatible change for us; 4. Lack of dynamics. Nothing in the currently published API for J9 modules suggests to me that we can support the refresh, update, uninstall or install operations.


Regards,
Neil

> 
>> 
>> All these problems seem surmountable to me, but it becomes substantially more difficult when it is necessary to report all of a module's packages to the module when it is created, since this information is now not easily changed.
> I'm surprised that this is an issue as module membership is critical to access control.
> 
> -Alan.



More information about the jigsaw-dev mailing list