lightbend / config

configuration library for JVM languages using HOCON files
https://lightbend.github.io/config/
6.12k stars 968 forks source link

Add a module info #803

Open bowbahdoe opened 4 months ago

bowbahdoe commented 4 months ago

Adding a module info would let projects using this use jlink to package their app.

(every dep needs a module-info for that to work)

agentgt commented 4 months ago

Ethan I noticed this myself when I was planning on adding it as a config option to: https://github.com/jstachio/rainbowgum

They probably cannot do this without a major version change and will likely break everyone I think because they will no longer be able to do the default loading of resources from the jar (actually it probably doesn't even work now if your application is modular).

This is because the module will block accessing of resources named resource.conf and friends!

That is ConfigFactory.load() is fundamentally broken in a modular environment. That being said ConfigFactory.load() is very inefficient in terms of initialization so I'm not sure fixing it but this how I would do it.

They should use the META-INF directory to access the default resource.conf perhaps something like jar/META-INF/config/resource.conf. The reason that works is not really because there is anything special about META-INF but rather resources with - at the root are always open and not encapsulated.

So they would need to try META-INF and then try the original resource path for backward compatibility.

A better solution might be to use the ServiceLoader and have an SPI that can load the default configuration resources as the module that has the SPI implementation will have access.

bowbahdoe commented 4 months ago

I thought module resources were only encapsulated if they had a path prefix that matched a package with classes in it?

Do you have a good resource on the resource rules @agentgt ?

agentgt commented 4 months ago

Do you have a good resource on the resource rules @agentgt ?

No I don't sadly. But you can read this answer on SO (see who is answering the question for officialness).

https://stackoverflow.com/questions/45166757/loading-classes-and-resources-post-java-9

A final important difference is that non-class-file resources in a module are encapsulated by default, and hence cannot be located from outside the module unless their effective package is open. To load resources from your own module it’s best to use the resource-lookup methods in Class or Module, which can locate any resource in your module, rather than those in ClassLoader, which can only locate non-class-file resources in the open packages of a module.

So obviously reference.conf is not owned by lightbends module since you would put that in your own jar.

The hyphen rule - at the root I can't recall how I figured out that but its mainly because it can't possibly be a Java class name or package name. There are probably other characters as well besides - that I don't know about.

Now if resource.conf was in a package it would work but you would have to open that package.

I thought module resources were only encapsulated if they had a path prefix that matched a package with classes in it?

I'm not really sure I follow that or how that would work. How would the module loader know about that?

But I suppose what you are asking is if there is a resource at the root not in a directory it should always be open and for that I have found through experimentation not to be the case at all unless your module is not a module or something. That is I had similar experience as this comment: https://stackoverflow.com/questions/45166757/loading-classes-and-resources-post-java-9#comment133075438_67852944

agentgt commented 4 months ago

https://docs.oracle.com/javase/9/docs/api/java/lang/Module.html#getResourceAsStream-java.lang.String-

A package name is derived from the resource name. If the package name is a package in the module then the resource can only be located by the caller of this method when the package is open to at least the caller's module. If the resource is not in a package in the module then the resource is not encapsulated.

I guess according to the doc reference.conf at root should work but I don't know why I have found that incredibly unreliable especially if you mix modules and non modules together.

bowbahdoe commented 4 months ago

Yeah i remember learning that from this talk https://www.youtube.com/watch?v=4fevIDAxQAM

agentgt commented 4 months ago

I guess I should just setup a project to see how it comes up but there are lots of caveats switching from Automatic-Module-Name to module-info. It changes the visibility graph. Automatic modules can basically see everything.

What I think has happened for me with root resources (e.g. resources.conf) is either Java confuses it with a possible package or there is some custom class loader at play. I think it is most likely the latter as I think I saw the issue in Spring Boot where it has a special boot class loader. I think folks do not run into this frequently is because they are not using modules and or weird classloader combinations as well as root resources like log4j.properties are in the applications module and those are often straight up open.

Either way you go from being able to always see it as an automatic module to possibly not seeing it when you switch to module-info.java.

In theory the reference.conf or any file at the root with a dot should work as it cannot possibly be a package name but for whatever reasons that has not worked reliably for me. This is the part that may (and by doc should work) but they need to test extensively and if they have problems they should fallback to the META-INF solution I proposed earlier.

What I know for sure does not work despite various conflicting doc is that you cannot have a directory in your jar that has a package like name (e.g no -) and expect to read from it and ditto for a resource with a possible package name regardless of what actual packages (directory with classes) are in the jar unless of course they are open.