All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.monosoul.jooq.GenerateJooqClassesTask.kt Maven / Gradle / Ivy

There is a newer version: 6.1.14
Show newest version
package dev.monosoul.jooq

import dev.monosoul.jooq.codegen.ConfigurationProvider
import dev.monosoul.jooq.codegen.ConfigurationProvider.Companion.postProcess
import dev.monosoul.jooq.codegen.UniversalJooqCodegenRunner
import dev.monosoul.jooq.migration.MigrationLocation
import dev.monosoul.jooq.migration.SchemaVersion
import dev.monosoul.jooq.migration.UniversalMigrationRunner
import dev.monosoul.jooq.settings.DatabaseCredentials
import dev.monosoul.jooq.settings.JooqDockerPluginSettings
import dev.monosoul.jooq.settings.JooqDockerPluginSettings.WithContainer
import dev.monosoul.jooq.settings.JooqDockerPluginSettings.WithoutContainer
import dev.monosoul.jooq.settings.SettingsAware
import dev.monosoul.jooq.util.CodegenClasspathAwareClassLoaders
import dev.monosoul.jooq.util.callWith
import dev.monosoul.jooq.util.copy
import dev.monosoul.jooq.util.getCodegenLogging
import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.gradle.api.file.FileSystemOperations
import org.gradle.api.file.ProjectLayout
import org.gradle.api.file.RegularFile
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.provider.ProviderFactory
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Classpath
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Nested
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.gradle.kotlin.dsl.getByType
import org.gradle.kotlin.dsl.listProperty
import org.gradle.kotlin.dsl.mapProperty
import org.gradle.kotlin.dsl.property
import org.gradle.kotlin.dsl.setProperty
import org.jooq.meta.jaxb.Configuration
import org.jooq.meta.jaxb.Generator
import javax.inject.Inject
import org.gradle.api.artifacts.Configuration as GradleConfiguration

