[OMRS 2.4.2] Liquibase migrations are not running due to excluded dependency of "logback-classic" from liquibase-core in openmrs-core

As part of the upgrade from openmrs 2.1.7 to 2.4.2 in Bahmni we see that liquibase-core dependency version got upgraded from 2.0.5 to 3.10.2 and logback-classic got excluded from the dependency as shown here

<dependency>
			<groupId>org.liquibase</groupId>
			<artifactId>liquibase-core</artifactId>
			<version>3.10.2</version>
			<exclusions>
				<exclusion>
					<groupId>ch.qos.logback</groupId>
					<artifactId>logback-classic</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

On running liquibase migrations we are getting the below error and module is not starting up.

Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: ch/qos/logback/core/Context
	at java.lang.Class.getDeclaredMethods0(Native Method)
	at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
	at java.lang.Class.privateGetMethodRecursive(Unknown Source)
	at java.lang.Class.getMethod0(Unknown Source)
	at java.lang.Class.getMethod(Unknown Source)
	at sun.launcher.LauncherHelper.validateMainClass(Unknown Source)
	at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)
Caused by: java.lang.ClassNotFoundException: ch.qos.logback.core.Context
	at java.net.URLClassLoader.findClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	... 7 more

We would like to know why the logback-classic dependecny got exlcuded. Any pointers on fixing the issue ? @dkayiwa @ibacher @angshuonline @n0man @gsluthra @atish1603

It’s not clear from the information you provided what’s causing the issue, though I can certainly explain the exclusion.

OpenMRS uses Log4J or (as of 2.4.0) Log4J2 for logging. However, since 1.8.3, we’ve also bundled SLF4J to provide the logging interface that we hoped most people would use, as SLF4J is an API independent of the underlying logging mechanism. SLF4J binds to a single logging implementation. In SLF4J 1.x, this is done by simply asking the classpath for the instance of org/slf4j/impl/StaticLoggerBinder.class, which basically resolves to the first instance of that class in the classloader, meaning that whatever the first library that the classloader searches that contains that class will be the version bound to the SLF4J interface. The upshot of this is that if we include logback on the OpenMRS classpath in core, SLF4J winds up being bound to the logback implementation rather than the log4j implementation and these means that logging messages sent using SLF4J would not hit OpenMRS’s configured logger.

As a more general principle here: libraries (like liquibase) shouldn’t be dependent on a specific version of a logging mechanism, and with liquibase 4, this is the case.

Now for what we can do about it, it would be helpful to have a more complete stack trace to see where ch.qos.logback.core.Context is being imported so we could see if there’s a plausible workaround. The most crude possibility would be to make logback-classic a dependency of the module which is triggering the liquibase changeset that is causing that error, as this would ensure that SLF4J was still bound to the Log4J2 implementation and ch.qos.logback.core.Context would be available on the classpath…

3 Likes

Thanks @ibacher for the reply. When we run the command mentioned in this file we see the above stack trace coming up(replaced liquibase-core-2.0.5.jar with liquibase-core-3.10.2.jar in the file before executing).

That’s helpful context… It looks like for Liquibase 3.10.2 there really is a hard dependency on Logback in the command line run of liquibase (see here). I don’t really see a way around setting up Ansible to download the JAR file and manually adding it to the classpath.

You can probably get away with this version of logback-classic, this version of logback-core, and SLF4J from the OpenMRS classpath.

2 Likes

Hi @ibacher ,

I’ve resolved this issue by changing the command to run the liquibase-core jar file. The classpath is being specified with the -cp flag and the liquibase integration main file has been specified on the command itself. In short replaced the -jar flag with -cp flag and followed the structure to execute the same.

To resolve the issue, logback-core-1.2.3.jar and slf4j-api-1.7.28.jar have been added to the classpath as per your suggestion, and it resolved the issue

The changes can be accessed here BAH-1932 [Docker] Liquibase migrations are not running due to excluded dependency of "logback-classic" from liquibase-core in openmrs-core by ayush0260 · Pull Request #53 · Bahmni/openmrs-distro-bahmni · GitHub

2 Likes