Artical on JPMS integration with existing module systems
Below is the plain text format from an article I wrote on using JPMS layers with an existing module system (OSGi). Please excuse the poor formatting. I tried to make it is pretty as I could in plain text to allow Mailman to let it through. I have omitted the diagrams to avoid this message getting held up by Mailman also. If the diagrams are important I can post them some other way later: In my previous blog post I discussed an experiment that creates a JPMS Bundle Layer which can represent resolved OSGi bundles as JPMS modules. This would allow child JPMS layers to be created that have modules that require the OSGi bundles as they would any other module. In that experiment I took a hybrid approach where the Framework implementation and the OSGi bundles themselves did not really live in the JPMS world. Instead I dynamically created a layer on top that attempted to represent that OSGi world indirectly within the JPMS world. Then real JPMS modules could be configured to work on top of this facade layer that represented the OSGi world. This can be thought of as taking a top down approach to migrating to JPMS. Unfortunately this approach has a major shortcoming because all classes that are loaded in the OSGi bundle layer will be associated with an unnamed module. The fact that the bundle classes are associated with an unnamed module caused me to have to do a major hack to grant access to modules representing the OSGi bundles. This hack involved injecting code into the jpms modules which could invoke the addReads method in order to grant the necessary access to the unnamed module of the bundle class loaders. This does not seem like a real viable solution for running JPMS modules on top of and OSGi bundle layer. I learned much about how the JMPS layer works during that experiment. The hybrid has a major flaw because the delegation graph of class loaders involved are not associated with named modules all the way down. A better way would be to do a bottom up approach where each layer involved has class loaders which are mapped to one or more named modules. This way when the JPMS layer resolves the modules on top it will automatically grant read access as normal from a requiring module to all of its required modules it got resolved to. The following diagram illustrates how the layers would look: [IMAGE REMOVED] The boot layer contains the JPMS modules which were configured with the JVM when it was launched. In this diagram, the framework launcher has also been migrated to Java 9 in order to have it create a Layer for the class loader used to load the framework implementation. This layer configures a single module named system.bundle. This allows all the classes for the Framework implementation to be associated with the system.bundle module. Next is the bundle layer. This layer is configured to map each bundle class loader to a named module representing the bundle. Finally we have a module layer which uses all the built-in module class loaders of Java 9 for JPMS. My Experiment Over the past few days I have been reworking my github project (OSGi-JPMS layer) to investigate if this approach is possible. Again, I am trying to do this without requiring any modifications to the OSGi framework implementation itself and I am using only OSGi specified APIs. This approach uses a bottom up strategy for JPMS modules. With that in mind the first thing to do is to modify the OSGi Framework launcher to create the system.bundle module. The system.bundle Module I did not want to modify the framework itself to make it a real JPMS module. Instead I decided to modify the existing Equinox launcher to create a layer itself which maps the class loader it creates to load the OSGi Framework with a system.bundle module. While the Equinox launcher is specific to launching the Equinox Framework a similar thing could be done to launch any standard OSGi Framework. The system.bundle acts as the OSGi bundle that exports for all the non-java.* packages available in the boot layer. This allows OSGi bundles to use Import-Package to depend on packages from the boot layer. In order to grant the system.bundle class loader access to all packages available from the boot layer I have to generate a ModuleDescriptor programatically which requires all modules from the boot layer. The layer must be created with the system.bundle module resolved which maps the module to the class loader used to load the framework implementation before any classes are defined in packages that we want to be exported by system.bundle module. The ModuleDescriptor used for the system.bundle must specify that it exports the packages from the framework implementation, otherwise JPMS will still associate them with the unknown module. With this modified launcher, any classes defined in the packages we declared in the ModuleDescriptor will be associated with the system.bundle module. You can find the changes I made to the equinox launder on github at https://github.com/tjwatson/rt.equinox.framework/tree/tjwatson/jpms. You may notice I hard coded the list of packages to export from the system.bundle module. This was a hack to get going quickly. Ideally these packages would be discovered programmatically. The Bundle Layer One important detail to understand about JPMS layers is that the class loaders that are mapped to by the modules within a layer MUST NOT have defined any classes in packages for which a ModuleDescriptor declares as exports or conceals. This implies that the bundle layer used to represent bundle JPMS modules must be created as early as possible and ideally before any classes are loaded using the bundle class loaders. In order to achieve this I changed the bundle osgi.jpms.layer to a system.bundle fragment still named osgi.jpms.layer. The OSGi R6 Framework specification added a new feature which allows system.bundle fragments to be activated when the Framework is initializing before the rest of the bundles get activated. This allows for the code controlling the bundle layer to get in place in order to intercept any class defines from bundle class loaders. That way we can map the bundle class loaders for resolved bundles to their respective JPMS modules before any classes are defined. I used a WovenClassListener and WeavingHook to achieve this. Here I am not interested in actually weaving any class bytes, but these OSGi hooks allow for us to hook directly into the bundle class loader just before it is about to define a class. We can now insert the code in the correct place to create the bundle layer. I used a similar approach as before to achieve this, but some more information is needed now that the bundle classes will belong to a named module. Here are the steps: 1. Discover all resolved host bundles and map their symbolic name to their wiring. Note that we could get conflicts if multiple bundles are installed with the same symbolic name. For this experiment I choose only one to map into the bundle layer. 2. Create a module finder that is backed by the bundle wirings. The finder is what creates the ModuleReference and ModuleDescriptor objects to represent the bundles. The following information is used from the wiring: - The bundle symbolic name is the module name. - The bundle version is the module version. - The the package capabilities are the exports for the module. - Private packages must be discovered to specify the module's concealed packages. Here the private packages are treated as exported by the module instead of concealed. I will explain why later. - Dependencies on other bundles for class loading must become module requirements. 3. Create a configuration using the bundle finder. Default to using the system.bundle layer configuration as the parent configuration. 4. Create a layer that maps each module name to the bundle wiring class loader. Creating this layer exposes some issues with JPMS that make it difficult and sometimes impossible to properly represent OSGi bundles as modules. 1. JPMS-ISSUE-001 - Reflection is used by almost any framework in Java and the OSGi Framework is no exception. In JPMS the JVM will not allow reflection to be used on any class that is not known to JPMS as an exported package. Once I successfully got every class from a bundle associated with a JPMS module I found that the framework could no longer call Class.newInstance() for bundle activator classes contained in concealed packages!! In order to get that to work I had to treat every private package from a bundle as exported by the ModuleDescriptor for the bundle. This will also be necessary for other dependency injection containers on OSGi, for example, Declarative Services. I also imagine this has to cause issues for other DI containers such as Spring and CDI. 2. JPMS-ISSUE-002 - Private packages must be discovered and specified to JPMS. As pointed out already, I had to make the private packages exported by JPMS, but first I tried to make them concealed. Either way, all packages that are associated with a module must be known to JPMS as either exported or concealed. If this is not done then the classes from unknown packages will be associated with the unnamed module. This places an extra burden on the OSGi module system because in OSGi there was no reason for the framework to discover the private packages ahead of time. 3. JPMS-ISSUE-003 - JPMS must be aware of the OSGi bundle dependencies for class loader access. If the module descriptors representing OSGi bundles do not declare any module requires then JPMS will not grant the read access required to use a class from another module. The bundle class loaders will continue to be able to load the classes from other bundles according to import-package and require-bundle rules, but when the class is actually used the JVM will throw access exceptions. This forces us to translate the OSGi dependencies into module requires. If there are multiple bundles with the same symbolic name then there is no way to tell JPMS which version of the bundle a module depends on. 4. JPMS-ISSUE-004 - JPMS layers do not allow cycles between modules. OSGi bundles are allowed to have cycles. Since we must make JPMS aware of the OSGi bundle dependencies this restricts us to only bundles that have no cycles. 5. JPMS-ISSUE-005 - JPMS layers provide a static module resolution graph. This will prevent OSGi from successfully resolving dynamic package imports if they require read access to a new module. 6. JPMS-ISSUE-006 - JPMS layers allow for multiple versions of the same module but it does not appear that modules within that layer or contained child layers can influence which version of the module they get resolved to. 7.JPMS-ISSUE-008 - JPMS layers do not allow for split packages. If the OSGi bundles are resolved with split packages then the bundle layer cannot be created. If you can look past these issues we are left with a layer that can represent a static set of resolved OSGi bundles as real JPMS modules and we can use that layer to create child JPMS layers for loading other JPMS modules. OSGi Bundle Dynamics The bundle layer we have now represents a static set of resolved OSGi bundles in a Framework. But the bundles in an OSGi Framework are not static. They can be uninstalled, updated, re-resolved, and new bundles can be installed. How can this dynamic nature be represented in JPMS layers? The approach I took was to create a linear graph of layers where the youngest child layer represents the current state of the bundles. This would look something like this: [IMAGE REMOVED] In this scenario we started out with bundle.a and bundle.b resolved in the bundle layer 1. Then we created a module layer 1 to resolve jpms.a and jpms.b modules. Then bundle.b was updated and bundle.c was installed and then bundle.b was refreshed in order to flush out its old content and class loader. This leaves bundle layer 1 with a "dead" bundle.b module which also makes module layer 1 stale. So we decide to discard module layer 1 and create module layer 2 for jpms.a and jpms.b modules. To do that we need a new bundle layer that represents the current set of resolved bundles. Here we cannot discard bundle layer 1 because it still has at least one valid module bundle.a. We also cannot represent bundle.a module in a new layer because we may have already loaded classes from packages contained in bundle.a. Instead of throwing away bundle layer 1 a new bundle layer 2 is created that uses bundle layer 1 as its parent. Bundle layer 2 will contain all the new versions of modules that are not already represented in the parent layers. This allows the new bundle.b to shadow the "dead" bundle.b module in bundle layer 1. This appears to work. The only JPMS module that cannot be shadowed by a child layer is the java.base module. But we are left with a pretty big issue: - JPMS-ISSUE-007 - Discarded modules from a JPMS layer will be pinned in memory until the complete layer is discarded. This ultimately leads to a huge class loader leak because we cannot properly free up our stale bundle class loaders. It also causes issues for bundles that are uninstalled completely. The "dead" modules for these bundles will continue to be available since nothing is shadowing them from child layers. I suppose we could create a empty module that has the same name but exports nothing, but that will still allow modules on top to resolve when they shouldn't. Currently the code for the experiment is located in github at https://github.com/tjwatson/osgi-jpms-layer/tree/tjwatson/moduleClassLoader I did this in the tjwatson/moduleClassLoader branch. Conclusion This approach allows for a pretty accurate representation of a static set of resolved OSGi bundles as JPMS modules. But we are left with several issues that need to be addressed before this can be considered a truly viable solution. Some may decide these are permanent restrictions of JPMS that we will have to live with going forward. But I believe there are some tweaks to JPMS that could go a long ways to making this approach close to a complete solution. Listed below are some changes that would help. I listed them in the order of importance, but I think 1 and 2 are a close tie for most important. 1. Allow for code that manages a JPMS layer to have more control for establishing read access for the modules contained in the managed layer. The Module addReads method allows for read access to be added for a module dynamically at runtime. But it has a restriction that it must be called by a class defined by the module that wants new read access. It would be a great help if we could call addReads from the management code that created the layer. Perhaps an addReads(Module wantsRead, Module toTarget) method on Layer that checks the caller module is the same module get created the Layer? This could be used to solve a large set of issues outlined above: - JPMS-ISSUE-003 - We could avoid having to make JPMS aware of the OSGI dependencies if we would be allowed to establish the read access ourselves when the bundle layer is created. - JPMS-ISSUE-004 - If we avoid having to make JPMS aware of the OSGi dependencies then we no longer have worry about restricting cycles. - JPMS-ISSUE-005 - If we can dynamically add reads then we can enable dynamic package import to work by dynamically adding read access to the provider of the package at runtime. - JPMS-ISSUE-008 - If we avoid having to make JPMS aware of the OSGi dependencies then we no longer have to worry about restricting split packages. 2. Allow for reflection on classes from concealed packages. Many dependency injection containers depend on being able to act upon concealed classes in order to construct objects and inject the objects with dependencies. Forcing implementation details to be exported so that these classes can be acted upon by DI containers is wrong. - JPMS-ISSUE-001 - We would no longer have to declare the bundle private packages as exported by the JPMS module. Instead they can remain concealed as they should be. 3. Allow for sub-graphs of modules to be discarded within a layer. - JPMS-ISSUE-007 - This would allow us to flush out the "dead" modules which should never be used anymore. 4. Allow a layer to map a class loader to a default named module. Any classes from unknown packages to the JPMS would be assigned this named module instead of the unnamed module. - JPMS-ISSUE-002 - This would allow us to avoid having to scan for private packages. Instead we would map the bundle classloader to a module and that module could be used for the private packages. 5. Allow the JPMS requires statement to specify a module version. - JPMS-ISSUE-006 - This would allow us to represent multiple versions of a bundle within the bundle layer and give JPMS modules the ability to specify which version they want. My hope is that this experiment is useful in providing constructive feedback to the JPMS expert group. I hope they consider enhancing JPMS to make JPMS layers more usable with existing module systems like OSGi. This was extracted from my article at: http://blog.osgi.org/2016/08/osgi-with-java-modules-all-way-down.html Tom
Obviously Tom has come at this integration challenge from his deep knowledge of a particular OSGi implementation. There are some important observations, and minor tweaks to the current model that will improve JPMS. "Thomas Watson" <tjwatson@us.ibm.com> wrote on 18/08/2016 14:45:06:
Below is the plain text format from an article I wrote on using JPMS layers with an existing module system (OSGi). Please excuse the poor formatting. I tried to make it is pretty as I could in plain text to allow Mailman to let it through. I have omitted the diagrams to avoid this message getting held up by Mailman also. If the diagrams are important I can post them some other way later:
Disclosure: I read the html version, and trust that this plain text is a faithful reproduction. I'm going to snip out most of the original post too, which of course is required reading to get the context and details for this experiment. However, I want to focus on the specific recommendations for this group, so I will skip straight to the conclusions. <snip/>
Conclusion
Ok, this is where there are a few issues and recommendations that this group can debate...
This approach allows for a pretty accurate representation of a static set of resolved OSGi bundles as JPMS modules. But we are left with several issues that need to be addressed before this can be considered a truly viable solution. Some may decide these are permanent restrictions of JPMS that we will have to live with going forward. But I believe there are some tweaks to JPMS that could go a long ways to making this approach close to a complete solution. Listed below are some changes that would help. I listed them in the order of importance, but I think 1 and 2 are a close tie for most important.
1. Allow for code that manages a JPMS layer to have more control for
establishing read access for the modules contained in the managed layer.
The Module addReads method allows for read access to be added for a module dynamically at runtime. But it has a restriction that it must be called
by a class defined by the module that wants new read access. It would be a great help if we could call addReads from the management code that created the layer. Perhaps an addReads(Module wantsRead, Module toTarget) method on Layer that checks the caller module is the same module get created the Layer? This could be used to solve a large set of issues outlined above:
That sounds like a reasonable request.
- JPMS-ISSUE-003 - We could avoid having to make JPMS aware of
the
OSGI dependencies if we would be allowed to establish the read access ourselves when the bundle layer is created.
- JPMS-ISSUE-004 - If we avoid having to make JPMS aware of the OSGi dependencies then we no longer have worry about restricting cycles.
- JPMS-ISSUE-005 - If we can dynamically add reads then we can enable dynamic package import to work by dynamically adding read access to the provider of the package at runtime.
- JPMS-ISSUE-008 - If we avoid having to make JPMS aware of the OSGi dependencies then we no longer have to worry about restricting split packages.
2. Allow for reflection on classes from concealed packages. Many dependency injection containers depend on being able to act upon concealed classes in order to construct objects and inject the objects with dependencies. Forcing implementation details to be exported so that these classes can be acted upon by DI containers is wrong.
I believe this is already covered by #ReflectiveAccessToNonExportedTypes http://openjdk.java.net/projects/jigsaw/spec/issues/#ReflectiveAccessToNonEx...
- JPMS-ISSUE-001 - We would no longer have to declare the bundle
private packages as exported by the JPMS module. Instead they can remain concealed as they should be.
3. Allow for sub-graphs of modules to be discarded within a layer.
Again, this sounds like a reasonable request.
- JPMS-ISSUE-007 - This would allow us to flush out the "dead" modules which should never be used anymore.
4. Allow a layer to map a class loader to a default named module.
Any
classes from unknown packages to the JPMS would be assigned this named module instead of the unnamed module.
- JPMS-ISSUE-002 - This would allow us to avoid having to scan for private packages. Instead we would map the bundle classloader to a module and that module could be used for the private packages.
5. Allow the JPMS requires statement to specify a module version.
Consideration of versioning was explicitly excluded from the original requirements, and I don't see it coming in at this stage. If you are controlling the resolution of modules then you would be able to take account of version numbers in the integration.
- JPMS-ISSUE-006 - This would allow us to represent multiple versions of a bundle within the bundle layer and give JPMS modules the ability to specify which version they want.
My hope is that this experiment is useful in providing constructive feedback to the JPMS expert group. I hope they consider enhancing JPMS
to
make JPMS layers more usable with existing module systems like OSGi.
Most of the suggestions are modest enhancements to the current model, so it is encouraging to see that a good level of interoperability can be obtained with a few minor tweaks. Thanks for doing this experiment Tom. I look forward to others' comments too.
This was extracted from my article at: http://blog.osgi.org/2016/08/osgi-with-java-modules-all-way-down.html
Regards, Tim Unless stated otherwise above: IBM United Kingdom Limited - Registered in England and Wales with number 741598. Registered office: PO Box 41, North Harbour, Portsmouth, Hampshire PO6 3AU
2016/9/7 6:57:55 -0700, tim_ellison@uk.ibm.com:
Obviously Tom has come at this integration challenge from his deep knowledge of a particular OSGi implementation. There are some important observations, and minor tweaks to the current model that will improve JPMS.
Tom -- thanks very much for your detailed feedback. I'll reply at length in due course, but most likely not until after JavaOne. - Mark
2016/9/7 6:57:55 -0700, tim_ellison@uk.ibm.com:
Obviously Tom has come at this integration challenge from his deep knowledge of a particular OSGi implementation. There are some important observations, and minor tweaks to the current model that will improve JPMS.
Thomas Watson <tjwatson@us.ibm.com> wrote on 18/08/2016 14:45:06:
Below is the plain text format from an article I wrote on using JPMS layers with an existing module system (OSGi). ... [1]
...
Conclusion
Ok, this is where there are a few issues and recommendations that this group can debate...
This approach allows for a pretty accurate representation of a static set of resolved OSGi bundles as JPMS modules. But we are left with several issues that need to be addressed before this can be considered a truly viable solution. Some may decide these are permanent restrictions of JPMS that we will have to live with going forward. But I believe there are some tweaks to JPMS that could go a long ways to making this approach close to a complete solution. Listed below are some changes that would help. I listed them in the order of importance, but I think 1 and 2 are a close tie for most important.
1. Allow for code that manages a JPMS layer to have more control for establishing read access for the modules contained in the managed layer. The Module addReads method allows for read access to be added for a module dynamically at runtime. But it has a restriction that it must be called by a class defined by the module that wants new read access. It would be a great help if we could call addReads from the management code that created the layer. Perhaps an addReads(Module wantsRead, Module toTarget) method on Layer that checks the caller module is the same module get created the Layer? This could be used to solve a large set of issues outlined above:
That sounds like a reasonable request.
This has come up in past discussions. I don't think an API for this is strictly necessary. Code that manages a layer presumably controls the class loaders in that layer, and hence already has the power to load arbitrary classes. It can use that power to load tiny synthetic classes to add additional read edges as required, as Tom realized in his first experiment. That's all a bit clunky, however, and we could make it easier, so for the record I'll enter a new issue: #ReadabilityAddedByLayerCreator -- Provide a means by which the code that creates a layer can add readability edges from the modules in that layer to other modules, whether those modules are in that layer or in other layers.
...
2. Allow for reflection on classes from concealed packages. Many dependency injection containers depend on being able to act upon concealed classes in order to construct objects and inject the objects with dependencies. Forcing implementation details to be exported so that these classes can be acted upon by DI containers is wrong.
I believe this is already covered by #ReflectiveAccessToNonExportedTypes http://openjdk.java.net/projects/jigsaw/spec/issues/#ReflectiveAccessToNonEx...
Yes, I think that's right.
...
3. Allow for sub-graphs of modules to be discarded within a layer.
Again, this sounds like a reasonable request.
- JPMS-ISSUE-007 - This would allow us to flush out the "dead" modules which should never be used anymore.
This is a narrower form of the existing #MutableConfigurations issue [2]. It could be easier to implement, however, so I'll enter it separately as #DiscardableModules -- Provide a means by which a sub-graph of modules within a layer can be discarded, so that both those modules and their class loaders can be reclaimed.
4. Allow a layer to map a class loader to a default named module. Any classes from unknown packages to the JPMS would be assigned this named module instead of the unnamed module.
- JPMS-ISSUE-002 - This would allow us to avoid having to scan for private packages. Instead we would map the bundle classloader to a module and that module could be used for the private packages.
Rather than introduce a notion of default named modules, I wonder if this problem could instead be addressed by tooling. In JPMS we avoid scanning a modular JAR file for concealed packages by having the `jar` tool add a list of all the module's packages to the module descriptor as the JAR file is created. This list is optional, so if it's missing we do scan the JAR file, but we expect that to be a rare occurrence. If I understand the documentation correctly, bndtools does something similar by adding a `Private-Package` manifest header to record the list of non-exported packages in a bundle [3]. Could your OSGi-to-JPMS adaptation use that manifest header, when available, to avoid scanning for private packages?
5. Allow the JPMS requires statement to specify a module version.
Consideration of versioning was explicitly excluded from the original requirements, and I don't see it coming in at this stage. If you are controlling the resolution of modules then you would be able to take account of version numbers in the integration.
Right. If you create your own ModuleFinder then you can arrange for the preferred version of each module to appear ahead of any other versions.
- JPMS-ISSUE-006 - This would allow us to represent multiple versions of a bundle within the bundle layer and give JPMS modules the ability to specify which version they want.
I think the first part of this, i.e., representing multiple versions of a bundle within a single layer, can be avoided entirely, as I'll describe in a separate message.
My hope is that this experiment is useful in providing constructive feedback to the JPMS expert group. I hope they consider enhancing JPMS to make JPMS layers more usable with existing module systems like OSGi.
Most of the suggestions are modest enhancements to the current model, so it is encouraging to see that a good level of interoperability can be obtained with a few minor tweaks.
Thanks for doing this experiment Tom. I look forward to others' comments too.
Tom -- thanks again for this detailed feedback; it's been very helpful. Thinking about your second experiment led me to a slightly different approach to supporting bidirectional interoperation between OSGi and JPMS. It's a big topic, though, so rather than present it here I'll describe it in a separate message. - Mark [1] http://blog.osgi.org/2016/08/osgi-with-java-modules-all-way-down.html [2] http://openjdk.java.net/projects/jigsaw/spec/issues/#MutableConfigurations [3] http://bnd.bndtools.org/heads/private_package.html
From: mark.reinhold@oracle.com
2016/9/7 6:57:55 -0700, tim_ellison@uk.ibm.com:
1. Allow for code that manages a JPMS layer to have more control for establishing read access for the modules contained in the managed layer. The Module addReads method allows for read access to be added fora module dynamically at runtime. But it has a restriction that it must be called by a class defined by the module that wants new read access. It would be a great help if we could call addReads from the management code that created the layer. Perhaps an addReads(Module wantsRead, Module toTarget) method on Layer that checks the caller module is the same module get created the Layer? This could be used to solve a large set of issues
outlined above:
That sounds like a reasonable request.
This has come up in past discussions. I don't think an API for this is strictly necessary. Code that manages a layer presumably controls the class loaders in that layer, and hence already has the power to load arbitrary classes. It can use that power to load tiny synthetic classes to add additional read edges as required, as Tom realized in his first experiment. That's all a bit clunky, however, and we could make it easier, so for the record I'll enter a new issue:
#ReadabilityAddedByLayerCreator -- Provide a means by which the code that creates a layer can add readability edges from the modules in that layer to other modules, whether those modules are in that layer or in other layers.
In my view injecting code into a module in order to give the appearance that the call is coming from the module itself is pretty ugly so a solution to this issue would be very helpful for my scenario. Not only would the code have to be injected into the module but I think the package the injected code is in would have to be 'exposed' for reflective reasons, during my prototype that meant I had to export the package in the ModuleDescriptor because there was no notion of 'weak' module or 'private' exports at the time. That then causes issues for any JPMS layers on top because it would not allow modules in these layers to wire to more than one OSGi module because of split package restrictions. To get around that we would have to generate a unique package to hold the code which is injected into each module. Perhaps that would be less ugly depending on the outcome of #ReflectiveAccessToNonExportedTypes. Exposing packages for reflective reasons should not mean the package is also available for classloading/linking like a normal package export in JPMS.
...
2. Allow for reflection on classes from concealed packages. Many dependency injection containers depend on being able to act upon
concealed
classes in order to construct objects and inject the objects with dependencies. Forcing implementation details to be exported so that these classes can be acted upon by DI containers is wrong.
I believe this is already covered by #ReflectiveAccessToNonExportedTypes http://openjdk.java.net/projects/jigsaw/spec/issues/ #ReflectiveAccessToNonExportedTypes
Yes, I think that's right.
I agree.
...
3. Allow for sub-graphs of modules to be discarded within a layer.
Again, this sounds like a reasonable request.
- JPMS-ISSUE-007 - This would allow us to flush out the "dead"
modules which should never be used anymore.
This is a narrower form of the existing #MutableConfigurations issue [2]. It could be easier to implement, however, so I'll enter it separately as
#DiscardableModules -- Provide a means by which a sub-graph of modules within a layer can be discarded, so that both those modules and their class loaders can be reclaimed.
Thanks, although your follow on proposal may make this unnecessary.
4. Allow a layer to map a class loader to a default named module.
Any
classes from unknown packages to the JPMS would be assigned this named module instead of the unnamed module.
- JPMS-ISSUE-002 - This would allow us to avoid having to scan for private packages. Instead we would map the bundle classloader toa module and that module could be used for the private packages.
Rather than introduce a notion of default named modules, I wonder if this problem could instead be addressed by tooling. In JPMS we avoid scanning a modular JAR file for concealed packages by having the `jar` tool add a list of all the module's packages to the module descriptor as the JAR file is created. This list is optional, so if it's missing we do scan the JAR file, but we expect that to be a rare occurrence.
If I understand the documentation correctly, bndtools does something similar by adding a `Private-Package` manifest header to record the list of non-exported packages in a bundle [3]. Could your OSGi-to-JPMS adaptation use that manifest header, when available, to avoid scanning for private packages?
Yes, this was going to be my approach for bundles built by bnd. To be honest this item is #4 on my list of importance and approaches the category of "nice to have". Even for the bundles where we have to scan for all packages the framework can easily cache the private-package information so that upon restart the scan is not needed again.
5. Allow the JPMS requires statement to specify a module version.
Consideration of versioning was explicitly excluded from the original requirements, and I don't see it coming in at this stage. If you are controlling the resolution of modules then you would be able to take
account
of version numbers in the integration.
Right. If you create your own ModuleFinder then you can arrange for the preferred version of each module to appear ahead of any other versions.
- JPMS-ISSUE-006 - This would allow us to represent multiple versions of a bundle within the bundle layer and give JPMS modules
the
ability to specify which version they want.
I think the first part of this, i.e., representing multiple versions of a bundle within a single layer, can be avoided entirely, as I'll describe in a separate message.
I agree at this point, but have to think over your separate proposal in more detail. Tom
From: mark.reinhold@oracle.com That's all a bit clunky, however, and we could make it easier, so for the record I'll enter a new issue: #ReadabilityAddedByLayerCreator -- Provide a means by which the code that creates a layer can add readability edges from the modules in that layer to other modules, whether those modules are in that layer or in other layers.
This is a narrower form of the existing #MutableConfigurations issue [2]. It could be easier to implement, however, so I'll enter it separately as
#DiscardableModules -- Provide a means by which a sub-graph of modules within a layer can be discarded, so that both those modules and their class loaders can be reclaimed.
I don't see these two issues included yet in http://openjdk.java.net/projects/jigsaw/spec/issues/ I assume they simply haven't been added yet? Tom
2016/10/20 12:33:11 -0700, Thomas Watson <tjwatson@us.ibm.com>:
From: mark.reinhold@oracle.com That's all a bit clunky, however, and we could make it easier, so for the record I'll enter a new issue:
#ReadabilityAddedByLayerCreator -- Provide a means by which the code that creates a layer can add readability edges from the modules in that layer to other modules, whether those modules are in that layer or in other layers.
This is a narrower form of the existing #MutableConfigurations issue [2]. It could be easier to implement, however, so I'll enter it separately as
#DiscardableModules -- Provide a means by which a sub-graph of modules within a layer can be discarded, so that both those modules and their class loaders can be reclaimed.
I don't see these two issues included yet in http://openjdk.java.net/projects/jigsaw/spec/issues/
I assume they simply haven't been added yet?
Correct. Will update soon. - Mark
participants (3)
-
mark.reinhold@oracle.com
-
Thomas Watson
-
Tim Ellison