Hi Steinar, What I was trying to tell you, is that by replacing your spifly-feature with the aries-proxy-feature and a single spifly bundle, the classloading issue might be fixed. Liquibase is able to find the Default Log Service, as far as I can see.
I got 2 new exceptions, one of which can be ignored.If you manage to fix the other exception, I think you get a little closer to the solution. Kind regards, Steven On Tue, Aug 9, 2022 at 12:51 PM Steinar Bang <s...@dod.no> wrote: > >>>>> 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... > >