diff --git a/bom/application/pom.xml b/bom/application/pom.xml index b28d3c6f59350..02659ee82907e 100644 --- a/bom/application/pom.xml +++ b/bom/application/pom.xml @@ -163,8 +163,8 @@ 10.17.0 3.0.4 - 4.27.0 - 4.24.0 + 4.29.1 + 4.28.0 2.2 6.0.0 4.11.1 diff --git a/core/deployment/src/main/java/io/quarkus/deployment/annotations/BuildProducer.java b/core/deployment/src/main/java/io/quarkus/deployment/annotations/BuildProducer.java index 5f59db467b6a3..2bec80a3b38fd 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/annotations/BuildProducer.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/annotations/BuildProducer.java @@ -1,5 +1,7 @@ package io.quarkus.deployment.annotations; +import java.util.Collection; + import io.quarkus.builder.item.BuildItem; /** @@ -16,4 +18,7 @@ public interface BuildProducer { void produce(T item); + default void produce(Collection items) { + items.forEach(this::produce); + } } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/NativeImageResourceBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/NativeImageResourceBuildItem.java index f58313c940ae5..5e491ed3da0e3 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/NativeImageResourceBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/NativeImageResourceBuildItem.java @@ -2,15 +2,21 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; import io.quarkus.builder.item.MultiBuildItem; +import io.quarkus.deployment.util.ArtifactResourceResolver; +import io.quarkus.maven.dependency.ArtifactCoords; +import io.quarkus.maven.dependency.ResolvedDependency; +import io.quarkus.paths.PathFilter; +import io.quarkus.util.GlobUtil; /** * A build item that indicates that a static resource should be included in the native image. *

* A static resource is a file that is not processed by the build steps, but is included in the native image as-is. - * The resource path passed to the constructor is a {@code /}-separated path name (with the same semantics as the parameters + * The resource path passed to the constructor is a {@code /}-separated path name (with the same semantics as the parameters) * passed to {@link java.lang.ClassLoader#getResources(String)}. *

* Related build items: @@ -23,6 +29,23 @@ public final class NativeImageResourceBuildItem extends MultiBuildItem { private final List resources; + /** + * Builds a {@code NativeImageResourceBuildItem} for the given artifact and path + * + * @param dependencies the resolved dependencies of the build + * @param artifactCoordinates the coordinates of the artifact containing the resources + * @param resourceFilter the filter for the resources in glob syntax (see {@link GlobUtil}) + * @return + */ + public static NativeImageResourceBuildItem ofDependencyResources( + Collection dependencies, + ArtifactCoords artifactCoordinates, + PathFilter resourceFilter) { + + var resolver = ArtifactResourceResolver.of(dependencies, artifactCoordinates); + return new NativeImageResourceBuildItem(resolver.resourceList(resourceFilter)); + } + public NativeImageResourceBuildItem(String... resources) { this.resources = Arrays.asList(resources); } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ServiceProviderBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ServiceProviderBuildItem.java index 28854dcb5bfc1..590adea75ab3f 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ServiceProviderBuildItem.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/nativeimage/ServiceProviderBuildItem.java @@ -11,7 +11,11 @@ import java.util.Set; import io.quarkus.builder.item.MultiBuildItem; +import io.quarkus.deployment.util.ArtifactResourceResolver; import io.quarkus.deployment.util.ServiceUtil; +import io.quarkus.maven.dependency.ArtifactCoords; +import io.quarkus.maven.dependency.ResolvedDependency; +import io.quarkus.paths.PathFilter; /** * Represents a Service Provider registration. @@ -21,6 +25,8 @@ public final class ServiceProviderBuildItem extends MultiBuildItem { public static final String SPI_ROOT = "META-INF/services/"; + private static final PathFilter SPI_FILTER = PathFilter.forIncludes(List.of(SPI_ROOT + "*")); + private final String serviceInterface; private final List providers; @@ -52,7 +58,7 @@ public static ServiceProviderBuildItem allProviders(final String serviceInterfac line = line.substring(0, commentIndex); } line = line.trim(); - if (line.length() != 0) { + if (!line.isEmpty()) { classNames.add(line); } } @@ -87,6 +93,48 @@ public static ServiceProviderBuildItem allProvidersFromClassPath(final String se } } + /** + * Creates a new {@link Collection} of {@code ServiceProviderBuildItem}s for the selected artifact. + * It includes all the providers, that are contained in all the service interface descriptor files defined in + * {@code "META-INF/services/"} in the selected artifact. + * + * @param dependencies the resolved dependencies of the build + * @param artifactCoordinates the coordinates of the artifact containing the service definitions + * @return a {@link Collection} of {@code ServiceProviderBuildItem}s containing all the found service providers + */ + public static Collection allProvidersOfDependency( + Collection dependencies, + ArtifactCoords artifactCoordinates) { + + return allProvidersOfDependencies(dependencies, List.of(artifactCoordinates)); + } + + /** + * Creates a new {@link Collection} of {@code ServiceProviderBuildItem}s for the selected artifacts. + * It includes all the providers, that are contained in all the service interface descriptor files defined in + * {@code "META-INF/services/"} in all the selected artifacts. + * + * @param dependencies the resolved dependencies of the build + * @param artifactCoordinatesCollection a {@link Collection} of coordinates of the artifacts containing the service + * definitions + * @return a {@link Collection} of {@code ServiceProviderBuildItem}s containing all the found service providers + */ + public static Collection allProvidersOfDependencies( + Collection dependencies, + Collection artifactCoordinatesCollection) { + + var resolver = ArtifactResourceResolver.of(dependencies, artifactCoordinatesCollection); + return resolver.resourcePathList(SPI_FILTER).stream() + .map(ServiceProviderBuildItem::ofSpiPath) + .toList(); + } + + private static ServiceProviderBuildItem ofSpiPath(Path spiPath) { + return new ServiceProviderBuildItem( + spiPath.getFileName().toString(), + ServiceUtil.classNamesNamedIn(spiPath.toString())); + } + /** * Registers the specified service interface descriptor to be embedded and allow reflection (instantiation only) * of the specified provider classes. Note that the service interface descriptor file has to exist and match the @@ -136,12 +184,12 @@ private ServiceProviderBuildItem(String serviceInterfaceClassName, List this.providers = providers; // Validation - if (serviceInterface.length() == 0) { + if (serviceInterface.isEmpty()) { throw new IllegalArgumentException("The serviceDescriptorFile interface cannot be blank"); } providers.forEach(s -> { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { throw new IllegalArgumentException("The provider class name cannot be null or blank"); } }); diff --git a/core/deployment/src/main/java/io/quarkus/deployment/util/ArtifactResourceResolver.java b/core/deployment/src/main/java/io/quarkus/deployment/util/ArtifactResourceResolver.java new file mode 100644 index 0000000000000..2eb6b98a21ed6 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/util/ArtifactResourceResolver.java @@ -0,0 +1,101 @@ +package io.quarkus.deployment.util; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +import io.quarkus.maven.dependency.ArtifactCoords; +import io.quarkus.maven.dependency.ArtifactCoordsPattern; +import io.quarkus.maven.dependency.ResolvedDependency; +import io.quarkus.paths.PathFilter; +import io.quarkus.paths.PathVisit; +import io.quarkus.util.GlobUtil; + +/** + * Utility class to extract a list of resource paths from a given artifact and path. + */ +public final class ArtifactResourceResolver { + private final Collection artifacts; + + /** + * Creates a {@code ArtifactResourceResolver} for the given artifact + * + * @param dependencies the resolved dependencies of the build + * @param artifactCoordinates the coordinates of the artifact containing the resources + */ + public static ArtifactResourceResolver of( + Collection dependencies, ArtifactCoords artifactCoordinates) { + + return new ArtifactResourceResolver(dependencies, List.of(artifactCoordinates)); + } + + /** + * Creates a {@code ArtifactResourceResolver} for the given artifact + * + * @param dependencies the resolved dependencies of the build + * @param artifactCoordinatesCollection a coordinates {@link Collection} for the artifacts containing the resources + */ + public static ArtifactResourceResolver of( + Collection dependencies, Collection artifactCoordinatesCollection) { + + return new ArtifactResourceResolver(dependencies, artifactCoordinatesCollection); + } + + private ArtifactResourceResolver( + Collection dependencies, Collection artifactCoordinates) { + + var patterns = ArtifactCoordsPattern.toPatterns(artifactCoordinates); + artifacts = patterns.stream() + .map(p -> findArtifact(dependencies, p)) + .collect(Collectors.toSet()); + } + + private static ResolvedDependency findArtifact( + Collection dependencies, ArtifactCoordsPattern pattern) { + + return dependencies.stream() + .filter(pattern::matches) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException( + "%s artifact not found".formatted(pattern.toString()))); + } + + /** + * Extracts a {@link Collection} of resource paths with the given filter + * + * @param pathFilter the filter for the resources in glob syntax (see {@link GlobUtil}) + * @return a collection of the found resource paths + */ + public Collection resourcePathList(PathFilter pathFilter) { + return artifacts.stream() + .map(a -> pathsForArtifact(a, pathFilter)) + .flatMap(Collection::stream) + .toList(); + } + + private Collection pathsForArtifact(ResolvedDependency artifact, PathFilter pathFilter) { + var pathList = new ArrayList(); + var pathTree = artifact.getContentTree(pathFilter); + pathTree.walk(visit -> pathList.add(relativePath(visit))); + return pathList; + } + + private Path relativePath(PathVisit visit) { + var path = visit.getPath(); + return path.getRoot().relativize(path); + } + + /** + * Extracts a {@link List} of resource paths as strings with the given filter + * + * @param pathFilter the filter for the resources in glob syntax (see {@link GlobUtil}) + * @return a list of the found resource paths as strings + */ + public List resourceList(PathFilter pathFilter) { + return resourcePathList(pathFilter).stream() + .map(Path::toString) + .toList(); + } +} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/util/ServiceUtil.java b/core/deployment/src/main/java/io/quarkus/deployment/util/ServiceUtil.java index b45ee1f1f52f9..16f396f3efe0c 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/util/ServiceUtil.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/util/ServiceUtil.java @@ -19,6 +19,7 @@ /** */ public final class ServiceUtil { + private ServiceUtil() { } @@ -59,6 +60,14 @@ public static Set classNamesNamedIn(Path path) throws IOException { return set; } + public static Set classNamesNamedIn(String filePath) { + try { + return classNamesNamedIn(Thread.currentThread().getContextClassLoader(), filePath); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + /** * - Lines starting by a # (or white spaces and a #) are ignored. - For * lines containing data before a comment (#) are parsed and only the value diff --git a/extensions/liquibase-mongodb/deployment/pom.xml b/extensions/liquibase-mongodb/deployment/pom.xml index 6ac0f8adb7e93..cc4f53053719e 100644 --- a/extensions/liquibase-mongodb/deployment/pom.xml +++ b/extensions/liquibase-mongodb/deployment/pom.xml @@ -16,6 +16,12 @@ io.quarkus quarkus-liquibase-mongodb + + + org.liquibase + liquibase-commercial + + io.quarkus diff --git a/extensions/liquibase-mongodb/deployment/src/main/java/io/quarkus/liquibase/mongodb/deployment/LiquibaseMongodbProcessor.java b/extensions/liquibase-mongodb/deployment/src/main/java/io/quarkus/liquibase/mongodb/deployment/LiquibaseMongodbProcessor.java index febc0a6b9c47a..cb070ce870ee0 100644 --- a/extensions/liquibase-mongodb/deployment/src/main/java/io/quarkus/liquibase/mongodb/deployment/LiquibaseMongodbProcessor.java +++ b/extensions/liquibase-mongodb/deployment/src/main/java/io/quarkus/liquibase/mongodb/deployment/LiquibaseMongodbProcessor.java @@ -1,7 +1,5 @@ package io.quarkus.liquibase.mongodb.deployment; -import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT; - import java.io.IOException; import java.nio.file.Paths; import java.util.ArrayList; @@ -13,7 +11,6 @@ import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.stream.Stream; import jakarta.enterprise.context.ApplicationScoped; @@ -42,13 +39,17 @@ import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; +import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild; import io.quarkus.deployment.util.ServiceUtil; import io.quarkus.liquibase.mongodb.LiquibaseMongodbFactory; import io.quarkus.liquibase.mongodb.runtime.LiquibaseMongodbBuildTimeConfig; import io.quarkus.liquibase.mongodb.runtime.LiquibaseMongodbConfig; import io.quarkus.liquibase.mongodb.runtime.LiquibaseMongodbRecorder; +import io.quarkus.maven.dependency.ArtifactCoords; +import io.quarkus.maven.dependency.Dependency; import io.quarkus.mongodb.runtime.MongodbConfig; +import io.quarkus.paths.PathFilter; import liquibase.change.Change; import liquibase.change.DatabaseChangeProperty; import liquibase.change.core.CreateProcedureChange; @@ -68,6 +69,17 @@ class LiquibaseMongodbProcessor { private static final Logger LOGGER = Logger.getLogger(LiquibaseMongodbProcessor.class); + private static final ArtifactCoords LIQUIBASE_ARTIFACT = Dependency.of( + "org.liquibase", "liquibase-core", "*"); + private static final ArtifactCoords LIQUIBASE_MONGODB_ARTIFACT = Dependency.of( + "org.liquibase.ext", "liquibase-mongodb", "*"); + private static final PathFilter LIQUIBASE_RESOURCE_FILTER = PathFilter.forIncludes(List.of( + "*.properties", + "www.liquibase.org/xml/ns/dbchangelog/*.xsd")); + private static final PathFilter LIQUIBASE_MONGODB_RESOURCE_FILTER = PathFilter.forIncludes(List.of( + "www.liquibase.org/xml/ns/mongodb/*.xsd", + "liquibase.parser.core.xml/*.xsd")); + private static final DotName DATABASE_CHANGE_PROPERTY = DotName.createSimple(DatabaseChangeProperty.class.getName()); @BuildStep @@ -78,16 +90,16 @@ FeatureBuildItem feature() { @BuildStep(onlyIf = NativeOrNativeSourcesBuild.class) List indexLiquibase() { return List.of( - new IndexDependencyBuildItem("org.liquibase", "liquibase-core"), - new IndexDependencyBuildItem("org.liquibase.ext", "liquibase-mongodb")); + new IndexDependencyBuildItem(LIQUIBASE_ARTIFACT.getGroupId(), LIQUIBASE_ARTIFACT.getArtifactId()), + new IndexDependencyBuildItem( + LIQUIBASE_MONGODB_ARTIFACT.getGroupId(), LIQUIBASE_MONGODB_ARTIFACT.getArtifactId())); } @BuildStep(onlyIf = NativeOrNativeSourcesBuild.class) - @Record(STATIC_INIT) void nativeImageConfiguration( - LiquibaseMongodbRecorder recorder, LiquibaseMongodbBuildTimeConfig liquibaseBuildConfig, CombinedIndexBuildItem combinedIndex, + CurateOutcomeBuildItem curateOutcome, BuildProducer reflective, BuildProducer resource, BuildProducer services, @@ -113,6 +125,7 @@ void nativeImageConfiguration( liquibase.command.CommandFactory.class.getName(), liquibase.database.LiquibaseTableNamesFactory.class.getName(), liquibase.configuration.ConfiguredValueModifierFactory.class.getName(), + liquibase.changelog.FastCheckService.class.getName(), // deprecated, but still used by liquibase.nosql.lockservice.AbstractNoSqlLockService liquibase.configuration.GlobalConfiguration.class.getName()) .constructors().build()); @@ -154,82 +167,39 @@ void nativeImageConfiguration( resource.produce( new NativeImageResourceBuildItem(getChangeLogs(liquibaseBuildConfig).toArray(new String[0]))); - Stream.of(liquibase.change.Change.class, - liquibase.changelog.ChangeLogHistoryService.class, - liquibase.changeset.ChangeSetService.class, - liquibase.database.Database.class, - liquibase.database.DatabaseConnection.class, - liquibase.datatype.LiquibaseDataType.class, - liquibase.diff.compare.DatabaseObjectComparator.class, - liquibase.diff.DiffGenerator.class, - liquibase.diff.output.changelog.ChangeGenerator.class, - liquibase.executor.Executor.class, - liquibase.license.LicenseService.class, - liquibase.lockservice.LockService.class, - liquibase.logging.LogService.class, - liquibase.parser.ChangeLogParser.class, - liquibase.parser.LiquibaseSqlParser.class, - liquibase.parser.NamespaceDetails.class, - liquibase.parser.SnapshotParser.class, - liquibase.precondition.Precondition.class, - liquibase.report.ShowSummaryGenerator.class, - liquibase.serializer.ChangeLogSerializer.class, - liquibase.serializer.SnapshotSerializer.class, - liquibase.servicelocator.ServiceLocator.class, - liquibase.snapshot.SnapshotGenerator.class, - liquibase.sqlgenerator.SqlGenerator.class, - liquibase.structure.DatabaseObject.class, - liquibase.logging.mdc.MdcManager.class) - .forEach(t -> addService(services, reflective, t, false)); - // Register Precondition services, and the implementation class for reflection while also registering fields for reflection - addService(services, reflective, liquibase.precondition.Precondition.class, true); + addService(services, reflective, liquibase.precondition.Precondition.class.getName(), true); // CommandStep implementations are needed (just like in non-mongodb variant) - addService(services, reflective, liquibase.command.CommandStep.class, false, + addService(services, reflective, liquibase.command.CommandStep.class.getName(), false, "liquibase.command.core.StartH2CommandStep"); - // liquibase XSD - resource.produce(new NativeImageResourceBuildItem( - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.7.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.8.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.9.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.10.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.11.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.12.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.13.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.14.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.15.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.16.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.17.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.18.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.19.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.20.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.21.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.22.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.23.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.24.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.25.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd", - "www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd", - "liquibase.build.properties")); + var dependencies = curateOutcome.getApplicationModel().getRuntimeDependencies(); + + resource.produce(NativeImageResourceBuildItem.ofDependencyResources( + dependencies, LIQUIBASE_ARTIFACT, LIQUIBASE_RESOURCE_FILTER)); + resource.produce(NativeImageResourceBuildItem.ofDependencyResources( + dependencies, LIQUIBASE_MONGODB_ARTIFACT, LIQUIBASE_MONGODB_RESOURCE_FILTER)); + services.produce(ServiceProviderBuildItem.allProvidersOfDependencies( + dependencies, List.of(LIQUIBASE_ARTIFACT, LIQUIBASE_MONGODB_ARTIFACT))); // liquibase resource bundles resourceBundle.produce(new NativeImageResourceBundleBuildItem("liquibase/i18n/liquibase-core")); + resourceBundle.produce(new NativeImageResourceBundleBuildItem("liquibase/i18n/liquibase-mongo")); } private void addService(BuildProducer services, - BuildProducer reflective, Class serviceClass, + BuildProducer reflective, String serviceClassName, boolean shouldRegisterFieldForReflection, String... excludedImpls) { try { - String service = "META-INF/services/" + serviceClass.getName(); + String service = ServiceProviderBuildItem.SPI_ROOT + serviceClassName; Set implementations = ServiceUtil.classNamesNamedIn(Thread.currentThread().getContextClassLoader(), service); if (excludedImpls.length > 0) { implementations = new HashSet<>(implementations); - implementations.removeAll(Arrays.asList(excludedImpls)); + Arrays.asList(excludedImpls).forEach(implementations::remove); } - services.produce(new ServiceProviderBuildItem(serviceClass.getName(), implementations.toArray(new String[0]))); + services.produce(new ServiceProviderBuildItem(serviceClassName, implementations.toArray(new String[0]))); reflective.produce(ReflectiveClassBuildItem.builder( implementations.toArray(new String[0])) @@ -289,25 +259,21 @@ private List getChangeLogs(LiquibaseMongodbBuildTimeConfig liquibaseBuil ChangeLogParserFactory changeLogParserFactory = ChangeLogParserFactory.getInstance(); - Set resources = new LinkedHashSet<>(); + try (var classLoaderResourceAccessor = new ClassLoaderResourceAccessor( + Thread.currentThread().getContextClassLoader())) { - ClassLoaderResourceAccessor classLoaderResourceAccessor = new ClassLoaderResourceAccessor( - Thread.currentThread().getContextClassLoader()); - - try { - resources.addAll(findAllChangeLogFiles(liquibaseBuildConfig.changeLog, changeLogParserFactory, - classLoaderResourceAccessor, changeLogParameters)); + Set resources = new LinkedHashSet<>( + findAllChangeLogFiles(liquibaseBuildConfig.changeLog, changeLogParserFactory, + classLoaderResourceAccessor, changeLogParameters)); LOGGER.debugf("Liquibase changeLogs: %s", resources); return new ArrayList<>(resources); - } finally { - try { - classLoaderResourceAccessor.close(); - } catch (Exception ignored) { - // close() really shouldn't declare that exception, see also https://github.com/liquibase/liquibase/pull/2576 - } + } catch (Exception ex) { + // close() really shouldn't declare that exception, see also https://github.com/liquibase/liquibase/pull/2576 + throw new IllegalStateException( + "Error while loading the liquibase changelogs: %s".formatted(ex.getMessage()), ex); } } @@ -350,20 +316,16 @@ private Set findAllChangeLogFiles(String file, ChangeLogParserFactory ch private Optional extractChangeFile(Change change, String changeSetFilePath) { String path = null; Boolean relative = null; - if (change instanceof LoadDataChange) { - LoadDataChange loadDataChange = (LoadDataChange) change; + if (change instanceof LoadDataChange loadDataChange) { path = loadDataChange.getFile(); relative = loadDataChange.isRelativeToChangelogFile(); - } else if (change instanceof SQLFileChange) { - SQLFileChange sqlFileChange = (SQLFileChange) change; + } else if (change instanceof SQLFileChange sqlFileChange) { path = sqlFileChange.getPath(); relative = sqlFileChange.isRelativeToChangelogFile(); - } else if (change instanceof CreateProcedureChange) { - CreateProcedureChange createProcedureChange = (CreateProcedureChange) change; + } else if (change instanceof CreateProcedureChange createProcedureChange) { path = createProcedureChange.getPath(); relative = createProcedureChange.isRelativeToChangelogFile(); - } else if (change instanceof CreateViewChange) { - CreateViewChange createViewChange = (CreateViewChange) change; + } else if (change instanceof CreateViewChange createViewChange) { path = createViewChange.getPath(); relative = createViewChange.getRelativeToChangelogFile(); } diff --git a/extensions/liquibase-mongodb/runtime/pom.xml b/extensions/liquibase-mongodb/runtime/pom.xml index 42a277a255599..df90bef0fcdeb 100644 --- a/extensions/liquibase-mongodb/runtime/pom.xml +++ b/extensions/liquibase-mongodb/runtime/pom.xml @@ -36,6 +36,12 @@ org.liquibase.ext liquibase-mongodb + + + org.liquibase + liquibase-commercial + + org.graalvm.sdk diff --git a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/graal/SubstituteStringUtil.java b/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/graal/SubstituteStringUtil.java deleted file mode 100644 index 29bb32d1001e1..0000000000000 --- a/extensions/liquibase-mongodb/runtime/src/main/java/io/quarkus/liquibase/mongodb/runtime/graal/SubstituteStringUtil.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.quarkus.liquibase.mongodb.runtime.graal; - -import java.security.SecureRandom; - -import com.oracle.svm.core.annotate.Alias; -import com.oracle.svm.core.annotate.InjectAccessors; -import com.oracle.svm.core.annotate.TargetClass; - -@TargetClass(className = "liquibase.util.StringUtil") -final class SubstituteStringUtil { - - @Alias - @InjectAccessors(SecureRandomAccessors.class) - private static SecureRandom rnd; - - public static final class SecureRandomAccessors { - - private static volatile SecureRandom volatileRandom; - - public static SecureRandom get() { - SecureRandom localVolatileRandom = volatileRandom; - if (localVolatileRandom == null) { - synchronized (SecureRandomAccessors.class) { - localVolatileRandom = volatileRandom; - if (localVolatileRandom == null) { - volatileRandom = localVolatileRandom = new SecureRandom(); - } - } - } - return localVolatileRandom; - } - - public static void set(SecureRandom rnd) { - throw new IllegalStateException("The setter for liquibase.util.StringUtil#rnd shouldn't be called."); - } - } -} diff --git a/extensions/liquibase/deployment/src/main/java/io/quarkus/liquibase/deployment/LiquibaseProcessor.java b/extensions/liquibase/deployment/src/main/java/io/quarkus/liquibase/deployment/LiquibaseProcessor.java index 0afcedac241e0..29c6bdb104346 100644 --- a/extensions/liquibase/deployment/src/main/java/io/quarkus/liquibase/deployment/LiquibaseProcessor.java +++ b/extensions/liquibase/deployment/src/main/java/io/quarkus/liquibase/deployment/LiquibaseProcessor.java @@ -4,8 +4,6 @@ import java.io.FileNotFoundException; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; @@ -66,7 +64,9 @@ import io.quarkus.liquibase.runtime.LiquibaseDataSourceBuildTimeConfig; import io.quarkus.liquibase.runtime.LiquibaseFactoryProducer; import io.quarkus.liquibase.runtime.LiquibaseRecorder; -import io.quarkus.paths.PathTree; +import io.quarkus.maven.dependency.ArtifactCoords; +import io.quarkus.maven.dependency.Dependency; +import io.quarkus.paths.PathFilter; import io.quarkus.runtime.util.StringUtil; import liquibase.change.Change; import liquibase.change.DatabaseChangeProperty; @@ -91,11 +91,10 @@ class LiquibaseProcessor { private static final Logger LOGGER = Logger.getLogger(LiquibaseProcessor.class); private static final String LIQUIBASE_BEAN_NAME_PREFIX = "liquibase_"; - private static final String LIQUIBASE_GROUP_ID = "org.liquibase"; - private static final String LIQUIBASE_ARTIFACT_ID = "liquibase-core"; - private static final String LIQUIBASE_PROPERTIES_PATH = ""; - private static final String LIQUIBASE_DB_CHANGELOG_XSD_PATH = "www.liquibase.org/xml/ns/dbchangelog"; - private static final String LIQUIBASE_SERVICE_PATH = "META-INF/services/"; + private static final ArtifactCoords LIQUIBASE_ARTIFACT = Dependency.of("org.liquibase", "liquibase-core", "*"); + private static final PathFilter LIQUIBASE_RESOURCE_FILTER = PathFilter.forIncludes(List.of( + "*.properties", + "www.liquibase.org/xml/ns/dbchangelog/*.xsd")); private static final DotName DATABASE_CHANGE_PROPERTY = DotName.createSimple(DatabaseChangeProperty.class.getName()); @@ -106,7 +105,7 @@ FeatureBuildItem feature() { @BuildStep(onlyIf = NativeOrNativeSourcesBuild.class) IndexDependencyBuildItem indexLiquibase() { - return new IndexDependencyBuildItem("org.liquibase", "liquibase-core"); + return new IndexDependencyBuildItem(LIQUIBASE_ARTIFACT.getGroupId(), LIQUIBASE_ARTIFACT.getArtifactId()); } @BuildStep(onlyIf = NativeOrNativeSourcesBuild.class) @@ -139,7 +138,8 @@ void nativeImageConfiguration( reflective.produce(ReflectiveClassBuildItem.builder( liquibase.command.CommandFactory.class.getName(), liquibase.database.LiquibaseTableNamesFactory.class.getName(), - liquibase.configuration.ConfiguredValueModifierFactory.class.getName()) + liquibase.configuration.ConfiguredValueModifierFactory.class.getName(), + liquibase.changelog.FastCheckService.class.getName()) .constructors().build()); reflective.produce(ReflectiveClassBuildItem.builder( @@ -203,7 +203,10 @@ void nativeImageConfiguration( } }); - resolveLiquibaseResources(curateOutcome, services, reflective, resource); + var dependencies = curateOutcome.getApplicationModel().getRuntimeDependencies(); + resource.produce(NativeImageResourceBuildItem.ofDependencyResources( + dependencies, LIQUIBASE_ARTIFACT, LIQUIBASE_RESOURCE_FILTER)); + services.produce(ServiceProviderBuildItem.allProvidersOfDependency(dependencies, LIQUIBASE_ARTIFACT)); // liquibase resource bundles resourceBundle.produce(new NativeImageResourceBundleBuildItem("liquibase/i18n/liquibase-core")); @@ -219,7 +222,7 @@ private static Predicate commandStepPredicate(Capabilities capabilities) private void consumeService(String serviceClassName, BiConsumer> consumer) { try { - String service = LIQUIBASE_SERVICE_PATH + serviceClassName; + String service = ServiceProviderBuildItem.SPI_ROOT + serviceClassName; Set implementations = ServiceUtil.classNamesNamedIn(Thread.currentThread().getContextClassLoader(), service); consumer.accept(serviceClassName, implementations); @@ -228,78 +231,6 @@ private void consumeService(String serviceClassName, BiConsumer services, - BuildProducer reflective, - BuildProducer resource) { - - var dependencies = curateOutcome.getApplicationModel().getDependencies(); - var liquibaseDependency = dependencies.stream() - .filter(d -> LIQUIBASE_GROUP_ID.equals(d.getGroupId()) - && LIQUIBASE_ARTIFACT_ID.equals(d.getArtifactId())) - .findFirst() - .orElseThrow(() -> new IllegalStateException("Liquibase dependency not found")); - - var tree = liquibaseDependency.getContentTree(); - loadLiquibaseRootProperties(tree, resource); - loadLiquibaseXsdResources(tree, resource); - loadLiquibaseServiceProviderConfig(tree, services, reflective); - } - - private List getResourceNames(PathTree pathTree, String basePath, String fileExtension, boolean stripPath) { - return pathTree.apply(basePath, visit -> { - try (var pathStream = Files.list(visit.getPath())) { - return pathStream - .map(p -> stripPath ? p.getFileName() : p.subpath(0, p.getNameCount())) - .map(Path::toString) - .filter(s -> s.endsWith(fileExtension)) - .toList(); - } catch (IOException ex) { - throw new IllegalStateException(ex); - } - }); - } - - private void loadLiquibaseRootProperties( - PathTree tree, - BuildProducer resource) { - - var rootProperties = getResourceNames( - tree, - LIQUIBASE_PROPERTIES_PATH, - ".properties", - true); - resource.produce(new NativeImageResourceBuildItem(rootProperties)); - } - - private void loadLiquibaseXsdResources( - PathTree tree, - BuildProducer resource) { - - var xsdResources = getResourceNames( - tree, - LIQUIBASE_DB_CHANGELOG_XSD_PATH, - ".xsd", - false); - resource.produce(new NativeImageResourceBuildItem(xsdResources)); - } - - private void loadLiquibaseServiceProviderConfig( - PathTree tree, - BuildProducer services, - BuildProducer reflective) { - - getResourceNames(tree, LIQUIBASE_SERVICE_PATH, "", true) - .forEach(t -> consumeService(t, (serviceClassName, implementations) -> { - services.produce(new ServiceProviderBuildItem( - serviceClassName, - implementations.toArray(String[]::new))); - reflective.produce(ReflectiveClassBuildItem.builder(implementations.toArray(String[]::new)) - .constructors().methods().build()); - })); - } - @BuildStep @Record(ExecutionTime.RUNTIME_INIT) void createBeans(LiquibaseRecorder recorder, diff --git a/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/graal/SubstituteStringUtil.java b/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/graal/SubstituteStringUtil.java deleted file mode 100644 index d4f28a00e8464..0000000000000 --- a/extensions/liquibase/runtime/src/main/java/io/quarkus/liquibase/runtime/graal/SubstituteStringUtil.java +++ /dev/null @@ -1,37 +0,0 @@ -package io.quarkus.liquibase.runtime.graal; - -import java.security.SecureRandom; - -import com.oracle.svm.core.annotate.Alias; -import com.oracle.svm.core.annotate.InjectAccessors; -import com.oracle.svm.core.annotate.TargetClass; - -@TargetClass(className = "liquibase.util.StringUtil") -final class SubstituteStringUtil { - - @Alias - @InjectAccessors(SecureRandomAccessors.class) - private static SecureRandom rnd; - - public static final class SecureRandomAccessors { - - private static volatile SecureRandom volatileRandom; - - public static SecureRandom get() { - SecureRandom localVolatileRandom = volatileRandom; - if (localVolatileRandom == null) { - synchronized (SecureRandomAccessors.class) { - localVolatileRandom = volatileRandom; - if (localVolatileRandom == null) { - volatileRandom = localVolatileRandom = new SecureRandom(); - } - } - } - return localVolatileRandom; - } - - public static void set(SecureRandom rnd) { - throw new IllegalStateException("The setter for liquibase.util.StringUtil#rnd shouldn't be called."); - } - } -}