<div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr">I help maintain a few service-loader-based modular annotation processor libraries with the avaje framework, so I have also run into this and had to work around it.<div><br><input name="virtru-metadata" type="hidden" value="{"email-policy":{"disableCopyPaste":false,"disablePrint":false,"disableForwarding":false,"enableNoauth":false,"expandedWatermarking":false,"expires":false,"sms":false,"expirationNum":1,"expirationUnit":"days","isManaged":false,"persistentProtection":false},"attachments":{},"compose-id":"1","compose-window":{"secure":false}}"><div><b>Ad 1:</b></div><div>So this is indeed an inconvenience but in my experience, it's not a crazy one. When processing is over we check the module-info's directives to throw compilation warnings like:</div><div><div><i>[ERROR] /M:/Dev/avaje-helidon-nima-api-example/src/main/java/module-info.java:[1,1] Missing `provides io.ava</i></div><div><i>je.http.client.HttpClient.GeneratedComponent with com.jojo.helidon.api.client.httpclient.GeneratedHttpCompon</i></div><div><i>ent;`</i></div><div><i>[ERROR] /M:/Dev/avaje-helidon-nima-api-example/src/main/java/module-info.java:[1,1] Missing `provides io.ava</i></div><div><i>je.jsonb.Jsonb.GeneratedComponent with com.jojo.helidon.api.jsonb.GeneratedJsonComponent;`</i></div><div><i>[ERROR] /M:/Dev/avaje-helidon-nima-api-example/src/main/java/module-info.java:[1,1] Missing `provides io.ava</i></div><div><i>je.validation.Validator.GeneratedComponent with com.jojo.helidon.api.controller.valid.GeneratedValidatorComp</i></div><div><i>onent;`</i></div><div><i>[ERROR] /M:/Dev/avaje-helidon-nima-api-example/src/main/java/module-info.java:[1,1] Missing "provides io.ava</i></div><div><i>je.inject.spi.Module with com.jojo.helidon.api.ApiModule;"</i></div></div><div><br></div><div>The <b>real problem </b>with checking the module info in a processor is that ModuleElement is bugged such that if you call <b>ModuleElement.getDirectives</b> on the project module <a href="https://bugs.openjdk.org/browse/JDK-8315125">It breaks compilation</a>. (they called this one a duplicate but I still have the problem even in JDK 22-ea and JDK 23-ea) </div><div><br></div><div>To get around this I have to use Filer to retrieve the module-info's sources file and parse it as a string to avoid calling <b>getDirectives. </b> I've built libraries to do this to make this easier, but the problem remains that if any other processor uses <b>getDirectives</b>, compilation will still break.</div><div><br></div><div><b style="color:rgb(0,0,0);font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:16px">Ad 2:</b><br></div><div>I asked this question here a while back, and what I got out of it is that we needed to do some form of circular dependency if we truly wanted optional services, the tech for compiling multi-module jars isn't here yet. It's a pain to deploy when we change the plugins, but we rarely do so this has worked fine for us. Example: <a href="https://github.com/avaje/avaje-validator">avaje-validator</a> provides a plugin for avaje inject that itself depends on avaje-validator.</div><div><br></div><div><b>Ad 3: </b></div><div>Yeah I got nothing, we never had to do this.</div><div><br></div><div><b>Ad 4: </b></div><div>This one was the simplest, we can define an annotation to go on the user's service class and process them to generate META-INF files and validate the module-info. In this way, one cannot forget to add the proper module-info information. In some of the libs we do this with their processors, but we also have a dedicated library for handling this sort of thing. </div><div><br></div></div></div></div></div></div></div></div></div></div><br><div class="gmail_quote" style=""><div dir="ltr" class="gmail_attr">On Fri, Jan 19, 2024 at 8:04 AM Tomas Langer <<a href="mailto:tomas.langer@oracle.com">tomas.langer@oracle.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div class="msg4876862232196186797">
<div dir="ltr">
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Helidon currently has around 300 modules with module-info.java. In general, this has
improved our module structure and design.</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Yet, we are now encountering some major issues related to extensibility.</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"> I will put down a few points that are problematic, and explain each in detail further
in the e-mail (it is quite long, sorry about that).</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">1. provider implementations cannot be code generated without major problems</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">2. the provider interface module MUST be on module path, even if it could have `requires
static`</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">3. the provider implementation must be public with public constructor</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">4. duality of definition between module path and class path</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">I am trying to propose a solution within the bounds of the current service loader design.
Of course there may be other solutions (both with current design, or even creating a brand new extensibility solution in Java, this is just to illustrate it).</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">The first two issues are quite major, as they force us to recommend not to use JPMS
to our users...</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">We could design our own extensibility approach, though that would require the use of
reflection to instantiate the services, and we would have to live with the limits of the module system (where Java ServiceLoader works around a few of them). </span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">I feel the right way is to use what Java provides, so I would welcome any help (and
possibly changes in the language) to support our use cases.</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Thanks,</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Tomas Langer</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Architect, project Helidon</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><b>Ad 1 - Code generation</b></span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">-------------------------------------</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Problem: We cannot code generate service implementations (well we can, but the user must handcraft them in
module-info.java, so we end up running the APT, generating a service, failing the compiler to tell the user to add the service, compiling again every time a new service is added).</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Possible solution: Provide extensibility to module-info.java that can be code generated</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Without JPMS: it just works, as `META-INF/services` files can be code generated without issues</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Details:</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">What I do not see is how we are supposed to do extensibility through annotation processing.</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">There are a lot of usecases for this, such as:</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">- generating code for serializers/deserializers for objects that persist to JSON, XML, YAML</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">- generating code for database entities</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">- generating descriptor for services in a service registry</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">We actually want to implement these three use cases, and it is a major pain for the user - I would not mind
much if this hurt us, as framework developers, but we must force the user to take action by breaking the compilation, or come up with some really weird solution (such as source code modification using some preprocessor before compilation, or postprocessor
running on bytecode to re-generate module-info.class)</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><b>Ad 2 - Provider module cannot be optional dependency</b></span></div>
<div><span style="letter-spacing:normal;font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:16px;color:rgb(0,0,0);background-color:rgb(255,255,255);font-weight:400">-------------------------------------</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Problem: We cannot declare `requires static` on a module that has the ServiceLoader provider interface (or
abstract class)</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Possible solution: Change the rules for JPMS to allow this</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Without JPMS: it just works, as `META-INF/services` to not impose any classpath structure</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Details:</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">If a module (my.json) defines a provider interface (let's say `JsonSerializer`), and I create a module with
`MyJsonSerializer` (provides JsonSerializer witih MyJsonSerializer), currently I MUST do the following "requires my.json". If I do a "requires static my.json" I fail to start the JVM if that module is not on module path.</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">The service CANNOT be used unless the module is on module path (as anybody attempting to load it must declare
`uses` in their module info with a proper `requires` on the `my.json` module.</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">So what are my options right now?</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">- have a "requires my.json" and just dump the module on all my users (not so good - people may want to use
my library without JSON altogether, I may also provide support for XML, YAML - all of these would need to be on module path)</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">- create a module for each (one for JSON, XML, YAML + my library) - resulting in 4 modules (and this may grow
if I decide to support other format); this looks kind of OK on the first look, but with the number of modules we have, and the number of features we support, this gets out of hand really really quickly; this approach is also very user unfriendly, as now the
user needs to understand 4 modules instead of just 1, and use the right ones at the right time).</span></div>
<div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"></span>- considering
the number of modules we already have, this would make our project unmaintainable (and unusable for users)</span></div>
</div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">As JPMS already allows "static" dependencies, there should be no reason not to allow it in this case as well.
We can break the module system even now - just use a class from static dependency in a public class - this will fail at runtime only. The service loader is not different (this is a reaction to text in <a href="https://bugs.openjdk.org/browse/JDK-8299504" target="_blank">https://bugs.openjdk.org/browse/JDK-8299504</a>).</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><b>Ad 3 - Provider implementation must be public with public constructor</b></span></div>
<div><span style="letter-spacing:normal;font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:16px;color:rgb(0,0,0);background-color:rgb(255,255,255);font-weight:400">-------------------------------------</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Problem: This creates a new public API that we may not want to expose, or document</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Possible solution: Change the rules to allow package local service provider implementations with package local
constructors</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Without JPMS: Same issue, even more problematic as there is no restrictions on package visibility</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Details:</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Provider implementations are not supposed to be visible to users - they are not public API of my module (the
fact that I provide a service is part of my public API).</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Right now there is only one option to work around this, and it only works in JPMS, and in my opinion it brings
in even more problems - put the provider implementation in an un-exported package.</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">The problem with this approach is that now the provider implementation MUST use only public methods of my module,
thus creating even more public APIs, where if I just put it in my exported package, I can use package private methods of my other classes to implement the service (so I pay the price of having one public class with one public constructor agains multiple public
classes and public methods). Also the "hiding" in unexported package is lost when on classpath anyway...</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><b>Ad 4 - Duality of definition between classpath and module path</b></span></div>
<div><span style="letter-spacing:normal;font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:16px;color:rgb(0,0,0);background-color:rgb(255,255,255);font-weight:400">-------------------------------------</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Problem: To support services, we MUST declare them twice - once in `provides` in module-info.java, once through
META-INF/services</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Possible solution: Java could read module descriptors even when running in classpath mode to add service implementations
and merge it with META-INF/services information</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Without JPMS: it just works, as `META-INF/services` is always honored on classpath and for non-JPMS modules
on module path</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)"><br>
</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">Details:</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">This is again quite a pain for us as framework develoepers, and a pitfall for users. When we started with JPMS,
we had both created manually, which obviously ended in a huge inconsistent mess.</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">So now we have a custom Maven plugin, that creates META-INF/services files based on the content in module-info.java
and fails on inconsistencies.</span></div>
<div><span style="font-family:Aptos,Aptos_EmbeddedFont,Aptos_MSFontService,Calibri,Helvetica,sans-serif;font-size:12pt;color:rgb(0,0,0)">I do not consider this a nice solution for us, and definitely not for end users. Also
there is no way to find out that you forgot to add one (or the other), as JVM just does not care. So basically you end up with a runtime issue that is really hard to troubleshoot.</span></div>
</div>
</div></blockquote></div></div>