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...
>
>

Reply via email to