>>>>> Steven Huypens <steven.huyp...@gmail.com>: > Hi Steinar, > As far as I can tell, there is a spifly-issue, which is why Liquibase > cannot create the correct Log Services. I tried
> <feature name="liquibase-core" start-level="20" > version="${liquibase.version}"> > <feature>aries-proxy</feature> > <bundle>mvn:org.apache.aries.spifly/org.apache.aries.spifly.dynamic.bundle/1.3.4</bundle> > <bundle>mvn:org.yaml/snakeyaml/${snakeyaml.version}</bundle> > <bundle>mvn:org.liquibase/liquibase-core/${liquibase.version}</bundle> > </feature> Yes, I tried to handle this by requiring the <feature>spifly</feature> This is what I've done in the liquibase-karaf-feature/use-liquibase-slf4j-410 branch. > which leads to new exceptions. The one from Liquibase Pro can be ignored > for now, I guess, and the other one might be related to your > ResourceAccessor, Good luck, I hope this helps you a little. Thanks! The fact that I know that running liquibase 4.x in OSGi works for you, is my hope that it should be possible to solve this. :-) I think I know what's going on now, but what I don't know is how to fix it. When debugging the init, I saw that a JavaLogService (which implements the LogService interface), was created here (I put a breakpoint in the baseclass constructor of JavaLogService, and the breakpoint was triggered): https://github.com/liquibase/liquibase/blob/master/liquibase-core/src/main/java/liquibase/Scope.java#L78 And then LogServiceFactory.getDefaultLogService() failed here https://github.com/liquibase/liquibase/blob/master/liquibase-core/src/main/java/liquibase/Scope.java#L85 (at this point I was already thinking "possible classloader issue...?") The LogServiceFactory.getDefaultLogService() method called AbstractServiceFactory<LogService>.getPlugin(), which returns null, because the ServiceLocator doesn't find any instances of LogService https://github.com/liquibase/liquibase/blob/master/liquibase-core/src/main/java/liquibase/plugin/AbstractPluginFactory.java#L98 And StandardServiceLocator.findInstances(Class<LogService>), used java.util.ServiceLoader.load() to find the instances https://github.com/liquibase/liquibase/blob/master/liquibase-core/src/main/java/liquibase/servicelocator/StandardServiceLocator.java#L21 So, yep, defintely a classloader issue. The java.util.ServiceLoader is what powers the spring dependency injection mechanism, and definitely works best when you have a single classloader. So I googled "ServiceLoader osgi" and immediately found this clarifying (but old (february 13 2013)) blog post: https://blog.osgi.org/2013/02/javautilserviceloader-in-osgi.html Liquibase is attempting to use the serviceloader. They are requiring the necessary capabilities (this is why liquibase now needs something like spifly, to load): https://github.com/liquibase/liquibase/blob/master/liquibase-core/pom.xml#L198 And they are providing the capabilities, which should make it possible for a different bundle to have ServiceLoader successfully do a classpath scan and find the services. So to sum it up: the reason LogService isn't found is a classloader issue, there is a known fix, and liquibase has implemented the fix. The only problem is... I haven't been able to make it work...:-/ I have tried the following: 1. Requiring the LogService feature in the maven-bundle-plugin config, based on the example in the feb 2013 blog post: <Require-Capability> osgi.extender; filter:="(osgi.extender=osgi.serviceloader.processor)", osgi.serviceloader;filter:="(osgi.serviceloader=liquibase.logging.LogService)";cardinality:=multiple </Require-Capability> but that failed in the karaf feature loader, because the osgi.serviceloader.processor capability couldn't be found. 2. So I copied the entire <Require-Capability> from liquibase-core https://github.com/liquibase/liquibase/blob/master/liquibase-core/pom.xml#L198 where all of the services are provided by the liquibase-core manifest https://github.com/liquibase/liquibase/blob/master/liquibase-core/pom.xml#L169 (the osgi.serviceloader.registrar and osgi.serviceloader.processor capabilities would presumably be provided by spifly, same as for the osgi-core itself) And this one have no load failures, but the karaf.log contains the dreaded "Cannot find default log service" stack trace I think that everything created inside the liquibase-core bundle itself, using that bundle's classloader(s), would work just fine. But Liquibase classes I instanciate in my own bundles will be unable to find the various services. I've been thinking of various exotic ideas, such as e.g. 1. Define a liquibase factory service interface, and make an OSGi bundle fragment that adds an implementation of this service (can bundle fragments even provide services...?) 2. Somehow expose the services created by liquibase init as OSGi services, and use DS to inject those services into my bundles. No idea how to go about this, except that it would probably involve a bundle fragment somehow But, as I say "exotic ideas". I don't think they will work, and they feel like a hack. I'm just throwing them out there, to clear them out of my head. I'm sure that the solution, if there is one, will turn out to be a lot simpler...