gradlex-org/extra-java-module-info

Sub-folders in 'META-INF/services' should be ignored

Closed this issue ยท 3 comments

We are using the dependency org.apache.qpid:qpid-jms-client in our project, and are generating a module-info for it with the help of your plugin, by defining:

extraJavaModuleInfo {
    failOnMissingModuleInfo.set(false)
    module("org.apache.qpid:qpid-jms-client", "qpid.jms.client")
    [...]
}

We are running into some issues with the provides sections in the module-info that are generated for the service providers in that library.

Expand for the generated module-info.class
open module qpid.jms.client@1.8.0 {
  requires java.base;
  provides  org.;
  provides  org.apache.;
  provides  org.apache.qpid.;
  provides  org.apache.qpid.jms.;
  provides  org.apache.qpid.jms.provider.;
  provides  org.apache.qpid.jms.provider.redirects.;
  provides  org.apache.qpid.jms.sasl.;
  provides  org.apache.qpid.jms.tracing.;
  provides  org.apache.qpid.jms.transports.;
  provides  org.apache.qpid.jms.provider.amqp with
    class=org.apache.qpid.jms.provider.amqp.AmqpProviderFactory,
    transportScheme=tcp,
    providerScheme=amqp;
  provides  org.apache.qpid.jms.provider.amqps with
    class=org.apache.qpid.jms.provider.amqp.AmqpProviderFactory,
    transportScheme=ssl,
    providerScheme=amqps;
  provides  org.apache.qpid.jms.provider.amqpws with
    class=org.apache.qpid.jms.provider.amqp.AmqpProviderFactory,
    transportScheme=ws,
    providerScheme=amqpws;
  provides  org.apache.qpid.jms.provider.amqpwss with
    class=org.apache.qpid.jms.provider.amqp.AmqpProviderFactory,
    transportScheme=wss,
    providerScheme=amqpwss;
  provides  org.apache.qpid.jms.provider.failover with
    class=org.apache.qpid.jms.provider.failover.FailoverProviderFactory;
  provides  org.apache.qpid.jms.provider.redirects.ws with
    class=org.apache.qpid.jms.provider.amqp.AmqpProviderFactory,
    transportScheme=ws,
    providerScheme=amqpws;
  provides  org.apache.qpid.jms.provider.redirects.wss with
    class=org.apache.qpid.jms.provider.amqp.AmqpProviderFactory,
    transportScheme=wss,
    providerScheme=amqpwss;
  provides  org.apache.qpid.jms.sasl.ANONYMOUS with
    class=org.apache.qpid.jms.sasl.AnonymousMechanismFactory;
  provides  org.apache.qpid.jms.sasl.CRAM-MD5 with
    class=org.apache.qpid.jms.sasl.CramMD5MechanismFactory;
  provides  org.apache.qpid.jms.sasl.EXTERNAL with
    class=org.apache.qpid.jms.sasl.ExternalMechanismFactory;
  provides  org.apache.qpid.jms.sasl.GSSAPI with
    class=org.apache.qpid.jms.sasl.GssapiMechanismFactory;
  provides  org.apache.qpid.jms.sasl.PLAIN with
    class=org.apache.qpid.jms.sasl.PlainMechanismFactory;
  provides  org.apache.qpid.jms.sasl.SCRAM-SHA-1 with
    class=org.apache.qpid.jms.sasl.ScramSHA1MechanismFactory;
  provides  org.apache.qpid.jms.sasl.SCRAM-SHA-256 with
    class=org.apache.qpid.jms.sasl.ScramSHA256MechanismFactory;
  provides  org.apache.qpid.jms.sasl.SCRAM-SHA-512 with
    class=org.apache.qpid.jms.sasl.ScramSHA512MechanismFactory;
  provides  org.apache.qpid.jms.sasl.XOAUTH2 with
    class=org.apache.qpid.jms.sasl.XOauth2MechanismFactory;
  provides  org.apache.qpid.jms.tracing.noop with
    class=org.apache.qpid.jms.tracing.JmsNoOpTracerFactory;
  provides  org.apache.qpid.jms.tracing.opentracing with
    class=org.apache.qpid.jms.tracing.opentracing.OpenTracingTracerFactory;
  provides  org.apache.qpid.jms.transports.ssl with
    class=org.apache.qpid.jms.transports.netty.NettySslTransportFactory;
  provides  org.apache.qpid.jms.transports.tcp with
    class=org.apache.qpid.jms.transports.netty.NettyTcpTransportFactory;
  provides  org.apache.qpid.jms.transports.ws with
    class=org.apache.qpid.jms.transports.netty.NettyWsTransportFactory;
  provides  org.apache.qpid.jms.transports.wss with
    class=org.apache.qpid.jms.transports.netty.NettyWssTransportFactory;
}

There seem to be 2 kinds of issues:
Empty provides (e.g. provides org.apache.;, provides org.apache.qpid.;) which are generated for all directories inside the META-INF/services/ directory.
These produce the following error when running the application:

