How to pass --add-exports to Strimzi Kafka Connect? - java

I'm using Strimzi 0.33.0 (which uses Java 17) and one of my connect plugins still needs to access sun.management.ManagementFactoryHelper. Obviously JVM throws:
cannot access class sun.management.ManagementFactoryHelper (in module java.management) because module java.management does not export sun.management to unnamed module #0x478cc5f1
So I need to pass --add-exports=java.management/sun.management=ALL-UNNAMED to JVM. Looking at the documentation, jvmOptions section doesn't support such a feature. Is there any other approach to enable this feature?

Looking at the source code kafka_connect_run.sh, injecting STRIMZI_JAVA_SYSTEM_PROPERTIES environment variable, resolves the issue.
connectContainer:
env:
- name: STRIMZI_JAVA_SYSTEM_PROPERTIES
value: "--add-exports=java.management/sun.management=ALL-UNNAMED"

Related

How can I configure Quarkus to load an SPI implementation into a specific ClassLoader?

I am running into an issue deploying a Quarkus App that uses an SPI implementation injected by our deployment system.
In our pom, we specify the SPI interface (which calls to ServiceLoader.load(class) in it's static initializer). When we deploy the Quarkus app, we decompose the QuarkusRunner jar, extract the Main-Class from the MANIFEST and construct a command line similar to "java -cp ... io.quarkus.bootstrap.runner.QuarkusEntryPoint". The class path includes everything in quarkus-app/app, lib/boot and lib/main plus the SPI implementation we intend to use.
When we run the app, and try to use code that invokes our SPI ServiceLoader code, we get the following error:
java.util.ServiceConfigurationError: : not a subtype.
I read this as the ClassLoader used by Quarkus (which contains the SPI-interface) and the ClassLoader that loads the SPI-Implementation, are somehow not connected (i.e., isolated from one another).
Things of interest:
We are using Quarkus 1.13.2-Final
I have tried to make our SPI Interface a parentFirstArtifact (it has no dependencies), with no luck.
Looking at the code for QuarkusEntryPoint, it looks like it loads all the classes placed into quarkus/quarkus-application.dat, which is created during the maven build, into the Quarkus RunnerClassLoader, whose parent is the System ClassLoader. My assumption was items on the classpath were added to the System ClassLoader.
Question:
At this point, I am completely lost as to what is actually happening. How do I get my SPI-Implementation to work with Quarkus?
When using Quarkus's fast-jar, almost everything is loaded into the JVM via the RunnerClassLoader (the exceptions are the classloader itself, and a tiny number of supporting classes and utility libraries).
What you would consider the classpath (that is User code, code generated or transformed by Quarkus and dependencies) are indexed in the quarkus-application.dat file which is built at build time and cannot be modified.

Add a ResourceBundle service provider directive in a Java 13 module through moditect maven plugin

I got a Java project that I'm migrating from Java 8 to Java 13. This project uses ResourceBundles to enable language localisation.
In Java 8, I provided a custom ResourceBundle.Control to ResourceBundle.getBundle(baseName, control) but this doesn't work anymore in Java 9+. As I understand it, I must instead provide a custom ResourceBundleProvider interface, which I called UiProvider, and an implementation of this interface, UiProviderImpl, which must be used as a service.
To generate module descriptors, I'm using the moditect maven plugin. But it doesn't look like I can add a provides directive anywhere, only exports, opens and uses directives. Or am I missing anything? Here's an excerpt of my pom.xml with what I tried to configure. Can this be fixed?
<module>
<moduleInfo>
<name>net.babelsoft.negatron</name>
<opens>net.babelsoft.negatron;</opens>
<uses>theme.language.spi.UiProvider</uses>
<provides>theme.language.spi.UiProvider with theme.language.spi.UiProviderImpl</provides>
</moduleInfo>
</module>
At the time I wrote my question, Moditect didn't support the provides directive within the moduleInfo tag.
The only way was to use a moduleInfoSource tag, which forces the developper to directly write the actual content of module-info.java, not very satisfactory.
After discussing with the author of Moditect, I submitted a pull request to add the support of the provides directive within the moduleInfo tag. It hasn't been merged to Moditect source code yet...

How to let a java class on classpath access a class belonging to a module on module-path

I am trying out the various access rules about who can access and what and I saw this statement in The State of the module system document,
The unnamed module reads every other module. Code in any type loaded from the class path will thus be able to access the exported types of all other readable modules, which by default will include all of the named, built-in platform modules.
So, I wrote the following code to test it out with the following structure:
moduleA/modA.A --> automod/automod.Foo --> nonmodular.Junk --> moduleX/modX.X
Basically,
moduleA's modA.A calls a method on a non-modular class automod.Foo. automod.Foo is packaged into automod.jar and put on the module-path. module-info for moduleA has requires automod; clause. This works fine, as expected.
automod.Foo calls a method on nonmodular.Junk class. nonmodular.Junk is packaged into nonmodular.jar and put on classpath. This works fine, as expected.
nonmodular.Junk calls a method on moduleX's modX.X. modX.X is packaged into moduleX.jar.
It is this step that has a problem. It works if I put moduleX.jar on classpath but not if I put moduleX.jar on module-path. (module-info for moduleX does have exports modX; clause.)
In other words, the following command works:
java --module-path moduleA.jar;automod.jar; -classpath nonmodular.jar;moduleX.jar --module moduleA/modA.A
With the following output:
In modA.A.main() Calling automod.Foo()
In automod.Foo()
In modA.A.main() Calling automod.foo.main()
In automod.Foo.main() Calling nonmodular.Junk()
In automod.Foo.main() Calling nonmodular.Junk.main()
In nonmodular.Junk.main calling new modX.X()
In modX.X()
But the following command doesn't work:
java --module-path moduleA.jar;automod.jar;moduleX.jar -classpath nonmodular.jar; --module moduleA/modA.A
Here is the output:
In modA.A.main() Calling automod.Foo()
In automod.Foo()
In modA.A.main() Calling automod.foo.main()
In automod.Foo.main() Calling nonmodular.Junk()
In automod.Foo.main() Calling nonmodular.Junk.main()
In nonmodular.Junk.main calling new modX.X()
Exception in thread "main" java.lang.NoClassDefFoundError: modX/X
at nonmodular.Junk.main(Junk.java:5)
at automod/automod.Foo.main(Foo.java:10)
at moduleA/modA.A.main(A.java:10)
Caused by: java.lang.ClassNotFoundException: modX.X
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 3 more
Any idea why? Any class loaded from the classpath should be able to access any classes exported by a module.
When you start a Java application with the --module command, the value you pass is a "root" module. The same is true of modules added via --add-modules. The module system determines the entire module graph from these root modules. In other words, it reads the module-info file, finds the requires directives, and then searches the modulepath for those required modules. It does this transitively. Some modules also declare one or more uses directives on a service. Any modules on the modulepath that provides any of those services will also be loaded, regardless of if any module requires them.
This means if there's a module on the modulepath that isn't required by any loaded module and doesn't provide any services needed by any loaded module then said module won't be loaded. If you're interested in seeing what modules are resolved you can use the following command:
java --show-module-resolution --dry-run -p [MODULEPATH] -m [MODULE]
In your case I can only assume that none of your other modules require modularX, so when its on the modulepath it doesn't get loaded. However, when its on the classpath things work differently and its found by your non-modular code that's also on the classpath. You can still use the modulepath though, just make sure your moduleX module is loaded. This can be forced by using --add-modules:
java -p moduleA.jar;automod.jar;moduleX.jar --add-modules moduleX -cp nonmodular.jar -m moduleA/modA.A
Note you can also limit the modules via --limit-modules.

Why is my Java 9 Module service not having its implementations registered?

I am learning Java 9 Module by reading the book "Modular Programming in Java 9". In chapter 7, Introducing services, at page 201, I followed the steps by the book to create a simple project using service. I will post the code below or you can visit the project on github.
service
module name: packt.sortutil
class full name: packt.util.SortUtil
public interface SortUtil {
<T extends Comparable> List<T> sortList(List<T> list);
}
2. providers
class full name: packt.util.impl.bubblesort.BubbleSortUtilImpl:
code:
package packt.util.impl.bubblesort;
public class BubbleSortUtilImpl implements SortUtil
...
module-info.java
module packt.sort.bubblesort {
requires packt.sortutil;
provides packt.util.SortUtil with packt.util.impl.bubblesort.BubbleSortUtilImpl;
}
There is also packt.util.impl.javasort.JavaSortUtilImpl with the only difference of a different implementation.
consumer
module-info.java:
module packt.addressbook {
requires packt.sortutil;
uses packt.util.SortUtil;
}
code:
Iterable<SortUtil> sortUtils = ServiceLoader.load(SortUtil.class);
for (SortUtil sortUtil : sortUtils){
System.out.println("Found an instance of SortUtil");
sortUtil.sortList(contacts);
}
When I run consumer, it should be:
Note the highlighted area, where 2 implementations are found. Also, the names are sorted by the last name.
But for my program, it is:
2月 03, 2019 12:12:55 上午 packt.addressbook.Main main
INFO: Address book viewer application: Started
2月 03, 2019 12:12:56 上午 packt.addressbook.Main main
INFO: Address book viewer application: Completed
[Edsger Dijkstra, Raid Black]
Notice there are no "Found an instance of SortUtil" and the names are not sorted. So it appears as if there are no implementations registered with the service.
Where did it go wrong? Thanks.
What I did
#nullpointer's suggestion is helpful. I used java --module-path out --module packt.addressbook/packt.addressbook.Main to debug the see the module path, and found out:
root packt.addressbook file:///D:/ebooks/Modular%20Programming%20in%20Java%209/
ce-code/p205_Implementing_sorting_services/out/production/packt.addressbook/
packt.addressbook requires packt.sortutil file:///D:/ebooks/Modular%20Programmi
n%20Java%209/practice-code/p205_Implementing_sorting_services/out/production/pa
rtutil/
packt.addressbook requires java.logging jrt:/java.logging
packt.addressbook requires packt.contact file:///D:/ebooks/Modular%20Programmin
%20Java%209/practice-code/p205_Implementing_sorting_services/out/production/pac
tact/
packt.contact requires java.xml jrt:/java.xml
...
Module packt.sort.bubblesort and packt.sort.javasort were not present in the module path. Then I checked the output folder and saw that these 2 provider modules' were not compiled. I figured out that the reason is these 2 modules werenot readable by any other modules and therefore omitted during compilation.
When I used the command D:\programs\java\jdk-11.0.2\bin\javac -d out --module-source-path .\*\src -m packt.addressbook, it looks for modules that modulepackt.addressbook dependent on, including service module packt.sortutil, but since packt.sortutil and other modules do not depend on the 2 service modules,packt.sort.bubblesort and packt.sort.javasort, they are omitted. Actually, they depend on the service module instead.
Solutions
So I changed to command to manually compile these 2 modules: javac -d out --module-source-path .\*\src -m packt.addressbook,packt.sort.bubblesort,packt.sort.javasort. And it is fixed now.
There is another project that uses maven to the fix this issue. The consumer module(packt.addressbook in my example)'s POM depends on the provider module(packt.sort.bubblesort and packt.sort.javasort in my example):
<dependency>
<groupId>javax0</groupId>
<artifactId>Provider</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
I am not sure if this is the best practice, though.
Follow up
If someone knows how to manually add modules for compilation or automatically discover provider modules 'providing` a service module for compilation inside a IDE like IntelliJ IDEA, please let me know.

Using ServiceLoader to implement a JRE service, but without modules

We have a very large project under Java 8. I temporarily need to use a feature1 introduced in Java 9 in order to debug a problem, so my plan was to build and run with mostly-current tools (probably 11.0.1), add minimal code to call the feature, and then simply not merge any of the debugging changes to the trunk.
The challenge is that I don't want to have to slog through constructing a module-info.java merely for this. (The existing non-module code actually builds and runs with no problems under Java SE 11, it's just that the production user environment is constrained to Java 8. Thus, we've made almost no effort to try and modularize this project.)
Why modules might matter: the feature I need to use is System.LoggerFinder, which is found via ServiceLoader. The hip new way of using ServiceLoader is via module declarations, and that's what the current service loader documentation spends most time describing. The old way of putting text files under META-INF/services/ still works, and we're already successfully using such services and provider classes, but the current setup isn't working with LoggerFinder.
The temporary class we're trying to load
package com.example.for.stackoverflow;
public class LoggerFinder extends System.LoggerFinder
{
public LoggerFinder()
{
System.err.println("behold, a LoggerFinder");
}
#Override
public System.Logger getLogger (String name, Module module)
{
org.slf4j.Logger real = ...existing function to fetch logging facade...
return new SystemLoggerWrapper (name, real);
}
}
class SystemLoggerWrapper implements System.Logger { ... }
This kind of wrapper implementation is pretty straightforward, and has been published elsewhere.2 However, the published examples all use
module-info.java to get ServiceLoader to DTRT.
This project's build system is done with Ant, so we've added lines under the <jar> task:
<service type="java.lang.System.LoggerFinder">
<provider classname="com.example.for.stackoverflow.LoggerFinder"/>
</service>
This works, in that the final JAR contains a META-INF/services/java.lang.System.LoggerFinder text file containing the proper classname.
However, at runtime, the provided LoggerFinder isn't used. Looking through the output of -verbose:class it's clear that the defaults are all still in play. There are no exceptions thrown from ServiceLoader itself. The runtime classpath is already picking up the JAR where the LoggerFinder implementation lives (it's not the only service, and the other ServiceLoader-esque providers are being found).
A very few existing questions on SO touch on the topic,
here
and
here,
but they also take the modular route.
Is there some way of getting visibility into what ServiceLoader is doing?
Is this just an exercise in futility without a modules-info.java?
1 I'm trying to activate debug messages from the sun.util.logging.PlatformLogger system, which (at least as of Java 9) will work through System.Logger, and become activated if -- according to its own documentation -- a System.LoggerFinder can be found by ServiceLoader. All of this juggling is aimed towards that PlatformLogger goal.
2 for example, https://www.baeldung.com/java-9-logging-api under section three
The way ServiceLoader loads classes on the classpath hasn't changed1. It still locates providers by searching for appropriate provider-configuration files under META-INF/services.
The documentation of LoggerFinder says it searches for providers visible to the system class loader. As you mention in a comment the provider is included via -cp this should not be an issue.
The provider-configuration file's name must be the fully qualified binary name of the SPI. From the ServiceLoader documentation:
Deploying service providers on the class path
A service provider that is packaged as a JAR file for the class path is identified by placing a provider-configuration file in the resource directory META-INF/services. The name of the provider-configuration file is the fully qualified binary name of the service. The provider-configuration file contains a list of fully qualified binary names of service providers, one per line.
The binary name of LoggerFinder, as returned by Class.getName, is java.lang.System$LoggerFinder. Based on that, the provider-configuration file's name should be:
META-INF/services/java.lang.System$LoggerFinder
I'm not at all familiar with Ant, but I'd guess you should change the value of type to use the fully qualified binary name.
<service type="java.lang.System$LoggerFinder">
<provider classname="com.example.for.stackoverflow.LoggerFinder"/>
</service>
Note: When testing this on my own, I initially couldn't get the system to use my LoggerFinder implementation either. When I changed the provider-configuration file name to java.lang.System$LoggerFinder (once finally reading the docs) then it worked as expected. Unfortunately, I don't have Ant available to test a solution with the <jar> task.
1. Unless you use ServiceLoader.load(ModuleLayer,Class) as it will ignore the unnamed modules (i.e. classpath).

Categories