# 4.21.1 is out, deprecates Liquibase.update()

**URL:** <https://forum.liquibase.org/t/4-21-1-is-out-deprecates-liquibase-update/8087>\
**Category:** General Discussion\
**Created:** [April 18, 2023, 4:04pm UTC](https://forum.liquibase.org/t/4-21-1-is-out-deprecates-liquibase-update/8087 "2023-04-18T16:04:41Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![eekboom](https://avatars.discourse-cdn.com/v4/letter/e/35a633/32.png) [@eekboom](https://forum.liquibase.org/u/eekboom)\
**Post date:** [April 18, 2023, 4:04pm UTC](https://forum.liquibase.org/t/4-21-1-is-out-deprecates-liquibase-update/8087/1 "2023-04-18T16:04:41Z")

</div>

4.21 seems to be out, but there are no release notes, yet. Could you please add them?

I am using liquibase embedded in a Kotlin application, as described in section " Embed Liquibase into your product" here: [3 Ways to Run Liquibase](https://www.liquibase.com/blog/3-ways-to-run-liquibase)

My gradle update check task alerted me of the new version, and I tried to update because it warns that 4.20 has a security flaw in one the dependencies [CVE-2022-1471].

However, after the update my build fails with a deprecated warning at Liquibase.update().  
At the update() method I use, there is just a `@Deprecated` annotation with no hint at all what to use instead.  
When follow the trail in the liquibase sources to the overloaded method that all the update variants delegate to, I find `@deprecated Use CommandStep`

Not that helpful ☹

Oh, I just noticed that there are new `update()` overloads which are not deprecated, all taking a `Writer` … and no such method has any JavaDoc ☹

What is this `Writer`? Is it used for logging? If so, how would I get a `Writer` from log4j2 or slf4j?

Finally, could you please update the [3 Ways to Run Liquibase](https://www.liquibase.com/blog/3-ways-to-run-liquibase) to use a non-deprecated `update` method?

---

<div class="post-metadata">

**Author:** ![daryldoak](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.liquibase.org/daryldoak/32/764_2.png) [@daryldoak](https://forum.liquibase.org/u/daryldoak)\
**Post date:** [April 18, 2023, 4:50pm UTC](https://forum.liquibase.org/t/4-21-1-is-out-deprecates-liquibase-update/8087/2 "2023-04-18T16:50:28Z")

</div>

Here are the release notes:

> **[Release v4.21.1 · liquibase/liquibase](https://github.com/liquibase/liquibase/releases/tag/v4.21.1)**
>
> Liquibase 4.21.1 is a patch release
> Notable Changes
> \[PRO\] Observability Initiative - Structured Logging
> Structured Logging makes Liquibase operation data easily available for automated monitoring a...

---

<div class="post-metadata">

**Author:** ![nvoxland](https://avatars.discourse-cdn.com/v4/letter/n/87869e/32.png) [@nvoxland](https://forum.liquibase.org/u/nvoxland)\
**Post date:** [April 19, 2023, 2:20pm UTC](https://forum.liquibase.org/t/4-21-1-is-out-deprecates-liquibase-update/8087/3 "2023-04-19T14:20:21Z")

</div>

Yeah, that javadoc could be improved. “Use CommandStep” is not really even the right answer for most people. Also the versions with Writer should either be deprecated now or will be soon. We aren’t planning to actually remove the methods for a while, but still good to shift to the newer APIs when you can

The replacement for the Liquibase class in general is the `liquibase.command.CommandScope` class. [command.CommandScope - contribute.liquibase.com](https://contribute.liquibase.com/code/api/command-commandscope/) gives some more information on it. We are creating that class vs. the original Liquibase facade to be flexible enough to work with everything Liquibase can do now and is looking to do in the future. A static list of methods and arguments just wasn’t cutting it anymore.

Your new version of liquibase.update() will be more like:

```auto
   try {
            new CommandScope("update")
                    .addArgumentValue("changeLogFile", "my/changelog.xml")
                    .execute();
        } catch (CommandExecutionException e) {
            System.out.println("Error running update: "+e.getMessage());
        }

```

Nathan

---

<div class="post-metadata">

**Author:** ![SteinarBang](https://avatars.discourse-cdn.com/v4/letter/s/919ad9/32.png) [@SteinarBang](https://forum.liquibase.org/u/SteinarBang)\
**Post date:** [May 20, 2023, 10:54am UTC](https://forum.liquibase.org/t/4-21-1-is-out-deprecates-liquibase-update/8087/4 "2023-05-20T10:54:26Z")

</div>

I’m doing this (copy paste from [a sample used in the integration test](https://github.com/steinarb/liquibase-karaf-feature/blob/2fa5c64690209c492e147576f596af58758540d3/liquibase-integration-test/karaf.liquibase.sample/src/main/java/no/priv/bang/karaf/sample/db/liquibase/test/SampleDbLiquibaseRunner.java#L75) for my [Liquibase apache karaf feature](https://github.com/steinarb/liquibase-karaf-feature)):

```auto
            applyLiquibaseChangelist(connection, "sample-db-changelog/db-changelog-1.0.0.xml");

```

where applyLiquibaseChangeList first creates a Liquibase object, with a JDBC connection and a resource accessor and the resource classpath to the changelog file embedded in the OSGi bundle (i.e. the jar file):

```auto
    private void applyLiquibaseChangelist(Connection connection, String changelistClasspathResource) throws LiquibaseException {
        try(Liquibase liquibase = createLiquibaseInstance(connection, changelistClasspathResource)) {
            liquibase.update("");
        }
    }

    private Liquibase createLiquibaseInstance(Connection connection, String changelistClasspathResource) throws LiquibaseException {
        DatabaseConnection databaseConnection = new JdbcConnection(connection);
        var resourceAccessor = new OSGiResourceAccessor(bundle);
        return new Liquibase(changelistClasspathResource, resourceAccessor, databaseConnection);
    }

```

It’s not obvious to me how this should be translated to a command scope.

I’ve looked at [the linked to page](https://contribute.liquibase.com/code/api/command-commandscope/) and I’ve looked at the [CommandScope javadocs](https://javadocs.liquibase.com/liquibase-core/liquibase/command/CommandScope.html) but I didn’t find enlightnment, unfortunately… 🙂

---

<div class="post-metadata">

**Author:** ![SteinarBang](https://avatars.discourse-cdn.com/v4/letter/s/919ad9/32.png) [@SteinarBang](https://forum.liquibase.org/u/SteinarBang)\
**Post date:** [May 20, 2023, 6:39pm UTC](https://forum.liquibase.org/t/4-21-1-is-out-deprecates-liquibase-update/8087/5 "2023-05-20T18:39:36Z")

</div>

First hurdle: how do I specify that a changelog file should be fetched from a classpath resource and not from a file on disk?

(and do I need to do something OSGi specific here?)

Second hurdle: if I have a JDBC DataSource, how can I use that (instead of JDBC connection info)

---

<div class="post-metadata">

**Author:** ![eekboom](https://avatars.discourse-cdn.com/v4/letter/e/35a633/32.png) [@eekboom](https://forum.liquibase.org/u/eekboom)\
**Post date:** [May 20, 2023, 8:06pm UTC](https://forum.liquibase.org/t/4-21-1-is-out-deprecates-liquibase-update/8087/6 "2023-05-20T20:06:59Z")

</div>

Here is what I ended up with, also using an existing javax.sql.DataSource.  
It’s Kotlin code but should be readable enough for a Java dev:

```auto
private fun migrateDb(dataSource: DataSource) {
    logger.info("Running liquibase update")

    dataSource.connection.use { connection ->
        val database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(JdbcConnection(connection))
        val changeLogFile = "/liquibase-changelog.xml"

        val scopeObjects = mapOf(
            Scope.Attr.database.name to database,
            Scope.Attr.resourceAccessor.name to ClassLoaderResourceAccessor()
        )

        Scope.child(scopeObjects) {
            val updateCommand = CommandScope(*UpdateCommandStep.COMMAND_NAME).apply {
                addArgumentValue(DbUrlConnectionCommandStep.DATABASE_ARG, database)
                addArgumentValue(UpdateCommandStep.CHANGELOG_FILE_ARG, changeLogFile)
                addArgumentValue(UpdateCommandStep.CONTEXTS_ARG, Contexts().toString())
                addArgumentValue(UpdateCommandStep.LABEL_FILTER_ARG, LabelExpression().originalString)
                addArgumentValue(ChangeExecListenerCommandStep.CHANGE_EXEC_LISTENER_ARG, null as ChangeExecListener?)
                addArgumentValue(DatabaseChangelogCommandStep.CHANGELOG_PARAMETERS, ChangeLogParameters(database))

                setOutput(OutputStream.nullOutputStream()) // Suppress output to stdout (logging will still occur)
            }
            updateCommand.execute()
        }
    }
}

```

---

<div class="post-metadata">

**Author:** ![SteinarBang](https://avatars.discourse-cdn.com/v4/letter/s/919ad9/32.png) [@SteinarBang](https://forum.liquibase.org/u/SteinarBang)\
**Post date:** [May 23, 2023, 10:05pm UTC](https://forum.liquibase.org/t/4-21-1-is-out-deprecates-liquibase-update/8087/7 "2023-05-23T22:05:43Z")

</div>

Ok, I’ve tried to translate this into Java code:

```auto
    private void applyLiquibaseChangelist(Connection connection, String changelistClasspathResource) throws Exception {
        var database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection));
        Map<String, Object> scopeObjects = Map.of(
            Scope.Attr.database.name(), database,
            Scope.Attr.resourceAccessor.name(), new OSGiResourceAccessor(bundle));

        Scope.child(scopeObjects, (ScopedRunner) () -> new CommandScope("update")
                    .addArgumentValue(DbUrlConnectionCommandStep.DATABASE_ARG, database)
                    .addArgumentValue(UpdateCommandStep.CHANGELOG_FILE_ARG, changelistClasspathResource)
                    .addArgumentValue(DatabaseChangelogCommandStep.CHANGELOG_PARAMETERS, new ChangeLogParameters(database))
                    .execute());
    }

```

But it fails with the following error (fails in execute()):

```auto
liquibase.exception.CommandExecutionException: java.lang.RuntimeException: Cannot end scope xlveporhmt when currently at scope bsmuuxncug
	at liquibase.command.CommandScope.execute(CommandScope.java:235) ~[?:?]
	at no.priv.bang.karaf.sample.db.liquibase.test.SampleDbLiquibaseRunner.lambda$applyLiquibaseChangelist$1(SampleDbLiquibaseRunner.java:100) ~[?:?]
	at liquibase.Scope.lambda$child$0(Scope.java:194) ~[?:?]
	at liquibase.Scope.child(Scope.java:203) ~[?:?]
	at liquibase.Scope.child(Scope.java:193) ~[?:?]
	at liquibase.Scope.child(Scope.java:172) ~[?:?]
	at no.priv.bang.karaf.sample.db.liquibase.test.SampleDbLiquibaseRunner.applyLiquibaseChangelist(SampleDbLiquibaseRunner.java:96) ~[?:?]
	at no.priv.bang.karaf.sample.db.liquibase.test.SampleDbLiquibaseRunner.prepare(SampleDbLiquibaseRunner.java:65) ~[?:?]
	at org.ops4j.pax.jdbc.config.impl.DataSourceRegistration.<init>(DataSourceRegistration.java:88) ~[?:?]
	at org.ops4j.pax.jdbc.config.impl.DataSourceConfigManager.lambda$null$4(DataSourceConfigManager.java:95) ~[?:?]
	at org.ops4j.pax.jdbc.config.impl.ServiceTrackerHelper$1.addingService(ServiceTrackerHelper.java:132) ~[?:?]
	at org.osgi.util.tracker.ServiceTracker$Tracked.customizerAdding(ServiceTracker.java:943) ~[osgi.core-8.0.0.jar:?]
	at org.osgi.util.tracker.ServiceTracker$Tracked.customizerAdding(ServiceTracker.java:871) ~[osgi.core-8.0.0.jar:?]
	at org.osgi.util.tracker.AbstractTracked.trackAdding(AbstractTracked.java:256) ~[osgi.core-8.0.0.jar:?]
	at org.osgi.util.tracker.AbstractTracked.track(AbstractTracked.java:229) ~[osgi.core-8.0.0.jar:?]
	at org.osgi.util.tracker.ServiceTracker$Tracked.serviceChanged(ServiceTracker.java:903) ~[osgi.core-8.0.0.jar:?]
	at org.apache.felix.framework.EventDispatcher.invokeServiceListenerCallback(EventDispatcher.java:990) ~[?:?]
	at org.apache.felix.framework.EventDispatcher.fireEventImmediately(EventDispatcher.java:838) ~[?:?]
	at org.apache.felix.framework.EventDispatcher.fireServiceEvent(EventDispatcher.java:545) ~[?:?]
	at org.apache.felix.framework.Felix.fireServiceEvent(Felix.java:4863) ~[?:?]
	at org.apache.felix.framework.Felix.registerService(Felix.java:3834) ~[?:?]
	at org.apache.felix.framework.BundleContextImpl.registerService(BundleContextImpl.java:328) ~[?:?]
	at org.apache.felix.framework.BundleContextImpl.registerService(BundleContextImpl.java:302) ~[?:?]
	at org.ops4j.pax.jdbc.derby.impl.Activator.start(Activator.java:34) ~[?:?]
	at org.apache.felix.framework.util.SecureAction.startActivator(SecureAction.java:849) ~[?:?]
	at org.apache.felix.framework.Felix.activateBundle(Felix.java:2429) ~[?:?]
	at org.apache.felix.framework.Felix.startBundle(Felix.java:2335) ~[?:?]
	at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:1006) ~[?:?]
	at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:992) ~[?:?]
	at org.apache.karaf.features.internal.service.BundleInstallSupportImpl.startBundle(BundleInstallSupportImpl.java:165) ~[?:?]
	at org.apache.karaf.features.internal.service.FeaturesServiceImpl.startBundle(FeaturesServiceImpl.java:1160) ~[?:?]
	at org.apache.karaf.features.internal.service.Deployer.deploy(Deployer.java:1041) ~[?:?]
	at org.apache.karaf.features.internal.service.FeaturesServiceImpl.doProvision(FeaturesServiceImpl.java:1069) ~[?:?]
	at org.apache.karaf.features.internal.service.FeaturesServiceImpl.lambda$doProvisionInThread$13(FeaturesServiceImpl.java:1004) ~[?:?]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
	at java.lang.Thread.run(Thread.java:829) ~[?:?]
	Suppressed: java.sql.SQLException: Cannot close a connection while a transaction is still active.
		at org.apache.derby.impl.jdbc.SQLExceptionFactory.getSQLException(Unknown Source) ~[?:?]
		at org.apache.derby.impl.jdbc.SQLExceptionFactory.getSQLException(Unknown Source) ~[?:?]
		at org.apache.derby.impl.jdbc.Util.generateCsSQLException(Unknown Source) ~[?:?]
		at org.apache.derby.impl.jdbc.Util.generateCsSQLException(Unknown Source) ~[?:?]
		at org.apache.derby.impl.jdbc.EmbedConnection.newSQLException(Unknown Source) ~[?:?]
		at org.apache.derby.impl.jdbc.EmbedConnection.checkForTransactionInProgress(Unknown Source) ~[?:?]
		at org.apache.derby.impl.jdbc.EmbedConnection.close(Unknown Source) ~[?:?]
		at no.priv.bang.karaf.sample.db.liquibase.test.SampleDbLiquibaseRunner.prepare(SampleDbLiquibaseRunner.java:64) ~[?:?]
		at org.ops4j.pax.jdbc.config.impl.DataSourceRegistration.<init>(DataSourceRegistration.java:88) ~[?:?]
		at org.ops4j.pax.jdbc.config.impl.DataSourceConfigManager.lambda$null$4(DataSourceConfigManager.java:95) ~[?:?]
		at org.ops4j.pax.jdbc.config.impl.ServiceTrackerHelper$1.addingService(ServiceTrackerHelper.java:132) ~[?:?]
		at org.osgi.util.tracker.ServiceTracker$Tracked.customizerAdding(ServiceTracker.java:943) ~[osgi.core-8.0.0.jar:?]
		at org.osgi.util.tracker.ServiceTracker$Tracked.customizerAdding(ServiceTracker.java:871) ~[osgi.core-8.0.0.jar:?]
		at org.osgi.util.tracker.AbstractTracked.trackAdding(AbstractTracked.java:256) ~[osgi.core-8.0.0.jar:?]
		at org.osgi.util.tracker.AbstractTracked.track(AbstractTracked.java:229) ~[osgi.core-8.0.0.jar:?]
		at org.osgi.util.tracker.ServiceTracker$Tracked.serviceChanged(ServiceTracker.java:903) ~[osgi.core-8.0.0.jar:?]
		at org.apache.felix.framework.EventDispatcher.invokeServiceListenerCallback(EventDispatcher.java:990) ~[?:?]
		at org.apache.felix.framework.EventDispatcher.fireEventImmediately(EventDispatcher.java:838) ~[?:?]
		at org.apache.felix.framework.EventDispatcher.fireServiceEvent(EventDispatcher.java:545) ~[?:?]
		at org.apache.felix.framework.Felix.fireServiceEvent(Felix.java:4863) ~[?:?]
		at org.apache.felix.framework.Felix.registerService(Felix.java:3834) ~[?:?]
		at org.apache.felix.framework.BundleContextImpl.registerService(BundleContextImpl.java:328) ~[?:?]
		at org.apache.felix.framework.BundleContextImpl.registerService(BundleContextImpl.java:302) ~[?:?]
		at org.ops4j.pax.jdbc.derby.impl.Activator.start(Activator.java:34) ~[?:?]
		at org.apache.felix.framework.util.SecureAction.startActivator(SecureAction.java:849) ~[?:?]
		at org.apache.felix.framework.Felix.activateBundle(Felix.java:2429) ~[?:?]
		at org.apache.felix.framework.Felix.startBundle(Felix.java:2335) ~[?:?]
		at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:1006) ~[?:?]
		at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:992) ~[?:?]
		at org.apache.karaf.features.internal.service.BundleInstallSupportImpl.startBundle(BundleInstallSupportImpl.java:165) ~[?:?]
		at org.apache.karaf.features.internal.service.FeaturesServiceImpl.startBundle(FeaturesServiceImpl.java:1160) ~[?:?]
		at org.apache.karaf.features.internal.service.Deployer.deploy(Deployer.java:1041) ~[?:?]
		at org.apache.karaf.features.internal.service.FeaturesServiceImpl.doProvision(FeaturesServiceImpl.java:1069) ~[?:?]
		at org.apache.karaf.features.internal.service.FeaturesServiceImpl.lambda$doProvisionInThread$13(FeaturesServiceImpl.java:1004) ~[?:?]
		at java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[?:?]
		at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
		at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
		at java.lang.Thread.run(Thread.java:829) ~[?:?]
	Caused by: org.apache.derby.iapi.error.StandardException: Cannot close a connection while a transaction is still active.
		at org.apache.derby.iapi.error.StandardException.newException(Unknown Source) ~[?:?]
		at org.apache.derby.impl.jdbc.SQLExceptionFactory.wrapArgsForTransportAcrossDRDA(Unknown Source) ~[?:?]
		... 38 more
Caused by: java.lang.RuntimeException: Cannot end scope xlveporhmt when currently at scope bsmuuxncug
	at liquibase.Scope.exit(Scope.java:241) ~[?:?]
	at liquibase.Scope.child(Scope.java:205) ~[?:?]
	at liquibase.Scope.child(Scope.java:193) ~[?:?]
	at liquibase.Scope.child(Scope.java:189) ~[?:?]
	at liquibase.command.CommandScope.addOutputFileToMdc(CommandScope.java:254) ~[?:?]
	at liquibase.command.CommandScope.execute(CommandScope.java:207) ~[?:?]
	... 37 more

```

---

<div class="post-metadata">

**Author:** ![arwed](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.liquibase.org/arwed/32/1206_2.png) [@arwed](https://forum.liquibase.org/u/arwed)\
**Post date:** [May 3, 2024, 1:35pm UTC](https://forum.liquibase.org/t/4-21-1-is-out-deprecates-liquibase-update/8087/8 "2024-05-03T13:35:19Z")

</div>

Is it just me or is the CommandScope interface very confusing. How do I pass the connection it should use? I see it can be done by setting key values, but why is this so complicated? Not really self documenting…

---

<div class="post-metadata">

**Author:** ![SteinarBang](https://avatars.discourse-cdn.com/v4/letter/s/919ad9/32.png) [@SteinarBang](https://forum.liquibase.org/u/SteinarBang)\
**Post date:** [June 7, 2024, 6:11pm UTC](https://forum.liquibase.org/t/4-21-1-is-out-deprecates-liquibase-update/8087/9 "2024-06-07T18:11:30Z")

</div>

It’s not you.

I posted this today: [Is the commandscope still the current Java API](https://forum.liquibase.org/t/is-the-commandscope-still-the-current-java-api/9465)

---

<div class="post-metadata">

**Author:** ![SteinarBang](https://avatars.discourse-cdn.com/v4/letter/s/919ad9/32.png) [@SteinarBang](https://forum.liquibase.org/u/SteinarBang)\
**Post date:** [June 7, 2024, 6:38pm UTC](https://forum.liquibase.org/t/4-21-1-is-out-deprecates-liquibase-update/8087/10 "2024-06-07T18:38:17Z")

</div>

I did get it working eventually, even though I didn’t post an update on this thread.

[The working code looks like this](https://github.com/steinarb/sampleapp/blob/a284a7d980c836a9f1a60f0fa08b96d8f3787676/sampleapp.db.liquibase/src/main/java/no/priv/bang/sampleapp/db/liquibase/SampleappLiquibase.java#L43) (which looks very similar to the example that didn’t work for me in 2023…?)

I’m currently using OpenJDK java 17 on debian 12.5 “bookworm” on amd64
