Issues
- includeAll does not work with java 9+ and multi-module projects.CORE-3550
- ChangeLogParseException with Spring BootCORE-3459
- Close connection after useCORE-3444
- SpringLiquibase SetupException using "includeAll" tagCORE-3354
- Logging set to DEBUG when using liquibase.integration.spring.SpringLiquibaseCORE-3254
- SQL statements in <sql> tag are not using defaultSchema when configured with MultiTenantSpringLiquibaseCORE-3225
- settting deferrable or initiallyDeferred fails for any version of derby >= 10.11.1.1CORE-3216
- Changelog with includeAll will not find child changelogs in multi-modules Spring Boot's executable JARCORE-3213Resolved issue: CORE-3213
- zip file support on ClassLoaderResourceAccessorCORE-3172
- ClassLoaderResourceAccessor cannot read jar path resources from SpringLiquibaseCORE-3139Resolved issue: CORE-3139
- SpringLiquibase class has a schema bug with multischema implementationCORE-3137
- generateChangelog does not order views correctly based on usageCORE-3070
- Failure to locate correct MANIFEST.MF in SpringLiquibase classCORE-3001
- Running Liquibase with non-xa datasourceCORE-2964
- java.sql.SQLException: PooledConnection has already been closedCORE-2780Resolved issue: CORE-2780
- Add possibility to test rollback with SpringLiquibaseCORE-2735Resolved issue: CORE-2735
- Generating a futureRollbackSql when using "classpath:" prefix doesn't recognise any of the ran change sets.CORE-2680Resolved issue: CORE-2680
- includeAll includes just first xmlCORE-2592
- liquibase.exception.ServiceNotFoundException: Could not find implementation of liquibase.logging.LoggerCORE-2403Resolved issue: CORE-2403
- NoClassDefFoundError in ServiceLocatorCORE-2372
- FileNotFoundException in SpringLiquibaseCORE-2335Resolved issue: CORE-2335
- Inefficient multi tenant updateCORE-2293
- SpringLiquibase: includeAll within jar causes SetupExceptionCORE-2166Resolved issue: CORE-2166
- SpringLiquibase: includeAll within jar causes NullPointerExceptionCORE-2164Resolved issue: CORE-2164
- Overriding liquibase.databaseChangeLogLockTableName does not work via Change Log PropertiesCORE-2091
- Problem using includeAll with SpringLiquibaseCORE-2023Resolved issue: CORE-2023
- Could not find implementation of liquibase.logging.LoggerCORE-2003Resolved issue: CORE-2003
- SpringLiqubase includeAll is not including filesCORE-1920Resolved issue: CORE-1920
- SpringLiquibase fails when dropFirst is trueCORE-1919Resolved issue: CORE-1919
- mysql case insensitive affects databasechangelog creationCORE-1614Resolved issue: CORE-1614
- Regression: diff generates full schema instead of changes when using liquibase-hibernate4CORE-1572Resolved issue: CORE-1572
- Support multi-tenant spring applicationsCORE-1472Resolved issue: CORE-1472
- Support Springs classpath*: prefix for includeAllCORE-1428Resolved issue: CORE-1428
- SpringLiquibase not closing connection if rollback exceptionCORE-1405Resolved issue: CORE-1405
34 of 34
includeAll does not work with java 9+ and multi-module projects.
Description
Environment
SpringBoot 2.2.2 and 2.1.11
Maven
Windows
Details
Details
Created January 4, 2020 at 10:23 PM
Updated January 6, 2020 at 3:47 AM
Activity
Show:
mat January 6, 2020 at 3:47 AM
After further research, it seems like there is no clean equivalent in java 9 for the current approach.
See: https://stackoverflow.com/questions/49557431/how-to-safely-access-the-urls-of-all-resource-files-in-the-classpath-in-java-9-1
For now, my work around is to override SpringResourceOpener.list() with:
@Override
public Set<String> list(String relativeTo, String path, boolean includeFiles, boolean includeDirectories,
boolean recursive) throws IOException {
if (path == null) {
return null;
}
//
// Possible Resources Types
//
// Standalone Jar
// Root Path: jar:file:/Projects/my-project/second-module/target/second-module-1.0.0-SNAPSHOT-exec.jar!/BOOT-INF/lib/first-module-1.0.0-SNAPSHOT.jar!/
// +Resource: jar:file:/Projects/my-project/second-module/target/second-module-1.0.0-SNAPSHOT-exec.jar!/BOOT-INF/lib/first-module-1.0.0-SNAPSHOT.jar!/db/changelog/0-initial-schema.xml
// Standalone War
// Root Path: jar:file:/Projects/my-project/second-module/target/second-module-1.0.0-SNAPSHOT-exec.war!/WEB-INF/lib/first-module-1.0.0-SNAPSHOT.jar!/
// +Resource: jar:file:/Projects/my-project/second-module/target/second-module-1.0.0-SNAPSHOT-exec.war!/WEB-INF/lib/first-module-1.0.0-SNAPSHOT.jar!/-db/changelog/0-initial-schema.xml
// Openned Jar Dependency
// Root Path: file:/Projects/my-project/first-module/target/classes/
// +Resource: file:/Projects/my-project/first-module/target/classes/db/changelog/0-initial-schema.xml
// War Wild-Fly Exploded
// Root Path: vfs:/Projects/my-project/second-module/target/second-module-1.0.0-SNAPSHOT/WEB-INF/lib/first-module-1.0.0-SNAPSHOT.jar/
// +Resource: vfs:/Projects/my-project/second-module/target/second-module-1.0.0-SNAPSHOT/WEB-INF/lib/first-module-1.0.0-SNAPSHOT.jar/db/changelog/0-initial-schema.xml
// War Wild-Fly Artifact
// Root Path: vfs:/content/second-module-1.0.0-SNAPSHOT.war/WEB-INF/lib/first-module-1.0.0-SNAPSHOT.jar/
// +Resource: vfs:/content/second-module-1.0.0-SNAPSHOT.war/WEB-INF/lib/first-module-1.0.0-SNAPSHOT.jar/db/changelog/0-initial-schema.xml
Set<String> returnSet = new HashSet<>();
path = path + (recursive ? "**" : '*'); // All files inside!
String tempFile = FilenameUtils.concat(FilenameUtils.getFullPath(relativeTo), path);
Resource[] resources = getResources(adjustClasspath(tempFile));
for (Resource resource : resources) {
String resourceStr = resource.getURL().toExternalForm();
String resourcePath = convertToPath(resourceStr);
if (resourceStr.endsWith(resourcePath) && !resourceStr.equals(resourcePath)) {
returnSet.add(resourcePath);
} else {
// Closed Jar Dependency
// Root Path: file:/.m2/repository/org/liquibase/test/first-module/1.0.0-SNAPSHOT/first-module-1.0.0-SNAPSHOT.jar/
// +Resource: jar:file:/.m2/repository/org/liquibase/test/first-module/1.0.0-SNAPSHOT/first-module-1.0.0-SNAPSHOT.jar!/db/changelog/0-initial-schema.xml
String newResourceStr = resource.getURL().getFile(); // Remove "jar:" from begining.
newResourceStr = newResourceStr.replaceAll("!", "");
String newResourcePath = convertToPath(newResourceStr);
if (newResourceStr.endsWith(newResourcePath) && !newResourceStr.equals(newResourcePath)) {
returnSet.add(newResourcePath);
} else {
// TODO https://liquibase.jira.com/browse/CORE-3550: Truncating URLs based on exclamation marks
// as fallback. Jar root paths cannot be obtained from the classloader in java 9+.
int exclamationMarkIndex;
if (resource.getURL().toString().startsWith("jar:file:")
&& (exclamationMarkIndex = resource.getURL().getFile().lastIndexOf("!")) > 0) {
returnSet.add(resource.getURL().getFile().substring(exclamationMarkIndex +1));
}else {
LogService.getLog(getClass()).warning(
LogType.LOG, "Not a valid resource entry: " + resourceStr);
}
}
}
}
return returnSet;
}
Please advice on what would possibly be a better fix.
As far as I can tell, IncludeAll relies on legacy java ClassLoader capabilities to compute the relative path of the files to include.
Specifically AbstractResourceAccessor.convertToPath() relies on rootStrings which was populated with the URLs of all jars present in the classpath before java 9.
if (classLoader instanceof URLClassLoader) { // only true before java 9 baseUrls = new Vector<>(Arrays.asList(((URLClassLoader) classLoader).getURLs())).elements(); while (baseUrls.hasMoreElements()) { addRootPath(baseUrls.nextElement()); // add jar URLs } } baseUrls = classLoader.getResources(""); while (baseUrls.hasMoreElements()) { addRootPath(baseUrls.nextElement()); // do not add jar URLs }
This approach does not work with java 9+.
As result changelog files from external jars are not included and the following warning is logged:
WARN Not a valid resource entry: jar:file:/.m2/repository/com/foo/1.0.0/foo-1.0.0.jar!/db/changelog/changelog-001.xml