> Task :sources:application:run FAILED
Error occurred during initialization of boot layer
java.lang.module.FindException: Error reading module: /Users/xxxxxx/.gradle/caches/transforms-3/c59ca46c3ed823128e0b390a8b01c364/transformed/qpid-jms-client-1.8.0-module.jar
Caused by: java.lang.module.InvalidModuleDescriptorException: Empty providers set

These can be ignored with ignoreServiceProvider statement, e.g.:

Expand for ignoreServiceProvider statements for empty providers
        ignoreServiceProvider("org/")
        ignoreServiceProvider("org/apache/")
        ignoreServiceProvider("org/apache/qpid/")
        ignoreServiceProvider("org/apache/qpid/jms/")
        ignoreServiceProvider("org/apache/qpid/jms/provider/")
        ignoreServiceProvider("org/apache/qpid/jms/provider/redirects/")
        ignoreServiceProvider("org/apache/qpid/jms/sasl/")
        ignoreServiceProvider("org/apache/qpid/jms/tracing/")
        ignoreServiceProvider("org/apache/qpid/jms/transports/")

Then, the following error occurs with the remaining providers:

> Task :sources:application:run FAILED
Error occurred during initialization of boot layer
java.lang.module.FindException: Error reading module: /Users/xxxxx/.gradle/caches/transforms-3/38a74fc7ce0078574e63820cb84f1f08/transformed/qpid-jms-client-1.8.0-module.jar
Caused by: java.lang.module.InvalidModuleDescriptorException: transportScheme=tcp: unnamed package

I'm no expert on service providers, so I'm not sure whether the library is not using them correctly, or whether the gradle plugin doesn't deal with them properly.

Any advice is appreciated ๐Ÿ™‚ Thanks in advance already!

Yes, it's a bug in this plugin. It shouldn't scan the subfolders located in the META-INF\services folder.

According to the spec, only the files contained in the META-INF\services folder should be treated as services.

A service provider is identified by placing a provider-configuration file in the resource directory META-INF/services. The file's name is the fully-qualified binary name of the service's type. The file contains a list of fully-qualified binary names of concrete provider classes, one per line. Space and tab characters surrounding each name, as well as blank lines, are ignored. The comment character is '#' ('\u0023', NUMBER SIGN); on each line all characters following the first comment character are ignored. The file must be encoded in UTF-8.

As a workaround, try specifying the following ignore list in the plugin configuration.

module("org.apache.qpid:qpid-jms-client", "qpid.jms.client") {
    ...
    ignoreServiceProvider("org/")
    ignoreServiceProvider("org/apache/")
    ignoreServiceProvider("org/apache/qpid/")
    ignoreServiceProvider("org/apache/qpid/jms/")
    ignoreServiceProvider("org/apache/qpid/jms/provider/")
    ignoreServiceProvider("org/apache/qpid/jms/provider/redirects/")
    ignoreServiceProvider("org/apache/qpid/jms/sasl/")
    ignoreServiceProvider("org/apache/qpid/jms/tracing/")
    ignoreServiceProvider("org/apache/qpid/jms/transports/")
    ignoreServiceProvider("org/apache/qpid/jms/provider/amqp")
    ignoreServiceProvider("org/apache/qpid/jms/provider/amqps")
    ignoreServiceProvider("org/apache/qpid/jms/provider/amqpws")
    ignoreServiceProvider("org/apache/qpid/jms/provider/amqpwss")
    ignoreServiceProvider("org/apache/qpid/jms/provider/failover")
    ignoreServiceProvider("org/apache/qpid/jms/provider/redirects/ws")
    ignoreServiceProvider("org/apache/qpid/jms/provider/redirects/wss")
    ignoreServiceProvider("org/apache/qpid/jms/sasl/ANONYMOUS")
    ignoreServiceProvider("org/apache/qpid/jms/sasl/CRAM-MD5")
    ignoreServiceProvider("org/apache/qpid/jms/sasl/EXTERNAL")
    ignoreServiceProvider("org/apache/qpid/jms/sasl/GSSAPI")
    ignoreServiceProvider("org/apache/qpid/jms/sasl/PLAIN")
    ignoreServiceProvider("org/apache/qpid/jms/sasl/SCRAM-SHA-1")
    ignoreServiceProvider("org/apache/qpid/jms/sasl/SCRAM-SHA-256")
    ignoreServiceProvider("org/apache/qpid/jms/sasl/SCRAM-SHA-512")
    ignoreServiceProvider("org/apache/qpid/jms/sasl/XOAUTH2")
    ignoreServiceProvider("org/apache/qpid/jms/tracing/noop")
    ignoreServiceProvider("org/apache/qpid/jms/tracing/opentracing")
    ignoreServiceProvider("org/apache/qpid/jms/transports/ssl")
    ignoreServiceProvider("org/apache/qpid/jms/transports/tcp")
    ignoreServiceProvider("org/apache/qpid/jms/transports/ws")
    ignoreServiceProvider("org/apache/qpid/jms/transports/wss")
}

However, Apache Qpid must be doing some classpath scanning for its service discovery and chances are it will not work when the library is placed on the module path.

Thank you for reporting @j-beyer and thanks for analyzing @iherasymenko! Lets see that we get this fixed in the next release.

Thanks, that's great news! Also thanks for the details, will apply the workaround and experiment with it a bit :)