From dd9df6dcd003ab8d1a06efe5d2e42a377750371f Mon Sep 17 00:00:00 2001 From: Duane May Date: Thu, 4 Jun 2026 14:05:45 -0400 Subject: [PATCH 1/5] Migrate to kotlin Gradle files --- build.gradle | 363 ------------------ build.gradle.kts | 300 +++++++++++++++ gradle/libs.versions.toml | 5 + metrics-data/build.gradle | 12 - metrics-data/build.gradle.kts | 32 ++ model/build.gradle | 54 --- model/build.gradle.kts | 69 ++++ model/build_properties.gradle | 50 --- model/build_properties.gradle.kts | 64 +++ server/{build.gradle => build.gradle.kts} | 99 +++-- settings.gradle | 14 - settings.gradle.kts | 14 + statsd-lib/{build.gradle => build.gradle.kts} | 24 +- statsd/build.gradle | 30 -- statsd/build.gradle.kts | 48 +++ uaa/build.gradle | 317 --------------- uaa/build.gradle.kts | 348 +++++++++++++++++ 17 files changed, 961 insertions(+), 882 deletions(-) delete mode 100644 build.gradle create mode 100644 build.gradle.kts delete mode 100644 metrics-data/build.gradle create mode 100644 metrics-data/build.gradle.kts delete mode 100644 model/build.gradle create mode 100644 model/build.gradle.kts delete mode 100644 model/build_properties.gradle create mode 100644 model/build_properties.gradle.kts rename server/{build.gradle => build.gradle.kts} (57%) delete mode 100644 settings.gradle create mode 100644 settings.gradle.kts rename statsd-lib/{build.gradle => build.gradle.kts} (53%) delete mode 100644 statsd/build.gradle create mode 100644 statsd/build.gradle.kts delete mode 100644 uaa/build.gradle create mode 100644 uaa/build.gradle.kts diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 130a953f426..00000000000 --- a/build.gradle +++ /dev/null @@ -1,363 +0,0 @@ -import org.apache.tools.ant.filters.ReplaceTokens - -import java.nio.file.Path -import java.nio.file.Paths - -buildscript { - repositories { - mavenCentral() - gradlePluginPortal() - } - - dependencies { - classpath(libs.springDependencyManagementGradlePlugin) - classpath(libs.springBootGradlePlugin) - classpath(libs.gradleJacocoPlugin) - classpath(libs.sonarqubePlugin) - } -} - -allprojects { - apply(plugin: "io.spring.dependency-management") - apply(plugin: "org.barfuin.gradle.jacocolog") - apply(plugin: "org.sonarqube") - - // Override Spring Boot managed dependency versions using ext properties - // See: https://docs.spring.io/spring-boot/appendix/dependency-versions/properties.html - ext['tomcat.version'] = libs.versions.tomcat.get() - ext['commons-codec.version'] = libs.versions.commonsCodec.get() - ext['selenium.version'] = libs.versions.selenium.get() - - dependencyManagement { - imports { - mavenBom(libs.springBootBom.get().toString()) - } - - // Libraries that have version conflicts (not managed by BOMs above) - dependencies { - // Guava - multiple versions from different transitive deps - dependency "com.google.guava:guava:${libs.versions.guava.get()}" - } - } - - repositories { - mavenCentral() - maven { - url = uri("https://build.shibboleth.net/nexus/content/repositories/releases/") - } - maven { url = uri("https://repository.mulesoft.org/releases/") } - } -} - - -subprojects { - /** Log each test PASSED/SKIPPED/FAILED unless disabled (-PverboseTests=false or UAA_VERBOSE_TESTS=0/off). */ - def uaaVerboseTestRaw = (findProperty('verboseTests') ?: System.getenv('UAA_VERBOSE_TESTS') ?: 'true') - .toString().trim().toLowerCase() - def uaaVerboseTestEvents = !['false', '0', 'off', 'no'].contains(uaaVerboseTestRaw) - - apply(plugin: "java") - java { - sourceCompatibility = JavaVersion.VERSION_25 - targetCompatibility = JavaVersion.VERSION_25 - } - - configurations.configureEach { - exclude(group: "org.hamcrest", module: "hamcrest-all") - exclude(group: "org.hamcrest", module: "hamcrest-core") - exclude(group: "org.hamcrest", module: "hamcrest-library") - exclude(group: "org.springframework.boot", module: "spring-boot-starter-logging") - exclude(group: "org.apache.directory.server", module: "apacheds-core") - exclude(group: "org.apache.directory.server", module: "apacheds-protocol-ldap") - exclude(group: "org.skyscreamer", module: "jsonassert") - exclude(group: "com.vaadin.external.google", module: "android-json") - exclude(group: "com.unboundid.components", module: "json") - - // Exclude opensaml-security-api and non-FIPS bouncycastle libs, and use Shadow library for FIPS compliance - exclude(group: "org.bouncycastle", module: "bcpkix-jdk15on") - exclude(group: "org.bouncycastle", module: "bcprov-jdk15on") - exclude(group: "org.bouncycastle", module: "bcutil-jdk15on") - exclude(group: "org.bouncycastle", module: "bcprov-jdk18on") - exclude(group: "org.bouncycastle", module: "bcpkix-jdk18on") - exclude(group: "org.bouncycastle", module: "bcutil-jdk18on") - - resolutionStrategy { - resolutionStrategy.eachDependency { DependencyResolveDetails details -> - // Dependencies not managed by any BOM - // OpenSAML modules - align for migration - if (details.requested.group == 'org.opensaml' && details.requested.name.startsWith("opensaml-")) { - details.useVersion "${libs.versions.opensaml.get()}" - details.because 'Pinning all opensaml modules to the same version for OpenSAML 5 migration.' - } - // Cryptacular - avoid regressions, explicit downgrade to 1.2.6 - else if (details.requested.group == 'org.cryptacular' && details.requested.name == 'cryptacular') { - details.useVersion "${libs.versions.cryptacular.get()}" - details.because 'Pinning cryptacular to 1.2.6 to avoid regressions from newer OpenSAML versions.' - } - } - } - } - - dependencies { - testImplementation(libs.springBootStarterTest) { - exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' - } - testImplementation(libs.hamcrest) - testImplementation(libs.junit5JupiterApi) - testImplementation(libs.junit5JupiterParams) - testImplementation(libs.junit5JupiterEngine) - testImplementation(libs.unboundIdLdapSdk) - testRuntimeOnly(libs.jacocoAgent) - - // Ensure test runtime dependencies are included - testRuntimeOnly(libs.junit5PlatformLauncher) - - compileOnly(libs.lombok) - annotationProcessor(libs.lombok) - } - - [compileJava, compileTestJava]*.options*.compilerArgs = ["-Xlint:none", "-nowarn", "-parameters"] - - test { - // when failFast = true AND retry is on, there is a serious issue: - // gradle might stop the test run due to the failFast but still concludes with BUILD SUCCESSFUL (if the retry is successful) - failFast = false - useJUnitPlatform() - // Increased to 1536m for Spring Boot 4 compatibility - jvmArgs += ["-Xmx1536m", - "-Xms512m", - "-XX:MaxMetaspaceSize=512m", - "-XX:+UseG1GC", - "-XX:+StartAttachListener", - "-XX:+HeapDumpOnOutOfMemoryError", - "-XX:HeapDumpPath=/var/log/uaa-tests.hprof" - ] - - // Enable test timing to identify slow tests - // This helps debug CI performance issues - reports { - junitXml.required = true - html.required = true - } - - testLogging { - if (uaaVerboseTestEvents) { - events("passed", "skipped", "failed") - } else { - events("failed") - } - exceptionFormat = "full" - // Set showStandardStreams=true to see all standard output from tests (there's a ton of it!) - showStandardStreams = false - showCauses = true - showExceptions = true - showStackTraces = true - } - - // Add system property to enable detailed test timing - systemProperty("junit.jupiter.execution.timeout.default", System.getProperty("junit.jupiter.execution.timeout.default", "0")) - - // Log slow tests to help identify bottlenecks - afterTest { descriptor, result -> - def duration = result.endTime - result.startTime - if (duration > 10000) { // 10 seconds - logger.warn("SLOW TEST: ${descriptor.className}.${descriptor.name} took ${duration}ms") - } - } - - // Log summary of test execution times - afterSuite { descriptor, result -> - if (descriptor.parent == null) { - def duration = result.endTime - result.startTime - def testLabel = result.testCount == 1 ? "unit-test" : "unit-tests" - logger.lifecycle("[${project.name}] Completed ${result.testCount} ${testLabel} in ${duration}ms (passed=${result.successfulTestCount};failed=${result.failedTestCount};skipped=${result.skippedTestCount})") - } - } - } - - tasks.register('integrationTest', Test) { - dependsOn subprojects.integrationTest - - useJUnitPlatform() - - // This prevents integrationTests from hanging indefinitely - timeout = Duration.ofMinutes(180) - - // Integration test workers need same memory as unit tests - // Actual CI configuration is controlled via integration_tests.sh script - jvmArgs += ["-Xmx640m", - "-XX:+StartAttachListener", - "-XX:+HeapDumpOnOutOfMemoryError", - "-XX:HeapDumpPath=/var/log/uaa-tests.hprof" - ] - - // Enable JaCoCo for integration tests - jacoco { - enabled = true - } - - testLogging { - if (uaaVerboseTestEvents) { - events("passed", "skipped", "failed") - } else { - events("failed") - } - exceptionFormat = "full" - // Set showStandardStreams=true to see all standard output from tests (there's a ton of it!) - showStandardStreams = false - showCauses = true - showExceptions = true - showStackTraces = true - } - - // Add system property to enable detailed test timing - systemProperty("junit.jupiter.execution.timeout.default", System.getProperty("junit.jupiter.execution.timeout.default", "0")) - - // Log slow tests to help identify bottlenecks - afterTest { descriptor, result -> - def duration = result.endTime - result.startTime - if (duration > 20000) { // 20 seconds - logger.warn("SLOW TEST: ${descriptor.className}.${descriptor.name} took ${duration}ms") - } - } - - // Log summary of test execution times - afterSuite { descriptor, result -> - if (descriptor.parent == null) { - def duration = result.endTime - result.startTime - def testLabel = result.testCount == 1 ? "integration-test" : "integration-tests" - logger.lifecycle("[${project.name}] Completed ${result.testCount} ${testLabel} in ${duration}ms (passed=${result.successfulTestCount};failed=${result.failedTestCount};skipped=${result.skippedTestCount})") - } - } - } - - tasks.register('generateDocs') {} - - tasks.register('allDeps', DependencyReportTask) {} - - tasks.register('writeNewPom') { - doLast { - pom { - project { - licenses { - license { - name("The Apache Software License, Version 2.0") - url("http://www.apache.org/licenses/LICENSE-2.0.txt") - distribution("repo") - } - } - } - }.writeTo("./pom.xml") - } - } - - repositories { - mavenCentral() - maven { - url = uri("https://repo.maven.apache.org/maven2") - } - } -} - -sonarqube { - properties { - property "sonar.projectKey", "cloudfoundry-identity-parent" - property "sonar.organization", "cloudfoundry-1" - property "sonar.host.url", "https://sonarcloud.io" - property "sonar.exclusions", "samples/**/*.*,**/*Test.java,**/*Tests.java,**/*IT.java,**/*SecurityConfiguration.java" - property "sonar.java.source", JavaVersion.current() - } -} - -project.gradle.taskGraph.whenReady { TaskExecutionGraph graph -> - project.allprojects.collect({ it.tasks.withType(Test) }).flatten().each { - it.systemProperty("spring.profiles.active", System.getProperty("spring.profiles.active", "hsqldb")) - it.systemProperty("testId", System.getProperty("testId", "")) - it.systemProperty("zones.paths.enabled", System.getProperty("zones.paths.enabled", "true")) - } -} - -tasks.register('manifests', Copy) { - dependsOn subprojects.assemble - from("uaa/src/test/resources/sample-manifests") { - include("**/*.yml") - filter(ReplaceTokens, - tokens: [ - version : version, - app : System.getProperty("app", "myuaa"), - appdomain: System.getProperty("app-domain", "bosh-lite.com"), - ] - ) - } - into("build/sample-manifests") -} - -tasks.register('cleanBootTomcatDir') { - doLast { - String tomcatBase = file("scripts/boot/tomcat/").getAbsolutePath() - delete(Path.of(tomcatBase)) - Paths.get(tomcatBase, "work").toFile().mkdirs() - Paths.get(tomcatBase, "webapps").toFile().mkdirs() - } -} - -tasks.register('bootWarRun', JavaExec) { - dependsOn cleanBootTomcatDir - dependsOn subprojects.assemble - classpath = files(file("uaa/build/libs/cloudfoundry-identity-uaa-0.0.0.war")) - systemProperty("server.tomcat.basedir", file("scripts/boot/tomcat/").getAbsolutePath()) - systemProperty("SECRETS_DIR", System.getProperty("SECRETS_DIR", file("scripts/boot").getAbsolutePath())) - systemProperty("spring.profiles.active", System.getProperty("spring.profiles.active", "hsqldb")) - systemProperty("metrics.perRequestMetrics", System.getProperty("metrics.perRequestMetrics", "true")) - systemProperty("smtp.host", "localhost") - systemProperty("smtp.port", 2525) - systemProperty("java.security.egd", "file:/dev/./urandom") - systemProperty("CLOUDFOUNDRY_CONFIG_PATH", file("scripts/boot").getAbsolutePath()) - systemProperty("server.servlet.context-path", "/uaa") - systemProperty("statsd.enabled", "true") - systemProperty("logging.config", file("scripts/boot/log4j2.properties").getAbsolutePath()) - systemProperty("zones.paths.enabled", System.getProperty("zones.paths.enabled", "true")) -} - -tasks.register('killUaa', Exec) { - workingDir './' - executable = 'scripts/kill_uaa.sh' -} - -/** - * Writes the resolved tomcat-embed-core version (from the Spring Boot BOM) to - * {@code build/tomcat-distribution.version} for scripts that download a matching - * Apache Tomcat binary distribution (standalone integration tests). - */ -tasks.register('writeTomcatDistributionVersion') { - group = 'build' - description = 'Writes build/tomcat-distribution.version from tomcat-embed-core on cloudfoundry-identity-uaa runtimeClasspath' - def uaaProject = project(':cloudfoundry-identity-uaa') - def outFile = layout.buildDirectory.file('tomcat-distribution.version') - outputs.file(outFile) - doLast { - def cfg = uaaProject.configurations.named('runtimeClasspath').get() - def artifact = cfg.resolvedConfiguration.resolvedArtifacts.find { it.name == 'tomcat-embed-core' } - if (artifact == null) { - throw new GradleException('Could not resolve tomcat-embed-core version from :cloudfoundry-identity-uaa runtimeClasspath') - } - outFile.get().asFile.parentFile.mkdirs() - outFile.get().asFile.text = artifact.moduleVersion.id.version - } -} - -tasks.register('printTomcatDistributionVersion') { - group = 'help' - description = 'Prints the Apache Tomcat version matching Spring Boot embedded tomcat-embed-core' - dependsOn('writeTomcatDistributionVersion') - doLast { - println file("${layout.buildDirectory.get().asFile}/tomcat-distribution.version").text.trim() - } -} - -tasks.register('run') { - dependsOn killUaa - dependsOn ':cloudfoundry-identity-uaa:bootRun' - description = "Kills any running UAA and starts the application. Use -Pdebug to enable debug mode (starts immediately) or -Pdebugs to enable debug mode with suspend (waits for debugger). Default port 5005, configurable with -PdebugPort=" - group = "application" -} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000000..2d446d23940 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,300 @@ +import org.apache.tools.ant.filters.ReplaceTokens +import java.nio.file.Path +import java.nio.file.Paths + +plugins { + id("io.spring.dependency-management") version "1.1.7" apply false + id("org.springframework.boot") version "4.0.6" apply false + id("org.barfuin.gradle.jacocolog") version "4.0.2" apply false + id("org.sonarqube") version "7.3.1.8318" apply false +} + +allprojects { + apply(plugin = "io.spring.dependency-management") + apply(plugin = "org.barfuin.gradle.jacocolog") + apply(plugin = "org.sonarqube") + + repositories { + mavenCentral() + maven { + url = uri("https://build.shibboleth.net/nexus/content/repositories/releases/") + } + maven { + url = uri("https://repository.mulesoft.org/releases/") + } + } +} + +subprojects { + /** Log each test PASSED/SKIPPED/FAILED unless disabled (-PverboseTests=false or UAA_VERBOSE_TESTS=0/off). */ + val uaaVerboseTestRaw = (findProperty("verboseTests") ?: System.getenv("UAA_VERBOSE_TESTS") ?: "true") + .toString().trim().lowercase() + val uaaVerboseTestEvents = !listOf("false", "0", "off", "no").contains(uaaVerboseTestRaw) + + apply(plugin = "java") + + configure { + sourceCompatibility = JavaVersion.VERSION_25 + targetCompatibility = JavaVersion.VERSION_25 + } + + // Use afterEvaluate to access version catalog after project evaluation + afterEvaluate { + // Override Spring Boot managed dependency versions using ext properties + // See: https://docs.spring.io/spring-boot/appendix/dependency-versions/properties.html + extra["tomcat.version"] = libs.versions.tomcat.get() + extra["commons-codec.version"] = libs.versions.commonsCodec.get() + extra["selenium.version"] = libs.versions.selenium.get() + + configure { + imports { + mavenBom(libs.springBootBom.get().toString()) + } + + // Libraries that have version conflicts (not managed by BOMs above) + dependencies { + // Guava - multiple versions from different transitive deps + dependency("com.google.guava:guava:${libs.versions.guava.get()}") + } + } + } + + configurations.configureEach { + exclude(group = "org.hamcrest", module = "hamcrest-all") + exclude(group = "org.hamcrest", module = "hamcrest-core") + exclude(group = "org.hamcrest", module = "hamcrest-library") + exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging") + exclude(group = "org.apache.directory.server", module = "apacheds-core") + exclude(group = "org.apache.directory.server", module = "apacheds-protocol-ldap") + exclude(group = "org.skyscreamer", module = "jsonassert") + exclude(group = "com.vaadin.external.google", module = "android-json") + exclude(group = "com.unboundid.components", module = "json") + + // Exclude opensaml-security-api and non-FIPS bouncycastle libs, and use Shadow library for FIPS compliance + exclude(group = "org.bouncycastle", module = "bcpkix-jdk15on") + exclude(group = "org.bouncycastle", module = "bcprov-jdk15on") + exclude(group = "org.bouncycastle", module = "bcutil-jdk15on") + exclude(group = "org.bouncycastle", module = "bcprov-jdk18on") + exclude(group = "org.bouncycastle", module = "bcpkix-jdk18on") + exclude(group = "org.bouncycastle", module = "bcutil-jdk18on") + + resolutionStrategy { + eachDependency { + // Dependencies not managed by any BOM + // OpenSAML modules - align for migration + if (requested.group == "org.opensaml" && requested.name.startsWith("opensaml-")) { + useVersion("5.2.2") + because("Pinning all opensaml modules to the same version for OpenSAML 5 migration.") + } + // Cryptacular - avoid regressions, explicit downgrade to 1.2.6 + else if (requested.group == "org.cryptacular" && requested.name == "cryptacular") { + useVersion("1.2.6") + because("Pinning cryptacular to 1.2.6 to avoid regressions from newer OpenSAML versions.") + } + } + } + } + + tasks.withType().configureEach { + options.compilerArgs.addAll(listOf("-Xlint:none", "-nowarn", "-parameters")) + } + + tasks.named("test") { + // when failFast = true AND retry is on, there is a serious issue: + // gradle might stop the test run due to the failFast but still concludes with BUILD SUCCESSFUL (if the retry is successful) + failFast = false + useJUnitPlatform() + // Increased to 1536m for Spring Boot 4 compatibility + jvmArgs( + "-Xmx1536m", + "-Xms512m", + "-XX:MaxMetaspaceSize=512m", + "-XX:+UseG1GC", + "-XX:+StartAttachListener", + "-XX:+HeapDumpOnOutOfMemoryError", + "-XX:HeapDumpPath=/var/log/uaa-tests.hprof" + ) + + // Enable test timing to identify slow tests + // This helps debug CI performance issues + reports { + junitXml.required.set(true) + html.required.set(true) + } + + testLogging { + if (uaaVerboseTestEvents) { + events("passed", "skipped", "failed") + } else { + events("failed") + } + exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL + // Set showStandardStreams=true to see all standard output from tests (there's a ton of it!) + showStandardStreams = false + showCauses = true + showExceptions = true + showStackTraces = true + } + + // Add system property to enable detailed test timing + systemProperty("junit.jupiter.execution.timeout.default", System.getProperty("junit.jupiter.execution.timeout.default", "0")) + + // Log slow tests to help identify bottlenecks + addTestListener(object : TestListener { + override fun beforeSuite(suite: org.gradle.api.tasks.testing.TestDescriptor) {} + override fun beforeTest(testDescriptor: org.gradle.api.tasks.testing.TestDescriptor) {} + override fun afterTest(testDescriptor: org.gradle.api.tasks.testing.TestDescriptor, result: org.gradle.api.tasks.testing.TestResult) { + val duration = result.endTime - result.startTime + if (duration > 10000) { // 10 seconds + logger.warn("SLOW TEST: ${testDescriptor.className}.${testDescriptor.name} took ${duration}ms") + } + } + override fun afterSuite(suite: org.gradle.api.tasks.testing.TestDescriptor, result: org.gradle.api.tasks.testing.TestResult) { + if (suite.parent == null) { + val duration = result.endTime - result.startTime + val testLabel = if (result.testCount == 1L) "unit-test" else "unit-tests" + logger.lifecycle("[${project.name}] Completed ${result.testCount} $testLabel in ${duration}ms (passed=${result.successfulTestCount};failed=${result.failedTestCount};skipped=${result.skippedTestCount})") + } + } + }) + } + + tasks.register("allDeps") {} + + tasks.register("writeNewPom") { + doLast { + // This task is deprecated - just create a simple POM + file("./pom.xml").writeText(""" + + 4.0.0 + org.cloudfoundry.identity + cloudfoundry-identity-parent + ${version} + pom + CloudFoundry Identity Parent + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + +""") + } + } + + repositories { + mavenCentral() + maven { + url = uri("https://repo.maven.apache.org/maven2") + } + } +} + +configure { + properties { + property("sonar.projectKey", "cloudfoundry-identity-parent") + property("sonar.organization", "cloudfoundry-1") + property("sonar.host.url", "https://sonarcloud.io") + property("sonar.exclusions", "samples/**/*.*,**/*Test.java,**/*Tests.java,**/*IT.java,**/*SecurityConfiguration.java") + property("sonar.java.source", JavaVersion.current()) + } +} + +gradle.taskGraph.whenReady { + allprojects.forEach { proj -> + proj.tasks.withType().forEach { testTask -> + testTask.systemProperty("spring.profiles.active", System.getProperty("spring.profiles.active", "hsqldb")) + testTask.systemProperty("testId", System.getProperty("testId", "")) + testTask.systemProperty("zones.paths.enabled", System.getProperty("zones.paths.enabled", "true")) + } + } +} + +tasks.register("manifests") { + dependsOn(subprojects.map { it.tasks.named("assemble") }) + from("uaa/src/test/resources/sample-manifests") { + include("**/*.yml") + filter( + "tokens" to mapOf( + "version" to version, + "app" to System.getProperty("app", "myuaa"), + "appdomain" to System.getProperty("app-domain", "bosh-lite.com") + ) + ) + } + into("build/sample-manifests") +} + +tasks.register("cleanBootTomcatDir") { + doLast { + val tomcatBase = file("scripts/boot/tomcat/").absolutePath + delete(Path.of(tomcatBase)) + Paths.get(tomcatBase, "work").toFile().mkdirs() + Paths.get(tomcatBase, "webapps").toFile().mkdirs() + } +} + +tasks.register("bootWarRun") { + dependsOn("cleanBootTomcatDir") + dependsOn(subprojects.map { it.tasks.named("assemble") }) + classpath(files(file("uaa/build/libs/cloudfoundry-identity-uaa-0.0.0.war"))) + systemProperty("server.tomcat.basedir", file("scripts/boot/tomcat/").absolutePath) + systemProperty("SECRETS_DIR", System.getProperty("SECRETS_DIR", file("scripts/boot").absolutePath)) + systemProperty("spring.profiles.active", System.getProperty("spring.profiles.active", "hsqldb")) + systemProperty("metrics.perRequestMetrics", System.getProperty("metrics.perRequestMetrics", "true")) + systemProperty("smtp.host", "localhost") + systemProperty("smtp.port", 2525) + systemProperty("java.security.egd", "file:/dev/./urandom") + systemProperty("CLOUDFOUNDRY_CONFIG_PATH", file("scripts/boot").absolutePath) + systemProperty("server.servlet.context-path", "/uaa") + systemProperty("statsd.enabled", "true") + systemProperty("logging.config", file("scripts/boot/log4j2.properties").absolutePath) + systemProperty("zones.paths.enabled", System.getProperty("zones.paths.enabled", "true")) +} + +tasks.register("killUaa") { + workingDir("./") + executable = "scripts/kill_uaa.sh" +} + +/** + * Writes the resolved tomcat-embed-core version (from the Spring Boot BOM) to + * {@code build/tomcat-distribution.version} for scripts that download a matching + * Apache Tomcat binary distribution (standalone integration tests). + */ +tasks.register("writeTomcatDistributionVersion") { + group = "build" + description = "Writes build/tomcat-distribution.version from tomcat-embed-core on cloudfoundry-identity-uaa runtimeClasspath" + val uaaProject = project(":cloudfoundry-identity-uaa") + val outFile = layout.buildDirectory.file("tomcat-distribution.version") + outputs.file(outFile) + doLast { + val cfg = uaaProject.configurations.named("runtimeClasspath").get() + val artifact = cfg.resolvedConfiguration.resolvedArtifacts.find { it.name == "tomcat-embed-core" } + if (artifact == null) { + throw GradleException("Could not resolve tomcat-embed-core version from :cloudfoundry-identity-uaa runtimeClasspath") + } + outFile.get().asFile.parentFile.mkdirs() + outFile.get().asFile.writeText(artifact.moduleVersion.id.version) + } +} + +tasks.register("printTomcatDistributionVersion") { + group = "help" + description = "Prints the Apache Tomcat version matching Spring Boot embedded tomcat-embed-core" + dependsOn("writeTomcatDistributionVersion") + doLast { + println(file("${layout.buildDirectory.get().asFile}/tomcat-distribution.version").readText().trim()) + } +} + +tasks.register("run") { + dependsOn("killUaa") + dependsOn(":cloudfoundry-identity-uaa:bootRun") + description = "Kills any running UAA and starts the application. Use -Pdebug to enable debug mode (starts immediately) or -Pdebugs to enable debug mode with suspend (waits for debugger). Default port 5005, configurable with -PdebugPort=" + group = "application" +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6395aa31523..d573ee0921e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,6 +15,7 @@ greenmail = "2.1.8" guava = "33.6.0-jre" jacoco = "4.0.2" jacocoAgent = "0.8.14" +jacocoGradlePlugin = "4.0.2" nimbusJwt = "10.9.1" opensaml = "5.2.2" orgJson = "20260522" @@ -225,3 +226,7 @@ sonarqubePlugin = { module = "org.sonarsource.scanner.gradle:sonarqube-gradle-pl springBootGradlePlugin = { module = "org.springframework.boot:spring-boot-gradle-plugin", version.ref = "springBoot" } springDependencyManagementGradlePlugin = { module = "io.spring.gradle:dependency-management-plugin", version.ref = "springDependencyManagement" } +[plugins] +springBoot = { id = "org.springframework.boot", version.ref = "springBoot" } +jacocoLog = { id = "org.barfuin.gradle.jacocolog", version.ref = "jacocoGradlePlugin" } +sonarqube = { id = "org.sonarqube", version.ref = "sonarqube" } diff --git a/metrics-data/build.gradle b/metrics-data/build.gradle deleted file mode 100644 index 364ea1d0573..00000000000 --- a/metrics-data/build.gradle +++ /dev/null @@ -1,12 +0,0 @@ -description = "CloudFoundry Identity Metrics Data Jar" - -dependencies { - implementation(libs.jacksonDatabind) - implementation(libs.jacksonAnnotations) -} - -processResources { - //maven replaces project.artifactId in the log4j2.properties file - //https://www.pivotaltracker.com/story/show/74344574 - filter { line -> line.contains('${project.artifactId}') ? line.replace('${project.artifactId}', 'cloudfoundry-identity-metrics-data') : line } -} diff --git a/metrics-data/build.gradle.kts b/metrics-data/build.gradle.kts new file mode 100644 index 00000000000..ad69878dbcc --- /dev/null +++ b/metrics-data/build.gradle.kts @@ -0,0 +1,32 @@ +description = "CloudFoundry Identity Metrics Data Jar" + +dependencies { + implementation(libs.jacksonDatabind) + implementation(libs.jacksonAnnotations) + + testImplementation(libs.springBootStarterTest) { + exclude(group = "org.junit.vintage", module = "junit-vintage-engine") + } + testImplementation(libs.hamcrest) + testImplementation(libs.junit5JupiterApi) + testImplementation(libs.junit5JupiterParams) + testImplementation(libs.junit5JupiterEngine) + testImplementation(libs.unboundIdLdapSdk) + testRuntimeOnly(libs.jacocoAgent) + testRuntimeOnly(libs.junit5PlatformLauncher) + + compileOnly(libs.lombok) + annotationProcessor(libs.lombok) +} + +tasks.named("processResources") { + //maven replaces project.artifactId in the log4j2.properties file + //https://www.pivotaltracker.com/story/show/74344574 + filter { line: String -> + if (line.contains("\${project.artifactId}")) { + line.replace("\${project.artifactId}", "cloudfoundry-identity-metrics-data") + } else { + line + } + } +} \ No newline at end of file diff --git a/model/build.gradle b/model/build.gradle deleted file mode 100644 index 66cea10014e..00000000000 --- a/model/build.gradle +++ /dev/null @@ -1,54 +0,0 @@ -description = "CloudFoundry Identity Model JAR" - -dependencies { - implementation(project(":cloudfoundry-identity-metrics-data")) - - implementation(libs.jacksonDatabind) - implementation(libs.jacksonAnnotations) - - implementation(libs.jakartaValidationApi) - - implementation(libs.commonsCodec) - implementation(libs.commonsIo) - - implementation(libs.springWeb) - implementation(libs.springWebMvc) - implementation(libs.springSecurityConfig) - - implementation(libs.nimbusJwt) - - implementation(libs.slf4jApi) - - testImplementation(libs.springBootStarterTest) - testImplementation(libs.hamcrest) - testImplementation(libs.junit5JupiterApi) - testImplementation(libs.junit5JupiterParams) - - testImplementation(libs.jsonAssert) -} - -configurations { - testArtifacts -} - -tasks.register('testJar', Jar) { - dependsOn(tasks.testClasses) - archiveClassifier = 'tests' - from(sourceSets.test.output) -} - -artifacts { - testArtifacts(testJar) -} - -apply(from: file("build_properties.gradle")) - -processResources { - //maven replaces project.artifactId in the log4j2.properties file - //https://www.pivotaltracker.com/story/show/74344574 - filter { line -> line.contains('${project.artifactId}') ? line.replace('${project.artifactId}', 'cloudfoundry-identity-model') : line } -} - -integrationTest {}.onlyIf { //disable since we don't have any - false -} diff --git a/model/build.gradle.kts b/model/build.gradle.kts new file mode 100644 index 00000000000..4d67ff260fa --- /dev/null +++ b/model/build.gradle.kts @@ -0,0 +1,69 @@ +description = "CloudFoundry Identity Model JAR" + +dependencies { + implementation(project(":cloudfoundry-identity-metrics-data")) + + implementation(libs.jacksonDatabind) + implementation(libs.jacksonAnnotations) + + implementation(libs.jakartaValidationApi) + + implementation(libs.commonsCodec) + implementation(libs.commonsIo) + + implementation(libs.springWeb) + implementation(libs.springWebMvc) + implementation(libs.springSecurityConfig) + + implementation(libs.nimbusJwt) + + implementation(libs.slf4jApi) + + testImplementation(libs.springBootStarterTest) { + exclude(group = "org.junit.vintage", module = "junit-vintage-engine") + } + testImplementation(libs.hamcrest) + testImplementation(libs.junit5JupiterApi) + testImplementation(libs.junit5JupiterParams) + testImplementation(libs.junit5JupiterEngine) + testImplementation(libs.unboundIdLdapSdk) + testRuntimeOnly(libs.jacocoAgent) + testRuntimeOnly(libs.junit5PlatformLauncher) + + testImplementation(libs.jsonAssert) + + compileOnly(libs.lombok) + annotationProcessor(libs.lombok) +} + +val testArtifacts by configurations.creating + +val testJar by tasks.registering(Jar::class) { + dependsOn(tasks.testClasses) + archiveClassifier.set("tests") + from(sourceSets.test.get().output) +} + +artifacts { + add("testArtifacts", testJar) +} + +apply(from = file("build_properties.gradle.kts")) + +tasks.named("processResources") { + //maven replaces project.artifactId in the log4j2.properties file + //https://www.pivotaltracker.com/story/show/74344574 + filter { line: String -> + if (line.contains("\${project.artifactId}")) { + line.replace("\${project.artifactId}", "cloudfoundry-identity-model") + } else { + line + } + } +} + +tasks.register("integrationTest") { + onlyIf { //disable since we don't have any + false + } +} \ No newline at end of file diff --git a/model/build_properties.gradle b/model/build_properties.gradle deleted file mode 100644 index cc4bab99374..00000000000 --- a/model/build_properties.gradle +++ /dev/null @@ -1,50 +0,0 @@ -import org.eclipse.jgit.storage.file.FileRepositoryBuilder -import org.eclipse.jgit.revwalk.RevWalk -import java.text.SimpleDateFormat - -buildscript { - repositories { - mavenCentral() - } - - dependencies { - classpath(libs.eclipseJgit) - } -} - -task mainOutputResourcesDir { - sourceSets.main.output.resourcesDir.mkdirs() -} - -task gitInfo { - dependsOn mainOutputResourcesDir - - def props = new Properties() - try { - def builder = new FileRepositoryBuilder() - builder.readEnvironment() - if (builder.getGitDir() == null) { - builder.findGitDir(projectDir) - } - def repository = builder.build() - def objectId = repository.resolve("HEAD") - def commit = new RevWalk(repository).parseCommit(objectId) - props.setProperty("git.commit.id.abbrev", commit.getId().getName().substring(0, 7)) - props.setProperty("git.commit.time", new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(commit.getAuthorIdent().getWhen())) - } catch (e) { - logger.warn "Exception raised while reading git metadata: $e" - props.setProperty("git.commit.id.abbrev", "git-metadata-not-found") - props.setProperty("git.commit.time", new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(new java.util.Date())) - } - props.store(new FileOutputStream("${sourceSets.main.resources.srcDirs.find()}/git.properties"), "DO NOT EDIT. This is generated file.") -} - -task buildInfo { - dependsOn(mainOutputResourcesDir) - - def props = new Properties() - props.setProperty("build.version", version) - props.store(new FileOutputStream("${sourceSets.main.resources.srcDirs.find()}/build.properties"), "DO NOT EDIT. This is generated file.") -} - -processResources.dependsOn(gitInfo, buildInfo) diff --git a/model/build_properties.gradle.kts b/model/build_properties.gradle.kts new file mode 100644 index 00000000000..83aa74d0916 --- /dev/null +++ b/model/build_properties.gradle.kts @@ -0,0 +1,64 @@ +import org.eclipse.jgit.storage.file.FileRepositoryBuilder +import org.eclipse.jgit.revwalk.RevWalk +import java.text.SimpleDateFormat +import java.util.Properties +import java.io.FileOutputStream + +buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath(libs.eclipseJgit) + } +} + +tasks.register("mainOutputResourcesDir") { + doLast { + val mainSourceSet = project.extensions.getByType().named("main").get() + mainSourceSet.output.resourcesDir?.mkdirs() + } +} + +tasks.register("gitInfo") { + dependsOn("mainOutputResourcesDir") + + doLast { + val props = Properties() + try { + val builder = FileRepositoryBuilder() + builder.readEnvironment() + if (builder.gitDir == null) { + builder.findGitDir(projectDir) + } + val repository = builder.build() + val objectId = repository.resolve("HEAD") + val commit = RevWalk(repository).parseCommit(objectId) + props.setProperty("git.commit.id.abbrev", commit.id.name.substring(0, 7)) + @Suppress("DEPRECATION") + props.setProperty("git.commit.time", SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(commit.authorIdent.getWhen())) + } catch (e: Exception) { + logger.warn("Exception raised while reading git metadata: $e") + props.setProperty("git.commit.id.abbrev", "git-metadata-not-found") + props.setProperty("git.commit.time", SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").format(java.util.Date())) + } + val mainSourceSet = project.extensions.getByType().named("main").get() + props.store(FileOutputStream("${mainSourceSet.resources.srcDirs.first()}/git.properties"), "DO NOT EDIT. This is generated file.") + } +} + +tasks.register("buildInfo") { + dependsOn("mainOutputResourcesDir") + + doLast { + val props = Properties() + props.setProperty("build.version", version.toString()) + val mainSourceSet = project.extensions.getByType().named("main").get() + props.store(FileOutputStream("${mainSourceSet.resources.srcDirs.first()}/build.properties"), "DO NOT EDIT. This is generated file.") + } +} + +tasks.named("processResources") { + dependsOn("gitInfo", "buildInfo") +} \ No newline at end of file diff --git a/server/build.gradle b/server/build.gradle.kts similarity index 57% rename from server/build.gradle rename to server/build.gradle.kts index 0b8de4e3660..65a1c395d6a 100644 --- a/server/build.gradle +++ b/server/build.gradle.kts @@ -1,4 +1,6 @@ -apply(plugin: "war") +plugins { + war +} description = "CloudFoundry Identity Server JAR" @@ -7,11 +9,11 @@ dependencies { implementation(project(":cloudfoundry-identity-model")) implementation(libs.tomcatJdbc) - providedCompile(libs.tomcatEmbed) + compileOnly(libs.tomcatEmbed) implementation(libs.jacksonDatabind) implementation(libs.jsonPath) { - exclude(module: "json-smart") + exclude(module = "json-smart") } implementation(libs.springBeans) implementation(libs.springContext) @@ -46,24 +48,24 @@ dependencies { implementation(libs.aspectJWeaver) implementation(libs.thymeLeaf) { - exclude(module: "ognl") + exclude(module = "ognl") } implementation(libs.thymeleafSpring) { - exclude(module: "ognl") + exclude(module = "ognl") } implementation(libs.thymeleafDialect) { - exclude(module: "ognl") + exclude(module = "ognl") } implementation(libs.thymeleafExtrasSpringSecurity) { - exclude(module: "ognl") + exclude(module = "ognl") } implementation(libs.unboundIdScimSdk) { - exclude(module: "servlet-api") - exclude(module: "commons-logging") - exclude(module: "httpclient") - exclude(module: "wink-client-apache-httpclient") - exclude(module: "json") + exclude(module = "servlet-api") + exclude(module = "commons-logging") + exclude(module = "httpclient") + exclude(module = "wink-client-apache-httpclient") + exclude(module = "json") } implementation(libs.hibernateValidator) @@ -80,8 +82,8 @@ dependencies { implementation(libs.springSecurityLdap) implementation(libs.springLdapCore) implementation(libs.apacheLdapApi) { - exclude(module: "slf4j-api") - exclude(module: "mina-core") + exclude(module = "slf4j-api") + exclude(module = "mina-core") } implementation(libs.passay) @@ -94,10 +96,20 @@ dependencies { implementation(libs.apacheHttpClient) - testImplementation(project(path: ":cloudfoundry-identity-model", configuration: "testArtifacts")) + testImplementation(project(mapOf("path" to ":cloudfoundry-identity-model", "configuration" to "testArtifacts"))) - testImplementation(libs.springTest) + testImplementation(libs.springBootStarterTest) { + exclude(group = "org.junit.vintage", module = "junit-vintage-engine") + } + testImplementation(libs.hamcrest) + testImplementation(libs.junit5JupiterApi) + testImplementation(libs.junit5JupiterParams) + testImplementation(libs.junit5JupiterEngine) + testImplementation(libs.unboundIdLdapSdk) + testRuntimeOnly(libs.jacocoAgent) + testRuntimeOnly(libs.junit5PlatformLauncher) + testImplementation(libs.springTest) testImplementation(libs.bytebuddy) testImplementation(libs.bytebuddyagent) testImplementation(libs.mockitoJunit5) @@ -114,44 +126,55 @@ dependencies { testImplementation(libs.awaitility) implementation(libs.commonsIo) + + compileOnly(libs.lombok) + annotationProcessor(libs.lombok) } -configurations.all { - exclude(group: "org.beanshell", module: "bsh-core") - exclude(group: "org.apache-extras.beanshell", module: "bsh") - exclude(group: "com.fasterxml.woodstox", module: "woodstox-core") - exclude(group: "commons-beanutils", module: "commons-beanutils") - exclude(group: "commons-collections", module: "commons-collections") +configurations.configureEach { + exclude(group = "org.beanshell", module = "bsh-core") + exclude(group = "org.apache-extras.beanshell", module = "bsh") + exclude(group = "com.fasterxml.woodstox", module = "woodstox-core") + exclude(group = "commons-beanutils", module = "commons-beanutils") + exclude(group = "commons-collections", module = "commons-collections") // Exclude non-FIPS bouncycastle libs, and use Shadow library for FIPS compliance - exclude(group: "org.bouncycastle", module: "bcpkix-jdk15on") - exclude(group: "org.bouncycastle", module: "bcprov-jdk15on") - exclude(group: "org.bouncycastle", module: "bcutil-jdk15on") - exclude(group: "org.bouncycastle", module: "bcprov-jdk18on") - exclude(group: "org.bouncycastle", module: "bcpkix-jdk18on") - exclude(group: "org.bouncycastle", module: "bcutil-jdk18on") + exclude(group = "org.bouncycastle", module = "bcpkix-jdk15on") + exclude(group = "org.bouncycastle", module = "bcprov-jdk15on") + exclude(group = "org.bouncycastle", module = "bcutil-jdk15on") + exclude(group = "org.bouncycastle", module = "bcprov-jdk18on") + exclude(group = "org.bouncycastle", module = "bcpkix-jdk18on") + exclude(group = "org.bouncycastle", module = "bcutil-jdk18on") } -jar { +tasks.named("jar") { exclude("org/cloudfoundry/identity/uaa/web/tomcat/UaaStartupFailureListener.*") } -processResources { +tasks.named("processResources") { //maven replaces project.artifactId in the log4j2.properties file //https://www.pivotaltracker.com/story/show/74344574 - filter { line -> line.contains('${project.artifactId}') ? line.replace('${project.artifactId}', 'cloudfoundry-identity-server') : line } + filter { line: String -> + if (line.contains("\${project.artifactId}")) { + line.replace("\${project.artifactId}", "cloudfoundry-identity-server") + } else { + line + } + } } -integrationTest {}.onlyIf { //disable since we don't have any - false +tasks.register("integrationTest") { + onlyIf { //disable since we don't have any + false + } } -task tomcatListenerJar(type: Jar) { - archiveBaseName = "tomcat-listener" - from(sourceSets.main.output) +val tomcatListenerJar by tasks.registering(Jar::class) { + archiveBaseName.set("tomcat-listener") + from(sourceSets.main.get().output) include("org/cloudfoundry/identity/uaa/web/tomcat/UaaStartupFailureListener.*") } artifacts { - archives(tomcatListenerJar) -} + add("archives", tomcatListenerJar) +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 3cf307af1c9..00000000000 --- a/settings.gradle +++ /dev/null @@ -1,14 +0,0 @@ -rootProject.name = "cloudfoundry-identity-parent" -include(":cloudfoundry-identity-metrics-data") -include(":cloudfoundry-identity-model") -include(":cloudfoundry-identity-server") -include(":cloudfoundry-identity-statsd") -include(":cloudfoundry-identity-statsd-lib") -include(":cloudfoundry-identity-uaa") - -project(":cloudfoundry-identity-metrics-data").projectDir = "$rootDir/metrics-data" as File -project(":cloudfoundry-identity-model").projectDir = "$rootDir/model" as File -project(":cloudfoundry-identity-server").projectDir = "$rootDir/server" as File -project(":cloudfoundry-identity-uaa").projectDir = "$rootDir/uaa" as File -project(":cloudfoundry-identity-statsd").projectDir = "$rootDir/statsd" as File -project(":cloudfoundry-identity-statsd-lib").projectDir = "$rootDir/statsd-lib" as File diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000000..6e422e11586 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,14 @@ +rootProject.name = "cloudfoundry-identity-parent" +include(":cloudfoundry-identity-metrics-data") +include(":cloudfoundry-identity-model") +include(":cloudfoundry-identity-server") +include(":cloudfoundry-identity-statsd") +include(":cloudfoundry-identity-statsd-lib") +include(":cloudfoundry-identity-uaa") + +project(":cloudfoundry-identity-metrics-data").projectDir = file("metrics-data") +project(":cloudfoundry-identity-model").projectDir = file("model") +project(":cloudfoundry-identity-server").projectDir = file("server") +project(":cloudfoundry-identity-uaa").projectDir = file("uaa") +project(":cloudfoundry-identity-statsd").projectDir = file("statsd") +project(":cloudfoundry-identity-statsd-lib").projectDir = file("statsd-lib") \ No newline at end of file diff --git a/statsd-lib/build.gradle b/statsd-lib/build.gradle.kts similarity index 53% rename from statsd-lib/build.gradle rename to statsd-lib/build.gradle.kts index ae7297217e9..edfb1cfc970 100644 --- a/statsd-lib/build.gradle +++ b/statsd-lib/build.gradle.kts @@ -1,4 +1,6 @@ -apply(plugin: "java") +plugins { + java +} repositories { mavenCentral() @@ -10,20 +12,34 @@ dependencies { implementation(libs.springBootStarterWeb) implementation(libs.springBootStarterLog4j2) implementation(libs.statsdClient) + testImplementation(libs.springBootStarterTest) { + exclude(group = "org.junit.vintage", module = "junit-vintage-engine") + } + testImplementation(libs.hamcrest) + testImplementation(libs.junit5JupiterApi) + testImplementation(libs.junit5JupiterParams) + testImplementation(libs.junit5JupiterEngine) + testImplementation(libs.unboundIdLdapSdk) + testRuntimeOnly(libs.jacocoAgent) + testRuntimeOnly(libs.junit5PlatformLauncher) + testImplementation(libs.mockitoJunit5) testImplementation(libs.bytebuddy) testImplementation(libs.bytebuddyagent) + + compileOnly(libs.lombok) + annotationProcessor(libs.lombok) implementation(libs.jacksonDatabind) } -test { +tasks.named("test") { exclude("org/cloudfoundry/identity/statsd/integration/*.class") exclude("**/*IT.class") } -integrationTest { +tasks.register("integrationTest") { filter { includeTestsMatching("org.cloudfoundry.identity.statsd.integration.*") includeTestsMatching("*IT") } -} +} \ No newline at end of file diff --git a/statsd/build.gradle b/statsd/build.gradle deleted file mode 100644 index 38f632b5063..00000000000 --- a/statsd/build.gradle +++ /dev/null @@ -1,30 +0,0 @@ -apply(plugin: "java") -apply(plugin: "war") -apply(plugin: "org.springframework.boot") - -war { - archiveBaseName = "cloudfoundry-identity-statsd" -} - -repositories { - mavenCentral() -} - -dependencies { - implementation(project(":cloudfoundry-identity-statsd-lib")) - implementation(libs.springBootStarterWeb) - providedRuntime(libs.springBootStarterTomcatRuntime) -} - -test { - exclude("org/cloudfoundry/identity/statsd/integration/*.class") - exclude("**/*IT.class") -} - -integrationTest {}.onlyIf { //disable since we don't have any - false -} - -tasks.named('bootRun') { - enabled = false -} diff --git a/statsd/build.gradle.kts b/statsd/build.gradle.kts new file mode 100644 index 00000000000..7a75e7664f2 --- /dev/null +++ b/statsd/build.gradle.kts @@ -0,0 +1,48 @@ +plugins { + java + war + id("org.springframework.boot") +} + +tasks.named("war") { + archiveBaseName.set("cloudfoundry-identity-statsd") +} + +repositories { + mavenCentral() +} + +dependencies { + implementation(project(":cloudfoundry-identity-statsd-lib")) + implementation(libs.springBootStarterWeb) + providedRuntime(libs.springBootStarterTomcatRuntime) + + testImplementation(libs.springBootStarterTest) { + exclude(group = "org.junit.vintage", module = "junit-vintage-engine") + } + testImplementation(libs.hamcrest) + testImplementation(libs.junit5JupiterApi) + testImplementation(libs.junit5JupiterParams) + testImplementation(libs.junit5JupiterEngine) + testImplementation(libs.unboundIdLdapSdk) + testRuntimeOnly(libs.jacocoAgent) + testRuntimeOnly(libs.junit5PlatformLauncher) + + compileOnly(libs.lombok) + annotationProcessor(libs.lombok) +} + +tasks.named("test") { + exclude("org/cloudfoundry/identity/statsd/integration/*.class") + exclude("**/*IT.class") +} + +tasks.register("integrationTest") { + onlyIf { //disable since we don't have any + false + } +} + +tasks.named("bootRun") { + enabled = false +} \ No newline at end of file diff --git a/uaa/build.gradle b/uaa/build.gradle deleted file mode 100644 index c7880ba424a..00000000000 --- a/uaa/build.gradle +++ /dev/null @@ -1,317 +0,0 @@ -Project identityServer = parent.subprojects.find { "cloudfoundry-identity-server".equals(it.name) } - -apply(plugin: "war") - -processResources { - //maven replaces project.artifactId in the log4j2.properties file - //https://www.pivotaltracker.com/story/show/74344574 - from(new File('../common/src/main/resources/log4j2.properties')) - filter { line -> line.contains('${project.artifactId}') ? line.replace('${project.artifactId}', 'cloudfoundry-identity-uaa') : line } -} - -apply(plugin: "org.springframework.boot") -bootWar { - archiveClassifier = '' - //archiveClassifier = 'boot' - enabled = true -} -jar { enabled = false } -war { - archiveClassifier = '' - enabled = false - //workaround for maven optional - rootSpec.exclude("**/spring-security-oauth-*.jar") -} - -repositories { - maven { url = uri("https://build.shibboleth.net/nexus/content/repositories/releases/") } -} - -description = "UAA" -dependencies { - implementation(project(":cloudfoundry-identity-server")) { - exclude(module: "jna") - } - implementation(project(":cloudfoundry-identity-statsd-lib")) - implementation(project(":cloudfoundry-identity-model")) - implementation(libs.springSecurityConfig) - implementation(libs.springSecurityWeb) - implementation(libs.springBootStarter) - implementation(libs.springBootStarterWeb) - implementation(libs.thymeLeaf) { - exclude(module: "ognl") - } - implementation(libs.thymeleafSpring) { - exclude(module: "ognl") - } - implementation(libs.thymeleafDialect) { - exclude(module: "ognl") - } - implementation(libs.thymeleafExtrasSpringSecurity) { - exclude(module: "ognl") - } - runtimeOnly(libs.aspectJWeaver) - runtimeOnly(libs.postgresql) - runtimeOnly(libs.mariaJdbcDriver) - - implementation(libs.braveInstrumentation) - implementation(libs.braveContextSlf4j) - - // OpenAPI documentation - implementation(libs.springDocOpenapi) - - implementation(libs.springWeb) - implementation(libs.springWebMvc) - - providedCompile(libs.tomcatEmbed) - implementation(libs.bouncyCastleFipsProv) - - testImplementation(identityServer.sourceSets.test.output) - - testImplementation(project(":cloudfoundry-identity-model")) - testImplementation(project(":cloudfoundry-identity-metrics-data")) - testImplementation(libs.apacheLdapApi) { - exclude(module: "slf4j-api") - } - testImplementation(libs.flywayCore) - testImplementation(libs.hibernateValidator) - testImplementation(libs.selenium) - testImplementation(libs.seleniumRemoteDriver) - testImplementation(libs.seleniumHttp) - testImplementation(libs.orgJson) - testImplementation(libs.jsonAssert) - testImplementation(libs.jsonPathAssert) - testImplementation(libs.unboundIdScimSdk) { - exclude(module: "servlet-api") - exclude(module: "commons-logging") - exclude(module: "httpclient") - exclude(module: "wink-client-apache-httpclient") - } - testImplementation(libs.springBootStarterLog4j2) - testImplementation(libs.springContextSupport) - testImplementation(libs.springSessionJdbc) - testImplementation(libs.springTest) - testImplementation(libs.springSecurityLdap) - testImplementation(libs.springSecurityTest) - testImplementation(libs.springBootStarterMail) - testImplementation(libs.passay) - testImplementation(libs.mockitoJunit5) - testImplementation(libs.bytebuddy) - testImplementation(libs.bytebuddyagent) - testImplementation(libs.tomcatJdbc) - testImplementation(libs.springRestdocs) - testImplementation(libs.greenmail) - testImplementation(libs.commonsIo) - testImplementation(libs.apacheHttpClient) - testImplementation(libs.openSamlApi) - testImplementation(libs.xmlUnit) - testImplementation(libs.awaitility) - testImplementation(libs.nimbusJwt) -} - -ext { - snippetsDir = file("build/generated-snippets") -} - -test { - exclude("org/cloudfoundry/identity/uaa/integration/*.class") - exclude("**/*IT.class") - exclude("**/*Docs.class") - systemProperty("mock.suite.test", "true") - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // Running tests in parallel - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - // Count available cores. We assume 2 logical cores per physical core. - // In case there is only one vCPU, we count 1 full core. - var availableCpus = Math.max(Runtime.getRuntime().availableProcessors() / 2, 1.0) - - // We want some amount of parallelism, but it does not make sense to run too many - // tests in parallel, see docs/testing. We target 4 tests in parallel at most. If - // there are less CPUs available, we use all available CPUs but no more. - maxParallelForks = Math.min(availableCpus, 4) -} - -tasks.register("populateVersionfile") { - def versionFile = new File("$projectDir/slateCustomizations/source/versionfile") - versionFile.createNewFile() - assert versionFile.exists() - versionFile.text = version.toString().substring(0, version.toString().lastIndexOf(".")) + ".0" -} -tasks.register( "customizeSlate", Copy) { - dependsOn(tasks.named('populateVersionfile')) - from("slate") - from("slateCustomizations") - into("build/slate") -} -tasks.register("docsTestRestDocs", Test) { - dependsOn(tasks.named('testClasses')) - useJUnitPlatform() - testClassesDirs = sourceSets.test.output.classesDirs - classpath = sourceSets.test.runtimeClasspath - include("**/*Docs.class") - systemProperty("docs.build.generated.snippets.dir", snippetsDir.getPath()) -} - -tasks.register("gemInstallBundle", Exec) { - dependsOn("customizeSlate") - workingDir(file("build/slate")) - executable("gem") - args("install", "bundler:2.7.1") -} - -tasks.register("bundleInstall", Exec) { - dependsOn(tasks.named('gemInstallBundle')) - dependsOn("customizeSlate") - workingDir(file("build/slate")) - executable("bundle") - args("install") -} - -tasks.register("deleteDefaultContent", Delete) { - delete("build/slate/source/index.html.md") -} - -tasks.register("slate", Exec) { - dependsOn("customizeSlate", "deleteDefaultContent", "bundleInstall", "docsTestRestDocs") - workingDir(file("build/slate")) - executable("bundle") - args("exec", "middleman", "build", "--verbose", "--build-dir=../docs/version/" + version.toString().substring(0, version.toString().lastIndexOf(".")) + ".0") -} - -generateDocs { - dependsOn(slate) -} - -// Gradle bootBuildImage task configuration -tasks.named('bootBuildImage') { - imageName = "cloudfoundry/uaa:${version}" - boolean doDebug = false - def activeSpringProfiles = (System.getProperty('spring.profiles.active', '')?.split(',') ?: []) as List - if (activeSpringProfiles.contains("debugs") || Boolean.valueOf(System.getProperty("xdebugs")) || - activeSpringProfiles.contains("debug") || Boolean.valueOf(System.getProperty("xdebug"))) { - doDebug = true - } - // https://github.com/paketo-buildpacks/bellsoft-liberica - // use JRE since JDK gets too big - environment.putAll([ - 'BP_SPRING_CLOUD_BINDINGS_DISABLED': 'true', - 'BP_JVM_VERSION' : '25', - 'BP_JVM_TYPE' : System.getenv('BP_JVM_TYPE') ?: project.findProperty('BP_JVM_TYPE') ?: 'JRE', - 'BP_JVM_PLATFORM' : 'liberica' - ]) - if (doDebug) { - environment.put("BPL_DEBUG_ENABLED", "true") - environment.put("BPL_DEBUG_PORT", "5005") - } -} - -integrationTest { - // Use the test source set and classpath - testClassesDirs = sourceSets.test.output.classesDirs - classpath = sourceSets.test.runtimeClasspath - - filter { - includeTestsMatching("org.cloudfoundry.identity.uaa.integration.*") - includeTestsMatching("*IT") - } - String samlUrlPropKey = "integration.test.saml.url" - String samlUrl = System.getProperty(samlUrlPropKey) - if (samlUrl?.trim()) { - systemProperty("integration.test.saml.url", samlUrl) - project.logger.warn("UAA - Overriding SAML Url:"+samlUrl) - } - - def useExternalIntegrationServer = { - def v = rootProject.findProperty('uaaIntegrationServer') ?: findProperty('uaaIntegrationServer') ?: System.getenv('UAA_INTEGRATION_SERVER') - v != null && v.toString().equalsIgnoreCase('external') - } - - doFirst { - if (useExternalIntegrationServer()) { - logger.lifecycle("UAA_INTEGRATION_SERVER=external (or -PuaaIntegrationServer=external): skipping embedded java -jar; tests expect UAA on port 8080") - return - } - logger.lifecycle("Killing UAA before auto-start") - rootProject.tasks.named('killUaa').get().exec() - - def bootPidFile = rootProject.file('build/boot.pid') - def bootLogFile = rootProject.file('build/boot.log') - def springProfile = System.getProperty("spring.profiles.active", "hsqldb") - def warFile = file("build/libs/cloudfoundry-identity-uaa-0.0.0.war") - def bootDir = rootProject.file("scripts/boot") - - logger.lifecycle("Starting UAA application for integration tests...") - - def javaCmd = """nohup java \\ - -DCLOUDFOUNDRY_CONFIG_PATH=${bootDir.absolutePath} \\ - -DSECRETS_DIR=${bootDir.absolutePath} \\ - -Dserver.servlet.context-path=/uaa \\ - -Dsmtp.host=localhost \\ - -Dsmtp.port=2525 \\ - -Dspring.profiles.active=${springProfile} \\ - -jar ${warFile.absolutePath} > ${bootLogFile.absolutePath} 2>&1 & echo \$!""" - - def proc = ['bash', '-c', javaCmd].execute() - proc.waitFor() - def pid = proc.text.trim() - bootPidFile.text = pid - - logger.lifecycle("UAA started with PID: ${pid}, waiting for it to be ready...") - - def maxWaitSeconds = 300 - def startTime = System.currentTimeMillis() - while (System.currentTimeMillis() - startTime < maxWaitSeconds * 1000) { - if (bootLogFile.exists() && bootLogFile.text.contains("Started UaaBootApplication")) { - logger.lifecycle("UAA is ready!") - return - } - Thread.sleep(1000) - } - - logger.lifecycle("Killing UAA after failed auto-start") - rootProject.tasks.named('killUaa').get().exec() - throw new GradleException("UAA failed to start within ${maxWaitSeconds} seconds. Check ${bootLogFile.absolutePath}") - } - - doLast { - if (useExternalIntegrationServer()) { - logger.lifecycle("Skipping embedded UAA shutdown; external Tomcat lifecycle is managed by the caller") - return - } - logger.lifecycle("Stopping UAA application...") - rootProject.tasks.named('killUaa').get().exec() - rootProject.file('build/boot.pid').delete() - } - - dependsOn bootWar -} - -bootRun { - dependsOn rootProject.cleanBootTomcatDir - mainClass = "org.cloudfoundry.experimental.boot.UaaBootApplication" - systemProperty("logging.level.org.springframework.security", "TRACE") - systemProperty("logging.config", file("../scripts/boot/log4j2.properties").getAbsolutePath()) - systemProperty("uaa.boot.location.tomcat", System.getProperty("uaa.boot.location.tomcat", file("../scripts/boot/tomcat").getAbsolutePath())) - systemProperty("spring.profiles.active", System.getProperty("spring.profiles.active", "hsqldb")) - systemProperty("metrics.perRequestMetrics", System.getProperty("metrics.perRequestMetrics", "true")) - systemProperty("smtp.host", "localhost") - systemProperty("smtp.port", 2525) - systemProperty("java.security.egd", "file:/dev/./urandom") - systemProperty("CLOUDFOUNDRY_CONFIG_PATH", file("../scripts/boot").getAbsolutePath()) - systemProperty("server.servlet.context-path", "/uaa") - systemProperty("statsd.enabled", "true") - - // Enable debug mode if -Pdebug or -Pdebugs flag is provided - if (project.hasProperty('debug') || project.hasProperty('debugs')) { - def debugPort = project.findProperty('debugPort') ?: '5005' - // debugs = suspend and wait for debugger, debug = start immediately - def suspend = project.hasProperty('debugs') ? 'y' : 'n' - jvmArgs += [ - "-agentlib:jdwp=transport=dt_socket,server=y,suspend=${suspend},address=*:${debugPort}" - ] - def mode = suspend == 'y' ? 'Suspended debug' : 'Debug' - logger.lifecycle("${mode} mode enabled. Listening on port ${debugPort}") - } -} \ No newline at end of file diff --git a/uaa/build.gradle.kts b/uaa/build.gradle.kts new file mode 100644 index 00000000000..603af508c66 --- /dev/null +++ b/uaa/build.gradle.kts @@ -0,0 +1,348 @@ +val identityServer = parent!!.subprojects.find { "cloudfoundry-identity-server" == it.name }!! + +plugins { + war + id("org.springframework.boot") +} + +tasks.named("processResources") { + //maven replaces project.artifactId in the log4j2.properties file + //https://www.pivotaltracker.com/story/show/74344574 + from(File("../common/src/main/resources/log4j2.properties")) + filter { line: String -> + if (line.contains("\${project.artifactId}")) { + line.replace("\${project.artifactId}", "cloudfoundry-identity-uaa") + } else { + line + } + } +} + +tasks.named("bootWar") { + archiveClassifier.set("") + //archiveClassifier = 'boot' + isEnabled = true +} + +tasks.named("jar") { + isEnabled = false +} + +tasks.named("war") { + archiveClassifier.set("") + isEnabled = false + //workaround for maven optional + rootSpec.exclude("**/spring-security-oauth-*.jar") +} + +repositories { + maven { url = uri("https://build.shibboleth.net/nexus/content/repositories/releases/") } +} + +description = "UAA" + +dependencies { + implementation(project(":cloudfoundry-identity-server")) { + exclude(module = "jna") + } + implementation(project(":cloudfoundry-identity-statsd-lib")) + implementation(project(":cloudfoundry-identity-model")) + implementation(libs.springSecurityConfig) + implementation(libs.springSecurityWeb) + implementation(libs.springBootStarter) + implementation(libs.springBootStarterWeb) + implementation(libs.thymeLeaf) { + exclude(module = "ognl") + } + implementation(libs.thymeleafSpring) { + exclude(module = "ognl") + } + implementation(libs.thymeleafDialect) { + exclude(module = "ognl") + } + implementation(libs.thymeleafExtrasSpringSecurity) { + exclude(module = "ognl") + } + runtimeOnly(libs.aspectJWeaver) + runtimeOnly(libs.postgresql) + runtimeOnly(libs.mariaJdbcDriver) + + implementation(libs.braveInstrumentation) + implementation(libs.braveContextSlf4j) + + // OpenAPI documentation + implementation(libs.springDocOpenapi) + + implementation(libs.springWeb) + implementation(libs.springWebMvc) + + compileOnly(libs.tomcatEmbed) + implementation(libs.bouncyCastleFipsProv) + + testImplementation(identityServer.sourceSets.test.get().output) + + testImplementation(libs.springBootStarterTest) { + exclude(group = "org.junit.vintage", module = "junit-vintage-engine") + } + testImplementation(libs.hamcrest) + testImplementation(libs.junit5JupiterApi) + testImplementation(libs.junit5JupiterParams) + testImplementation(libs.junit5JupiterEngine) + testImplementation(libs.unboundIdLdapSdk) + testRuntimeOnly(libs.jacocoAgent) + testRuntimeOnly(libs.junit5PlatformLauncher) + + testImplementation(project(":cloudfoundry-identity-model")) + testImplementation(project(":cloudfoundry-identity-metrics-data")) + testImplementation(libs.apacheLdapApi) { + exclude(module = "slf4j-api") + } + testImplementation(libs.flywayCore) + testImplementation(libs.hibernateValidator) + testImplementation(libs.selenium) + testImplementation(libs.seleniumRemoteDriver) + testImplementation(libs.seleniumHttp) + testImplementation(libs.orgJson) + testImplementation(libs.jsonAssert) + testImplementation(libs.jsonPathAssert) + testImplementation(libs.unboundIdScimSdk) { + exclude(module = "servlet-api") + exclude(module = "commons-logging") + exclude(module = "httpclient") + exclude(module = "wink-client-apache-httpclient") + } + testImplementation(libs.springBootStarterLog4j2) + testImplementation(libs.springContextSupport) + testImplementation(libs.springSessionJdbc) + testImplementation(libs.springTest) + testImplementation(libs.springSecurityLdap) + testImplementation(libs.springSecurityTest) + testImplementation(libs.springBootStarterMail) + testImplementation(libs.passay) + testImplementation(libs.mockitoJunit5) + testImplementation(libs.bytebuddy) + testImplementation(libs.bytebuddyagent) + testImplementation(libs.tomcatJdbc) + testImplementation(libs.springRestdocs) + testImplementation(libs.greenmail) + testImplementation(libs.commonsIo) + testImplementation(libs.apacheHttpClient) + testImplementation(libs.openSamlApi) + testImplementation(libs.xmlUnit) + testImplementation(libs.awaitility) + testImplementation(libs.nimbusJwt) + + compileOnly(libs.lombok) + annotationProcessor(libs.lombok) +} + +extra["snippetsDir"] = file("build/generated-snippets") + +tasks.named("test") { + exclude("org/cloudfoundry/identity/uaa/integration/*.class") + exclude("**/*IT.class") + exclude("**/*Docs.class") + systemProperty("mock.suite.test", "true") + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Running tests in parallel + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + // Count available cores. We assume 2 logical cores per physical core. + // In case there is only one vCPU, we count 1 full core. + val availableCpus = maxOf(Runtime.getRuntime().availableProcessors() / 2.0, 1.0) + + // We want some amount of parallelism, but it does not make sense to run too many + // tests in parallel, see docs/testing. We target 4 tests in parallel at most. If + // there are less CPUs available, we use all available CPUs but no more. + maxParallelForks = minOf(availableCpus, 4.0).toInt() +} + +tasks.register("populateVersionfile") { + doLast { + val versionFile = File("$projectDir/slateCustomizations/source/versionfile") + versionFile.createNewFile() + assert(versionFile.exists()) + versionFile.writeText(version.toString().substring(0, version.toString().lastIndexOf(".")) + ".0") + } +} + +tasks.register("customizeSlate") { + dependsOn(tasks.named("populateVersionfile")) + from("slate") + from("slateCustomizations") + into("build/slate") +} + +tasks.register("docsTestRestDocs") { + dependsOn(tasks.named("testClasses")) + useJUnitPlatform() + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + include("**/*Docs.class") + systemProperty("docs.build.generated.snippets.dir", file("build/generated-snippets").absolutePath) +} + +tasks.register("gemInstallBundle") { + dependsOn("customizeSlate") + workingDir(file("build/slate")) + executable = "gem" + args("install", "bundler:2.7.1") +} + +tasks.register("bundleInstall") { + dependsOn(tasks.named("gemInstallBundle")) + dependsOn("customizeSlate") + workingDir(file("build/slate")) + executable = "bundle" + args("install") +} + +tasks.register("deleteDefaultContent") { + delete("build/slate/source/index.html.md") +} + +tasks.register("slate") { + dependsOn("customizeSlate", "deleteDefaultContent", "bundleInstall", "docsTestRestDocs") + workingDir(file("build/slate")) + executable = "bundle" + args("exec", "middleman", "build", "--verbose", "--build-dir=../docs/version/" + version.toString().substring(0, version.toString().lastIndexOf(".")) + ".0") +} + +tasks.register("generateDocs") { + dependsOn("slate") +} + +// Gradle bootBuildImage task configuration +tasks.named("bootBuildImage") { + imageName.set("cloudfoundry/uaa:$version") + var doDebug = false + val activeSpringProfiles = (System.getProperty("spring.profiles.active", "")?.split(",") ?: emptyList()) + if (activeSpringProfiles.contains("debugs") || System.getProperty("xdebugs")?.toBoolean() == true || + activeSpringProfiles.contains("debug") || System.getProperty("xdebug")?.toBoolean() == true) { + doDebug = true + } + // https://github.com/paketo-buildpacks/bellsoft-liberica + // use JRE since JDK gets too big + environment.set(mapOf( + "BP_SPRING_CLOUD_BINDINGS_DISABLED" to "true", + "BP_JVM_VERSION" to "25", + "BP_JVM_TYPE" to (System.getenv("BP_JVM_TYPE") ?: project.findProperty("BP_JVM_TYPE") ?: "JRE").toString(), + "BP_JVM_PLATFORM" to "liberica" + )) + if (doDebug) { + environment.set(environment.get() + mapOf( + "BPL_DEBUG_ENABLED" to "true", + "BPL_DEBUG_PORT" to "5005" + )) + } +} + +tasks.register("integrationTest") { + // Use the test source set and classpath + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + + filter { + includeTestsMatching("org.cloudfoundry.identity.uaa.integration.*") + includeTestsMatching("*IT") + } + val samlUrlPropKey = "integration.test.saml.url" + val samlUrl = System.getProperty(samlUrlPropKey) + if (!samlUrl?.trim().isNullOrEmpty()) { + systemProperty("integration.test.saml.url", samlUrl) + project.logger.warn("UAA - Overriding SAML Url:$samlUrl") + } + + val useExternalIntegrationServer = { + val v = rootProject.findProperty("uaaIntegrationServer") ?: findProperty("uaaIntegrationServer") ?: System.getenv("UAA_INTEGRATION_SERVER") + v != null && v.toString().lowercase() == "external" + } + + doFirst { + if (useExternalIntegrationServer()) { + logger.lifecycle("UAA_INTEGRATION_SERVER=external (or -PuaaIntegrationServer=external): skipping embedded java -jar; tests expect UAA on port 8080") + return@doFirst + } + logger.lifecycle("Killing UAA before auto-start") + rootProject.tasks.named("killUaa").get().actions.forEach { it.execute(rootProject.tasks.named("killUaa").get()) } + + val bootPidFile = rootProject.file("build/boot.pid") + val bootLogFile = rootProject.file("build/boot.log") + val springProfile = System.getProperty("spring.profiles.active", "hsqldb") + val warFile = file("build/libs/cloudfoundry-identity-uaa-0.0.0.war") + val bootDir = rootProject.file("scripts/boot") + + logger.lifecycle("Starting UAA application for integration tests...") + + val javaCmd = """nohup java \ + -DCLOUDFOUNDRY_CONFIG_PATH=${bootDir.absolutePath} \ + -DSECRETS_DIR=${bootDir.absolutePath} \ + -Dserver.servlet.context-path=/uaa \ + -Dsmtp.host=localhost \ + -Dsmtp.port=2525 \ + -Dspring.profiles.active=$springProfile \ + -jar ${warFile.absolutePath} > ${bootLogFile.absolutePath} 2>&1 & echo ${'$'}!""" + + val proc = ProcessBuilder("bash", "-c", javaCmd).start() + proc.waitFor() + val pid = proc.inputStream.bufferedReader().readText().trim() + bootPidFile.writeText(pid) + + logger.lifecycle("UAA started with PID: $pid, waiting for it to be ready...") + + val maxWaitSeconds = 300 + val startTime = System.currentTimeMillis() + while (System.currentTimeMillis() - startTime < maxWaitSeconds * 1000) { + if (bootLogFile.exists() && bootLogFile.readText().contains("Started UaaBootApplication")) { + logger.lifecycle("UAA is ready!") + return@doFirst + } + Thread.sleep(1000) + } + + logger.lifecycle("Killing UAA after failed auto-start") + rootProject.tasks.named("killUaa").get().actions.forEach { it.execute(rootProject.tasks.named("killUaa").get()) } + throw GradleException("UAA failed to start within $maxWaitSeconds seconds. Check ${bootLogFile.absolutePath}") + } + + doLast { + if (useExternalIntegrationServer()) { + logger.lifecycle("Skipping embedded UAA shutdown; external Tomcat lifecycle is managed by the caller") + return@doLast + } + logger.lifecycle("Stopping UAA application...") + rootProject.tasks.named("killUaa").get().actions.forEach { it.execute(rootProject.tasks.named("killUaa").get()) } + rootProject.file("build/boot.pid").delete() + } + + dependsOn(tasks.named("bootWar")) +} + +tasks.named("bootRun") { + dependsOn(rootProject.tasks.named("cleanBootTomcatDir")) + mainClass.set("org.cloudfoundry.experimental.boot.UaaBootApplication") + systemProperty("logging.level.org.springframework.security", "TRACE") + systemProperty("logging.config", file("../scripts/boot/log4j2.properties").absolutePath) + systemProperty("uaa.boot.location.tomcat", System.getProperty("uaa.boot.location.tomcat", file("../scripts/boot/tomcat").absolutePath)) + systemProperty("spring.profiles.active", System.getProperty("spring.profiles.active", "hsqldb")) + systemProperty("metrics.perRequestMetrics", System.getProperty("metrics.perRequestMetrics", "true")) + systemProperty("smtp.host", "localhost") + systemProperty("smtp.port", 2525) + systemProperty("java.security.egd", "file:/dev/./urandom") + systemProperty("CLOUDFOUNDRY_CONFIG_PATH", file("../scripts/boot").absolutePath) + systemProperty("server.servlet.context-path", "/uaa") + systemProperty("statsd.enabled", "true") + + // Enable debug mode if -Pdebug or -Pdebugs flag is provided + if (project.hasProperty("debug") || project.hasProperty("debugs")) { + val debugPort = project.findProperty("debugPort") ?: "5005" + // debugs = suspend and wait for debugger, debug = start immediately + val suspend = if (project.hasProperty("debugs")) "y" else "n" + jvmArgs( + "-agentlib:jdwp=transport=dt_socket,server=y,suspend=$suspend,address=*:$debugPort" + ) + val mode = if (suspend == "y") "Suspended debug" else "Debug" + logger.lifecycle("$mode mode enabled. Listening on port $debugPort") + } +} \ No newline at end of file From ded8725d2b010ba48d81560a134151a3dd5f762e Mon Sep 17 00:00:00 2001 From: Duane May Date: Thu, 4 Jun 2026 14:44:44 -0400 Subject: [PATCH 2/5] Use plugins from version catalog --- build.gradle.kts | 23 ++++++++++------------- gradle/libs.versions.toml | 9 ++------- metrics-data/build.gradle.kts | 7 +++++++ model/build.gradle.kts | 6 ++++++ server/build.gradle.kts | 14 +++++++------- statsd-lib/build.gradle.kts | 13 ++++++++----- statsd/build.gradle.kts | 6 ++++-- uaa/build.gradle.kts | 23 ++++++++++++----------- 8 files changed, 56 insertions(+), 45 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 2d446d23940..4a135f87644 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,17 +3,13 @@ import java.nio.file.Path import java.nio.file.Paths plugins { - id("io.spring.dependency-management") version "1.1.7" apply false - id("org.springframework.boot") version "4.0.6" apply false - id("org.barfuin.gradle.jacocolog") version "4.0.2" apply false - id("org.sonarqube") version "7.3.1.8318" apply false + alias(libs.plugins.springDependencyManagement) apply false + alias(libs.plugins.springBoot) apply false + alias(libs.plugins.jacocoLog) apply false + alias(libs.plugins.sonarqube) // Applied to root for global configuration } allprojects { - apply(plugin = "io.spring.dependency-management") - apply(plugin = "org.barfuin.gradle.jacocolog") - apply(plugin = "org.sonarqube") - repositories { mavenCentral() maven { @@ -31,8 +27,9 @@ subprojects { .toString().trim().lowercase() val uaaVerboseTestEvents = !listOf("false", "0", "off", "no").contains(uaaVerboseTestRaw) + // Apply Java plugin with consistent configuration across all modules apply(plugin = "java") - + configure { sourceCompatibility = JavaVersion.VERSION_25 targetCompatibility = JavaVersion.VERSION_25 @@ -141,15 +138,15 @@ subprojects { // Log slow tests to help identify bottlenecks addTestListener(object : TestListener { - override fun beforeSuite(suite: org.gradle.api.tasks.testing.TestDescriptor) {} - override fun beforeTest(testDescriptor: org.gradle.api.tasks.testing.TestDescriptor) {} - override fun afterTest(testDescriptor: org.gradle.api.tasks.testing.TestDescriptor, result: org.gradle.api.tasks.testing.TestResult) { + override fun beforeSuite(suite: TestDescriptor) {} + override fun beforeTest(testDescriptor: TestDescriptor) {} + override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) { val duration = result.endTime - result.startTime if (duration > 10000) { // 10 seconds logger.warn("SLOW TEST: ${testDescriptor.className}.${testDescriptor.name} took ${duration}ms") } } - override fun afterSuite(suite: org.gradle.api.tasks.testing.TestDescriptor, result: org.gradle.api.tasks.testing.TestResult) { + override fun afterSuite(suite: TestDescriptor, result: TestResult) { if (suite.parent == null) { val duration = result.endTime - result.startTime val testLabel = if (result.testCount == 1L) "unit-test" else "unit-tests" diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d573ee0921e..b2bc396d43d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -220,13 +220,8 @@ xmlUnit = { module = "org.xmlunit:xmlunit-assertj" } braveContextSlf4j = { module = "io.zipkin.brave:brave-context-slf4j", version.ref = "brave" } braveInstrumentation = { module = "io.zipkin.brave:brave-instrumentation-servlet-jakarta", version.ref = "brave" } -# Gradle Plugin Classpaths (for buildscript, move to [plugins] later) -gradleJacocoPlugin = { module = "org.barfuin.gradle.jacocolog:gradle-jacoco-log", version.ref = "jacoco" } -sonarqubePlugin = { module = "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin", version.ref = "sonarqube" } -springBootGradlePlugin = { module = "org.springframework.boot:spring-boot-gradle-plugin", version.ref = "springBoot" } -springDependencyManagementGradlePlugin = { module = "io.spring.gradle:dependency-management-plugin", version.ref = "springDependencyManagement" } - [plugins] springBoot = { id = "org.springframework.boot", version.ref = "springBoot" } -jacocoLog = { id = "org.barfuin.gradle.jacocolog", version.ref = "jacocoGradlePlugin" } +springDependencyManagement = { id = "io.spring.dependency-management", version.ref = "springDependencyManagement" } +jacocoLog = { id = "org.barfuin.gradle.jacocolog", version.ref = "jacoco" } sonarqube = { id = "org.sonarqube", version.ref = "sonarqube" } diff --git a/metrics-data/build.gradle.kts b/metrics-data/build.gradle.kts index ad69878dbcc..09306bd5c41 100644 --- a/metrics-data/build.gradle.kts +++ b/metrics-data/build.gradle.kts @@ -1,3 +1,9 @@ +plugins { + alias(libs.plugins.springDependencyManagement) + alias(libs.plugins.jacocoLog) + alias(libs.plugins.sonarqube) +} + description = "CloudFoundry Identity Metrics Data Jar" dependencies { @@ -12,6 +18,7 @@ dependencies { testImplementation(libs.junit5JupiterParams) testImplementation(libs.junit5JupiterEngine) testImplementation(libs.unboundIdLdapSdk) + testRuntimeOnly(libs.jacocoAgent) testRuntimeOnly(libs.junit5PlatformLauncher) diff --git a/model/build.gradle.kts b/model/build.gradle.kts index 4d67ff260fa..0ce4cb0cdcf 100644 --- a/model/build.gradle.kts +++ b/model/build.gradle.kts @@ -1,3 +1,9 @@ +plugins { + alias(libs.plugins.springDependencyManagement) + alias(libs.plugins.jacocoLog) + alias(libs.plugins.sonarqube) +} + description = "CloudFoundry Identity Model JAR" dependencies { diff --git a/server/build.gradle.kts b/server/build.gradle.kts index 65a1c395d6a..5e13964e5a2 100644 --- a/server/build.gradle.kts +++ b/server/build.gradle.kts @@ -1,5 +1,8 @@ plugins { war + alias(libs.plugins.springDependencyManagement) + alias(libs.plugins.jacocoLog) + alias(libs.plugins.sonarqube) } description = "CloudFoundry Identity Server JAR" @@ -9,8 +12,6 @@ dependencies { implementation(project(":cloudfoundry-identity-model")) implementation(libs.tomcatJdbc) - compileOnly(libs.tomcatEmbed) - implementation(libs.jacksonDatabind) implementation(libs.jsonPath) { exclude(module = "json-smart") @@ -95,6 +96,7 @@ dependencies { implementation(libs.orgJson) implementation(libs.apacheHttpClient) + implementation(libs.commonsIo) testImplementation(project(mapOf("path" to ":cloudfoundry-identity-model", "configuration" to "testArtifacts"))) @@ -106,9 +108,6 @@ dependencies { testImplementation(libs.junit5JupiterParams) testImplementation(libs.junit5JupiterEngine) testImplementation(libs.unboundIdLdapSdk) - testRuntimeOnly(libs.jacocoAgent) - testRuntimeOnly(libs.junit5PlatformLauncher) - testImplementation(libs.springTest) testImplementation(libs.bytebuddy) testImplementation(libs.bytebuddyagent) @@ -119,14 +118,15 @@ dependencies { testImplementation(libs.tomcatElApi) testImplementation(libs.tomcatJasperEl) - testImplementation(libs.tomcatJdbc) testImplementation(libs.jsonPathAssert) testImplementation(libs.xmlUnit) testImplementation(libs.awaitility) - implementation(libs.commonsIo) + testRuntimeOnly(libs.jacocoAgent) + testRuntimeOnly(libs.junit5PlatformLauncher) + compileOnly(libs.tomcatEmbed) compileOnly(libs.lombok) annotationProcessor(libs.lombok) } diff --git a/statsd-lib/build.gradle.kts b/statsd-lib/build.gradle.kts index edfb1cfc970..cb4f60f9920 100644 --- a/statsd-lib/build.gradle.kts +++ b/statsd-lib/build.gradle.kts @@ -1,5 +1,7 @@ plugins { - java + alias(libs.plugins.springDependencyManagement) + alias(libs.plugins.jacocoLog) + alias(libs.plugins.sonarqube) } repositories { @@ -12,6 +14,8 @@ dependencies { implementation(libs.springBootStarterWeb) implementation(libs.springBootStarterLog4j2) implementation(libs.statsdClient) + implementation(libs.jacksonDatabind) + testImplementation(libs.springBootStarterTest) { exclude(group = "org.junit.vintage", module = "junit-vintage-engine") } @@ -20,16 +24,15 @@ dependencies { testImplementation(libs.junit5JupiterParams) testImplementation(libs.junit5JupiterEngine) testImplementation(libs.unboundIdLdapSdk) - testRuntimeOnly(libs.jacocoAgent) - testRuntimeOnly(libs.junit5PlatformLauncher) - testImplementation(libs.mockitoJunit5) testImplementation(libs.bytebuddy) testImplementation(libs.bytebuddyagent) + testRuntimeOnly(libs.jacocoAgent) + testRuntimeOnly(libs.junit5PlatformLauncher) + compileOnly(libs.lombok) annotationProcessor(libs.lombok) - implementation(libs.jacksonDatabind) } tasks.named("test") { diff --git a/statsd/build.gradle.kts b/statsd/build.gradle.kts index 7a75e7664f2..1563e5f6691 100644 --- a/statsd/build.gradle.kts +++ b/statsd/build.gradle.kts @@ -1,7 +1,9 @@ plugins { - java war - id("org.springframework.boot") + alias(libs.plugins.springBoot) + alias(libs.plugins.springDependencyManagement) + alias(libs.plugins.jacocoLog) + alias(libs.plugins.sonarqube) } tasks.named("war") { diff --git a/uaa/build.gradle.kts b/uaa/build.gradle.kts index 603af508c66..0eb10fc6ae6 100644 --- a/uaa/build.gradle.kts +++ b/uaa/build.gradle.kts @@ -2,7 +2,10 @@ val identityServer = parent!!.subprojects.find { "cloudfoundry-identity-server" plugins { war - id("org.springframework.boot") + alias(libs.plugins.springBoot) + alias(libs.plugins.springDependencyManagement) + alias(libs.plugins.jacocoLog) + alias(libs.plugins.sonarqube) } tasks.named("processResources") { @@ -63,10 +66,6 @@ dependencies { implementation(libs.thymeleafExtrasSpringSecurity) { exclude(module = "ognl") } - runtimeOnly(libs.aspectJWeaver) - runtimeOnly(libs.postgresql) - runtimeOnly(libs.mariaJdbcDriver) - implementation(libs.braveInstrumentation) implementation(libs.braveContextSlf4j) @@ -75,12 +74,13 @@ dependencies { implementation(libs.springWeb) implementation(libs.springWebMvc) - - compileOnly(libs.tomcatEmbed) implementation(libs.bouncyCastleFipsProv) - testImplementation(identityServer.sourceSets.test.get().output) + runtimeOnly(libs.aspectJWeaver) + runtimeOnly(libs.postgresql) + runtimeOnly(libs.mariaJdbcDriver) + testImplementation(identityServer.sourceSets.test.get().output) testImplementation(libs.springBootStarterTest) { exclude(group = "org.junit.vintage", module = "junit-vintage-engine") } @@ -89,9 +89,6 @@ dependencies { testImplementation(libs.junit5JupiterParams) testImplementation(libs.junit5JupiterEngine) testImplementation(libs.unboundIdLdapSdk) - testRuntimeOnly(libs.jacocoAgent) - testRuntimeOnly(libs.junit5PlatformLauncher) - testImplementation(project(":cloudfoundry-identity-model")) testImplementation(project(":cloudfoundry-identity-metrics-data")) testImplementation(libs.apacheLdapApi) { @@ -132,6 +129,10 @@ dependencies { testImplementation(libs.awaitility) testImplementation(libs.nimbusJwt) + testRuntimeOnly(libs.jacocoAgent) + testRuntimeOnly(libs.junit5PlatformLauncher) + + compileOnly(libs.tomcatEmbed) compileOnly(libs.lombok) annotationProcessor(libs.lombok) } From 4496a132e40a10ad95ee65e63f4721ce3fe5be96 Mon Sep 17 00:00:00 2001 From: Duane May Date: Thu, 4 Jun 2026 15:03:28 -0400 Subject: [PATCH 3/5] Refactor java plugin --- build.gradle.kts | 16 ++++++++-------- metrics-data/build.gradle.kts | 1 + model/build.gradle.kts | 1 + statsd-lib/build.gradle.kts | 1 + 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 4a135f87644..6f4f3d15801 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -27,12 +27,12 @@ subprojects { .toString().trim().lowercase() val uaaVerboseTestEvents = !listOf("false", "0", "off", "no").contains(uaaVerboseTestRaw) - // Apply Java plugin with consistent configuration across all modules - apply(plugin = "java") - - configure { - sourceCompatibility = JavaVersion.VERSION_25 - targetCompatibility = JavaVersion.VERSION_25 + // Configure Java version for all projects with Java plugin applied + plugins.withType { + configure { + sourceCompatibility = JavaVersion.VERSION_25 + targetCompatibility = JavaVersion.VERSION_25 + } } // Use afterEvaluate to access version catalog after project evaluation @@ -96,9 +96,9 @@ subprojects { options.compilerArgs.addAll(listOf("-Xlint:none", "-nowarn", "-parameters")) } - tasks.named("test") { + tasks.withType().configureEach { // when failFast = true AND retry is on, there is a serious issue: - // gradle might stop the test run due to the failFast but still concludes with BUILD SUCCESSFUL (if the retry is successful) + // Gradle might stop the test run due to the failFast but still concludes with BUILD SUCCESSFUL (if the retry is successful) failFast = false useJUnitPlatform() // Increased to 1536m for Spring Boot 4 compatibility diff --git a/metrics-data/build.gradle.kts b/metrics-data/build.gradle.kts index 09306bd5c41..9f4d01865ed 100644 --- a/metrics-data/build.gradle.kts +++ b/metrics-data/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + java alias(libs.plugins.springDependencyManagement) alias(libs.plugins.jacocoLog) alias(libs.plugins.sonarqube) diff --git a/model/build.gradle.kts b/model/build.gradle.kts index 0ce4cb0cdcf..45ecdcee4a3 100644 --- a/model/build.gradle.kts +++ b/model/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + java alias(libs.plugins.springDependencyManagement) alias(libs.plugins.jacocoLog) alias(libs.plugins.sonarqube) diff --git a/statsd-lib/build.gradle.kts b/statsd-lib/build.gradle.kts index cb4f60f9920..732b92cbb92 100644 --- a/statsd-lib/build.gradle.kts +++ b/statsd-lib/build.gradle.kts @@ -1,4 +1,5 @@ plugins { + java alias(libs.plugins.springDependencyManagement) alias(libs.plugins.jacocoLog) alias(libs.plugins.sonarqube) From de2335a0212484e1bddd7320caa570d6644a3620 Mon Sep 17 00:00:00 2001 From: Duane May Date: Thu, 4 Jun 2026 15:33:25 -0400 Subject: [PATCH 4/5] Use version catalog for OpenSAML and Cryptacular pins --- build.gradle.kts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 6f4f3d15801..5053fa45e62 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -80,13 +80,13 @@ subprojects { // Dependencies not managed by any BOM // OpenSAML modules - align for migration if (requested.group == "org.opensaml" && requested.name.startsWith("opensaml-")) { - useVersion("5.2.2") + useVersion(libs.versions.opensaml.get()) because("Pinning all opensaml modules to the same version for OpenSAML 5 migration.") } // Cryptacular - avoid regressions, explicit downgrade to 1.2.6 else if (requested.group == "org.cryptacular" && requested.name == "cryptacular") { - useVersion("1.2.6") - because("Pinning cryptacular to 1.2.6 to avoid regressions from newer OpenSAML versions.") + useVersion(libs.versions.cryptacular.get()) + because("Pinning cryptacular to avoid regressions from newer OpenSAML versions.") } } } From 9daf6474c4a3888de451c8ef71c4f66381f58ebe Mon Sep 17 00:00:00 2001 From: Duane May Date: Thu, 4 Jun 2026 15:34:16 -0400 Subject: [PATCH 5/5] Remove unused dependencies --- gradle/libs.versions.toml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b2bc396d43d..1e7edf5e249 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,6 @@ greenmail = "2.1.8" guava = "33.6.0-jre" jacoco = "4.0.2" jacocoAgent = "0.8.14" -jacocoGradlePlugin = "4.0.2" nimbusJwt = "10.9.1" opensaml = "5.2.2" orgJson = "20260522" @@ -25,7 +24,6 @@ sonarqube = "7.3.1.8318" springBoot = "4.0.6" springDependencyManagement = "1.1.7" springDocOpenapi = "3.0.3" -springRetry = "2.0.12" statsdClient = "3.1.0" tomcat = "11.0.22" unboundIdScimSdk = "2.0.0" @@ -80,7 +78,6 @@ eclipseJgit = { module = "org.eclipse.jgit:org.eclipse.jgit", version.ref = "ecl # FasterXML Jackson jacksonAnnotations = { module = "com.fasterxml.jackson.core:jackson-annotations" } jacksonDatabind = { module = "tools.jackson.core:jackson-databind" } -jacksonDataformatYaml = { module = "tools.jackson.dataformat:jackson-dataformat-yaml" } # Flyway flywayCore = { module = "org.flywaydb:flyway-core" } @@ -128,7 +125,6 @@ lombok = { module = "org.projectlombok:lombok" } mariaJdbcDriver = { module = "org.mariadb.jdbc:mariadb-java-client" } # Mockito -mockito = { module = "org.mockito:mockito-core" } mockitoJunit5 = { module = "org.mockito:mockito-junit-jupiter" } # Nimbus JOSE+JWT @@ -159,7 +155,6 @@ springBeans = { module = "org.springframework:spring-beans" } springContext = { module = "org.springframework:spring-context" } springContextSupport = { module = "org.springframework:spring-context-support" } springJdbc = { module = "org.springframework:spring-jdbc" } -springRetry = { module ="org.springframework.retry:spring-retry", version.ref = "springRetry" } springTest = { module = "org.springframework:spring-test" } springTx = { module = "org.springframework:spring-tx" } springWeb = { module = "org.springframework:spring-web" } @@ -174,7 +169,6 @@ springBootStarter = { module = "org.springframework.boot:spring-boot-starter" } springBootStarterLog4j2 = { module = "org.springframework.boot:spring-boot-starter-log4j2" } springBootStarterMail = { module = "org.springframework.boot:spring-boot-starter-mail" } springBootStarterTest = { module = "org.springframework.boot:spring-boot-starter-test" } -springBootStarterTomcat = { module = "org.springframework.boot:spring-boot-starter-tomcat" } springBootStarterTomcatRuntime = { module = "org.springframework.boot:spring-boot-starter-tomcat-runtime" } springBootStarterWeb = { module = "org.springframework.boot:spring-boot-starter-web" } springBootTransaction = { module = "org.springframework.boot:spring-boot-transaction" }