Reputation: 17224
Libraries using Typesafe Config usually depend on one big configuration merged from /reference.conf
files on the classpath.
For example, Spray expects to find its config sections in the ActorSystem
's Typesafe Config instance, but they aren't available unless the bundle where I create the ActorSystem
imports Spray packages. In my application there isn't such an import, because I've got a dedicated bundle whose sole purpose is publishing ActorSystem
as a service. Other bundles use that, and some of them depend on Spray but not the bundle that just exports the AS.
This led me to the general problem of Typesafe Config finding /reference.conf
files in an OSGi environment. I know akka-osgi
's BundleDelegatingClassLoader
looks up resources down a chain of bundle dependencies so I thought why not just look through all bundles in the system to be in line with Typesafe Config's merging idiology?
What is the correct way to work with Typesafe Config under OSGi? I'll present my generic solution in an answer but I'm not an OSGi expert and would like to hear if that's wrong and why and what would be a better way to handle the merge.
Upvotes: 0
Views: 376
Reputation: 17224
This makes Typesafe Config's include
to look for the given resource in all installed bundles:
// create Typesafe Config
val myConfig = ConfigFactory.parseFile(
new File("myconfig.conf"),
ConfigParseOptions.defaults().setClassLoader(new ClassLoader() {
override def getResources(name: String): util.Enumeration[URL] = {
val resources = context.getBundles.flatMap { bundle =>
val found = JavaConversions.enumerationAsScalaIterator(
Option(bundle.getResources(name)).getOrElse(Collections.emptyEnumeration())
)
found
}
JavaConversions.asJavaEnumeration(resources.toIterator)
}
})
).resolve()
// create ActorSystem
/* This could've been myconfig.getConfig("myconfig").withOnlyPath("akka") but
* like I said, Spray expects to find "spray.*" section in the ActorSystem's config.
*/
val factory = OsgiActorSystemFactory(context, myconfig.getConfig("myconfig"))
val as = factory.createActorSystem("blah")
In the config itself I'm using:
myconfig {
// this will include reference.conf from every installed bundle in the container
include classpath("reference.conf")
// overrides
akka.loglevel = INFO
spray.version = "1.3.2"
// etc ...
other.stuff.for.my.app = 1
}
Having -Dconfig.trace=loads
shows:
karaf@root()> feature:install myfeature
Loading config from a file: C:\Users\YUUshakov\p\myconfig.conf
Loading config from URL bundleresource://925.fwk875016237/reference.conf from class loader mytest.ConfigActivator$$anon$1@1131e4e4
Loading config from URL bundleresource://931.fwk875016237/reference.conf from class loader mytest.ConfigActivator$$anon$1@1131e4e4
Loading config from URL bundleresource://933.fwk875016237/reference.conf from class loader mytest.ConfigActivator$$anon$1@1131e4e4
Loading config from URL bundleresource://934.fwk875016237/reference.conf from class loader mytest.ConfigActivator$$anon$1@1131e4e4
Loading config from URL bundleresource://946.fwk875016237/reference.conf from class loader mytest.ConfigActivator$$anon$1@1131e4e4
And resulting config instance got all the reference sections like myconfig.akka
, myconfig.spray
, etc.
Upvotes: 1