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