@CacheableTask
open class GenerateJooqClassesTask
    @Inject
    constructor(
        objectFactory: ObjectFactory,
        private val providerFactory: ProviderFactory,
        private val fsOperations: FileSystemOperations,
        private val projectLayout: ProjectLayout,
    ) : DefaultTask(), SettingsAware {
        /**
         * List of schemas to take into account when running migrations and generating code.
         */
        @Input
        val schemas = objectFactory.listProperty().convention(listOf("public"))

        /**
         * Base package for generated classes.
         */
        @Input
        val basePackageName = objectFactory.property().convention("org.jooq.generated")

        /**
         * Flyway configuration.
         */
        @Input
        val flywayProperties = objectFactory.mapProperty().convention(emptyMap())

        /**
         * List of schemas to not generate schema information for (generate classes as for default schema).
         */
        @Input
        val outputSchemaToDefault = objectFactory.setProperty().convention(emptySet())

        /**
         * Map of schema name to specific package name.
         */
        @Input
        val schemaToPackageMapping = objectFactory.mapProperty().convention(emptyMap())

        /**
         * Include Flyway migration history table to generated code.
         */
        @Input
        val includeFlywayTable = objectFactory.property().convention(false)

        private val _generatorConfig =
            objectFactory.property>().convention(
                providerFactory.provider {
                    configurationProvider.defaultConfiguration()
                }.map(::PrivateValueHolder),
            )

        /**
         * Use [usingJavaConfig] or [usingXmlConfig] to provide configuration.
         */
        @get:Input
        @Suppress("unused")
        val generatorConfig: Property> get() = _generatorConfig

        /**
         * Location of Flyway migrations to use for code generation.
         *
         * Can be:
         * - [MigrationLocation.Filesystem]:
         *    - directory with SQL migrations
         * - [MigrationLocation.Classpath]:
         *    - directory with Java-based migrations (compiled classes)
         *    - directory with JAR files having Java-based or SQL migrations
         *    - path to a single JAR file having Java-based or SQL migrations
         *
         * Default: "src/main/resources/db/migration" directory of current project.
         *
         * @see MigrationLocation
         */
        @Nested
        val migrationLocations =
            objectFactory.listProperty().convention(
                listOf(
                    MigrationLocation.Filesystem(objectFactory.fileCollection().from("src/main/resources/db/migration")),
                ),
            )

        /**
         * Location of generated classes.
         */
        @OutputDirectory
        val outputDirectory =
            objectFactory.directoryProperty().convention(projectLayout.buildDirectory.dir("generated-jooq"))

        /**
         * Classpath for code generation. Derived from jooqCodegen configuration.
         */
        @Classpath
        val codegenClasspath =
            objectFactory.fileCollection().from(
                project.configurations.named(JooqDockerPlugin.CONFIGURATION_NAME),
            )

        private val localPluginSettings = objectFactory.property()

        private val globalPluginSettings = project.extensions.getByType().pluginSettings

        private val _pluginSettings: Provider>
            get() = localPluginSettings.orElse(globalPluginSettings).map(::PrivateValueHolder)

        /**
         * Use [withContainer] or [withoutContainer] to provide configuration.
         */
        @Nested
        @Suppress("unused")
        fun getPluginSettings(): Provider> = _pluginSettings

        private val migrationRunner = UniversalMigrationRunner(schemas, migrationLocations, flywayProperties)

        private val codegenRunner = UniversalJooqCodegenRunner()

        private val configurationProvider =
            ConfigurationProvider(
                basePackageName = basePackageName,
                outputSchemaToDefault = outputSchemaToDefault,
                schemaToPackageMapping = schemaToPackageMapping,
                schemas = schemas,
                logLevel = logger.getCodegenLogging(),
            )

        private fun classLoaders() = CodegenClasspathAwareClassLoaders.from(codegenClasspath)

        init {
            group = "jooq"
            description = "Generates jOOQ classes from Flyway migrations"
        }

        override fun withContainer(configure: Action) =
            localPluginSettings.set(
                globalPluginSettings.map { settings ->
                    settings.let { it as? WithContainer }?.copy()?.apply(configure::execute) ?: WithContainer(configure)
                },
            )

        override fun withoutContainer(configure: Action) =
            localPluginSettings.set(
                globalPluginSettings.map { settings ->
                    settings.let { it as? WithoutContainer }?.copy()?.apply(configure::execute) ?: WithoutContainer(configure)
                },
            )

        /**
         * Configure the jOOQ code generator with an XML configuration file.
         */
        @Suppress("unused")
        fun usingXmlConfig(
            file: RegularFile = projectLayout.projectDirectory.file("src/main/resources/db/jooq.xml"),
            customizer: Action = Action { },
        ) {
            _generatorConfig.set(
                configurationProvider.fromXml(providerFactory.fileContents(file)).map { config ->
                    config.also { customizer.execute(it.generator) }
                }.map(::PrivateValueHolder),
            )
        }

        /**
         * Configure the jOOQ code generator with an XML configuration file.
         */
        @Suppress("unused")
        fun usingXmlConfig(
            file: RegularFile,
            closure: Closure,
        ) = usingXmlConfig(file, closure::callWith)

        /**
         * Configure the jOOQ code generator programmatically with [Generator].
         */
        @Suppress("unused")
        fun usingJavaConfig(customizer: Action) {
            _generatorConfig.set(
                providerFactory.provider {
                    configurationProvider.defaultConfiguration().also {
                        customizer.execute(it.generator)
                    }
                }.map(::PrivateValueHolder),
            )
        }

        /**
         * Configure the jOOQ code generator programmatically with [Generator].
         */
        @Suppress("unused")
        fun usingJavaConfig(closure: Closure) = usingJavaConfig(closure::callWith)

        @TaskAction
        fun generateClasses() {
            _pluginSettings.get().value
                .runWithDatabaseCredentials(classLoaders()) { classLoaders, credentials ->
                    val schemaVersion = migrationRunner.migrateDb(classLoaders, credentials)
                    generateJooqClasses(classLoaders, credentials, schemaVersion)
                }
        }

        private fun generateJooqClasses(
            jdbcAwareClassLoader: CodegenClasspathAwareClassLoaders,
            credentials: DatabaseCredentials,
            schemaVersion: SchemaVersion,
        ) {
            fsOperations.delete {
                delete(outputDirectory)
            }
            codegenRunner.generateJooqClasses(
                codegenAwareClassLoader = jdbcAwareClassLoader,
                configuration =
                    _generatorConfig.get().value.copy().postProcess(
                        schemaVersion = schemaVersion,
                        credentials = credentials,
                        extraTableExclusions =
                            listOfNotNull(
                                migrationRunner.flywayTableName.takeUnless { includeFlywayTable.get() },
                            ),
                    ),
                outputDirectory = outputDirectory,
            )
        }

        /**
         * Set location of Flyway migrations to use for code generation
         *
         * @see migrationLocations
         * @see MigrationLocation
         */
        fun ListProperty.set(migrationLocation: MigrationLocation) = set(listOf(migrationLocation))

        /**
         * Set location of SQL migrations on the file system to use for code generation
         *
         * Example:
         *
         * ```
         * migrationLocations.setFromFilesystem(project.files("src/main/resources/db/migration"))
         * ```
         *
         * @see migrationLocations
         * @see MigrationLocation
         * @see Project.files
         */
        fun ListProperty.setFromFilesystem(files: FileCollection) =
            set(
                MigrationLocation.Filesystem(files),
            )

        /**
         * Set location of SQL migrations on the file system to use for code generation
         *
         * Example:
         *
         * ```
         * migrationLocations.setFromFilesystem("src/main/resources/db/migration")
         * ```
         *
         * @see migrationLocations
         * @see MigrationLocation
         */
        fun ListProperty.setFromFilesystem(path: String) = setFromFilesystem(project.files(path))

        /**
         * Add location of Java-based or SQL migrations to Flyway classpath from the specified path
         *
         * Example:
         *
         * ```
         * tasks.generateJooqClasses {
         *     migrationLocations.setFromClasspath(project.files("build/libs/some.jar"))
         * }
         * ```
         *
         * @see migrationLocations
         * @see MigrationLocation
         * @see Project.files
         * @see Project.project
         * @see FileCollection
         */
        fun ListProperty.setFromClasspath(
            path: FileCollection,
            location: String = "/db/migration",
        ) = set(MigrationLocation.Classpath(path, location))

        /**
         * Add location of Java-based or SQL migrations to Flyway classpath from the specified provider
         *
         * Examples:
         *
         * Using compiled Java-based migrations from a Gradle submodule:
         *
         * ```
         * tasks.generateJooqClasses {
         *    migrationLocations.setFromClasspath(
         *       project(":migrations").sourceSets.main.map { it.output }
         *    )
         * }
         * ```
         *
         * Similarly, if you use some extra dependencies in your migrations module, you can do this:
         *
         *
         * ```
         * tasks.generateJooqClasses {
         *    migrationLocations.setFromClasspath(
         *       // notice extra + it.runtimeClasspath
         *       project(":migrations").sourceSets.main.map { it.output + it.runtimeClasspath }
         *    )
         * }
         * ```
         *
         * @see migrationLocations
         * @see MigrationLocation
         * @see Project.project
         */
        fun  ListProperty.setFromClasspath(
            pathProvider: Provider,
            location: String = "/db/migration",
        ) = setFromClasspath(project.files(pathProvider), location)

        /**
         * Add location of Java-based or SQL migrations to Flyway classpath from a configuration
         *
         * Example:
         *
         * ```
         * val migrationClasspath by configurations.creating
         *
         * dependencies {
         *    migrationClasspath("third.party:some.artifact:some.version")
         * }
         *
         * tasks.generateJooqClasses {
         *     migrationLocations.setFromClasspath(migrationClasspath)
         * }
         * ```
         *
         * @see migrationLocations
         * @see MigrationLocation
         * @see Project.getConfigurations
         * @see GradleConfiguration
         */
        fun ListProperty.setFromClasspath(
            configuration: GradleConfiguration,
            location: String = "/db/migration",
        ) = setFromClasspath(project.files(configuration), location)
    }

private data class PrivateValueHolder(
    @get:Nested val value: T,
) : ValueHolder()




© 2015 - 2025 Weber Informatics LLC | Privacy Policy