diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 09b466cab64..3ff8435eaff 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -27,6 +27,6 @@ jobs: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} if: env.SONAR_TOKEN != '' run: >- - ./gradlew --no-daemon -Dspring.profiles.active=hsqldb -DfailOnPassedAfterRetry=false --no-daemon test jacocoTestReport || true; - ./gradlew --no-daemon sonar; - ./gradlew --no-daemon jacocoTestReport; + ./gradlew --no-configuration-cache --no-daemon -Dspring.profiles.active=hsqldb -DfailOnPassedAfterRetry=false test jacocoTestReport || true; + ./gradlew --no-configuration-cache --no-daemon sonar; + ./gradlew --no-configuration-cache --no-daemon jacocoTestReport; diff --git a/build.gradle b/build.gradle deleted file mode 100644 index dc412ffa911..00000000000 --- a/build.gradle +++ /dev/null @@ -1,249 +0,0 @@ -import org.apache.tools.ant.filters.ReplaceTokens - -buildscript { - apply(from: "dependencies.gradle") - - repositories { - mavenCentral() - gradlePluginPortal() - maven { - url = uri("https://plugins.gradle.org/m2/") - } - } - - dependencies { - classpath(libraries.springDependencyManagementGradlePlugin) - classpath(libraries.springBootGradlePlugin) - classpath(libraries.gradleJcocoPlugin) - classpath(libraries.sonarqubePlugin) - } -} - -apply(from: "dependencies.gradle") - -def applicationPort = project.hasProperty('port') ? project.getProperty('port').toInteger() : 8080 - -allprojects { - apply(plugin: "io.spring.dependency-management") - apply(plugin: "org.barfuin.gradle.jacocolog") - apply(plugin: "org.sonarqube") - - dependencyManagement { - imports { - mavenBom(libraries.springBootBom) - } - } - - repositories { - mavenCentral() - maven { - url = uri("https://build.shibboleth.net/nexus/content/repositories/releases/") - } - maven { url = uri("https://repository.mulesoft.org/releases/") } - } -} - - -subprojects { - apply(plugin: "java") - java { - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 - } - - 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 -> - if (details.requested.group == 'org.opensaml' && details.requested.name.startsWith("opensaml-")) { - details.useVersion "${versions.opensaml}" - details.because 'Spring Security 5.8.x allows OpenSAML 3 or 4. OpenSAML 3 has reached its end-of-life. Spring Security 6 drops support for 3, using 4.' - } - } - } - } - - dependencies { - testImplementation(libraries.springBootStarterTest) { - exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' - } - testImplementation(libraries.hamcrest) - testImplementation(libraries.junit5JupiterApi) - testImplementation(libraries.junit5JupiterParams) - testImplementation(libraries.junit5JupiterEngine) - testImplementation(libraries.unboundIdLdapSdk) - testRuntimeOnly(libraries.jacocoAgent) - - // Ensure test runtime dependencies are included - testRuntimeOnly('org.junit.platform:junit-platform-launcher') - - compileOnly(libraries.lombok) - annotationProcessor(libraries.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() - // Reduced from 1024m to 640m - unit tests don't need as much as integration tests - jvmArgs += ["-Xmx640m", - "-XX:+StartAttachListener", - "-XX:+HeapDumpOnOutOfMemoryError", - "-XX:HeapDumpPath=/var/log/uaa-tests.hprof" - ] - - testLogging { - events("failed") - exceptionFormat = "full" - // Uncomment the following line to see all standard output from tests (there's a ton of it!) - //showStandardStreams = true - } - } - - 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 { - events("failed") - exceptionFormat = "full" - // Uncomment the following line to see all standard output from tests (there's a ton of it!) - //showStandardStreams = true - } - } - - 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://jitpack.io") - } - 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", "")) - } -} - -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') { - String tomcatBase = file("scripts/boot/tomcat/").getAbsolutePath() - delete(java.nio.file.Path.of(tomcatBase)) - new File(tomcatBase+"/work").mkdirs() - new File(tomcatBase+"/webapps").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()) -} - -tasks.register('killUaa', Exec) { - workingDir './' - executable = 'scripts/kill_uaa.sh' -} - -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..f25e4cf76a4 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,302 @@ +import org.apache.tools.ant.filters.ReplaceTokens +import java.io.File +import java.time.Duration + +buildscript { + repositories { + mavenCentral() + gradlePluginPortal() + } + dependencies { + classpath("io.spring.gradle:dependency-management-plugin:1.1.7") + } +} + +plugins { + alias(libs.plugins.springBoot) apply false + alias(libs.plugins.jacocoLog) apply false + alias(libs.plugins.sonarqube) apply false +} + +// Versions we're overriding from the Spring Boot Bom (Dependabot does not issue PRs to bump these versions, so we need to manually bump them) +val springBootVersion = libs.versions.springBoot.get() +extra["mariadb.version"] = libs.versions.mariadb.get() // Bumping to v3 breaks some pipeline jobs (and compatibility with Amazon Aurora MySQL), so pinning to v2 for now. v2 (current version) is stable and will be supported until about September 2025 (https://mariadb.com/kb/en/about-mariadb-connector-j/). +extra["flyway.version"] = libs.versions.flyway.get() // the next major (v8)'s community edition drops support with MySQL 5.7, which UAA still needs to support. Can bump to v8 once we solve this issue. +extra["hsqldb.version"] = libs.versions.hsqldb.get() // HSQL-DB used for tests but not supported for productive usage +extra["selenium.version"] = libs.versions.selenium.get() // Selenium for integration tests only + +// Extract library dependencies at root level for use in subprojects +val springBootStarterTest = libs.springBootStarterTest.get() +val hamcrest = libs.hamcrest.get() +val junit5JupiterApi = libs.junit5JupiterApi.get() +val junit5JupiterParams = libs.junit5JupiterParams.get() +val junit5JupiterEngine = libs.junit5JupiterEngine.get() +val unboundidLdapsdk = libs.unboundidLdapsdk.get() +val jacocoAgent = libs.jacocoAgent.get() +val lombok = libs.lombok.get() +val opensamlVersion = libs.versions.opensaml.get() + +allprojects { + apply(plugin = "io.spring.dependency-management") + apply(plugin = "org.barfuin.gradle.jacocolog") + apply(plugin = "org.sonarqube") + + + extensions.getByType().apply { + imports { + mavenBom("org.springframework.boot:spring-boot-dependencies:$springBootVersion") + } + } + + repositories { + mavenCentral() + maven { + url = uri("https://build.shibboleth.net/nexus/content/repositories/releases/") + } + maven { + url = uri("https://repository.mulesoft.org/releases/") + } + } +} + +subprojects { + apply(plugin = "java") + + configure { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + } + + // Configure specific configurations to avoid iterating over deprecated 'archives' configuration + // Use afterEvaluate to ensure configurations are created before accessing them + afterEvaluate { + listOf("implementation", "compileOnly", "runtimeOnly", "testImplementation", "testRuntimeOnly", "testCompileOnly", + "api", "apiElements", "runtimeElements", "compileClasspath", "runtimeClasspath", "testCompileClasspath", "testRuntimeClasspath").forEach { configName -> + try { + configurations.findByName(configName)?.apply { + 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 { + if (requested.group == "org.opensaml" && requested.name.startsWith("opensaml-")) { + useVersion(opensamlVersion) + because("Spring Security 5.8.x allows OpenSAML 3 or 4. OpenSAML 3 has reached its end-of-life. Spring Security 6 drops support for 3, using 4.") + } + } + } + } + } catch (e: Exception) { + // Ignore errors accessing configurations (e.g., deprecated archives config) + } + } + } + + dependencies { + // Use module notation for spring-boot-starter-test to allow exclusions + add("testImplementation", "org.springframework.boot:spring-boot-starter-test") { + exclude(group = "org.junit.vintage", module = "junit-vintage-engine") + } + add("testImplementation", hamcrest) + add("testImplementation", junit5JupiterApi) + add("testImplementation", junit5JupiterParams) + add("testImplementation", junit5JupiterEngine) + add("testImplementation", unboundidLdapsdk) + add("testRuntimeOnly", jacocoAgent) + + // Ensure test runtime dependencies are included + add("testRuntimeOnly", "org.junit.platform:junit-platform-launcher") + + add("compileOnly", lombok) + add("annotationProcessor", lombok) + } + + tasks.named("compileJava") { + options.compilerArgs = listOf("-Xlint:none", "-nowarn", "-parameters") + } + tasks.named("compileTestJava") { + options.compilerArgs = 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() + // Reduced from 1024m to 640m - unit tests don't need as much as integration tests + jvmArgs = (jvmArgs ?: emptyList()) + listOf( + "-Xmx640m", + "-XX:+StartAttachListener", + "-XX:+HeapDumpOnOutOfMemoryError", + "-XX:HeapDumpPath=/var/log/uaa-tests.hprof" + ) + + testLogging { + events("failed", "started", "skipped") + exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL + showExceptions = true + showCauses = true + showStackTraces = true + // Uncomment the following line to see all standard output from tests (there's a ton of it!) + //showStandardStreams = true + } + } + + tasks.register("integrationTest") { + description = "Runs UAA Integration Tests" + group = JavaBasePlugin.VERIFICATION_GROUP + + useJUnitPlatform() + + // This prevents integrationTests from hanging indefinitely + timeout.set(Duration.ofMinutes(180)) + + // Integration test workers need same memory as unit tests + // Actual CI configuration is controlled via integration_tests.sh script + jvmArgs = (jvmArgs ?: emptyList()) + listOf( + "-Xmx640m", + "-XX:+StartAttachListener", + "-XX:+HeapDumpOnOutOfMemoryError", + "-XX:HeapDumpPath=/var/log/uaa-tests.hprof" + ) + + // Enable JaCoCo for integration tests + extensions.configure { + isEnabled = true + } + + testLogging { + events("failed", "started", "skipped") + exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL + showExceptions = true + showCauses = true + showStackTraces = true + // Uncomment the following line to see all standard output from tests (there's a ton of it!) + //showStandardStreams = true + } + } + + tasks.register("generateDocs") { + description = "Generates the UAA API docs" + group = JavaBasePlugin.DOCUMENTATION_GROUP + } + + tasks.register("allDeps") { + description = "Generates Dependency Report" + group = ProjectReportsPlugin.DEPENDENCY_REPORT + } + + tasks.register("writeNewPom") { + description = "Generates the UAA pom.xml" + group = JavaBasePlugin.DOCUMENTATION_GROUP + + doLast { + // Note: This requires the maven-publish plugin to be applied + // For now, commenting out as it may not be needed + // project.pom { + // project { + // licenses { + // license { + // name.set("The Apache Software License, Version 2.0") + // url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") + // distribution.set("repo") + // } + // } + // } + // }.writeTo(File("./pom.xml")) + } + } + + repositories { + mavenCentral() + maven { + url = uri("https://jitpack.io") + } + maven { + url = uri("https://repo.maven.apache.org/maven2") + } + } +} + +gradle.taskGraph.whenReady { + allprojects.map { it.tasks.withType() }.flatten().forEach { + it.systemProperty("spring.profiles.active", System.getProperty("spring.profiles.active", "hsqldb")) + it.systemProperty("testId", System.getProperty("testId", "")) + } +} + +tasks.register("manifests") { + description = "Creates sample manifest" + group = LifecycleBasePlugin.BUILD_GROUP + + 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") { + description = "Cleans up the boot tomcat directory" + group = ApplicationPlugin.APPLICATION_GROUP + + val tomcatBase = file("scripts/boot/tomcat/").absolutePath + delete(java.nio.file.Path.of(tomcatBase)) + File("$tomcatBase/work").mkdirs() + File("$tomcatBase/webapps").mkdirs() +} + +tasks.register("bootWarRun") { + description = "Runs UAA as a boot war file" + group = ApplicationPlugin.APPLICATION_GROUP + + dependsOn(tasks.named("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) +} + +tasks.register("killUaa") { + description = "Kill a previously started UAA boot applicatoin" + group = ApplicationPlugin.APPLICATION_GROUP + + workingDir = file("./") + executable = "scripts/kill_uaa.sh" +} + +tasks.register("run") { + dependsOn(tasks.named("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" +} diff --git a/dependencies.gradle b/dependencies.gradle deleted file mode 100644 index 56751895ca4..00000000000 --- a/dependencies.gradle +++ /dev/null @@ -1,119 +0,0 @@ -ext { - versions = [:] - libraries = [:] -} - -// Versions shared between multiple dependencies -versions.apacheDsVersion = "2.0.0.AM27" -versions.bouncyCastleFipsVersion = "2.1.2" -versions.bouncyCastlePkixFipsVersion = "2.1.9" -versions.bouncyCastleTlsFipsVersion = "2.1.20" -versions.springBootVersion = "3.5.8" -versions.guavaVersion = "33.4.8-jre" -versions.seleniumVersion = "4.38.0" -versions.braveVersion = "6.3.0" -versions.opensaml = "4.0.1" - -// Versions we're overriding from the Spring Boot Bom (Dependabot does not issue PRs to bump these versions, so we need to manually bump them) -ext["mariadb.version"] = "2.7.12" // Bumping to v3 breaks some pipeline jobs (and compatibility with Amazon Aurora MySQL), so pinning to v2 for now. v2 (current version) is stable and will be supported until about September 2025 (https://mariadb.com/kb/en/about-mariadb-connector-j/). -ext["flyway.version"] = "7.15.0" // the next major (v8)'s community edition drops support with MySQL 5.7, which UAA still needs to support. Can bump to v8 once we solve this issue. -ext["hsqldb.version"] = "2.7.4" // HSQL-DB used for tests but not supported for productive usage -ext["selenium.version"] = "${versions.seleniumVersion}" // Selenium for integration tests only - -// Dependencies (some rely on shared versions, some are shared between projects) -libraries.apacheDsProtocolLdap = "org.apache.directory.server:apacheds-protocol-ldap:${versions.apacheDsVersion}" -libraries.apacheHttpClient = "org.apache.httpcomponents.client5:httpclient5" -libraries.aspectJRt = "org.aspectj:aspectjrt" -libraries.apacheLdapApi = "org.apache.directory.api:api-ldap-model:2.1.7" -libraries.aspectJWeaver = "org.aspectj:aspectjweaver" -libraries.awaitility = "org.awaitility:awaitility" -libraries.bouncyCastlePkixFips = "org.bouncycastle:bcpkix-fips:${versions.bouncyCastlePkixFipsVersion}" -libraries.bouncyCastleFipsProv = "org.bouncycastle:bc-fips:${versions.bouncyCastleFipsVersion}" -libraries.bouncyCastleTlsFips = "org.bouncycastle:bctls-fips:${versions.bouncyCastleTlsFipsVersion}" -libraries.braveInstrumentation = "io.zipkin.brave:brave-instrumentation-servlet-jakarta:${versions.braveVersion}" -libraries.braveContextSlf4j = "io.zipkin.brave:brave-context-slf4j:${versions.braveVersion}" -libraries.commonsCodec = "commons-codec:commons-codec:1.19.0" -libraries.commonsIo = "commons-io:commons-io:2.20.0" -libraries.dumbster = "dumbster:dumbster:1.6" -libraries.eclipseJgit = "org.eclipse.jgit:org.eclipse.jgit:7.3.0.202506031305-r" -libraries.flywayCore = "org.flywaydb:flyway-core" -libraries.greenmail = "com.icegreen:greenmail:2.1.5" -libraries.guava = "com.google.guava:guava:${versions.guavaVersion}" -libraries.guavaTestLib = "com.google.guava:guava-testlib:${versions.guavaVersion}" -libraries.hamcrest = "org.hamcrest:hamcrest" -libraries.hibernateValidator = "org.hibernate.validator:hibernate-validator" -libraries.hsqldb = "org.hsqldb:hsqldb" -libraries.jacksonAnnotations = "com.fasterxml.jackson.core:jackson-annotations" -libraries.jacksonDatabind = "com.fasterxml.jackson.core:jackson-databind" -libraries.jacksonDataformatYaml = "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml" -libraries.jakartaValidationApi = "jakarta.validation:jakarta.validation-api" -libraries.jacocoAgent = "org.jacoco:org.jacoco.agent:0.8.13" -libraries.jodaTime = "joda-time:joda-time:2.14.0" -libraries.jsonAssert = "org.skyscreamer:jsonassert" -libraries.jsonPath = "com.jayway.jsonpath:json-path" -libraries.jsonPathAssert = "com.jayway.jsonpath:json-path-assert" -libraries.junit5JupiterApi = "org.junit.jupiter:junit-jupiter-api" -libraries.junit5JupiterEngine = "org.junit.jupiter:junit-jupiter-engine" -libraries.junit5JupiterParams = "org.junit.jupiter:junit-jupiter-params" -libraries.log4jCore = "org.apache.logging.log4j:log4j-core" -libraries.lombok = "org.projectlombok:lombok" -libraries.mariaJdbcDriver = "org.mariadb.jdbc:mariadb-java-client" -libraries.mockito = "org.mockito:mockito-core" -libraries.mockitoJunit5 = "org.mockito:mockito-junit-jupiter" -libraries.bytebuddy = "net.bytebuddy:byte-buddy" -libraries.bytebuddyagent = "net.bytebuddy:byte-buddy-agent" -libraries.openSamlApi = "org.opensaml:opensaml-saml-api:${versions.opensaml}" -libraries.orgJson = "org.json:json:20250517" -libraries.passay = "org.passay:passay:1.6.6" -libraries.postgresql = "org.postgresql:postgresql" -libraries.selenium = "org.seleniumhq.selenium:selenium-java:${versions.seleniumVersion}" -libraries.seleniumRemoteDriver = "org.seleniumhq.selenium:selenium-remote-driver:${versions.seleniumVersion}" -libraries.slf4jApi = "org.slf4j:slf4j-api" -libraries.slf4jImpl = "org.apache.logging.log4j:log4j-slf4j2-impl" -libraries.snakeyaml = "org.yaml:snakeyaml" -libraries.springBeans = "org.springframework:spring-beans" -libraries.springBootBom = "org.springframework.boot:spring-boot-dependencies:${versions.springBootVersion}" -libraries.springBootStarter = "org.springframework.boot:spring-boot-starter" -libraries.springBootStarterLog4j2 = "org.springframework.boot:spring-boot-starter-log4j2" -libraries.springBootStarterTest = "org.springframework.boot:spring-boot-starter-test" -libraries.springBootStarterTomcat = "org.springframework.boot:spring-boot-starter-tomcat" -libraries.springBootStarterWeb = "org.springframework.boot:spring-boot-starter-web" -libraries.springBootStarterMail = "org.springframework.boot:spring-boot-starter-mail" -libraries.springContext = "org.springframework:spring-context" -libraries.springContextSupport = "org.springframework:spring-context-support" -libraries.springJdbc = "org.springframework:spring-jdbc" -libraries.springLdapCore = "org.springframework.ldap:spring-ldap-core" -libraries.springRestdocs = "org.springframework.restdocs:spring-restdocs-mockmvc" -libraries.springRetry = "org.springframework.retry:spring-retry" -libraries.springSecurityConfig = "org.springframework.security:spring-security-config" -libraries.springSecurityCore = "org.springframework.security:spring-security-core" -libraries.springSecurityLdap = "org.springframework.security:spring-security-ldap" -libraries.springSecuritySamlServiceProvider = "org.springframework.security:spring-security-saml2-service-provider" -libraries.springSecurityTest = "org.springframework.security:spring-security-test" -libraries.springSecurityWeb = "org.springframework.security:spring-security-web" -libraries.springSessionJdbc = "org.springframework.session:spring-session-jdbc" -libraries.springTest = "org.springframework:spring-test" -libraries.springTx = "org.springframework:spring-tx" -libraries.springWeb = "org.springframework:spring-web" -libraries.springWebMvc = "org.springframework:spring-webmvc" -libraries.statsdClient = "com.timgroup:java-statsd-client:3.1.0" -libraries.thymeleafDialect = "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect" -libraries.thymeleafExtrasSpringSecurity = "org.thymeleaf.extras:thymeleaf-extras-springsecurity6" -libraries.thymeLeaf = "org.thymeleaf:thymeleaf" -libraries.thymeleafSpring = "org.thymeleaf:thymeleaf-spring6" -libraries.tomcatElApi = "org.apache.tomcat.embed:tomcat-embed-el" -libraries.tomcatEmbed = "org.apache.tomcat.embed:tomcat-embed-core" -libraries.tomcatJasperEl = "org.apache.tomcat.embed:tomcat-embed-jasper" -libraries.tomcatJdbc = "org.apache.tomcat:tomcat-jdbc" -libraries.unboundIdLdapSdk = "com.unboundid:unboundid-ldapsdk" -libraries.unboundIdScimSdk = "com.unboundid.product.scim:scim-sdk:1.8.26" -libraries.velocity = "org.apache.velocity:velocity-engine-core:2.4.1" -libraries.nimbusJwt = "com.nimbusds:nimbus-jose-jwt:10.5" -libraries.xmlSecurity = "org.apache.santuario:xmlsec:4.0.4" -libraries.xmlUnit = "org.xmlunit:xmlunit-assertj" - -// gradle plugins -libraries.springBootGradlePlugin = "org.springframework.boot:spring-boot-gradle-plugin:${versions.springBootVersion}" -libraries.springDependencyManagementGradlePlugin = "io.spring.gradle:dependency-management-plugin" -libraries.gradleJcocoPlugin = "org.barfuin.gradle.jacocolog:gradle-jacoco-log:3.1.0" -libraries.sonarqubePlugin = "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:7.0.1.6134" diff --git a/gradle.properties b/gradle.properties index 2dd43b00efd..635665bae49 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ org.gradle.jvmargs=-Dfile.encoding=utf8 \ -XX:HeapDumpPath=/var/log/uaa-tests.hprof \ -Xmx1024m \ -Xms1024m \ - -XX:MaxMetaspaceSize=256m \ + -XX:MaxMetaspaceSize=384m \ -XX:+UseG1GC \ -XX:MaxGCPauseMillis=100 \ -XX:ParallelGCThreads=2 \ @@ -26,5 +26,17 @@ ossrhUsername= ossrhPassword= group=org.cloudfoundry.identity archivesBaseName="uaa" + # Limit workers to 2 for memory-constrained environments (CI containers) -org.gradle.workers.max=2 \ No newline at end of file +org.gradle.workers.max=2 +# Disable upload tasks to avoid getUploadTaskName() error with deprecated archives configuration +org.gradle.internal.publish.checksums=false +org.gradle.caching=true +org.gradle.configuration-cache=true + +# SonarCloud configuration +systemProp.sonar.projectKey=cloudfoundry-identity-parent +systemProp.sonar.organization=cloudfoundry-1 +systemProp.sonar.host.url=https://sonarcloud.io +systemProp.sonar.exclusions="samples/**/*.*,**/*Test.java,**/*Tests.java,**/*IT.java,**/*SecurityConfiguration.java" +systemProp.sonar.java.source=21 \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000000..5ec0b00362a --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,209 @@ +[versions] +apacheDs = "2.0.0.AM27" +bouncycastleFips = "2.1.2" +bouncycastlePkixFips = "2.1.9" +bouncycastleTlsFips = "2.1.20" +springBoot = "3.5.8" +guava = "33.4.8-jre" +selenium = "4.38.0" +brave = "6.3.0" +opensaml = "4.0.1" +mariadb = "2.7.12" +flyway = "7.15.0" +hsqldb = "2.7.4" +apacheLdapApi = "2.1.7" +commonsCodec = "1.19.0" +commonsIo = "2.20.0" +dumbster = "1.6" +eclipseJgit = "7.3.0.202506031305-r" +greenmail = "2.1.5" +jodaTime = "2.14.0" +jacocoAgent = "0.8.13" +orgJson = "20250517" +passay = "1.6.6" +statsdClient = "3.1.0" +unboundidScimSdk = "1.8.26" +velocity = "2.4.1" +nimbusJwt = "10.5" +xmlSecurity = "4.0.4" +gradleJacocoLog = "3.1.0" +sonarqubePlugin = "7.0.1.6134" + +[libraries] +# Apache Directory Server +apachedsProtocolLdap = { module = "org.apache.directory.server:apacheds-protocol-ldap", version.ref = "apacheDs" } +apacheHttpClient = { module = "org.apache.httpcomponents.client5:httpclient5" } +apacheApiLdapModel = { module = "org.apache.directory.api:api-ldap-model", version.ref = "apacheLdapApi" } + +# AspectJ +aspectjRt = { module = "org.aspectj:aspectjrt" } +aspectjWeaver = { module = "org.aspectj:aspectjweaver" } + +# Awaitility +awaitility = { module = "org.awaitility:awaitility" } + +# BouncyCastle FIPS +bouncycastlePkixFips = { module = "org.bouncycastle:bcpkix-fips", version.ref = "bouncycastlePkixFips" } +bouncycastleFipsProv = { module = "org.bouncycastle:bc-fips", version.ref = "bouncycastleFips" } +bouncycastleTlsFips = { module = "org.bouncycastle:bctls-fips", version.ref = "bouncycastleTlsFips" } + +# Brave (Zipkin) +braveInstrumentation = { module = "io.zipkin.brave:brave-instrumentation-servlet-jakarta", version.ref = "brave" } +braveContextSlf4j = { module = "io.zipkin.brave:brave-context-slf4j", version.ref = "brave" } + +# Commons +commonsCodec = { module = "commons-codec:commons-codec", version.ref = "commonsCodec" } +commonsIo = { module = "commons-io:commons-io", version.ref = "commonsIo" } + +# Dumbster +dumbster = { module = "dumbster:dumbster", version.ref = "dumbster" } + +# Eclipse JGit +jgit = { module = "org.eclipse.jgit:org.eclipse.jgit", version.ref = "eclipseJgit" } + +# Flyway +flywayCore = { module = "org.flywaydb:flyway-core" } + +# Greenmail +greenmail = { module = "com.icegreen:greenmail", version.ref = "greenmail" } + +# Guava +guava = { module = "com.google.guava:guava", version.ref = "guava" } +guavaTestlib = { module = "com.google.guava:guava-testlib", version.ref = "guava" } + +# Hamcrest +hamcrest = { module = "org.hamcrest:hamcrest" } + +# Hibernate +hibernateValidator = { module = "org.hibernate.validator:hibernate-validator" } + +# HSQLDB +hsqldb = { module = "org.hsqldb:hsqldb" } + +# Jackson +jacksonAnnotations = { module = "com.fasterxml.jackson.core:jackson-annotations" } +jacksonDatabind = { module = "com.fasterxml.jackson.core:jackson-databind" } +jacksonDataformatYaml = { module = "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml" } + +# Jakarta +jakartaValidationApi = { module = "jakarta.validation:jakarta.validation-api" } + +# JaCoCo +jacocoAgent = { module = "org.jacoco:org.jacoco.agent", version.ref = "jacocoAgent" } + +# Joda Time +jodaTime = { module = "joda-time:joda-time", version.ref = "jodaTime" } + +# JSON +jsonAssert = { module = "org.skyscreamer:jsonassert" } +jsonPath = { module = "com.jayway.jsonpath:json-path" } +jsonPathAssert = { module = "com.jayway.jsonpath:json-path-assert" } +json = { module = "org.json:json", version.ref = "orgJson" } + +# JUnit 5 +junit5JupiterApi = { module = "org.junit.jupiter:junit-jupiter-api" } +junit5JupiterEngine = { module = "org.junit.jupiter:junit-jupiter-engine" } +junit5JupiterParams = { module = "org.junit.jupiter:junit-jupiter-params" } + +# Log4j +log4jCore = { module = "org.apache.logging.log4j:log4j-core" } + +# Lombok +lombok = { module = "org.projectlombok:lombok" } + +# MariaDB +mariadbJavaClient = { module = "org.mariadb.jdbc:mariadb-java-client" } + +# Mockito +mockito = { module = "org.mockito:mockito-core" } +mockitoJunit5 = { module = "org.mockito:mockito-junit-jupiter" } + +# ByteBuddy +bytebuddy = { module = "net.bytebuddy:byte-buddy" } +bytebuddyAgent = { module = "net.bytebuddy:byte-buddy-agent" } + +# OpenSAML +opensamlSamlApi = { module = "org.opensaml:opensaml-saml-api", version.ref = "opensaml" } + +# Passay +passay = { module = "org.passay:passay", version.ref = "passay" } + +# PostgreSQL +postgresql = { module = "org.postgresql:postgresql" } + +# Selenium +selenium = { module = "org.seleniumhq.selenium:selenium-java", version.ref = "selenium" } +seleniumRemoteDriver = { module = "org.seleniumhq.selenium:selenium-remote-driver", version.ref = "selenium" } + +# SLF4J +slf4jApi = { module = "org.slf4j:slf4j-api" } +log4jSlf4j2Impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl" } + +# SnakeYAML +snakeyaml = { module = "org.yaml:snakeyaml" } + +# Spring +springBeans = { module = "org.springframework:spring-beans" } +springBootBom = { module = "org.springframework.boot:spring-boot-dependencies", version.ref = "springBoot" } +springBootStarter = { module = "org.springframework.boot:spring-boot-starter" } +springBootStarterLog4j2 = { module = "org.springframework.boot:spring-boot-starter-log4j2" } +springBootStarterTest = { module = "org.springframework.boot:spring-boot-starter-test" } +springBootStarterTomcat = { module = "org.springframework.boot:spring-boot-starter-tomcat" } +springBootStarterWeb = { module = "org.springframework.boot:spring-boot-starter-web" } +springBootStarterMail = { module = "org.springframework.boot:spring-boot-starter-mail" } +springContext = { module = "org.springframework:spring-context" } +springContextSupport = { module = "org.springframework:spring-context-support" } +springJdbc = { module = "org.springframework:spring-jdbc" } +springLdapCore = { module = "org.springframework.ldap:spring-ldap-core" } +springRestdocs = { module = "org.springframework.restdocs:spring-restdocs-mockmvc" } +springRetry = { module = "org.springframework.retry:spring-retry" } +springSecurityConfig = { module = "org.springframework.security:spring-security-config" } +springSecurityCore = { module = "org.springframework.security:spring-security-core" } +springSecurityLdap = { module = "org.springframework.security:spring-security-ldap" } +springSecuritySamlServiceProvider = { module = "org.springframework.security:spring-security-saml2-service-provider" } +springSecurityTest = { module = "org.springframework.security:spring-security-test" } +springSecurityWeb = { module = "org.springframework.security:spring-security-web" } +springSessionJdbc = { module = "org.springframework.session:spring-session-jdbc" } +springTest = { module = "org.springframework:spring-test" } +springTx = { module = "org.springframework:spring-tx" } +springWeb = { module = "org.springframework:spring-web" } +springWebmvc = { module = "org.springframework:spring-webmvc" } + +# StatsD +statsdClient = { module = "com.timgroup:java-statsd-client", version.ref = "statsdClient" } + +# Thymeleaf +thymeleafDialect = { module = "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect" } +thymeleafExtrasSpringSecurity = { module = "org.thymeleaf.extras:thymeleaf-extras-springsecurity6" } +thymeleaf = { module = "org.thymeleaf:thymeleaf" } +thymeleafSpring = { module = "org.thymeleaf:thymeleaf-spring6" } + +# Tomcat +tomcatElApi = { module = "org.apache.tomcat.embed:tomcat-embed-el" } +tomcatEmbed = { module = "org.apache.tomcat.embed:tomcat-embed-core" } +tomcatJasperEl = { module = "org.apache.tomcat.embed:tomcat-embed-jasper" } +tomcatJdbc = { module = "org.apache.tomcat:tomcat-jdbc" } + +# UnboundID +unboundidLdapsdk = { module = "com.unboundid:unboundid-ldapsdk" } +unboundidScimSdk = { module = "com.unboundid.product.scim:scim-sdk", version.ref = "unboundidScimSdk" } + +# Velocity +velocity = { module = "org.apache.velocity:velocity-engine-core", version.ref = "velocity" } + +# Nimbus JWT +nimbusJwt = { module = "com.nimbusds:nimbus-jose-jwt", version.ref = "nimbusJwt" } + +# XML Security +xmlSecurity = { module = "org.apache.santuario:xmlsec", version.ref = "xmlSecurity" } + +# XMLUnit +xmlUnit = { module = "org.xmlunit:xmlunit-assertj" } + +[plugins] +springBoot = { id = "org.springframework.boot", version.ref = "springBoot" } +jacocoLog = { id = "org.barfuin.gradle.jacocolog", version.ref = "gradleJacocoLog" } +sonarqube = { id = "org.sonarqube", version.ref = "sonarqubePlugin" } + +[bundles] +springBootTest = ["springBootStarterTest", "hamcrest", "junit5JupiterApi", "junit5JupiterParams", "junit5JupiterEngine"] diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bad7c2462f5..23449a2b543 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/metrics-data/build.gradle b/metrics-data/build.gradle deleted file mode 100644 index c44596e7fe7..00000000000 --- a/metrics-data/build.gradle +++ /dev/null @@ -1,12 +0,0 @@ -description = "CloudFoundry Identity Metrics Data Jar" - -dependencies { - implementation(libraries.jacksonDatabind) - implementation(libraries.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..215c39800b1 --- /dev/null +++ b/metrics-data/build.gradle.kts @@ -0,0 +1,12 @@ +description = "CloudFoundry Identity Metrics Data Jar" + +dependencies { + implementation(libs.jacksonDatabind) + implementation(libs.jacksonAnnotations) +} + +tasks.processResources { + //maven replaces project.artifactId in the log4j2.properties file + //https://www.pivotaltracker.com/story/show/74344574 + filter { line -> if (line.contains("\${project.artifactId}")) line.replace("\${project.artifactId}", "cloudfoundry-identity-metrics-data") else line } +} diff --git a/model/build.gradle b/model/build.gradle deleted file mode 100644 index 086b4838728..00000000000 --- a/model/build.gradle +++ /dev/null @@ -1,42 +0,0 @@ -description = "CloudFoundry Identity Model JAR" - -dependencies { - implementation(project(":cloudfoundry-identity-metrics-data")) - - implementation(libraries.jacksonDatabind) - implementation(libraries.jacksonAnnotations) - - implementation(libraries.jakartaValidationApi) - - implementation(libraries.commonsCodec) - implementation(libraries.commonsIo) - - implementation(libraries.springWeb) - implementation(libraries.springWebMvc) - implementation(libraries.springSecurityConfig) - - implementation(libraries.nimbusJwt) - - implementation(libraries.slf4jApi) - - testImplementation(libraries.springBootStarterTest) - testImplementation(libraries.hamcrest) - testImplementation(libraries.junit5JupiterApi) - testImplementation(libraries.junit5JupiterParams) - - testImplementation(libraries.jsonAssert) - - implementation(libraries.nimbusJwt) -} - -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..10f959667af --- /dev/null +++ b/model/build.gradle.kts @@ -0,0 +1,67 @@ +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) + implementation(libs.nimbusJwt) + + testImplementation(libs.springBootStarterTest) + testImplementation(libs.hamcrest) + testImplementation(libs.junit5JupiterApi) + testImplementation(libs.junit5JupiterParams) + + testImplementation(libs.jsonAssert) + +} + +tasks.processResources { + //maven replaces project.artifactId in the log4j2.properties file + //https://www.pivotaltracker.com/story/show/74344574 + filter { line -> if (line.contains("\${project.artifactId}")) line.replace("\${project.artifactId}", "cloudfoundry-identity-model") else line } +} + +// Expose test classes to other projects (configuration-cache friendly approach) +configurations { + create("testArtifacts") { + isCanBeResolved = true + isCanBeConsumed = true + // Don't extend from testImplementation to avoid configuration cache issues + } +} + +tasks.register("testJar") { + description = "Builds the test jar file" + group = LifecycleBasePlugin.BUILD_GROUP + + archiveClassifier.set("test") + // Use explicit source set reference that's configuration-cache friendly + val testSourceSet = sourceSets.named("test") + from(testSourceSet.map { it.output.classesDirs }) + from(testSourceSet.map { it.output.resourcesDir?.let { fileTree(it) } ?: files() }) + // Ensure test classes are compiled and resources are processed before creating the JAR + dependsOn(tasks.named("compileTestJava")) + dependsOn(tasks.named("processTestResources")) +} + +artifacts { + add("testArtifacts", tasks.named("testJar")) +} + +tasks.named("integrationTest") { + onlyIf { false } //disable since we don't have any +} diff --git a/scripts/integration_tests.sh b/scripts/integration_tests.sh index f69dad865aa..bc5f05f7ae5 100755 --- a/scripts/integration_tests.sh +++ b/scripts/integration_tests.sh @@ -77,7 +77,7 @@ function main() { # Explicit Gradle daemon memory for Kotlin 2.2 with additional GC tuning readonly assemble_code="./gradlew '-Dspring.profiles.active=${test_profile}' \ '-Djava.security.egd=file:/dev/./urandom' \ - '-Dorg.gradle.jvmargs=-Dfile.encoding=utf8 -Xms64m -Xmx${gradle_heap} -XX:MaxMetaspaceSize=128m -XX:+UseG1GC -XX:MaxGCPauseMillis=100' \ + '-Dorg.gradle.jvmargs=-Dfile.encoding=utf8 -Xms64m -Xmx${gradle_heap} -XX:MaxMetaspaceSize=384m -XX:+UseG1GC -XX:MaxGCPauseMillis=100' \ assemble \ --no-watch-fs \ --no-daemon \ @@ -93,7 +93,7 @@ function main() { '-Dspring.profiles.active=${test_profile}' \ '-Djava.security.egd=file:/dev/./urandom' \ '-DskipUaaAutoStart=true' \ - '-Dorg.gradle.jvmargs=-Dfile.encoding=utf8 -Xms64m -Xmx${gradle_test_heap} -XX:MaxMetaspaceSize=128m -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:ParallelGCThreads=2 -XX:CICompilerCount=2 -Djdk.lang.processReaperUseDefaultStackSize=true' \ + '-Dorg.gradle.jvmargs=-Dfile.encoding=utf8 -Xms64m -Xmx${gradle_test_heap} -XX:MaxMetaspaceSize=384m -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:ParallelGCThreads=2 -XX:CICompilerCount=2 -Djdk.lang.processReaperUseDefaultStackSize=true' \ '-Dorg.gradle.daemon.idletimeout=300000' \ '-Dorg.gradle.parallel=false' \ '-Dorg.gradle.workers.max=2' \ diff --git a/server/build.gradle b/server/build.gradle deleted file mode 100644 index cf8cc62ea47..00000000000 --- a/server/build.gradle +++ /dev/null @@ -1,148 +0,0 @@ -apply(plugin: "war") - -description = "CloudFoundry Identity Server JAR" - -dependencies { - implementation(project(":cloudfoundry-identity-metrics-data")) - implementation(project(":cloudfoundry-identity-model")) - - implementation(libraries.tomcatJdbc) - providedCompile(libraries.tomcatEmbed) - - implementation(libraries.jacksonDatabind) - implementation(libraries.jsonPath) { - exclude(module: "json-smart") - } - implementation(libraries.springBeans) - implementation(libraries.springContext) - implementation(libraries.springContextSupport) - implementation(libraries.springTx) - implementation(libraries.springJdbc) - implementation(libraries.springWeb) - implementation(libraries.springSecurityCore) - implementation(libraries.springSecurityWeb) - implementation(libraries.springSecurityConfig) - implementation(libraries.springBootStarterMail) - implementation(libraries.openSamlApi) - implementation(libraries.springSecuritySamlServiceProvider) - implementation(libraries.jodaTime) - implementation(libraries.xmlSecurity) - implementation(libraries.springSessionJdbc) - - implementation(libraries.bouncyCastleFipsProv) - implementation(libraries.bouncyCastleTlsFips) - implementation(libraries.bouncyCastlePkixFips) - - implementation(libraries.guava) - - implementation(libraries.aspectJRt) - implementation(libraries.aspectJWeaver) - - implementation(libraries.thymeLeaf) { - exclude(module: "ognl") - } - implementation(libraries.thymeleafSpring) { - exclude(module: "ognl") - } - implementation(libraries.thymeleafDialect) { - exclude(module: "ognl") - } - implementation(libraries.thymeleafExtrasSpringSecurity) { - exclude(module: "ognl") - } - - implementation(libraries.unboundIdScimSdk) { - exclude(module: "servlet-api") - exclude(module: "commons-logging") - exclude(module: "httpclient") - exclude(module: "wink-client-apache-httpclient") - exclude(module: "json") - } - - implementation(libraries.hibernateValidator) - implementation(libraries.flywayCore) - implementation(libraries.mariaJdbcDriver) - implementation(libraries.hsqldb) - - implementation(libraries.snakeyaml) - implementation(libraries.velocity) - - implementation(libraries.springWebMvc) - implementation(libraries.springSecurityLdap) - implementation(libraries.springLdapCore) - implementation(libraries.apacheLdapApi) { - exclude(module: "slf4j-api") - exclude(module: "mina-core") - } - - implementation(libraries.passay) - - implementation(libraries.slf4jImpl) - implementation(libraries.log4jCore) - - implementation(libraries.nimbusJwt) - implementation(libraries.orgJson) - - implementation(libraries.apacheHttpClient) - - testImplementation(project(":cloudfoundry-identity-model").sourceSets.test.output) - - testImplementation(libraries.springTest) - - testImplementation(libraries.bytebuddy) - testImplementation(libraries.bytebuddyagent) - testImplementation(libraries.mockitoJunit5) - - testImplementation(libraries.postgresql) - - testImplementation(libraries.tomcatElApi) - testImplementation(libraries.tomcatJasperEl) - testImplementation(libraries.tomcatJdbc) - - testImplementation(libraries.jsonPathAssert) - testImplementation(libraries.guavaTestLib) - testImplementation(libraries.xmlUnit) - testImplementation(libraries.awaitility) - - implementation(libraries.commonsIo) -} - -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") - - // 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") -} - -jar { - exclude("org/cloudfoundry/identity/uaa/web/tomcat/UaaStartupFailureListener.*") -} - -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 } -} - -integrationTest {}.onlyIf { //disable since we don't have any - false -} - -task tomcatListenerJar(type: Jar) { - archiveBaseName = "tomcat-listener" - from(sourceSets.main.output) - include("org/cloudfoundry/identity/uaa/web/tomcat/UaaStartupFailureListener.*") -} - -artifacts { - archives(tomcatListenerJar) -} diff --git a/server/build.gradle.kts b/server/build.gradle.kts new file mode 100644 index 00000000000..0040e256149 --- /dev/null +++ b/server/build.gradle.kts @@ -0,0 +1,171 @@ +import java.io.File + +apply(plugin = "war") + +description = "CloudFoundry Identity Server JAR" + +dependencies { + implementation(project(":cloudfoundry-identity-metrics-data")) + implementation(project(":cloudfoundry-identity-model")) + + implementation(libs.tomcatJdbc) + + implementation(libs.jacksonDatabind) + implementation(libs.jsonPath) { + exclude(module = "json-smart") + } + implementation(libs.springBeans) + implementation(libs.springContext) + implementation(libs.springContextSupport) + implementation(libs.springTx) + implementation(libs.springJdbc) + implementation(libs.springWeb) + implementation(libs.springSecurityCore) + implementation(libs.springSecurityWeb) + implementation(libs.springSecurityConfig) + implementation(libs.springBootStarterMail) + implementation(libs.opensamlSamlApi) + implementation(libs.springSecuritySamlServiceProvider) + implementation(libs.jodaTime) + implementation(libs.xmlSecurity) + implementation(libs.springSessionJdbc) + + implementation(libs.bouncycastleFipsProv) + implementation(libs.bouncycastleTlsFips) + implementation(libs.bouncycastlePkixFips) + + implementation(libs.guava) + + implementation(libs.aspectjRt) + implementation(libs.aspectjWeaver) + + implementation(libs.thymeleaf) { + exclude(module = "ognl") + } + implementation(libs.thymeleafSpring) { + exclude(module = "ognl") + } + implementation(libs.thymeleafDialect) { + exclude(module = "ognl") + } + implementation(libs.thymeleafExtrasSpringSecurity) { + 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") + } + + implementation(libs.hibernateValidator) + implementation(libs.flywayCore) + implementation(libs.mariadbJavaClient) + implementation(libs.hsqldb) + + implementation(libs.snakeyaml) + implementation(libs.velocity) + + implementation(libs.springWebmvc) + implementation(libs.springSecurityLdap) + implementation(libs.springLdapCore) + implementation(libs.apacheApiLdapModel) { + exclude(module = "slf4j-api") + exclude(module = "mina-core") + } + + implementation(libs.passay) + + implementation(libs.log4jSlf4j2Impl) + implementation(libs.log4jCore) + + implementation(libs.nimbusJwt) + implementation(libs.json) + + implementation(libs.apacheHttpClient) + implementation(libs.commonsIo) + compileOnly(libs.tomcatEmbed) + + testImplementation(project(":cloudfoundry-identity-model", "testArtifacts")) + + testImplementation(libs.springTest) + + testImplementation(libs.bytebuddy) + testImplementation(libs.bytebuddyAgent) + testImplementation(libs.mockitoJunit5) + + testImplementation(libs.postgresql) + + testImplementation(libs.tomcatElApi) + testImplementation(libs.tomcatJasperEl) + testImplementation(libs.tomcatJdbc) + + testImplementation(libs.jsonPathAssert) + testImplementation(libs.guavaTestlib) + testImplementation(libs.xmlUnit) + testImplementation(libs.awaitility) +} + +// Expose test classes to other projects (configuration-cache friendly approach) +configurations { + create("testArtifacts") { + isCanBeResolved = true + isCanBeConsumed = true + // Don't extend from testImplementation to avoid configuration cache issues + } +} + +tasks.register("testJar") { + description = "Builds the test jar file" + group = LifecycleBasePlugin.BUILD_GROUP + + archiveClassifier.set("test") + // Use explicit source set reference that's configuration-cache friendly + val testSourceSet = sourceSets.named("test") + from(testSourceSet.map { it.output.classesDirs }) + from(testSourceSet.map { it.output.resourcesDir?.let { fileTree(it) } ?: files() }) + // Ensure test classes are compiled and resources are processed before creating the JAR + dependsOn(tasks.named("compileTestJava")) + dependsOn(tasks.named("processTestResources")) +} + +artifacts { + add("testArtifacts", tasks.named("testJar")) +} + +// Configure specific configurations to avoid iterating over deprecated 'archives' configuration +afterEvaluate { + listOf("implementation", "compileOnly", "runtimeOnly", "testImplementation", "testRuntimeOnly", "testCompileOnly").forEach { configName -> + try { + configurations.findByName(configName)?.apply { + 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") + } + } catch (e: Exception) { + // Ignore errors accessing configurations (e.g., deprecated archives config) + } + } +} + +tasks.processResources { + //maven replaces project.artifactId in the log4j2.properties file + //https://www.pivotaltracker.com/story/show/74344574 + filter { line -> if (line.contains("\${project.artifactId}")) line.replace("\${project.artifactId}", "cloudfoundry-identity-server") else line } +} + +tasks.named("integrationTest") { + onlyIf { false } //disable since we don't have any +} diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/web/tomcat/UaaStartupFailureListener.java b/server/src/main/java/org/cloudfoundry/identity/uaa/web/tomcat/UaaStartupFailureListener.java deleted file mode 100644 index 53e72b37220..00000000000 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/web/tomcat/UaaStartupFailureListener.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.cloudfoundry.identity.uaa.web.tomcat; - -import org.apache.catalina.Container; -import org.apache.catalina.Lifecycle; -import org.apache.catalina.LifecycleEvent; -import org.apache.catalina.LifecycleException; -import org.apache.catalina.LifecycleListener; -import org.apache.catalina.LifecycleState; -import org.apache.catalina.Server; -import org.apache.catalina.Service; - -import java.util.function.Predicate; -import java.util.stream.Stream; - -public class UaaStartupFailureListener implements LifecycleListener { - - private final Predicate containerFailed = container -> { - if (container.getState() != LifecycleState.STARTED) { - return true; - } - - return Stream.of(container.findChildren()).anyMatch(this.containerFailed); - }; - - @Override - public void lifecycleEvent(LifecycleEvent event) { - String eventType = event.getType(); - Lifecycle lifecycle = event.getLifecycle(); - - if (lifecycle instanceof Server server && eventType.equals(Lifecycle.AFTER_START_EVENT)) { - - if (Stream.of(server.findServices()).map(Service::getContainer).anyMatch(containerFailed)) { - try { - server.stop(); - server.destroy(); - } catch (LifecycleException e) { - throw new RuntimeException(e); - } - } - } - } -} diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/web/tomcat/UaaStartupFailureListenerTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/web/tomcat/UaaStartupFailureListenerTest.java deleted file mode 100644 index 885781173fb..00000000000 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/web/tomcat/UaaStartupFailureListenerTest.java +++ /dev/null @@ -1,92 +0,0 @@ -package org.cloudfoundry.identity.uaa.web.tomcat; - -import org.apache.catalina.Container; -import org.apache.catalina.Engine; -import org.apache.catalina.Lifecycle; -import org.apache.catalina.LifecycleEvent; -import org.apache.catalina.LifecycleException; -import org.apache.catalina.LifecycleState; -import org.apache.catalina.Server; -import org.apache.catalina.Service; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -class UaaStartupFailureListenerTest { - @Nested - class ByDefault { - private Server server; - private UaaStartupFailureListener listener; - - @BeforeEach - void setUp() { - listener = new UaaStartupFailureListener(); - server = mockServer(mockService(LifecycleState.STARTED)); - } - - @Test - void doesNotStopTheServer() throws LifecycleException { - listener.lifecycleEvent(mockLifecycleEvent(server, Lifecycle.AFTER_START_EVENT)); - verify(server, times(0)).start(); - verify(server, times(0)).destroy(); - } - } - - @Nested - class WhenInitializationFails { - private Server server; - private UaaStartupFailureListener listener; - - @BeforeEach - void setUp() { - listener = new UaaStartupFailureListener(); - server = mockServer(mockService(LifecycleState.FAILED)); - } - - @Test - void stopsTheServer() throws LifecycleException { - listener.lifecycleEvent(mockLifecycleEvent(server, Lifecycle.AFTER_START_EVENT)); - verify(server, times(1)).stop(); - verify(server, times(1)).destroy(); - } - - @Test - void rethrowsAnyExceptions() throws LifecycleException { - doThrow(new LifecycleException()).when(server).stop(); - assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> listener.lifecycleEvent(mockLifecycleEvent(server, Lifecycle.AFTER_START_EVENT))); - verify(server, times(1)).stop(); - verify(server, times(0)).destroy(); - } - } - - private LifecycleEvent mockLifecycleEvent(Server server, String type) { - LifecycleEvent mockEvent = mock(LifecycleEvent.class); - when(mockEvent.getType()).thenReturn(type); - when(mockEvent.getLifecycle()).thenReturn(server); - return mockEvent; - } - - private Server mockServer(Service service) { - Server mockServer = mock(Server.class); - when(mockServer.findServices()).thenReturn(new Service[]{service}); - return mockServer; - } - - private Service mockService(LifecycleState state) { - Engine mockContainer = mock(Engine.class); - when(mockContainer.getState()).thenReturn(state); - when(mockContainer.findChildren()).thenReturn(new Container[]{}); - - Service mockService = mock(Service.class); - when(mockService.getContainer()).thenReturn(mockContainer); - - return mockService; - } -} 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..363736802ea --- /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("$rootDir/metrics-data") +project(":cloudfoundry-identity-model").projectDir = file("$rootDir/model") +project(":cloudfoundry-identity-server").projectDir = file("$rootDir/server") +project(":cloudfoundry-identity-uaa").projectDir = file("$rootDir/uaa") +project(":cloudfoundry-identity-statsd").projectDir = file("$rootDir/statsd") +project(":cloudfoundry-identity-statsd-lib").projectDir = file("$rootDir/statsd-lib") diff --git a/statsd-lib/build.gradle b/statsd-lib/build.gradle deleted file mode 100644 index ff814a735a6..00000000000 --- a/statsd-lib/build.gradle +++ /dev/null @@ -1,30 +0,0 @@ -apply(plugin: "java") - -repositories { - mavenCentral() -} - -dependencies { - implementation(project(":cloudfoundry-identity-metrics-data")) - implementation(libraries.springBootStarter) - implementation(libraries.springBootStarterWeb) - implementation(libraries.springBootStarterLog4j2) - implementation(libraries.statsdClient) - testImplementation(libraries.mockitoJunit5) - testImplementation(libraries.bytebuddy) - testImplementation(libraries.bytebuddyagent) - implementation(libraries.jacksonDataformatYaml) - implementation(libraries.jacksonDatabind) -} - -test { - exclude("org/cloudfoundry/identity/statsd/integration/*.class") - exclude("**/*IT.class") -} - -integrationTest { - filter { - includeTestsMatching("org.cloudfoundry.identity.statsd.integration.*") - includeTestsMatching("*IT") - } -} diff --git a/statsd-lib/build.gradle.kts b/statsd-lib/build.gradle.kts new file mode 100644 index 00000000000..163eba79ee3 --- /dev/null +++ b/statsd-lib/build.gradle.kts @@ -0,0 +1,30 @@ +apply(plugin = "java") + +repositories { + mavenCentral() +} + +dependencies { + implementation(project(":cloudfoundry-identity-metrics-data")) + implementation(libs.springBootStarter) + implementation(libs.springBootStarterWeb) + implementation(libs.springBootStarterLog4j2) + implementation(libs.statsdClient) + implementation(libs.jacksonDataformatYaml) + implementation(libs.jacksonDatabind) + testImplementation(libs.mockitoJunit5) + testImplementation(libs.bytebuddy) + testImplementation(libs.bytebuddyAgent) +} + +tasks.test { + exclude("org/cloudfoundry/identity/statsd/integration/*.class") + exclude("**/*IT.class") +} + +tasks.named("integrationTest") { + filter { + includeTestsMatching("org.cloudfoundry.identity.statsd.integration.*") + includeTestsMatching("*IT") + } +} diff --git a/statsd/build.gradle b/statsd/build.gradle deleted file mode 100644 index fa380622721..00000000000 --- a/statsd/build.gradle +++ /dev/null @@ -1,31 +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(libraries.springBootStarterWeb) - providedCompile(libraries.tomcatEmbed) - providedRuntime(libraries.springBootStarterTomcat) -} - -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..f54ebd780bc --- /dev/null +++ b/statsd/build.gradle.kts @@ -0,0 +1,33 @@ +apply(plugin = "java") +apply(plugin = "war") +plugins { + alias(libs.plugins.springBoot) +} + +tasks.named("war") { + archiveBaseName.set("cloudfoundry-identity-statsd") +} + +repositories { + mavenCentral() +} + +dependencies { + implementation(project(":cloudfoundry-identity-statsd-lib")) + implementation(libs.springBootStarterWeb) + compileOnly(libs.tomcatEmbed) + runtimeOnly(libs.springBootStarterTomcat) +} + +tasks.test { + exclude("org/cloudfoundry/identity/statsd/integration/*.class") + exclude("**/*IT.class") +} + +tasks.named("integrationTest") { + onlyIf { false } //disable since we don't have any +} + +tasks.named("bootRun") { + enabled = false +} diff --git a/uaa/build.gradle b/uaa/build.gradle deleted file mode 100644 index c762d862acf..00000000000 --- a/uaa/build.gradle +++ /dev/null @@ -1,321 +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://repo.spring.io/libs-milestone") } - 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(libraries.springSecurityConfig) - implementation(libraries.springSecurityWeb) - implementation(libraries.springBootStarter) - implementation(libraries.springBootStarterWeb) - implementation(libraries.thymeLeaf) { - exclude(module: "ognl") - } - implementation(libraries.thymeleafSpring) { - exclude(module: "ognl") - } - implementation(libraries.thymeleafDialect) { - exclude(module: "ognl") - } - implementation(libraries.thymeleafExtrasSpringSecurity) { - exclude(module: "ognl") - } - runtimeOnly(libraries.springSecurityConfig) - runtimeOnly(libraries.springRetry) - runtimeOnly(libraries.aspectJWeaver) - runtimeOnly(libraries.postgresql) - - implementation(libraries.braveInstrumentation) - implementation(libraries.braveContextSlf4j) - - implementation(libraries.springWeb) - implementation(libraries.springWebMvc) - - providedCompile(libraries.tomcatEmbed) - implementation(libraries.bouncyCastleFipsProv) - - testImplementation(identityServer.sourceSets.test.output) - - testImplementation(project(":cloudfoundry-identity-model")) - testImplementation(project(":cloudfoundry-identity-metrics-data")) - testImplementation(libraries.apacheDsProtocolLdap) { - exclude(module: "bcprov-jdk15") - exclude(module: "slf4j-api") - exclude(module: "slf4j-log4j12") - } - testImplementation(libraries.apacheLdapApi) { - exclude(module: "slf4j-api") - } - testImplementation(libraries.flywayCore) - testImplementation(libraries.hibernateValidator) - testImplementation(libraries.selenium) - testImplementation(libraries.seleniumRemoteDriver) - testImplementation(libraries.dumbster) { - exclude(module: "mail") - exclude(module: "activation") - } - testImplementation(libraries.orgJson) - testImplementation(libraries.jsonAssert) - testImplementation(libraries.jsonPathAssert) - testImplementation(libraries.unboundIdScimSdk) { - exclude(module: "servlet-api") - exclude(module: "commons-logging") - exclude(module: "httpclient") - exclude(module: "wink-client-apache-httpclient") - } - testImplementation(libraries.springBootStarterLog4j2) - testImplementation(libraries.springContextSupport) - testImplementation(libraries.springSessionJdbc) - testImplementation(libraries.springTest) - testImplementation(libraries.springSecurityLdap) - testImplementation(libraries.springSecurityTest) - testImplementation(libraries.springBootStarterMail) - testImplementation(libraries.mockitoJunit5) - testImplementation(libraries.bytebuddy) - testImplementation(libraries.bytebuddyagent) - testImplementation(libraries.tomcatJdbc) - testImplementation(libraries.springRestdocs) - testImplementation(libraries.greenmail) - testImplementation(libraries.jodaTime) - testImplementation(libraries.commonsIo) - testImplementation(libraries.apacheHttpClient) - testImplementation(libraries.openSamlApi) - testImplementation(libraries.xmlUnit) - testImplementation(libraries.awaitility) - testImplementation(libraries.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' : '21', - '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) - } - - doFirst { - if (System.getProperty("skipUaaAutoStart", "false").toBoolean()) { - logger.lifecycle("Skipping UAA auto-start (skipUaaAutoStart=true)") - 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') - - logger.lifecycle("Starting UAA application for integration tests...") - - 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") - - 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 = 120 - 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 (System.getProperty("skipUaaAutoStart", "false").toBoolean()) { - logger.lifecycle("Skipping UAA auto-stop (skipUaaAutoStart=true)") - 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..7343c0f9cd5 --- /dev/null +++ b/uaa/build.gradle.kts @@ -0,0 +1,370 @@ +import java.io.File + +apply(plugin = "war") + +tasks.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 -> if (line.contains("\${project.artifactId}")) line.replace("\${project.artifactId}", "cloudfoundry-identity-uaa") else line } +} + +// Apply Spring Boot plugin - using plugins block ensures version from catalog +plugins { + alias(libs.plugins.springBoot) +} +tasks.named("bootWar") { + archiveClassifier.set("") + //archiveClassifier = 'boot' + isEnabled = true +} +tasks.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://repo.spring.io/libs-milestone") + } + 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") + } + + implementation(libs.braveInstrumentation) + implementation(libs.braveContextSlf4j) + + implementation(libs.springWeb) + implementation(libs.springWebmvc) + implementation(libs.bouncycastleFipsProv) + + runtimeOnly(libs.springSecurityConfig) + runtimeOnly(libs.springRetry) + runtimeOnly(libs.aspectjWeaver) + runtimeOnly(libs.postgresql) + + compileOnly(libs.tomcatEmbed) + + testImplementation(project(":cloudfoundry-identity-server", "testArtifacts")) + + testImplementation(project(":cloudfoundry-identity-model", "testArtifacts")) + testImplementation(project(":cloudfoundry-identity-metrics-data")) + testImplementation(libs.apachedsProtocolLdap) { + exclude(module = "bcprov-jdk15") + exclude(module = "slf4j-api") + exclude(module = "slf4j-log4j12") + } + testImplementation(libs.apacheApiLdapModel) { + exclude(module = "slf4j-api") + } + testImplementation(libs.flywayCore) + testImplementation(libs.hibernateValidator) + testImplementation(libs.selenium) + testImplementation(libs.seleniumRemoteDriver) + testImplementation(libs.dumbster) { + exclude(module = "mail") + exclude(module = "activation") + } + testImplementation(libs.json) + 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.mockitoJunit5) + testImplementation(libs.bytebuddy) + testImplementation(libs.bytebuddyAgent) + testImplementation(libs.tomcatJdbc) + testImplementation(libs.springRestdocs) + testImplementation(libs.greenmail) + testImplementation(libs.jodaTime) + testImplementation(libs.commonsIo) + testImplementation(libs.apacheHttpClient) + testImplementation(libs.opensamlSamlApi) + testImplementation(libs.xmlUnit) + testImplementation(libs.awaitility) + testImplementation(libs.nimbusJwt) +} + +val snippetsDir = file("build/generated-snippets") + +tasks.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 = Math.max(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 = Math.min(availableCpus.toInt(), 4) +} + +tasks.register("populateVersionfile") { + description = "Setup Docs Version File" + group = JavaBasePlugin.DOCUMENTATION_GROUP + + val versionFile = File("$projectDir/slateCustomizations/source/versionfile") + versionFile.createNewFile() + require(versionFile.exists()) + versionFile.writeText(version.toString().substring(0, version.toString().lastIndexOf(".")) + ".0") +} + +tasks.register("customizeSlate") { + description = "Customize Slate for Docs" + group = JavaBasePlugin.DOCUMENTATION_GROUP + + dependsOn(tasks.named("populateVersionfile")) + from("slate") + from("slateCustomizations") + into("build/slate") +} + +tasks.register("docsTestRestDocs") { + description = "Builds Rest API Docs" + group = JavaBasePlugin.DOCUMENTATION_GROUP + + 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", snippetsDir.path) +} + +tasks.register("gemInstallBundle") { + description = "Installs the Ruby Gems for API Docs" + group = JavaBasePlugin.DOCUMENTATION_GROUP + + dependsOn("customizeSlate") + workingDir = file("build/slate") + executable = "gem" + args("install", "bundler:2.7.1") +} + +tasks.register("bundleInstall") { + description = "Installs the Ruby Bundle for API Docs" + group = JavaBasePlugin.DOCUMENTATION_GROUP + + dependsOn(tasks.named("gemInstallBundle")) + dependsOn("customizeSlate") + workingDir = file("build/slate") + executable = "bundle" + args("install") +} + +tasks.register("deleteDefaultContent") { + description = "Cleanup API Docs" + group = JavaBasePlugin.DOCUMENTATION_GROUP + + delete("build/slate/source/index.html.md") +} + +tasks.register("slate") { + description = "Setup Slate for Docs" + group = JavaBasePlugin.DOCUMENTATION_GROUP + + 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.named("generateDocs") { + description = "Generate API Docs" + group = JavaBasePlugin.DOCUMENTATION_GROUP + + dependsOn(tasks.named("slate")) +} + +// Gradle bootBuildImage task configuration +tasks.named("bootBuildImage") { + description = "Build the Boot Docker Image" + group = LifecycleBasePlugin.BUILD_GROUP + + imageName.set("cloudfoundry/uaa:${version}") + var doDebug = false + val activeSpringProfiles = (System.getProperty("spring.profiles.active", "")?.split(",") ?: emptyList()) as List + if (activeSpringProfiles.contains("debugs") || System.getProperty("xdebugs", "false").toBoolean() || + activeSpringProfiles.contains("debug") || System.getProperty("xdebug", "false").toBoolean()) { + doDebug = true + } + // https://github.com/paketo-buildpacks/bellsoft-liberica + // use JRE since JDK gets too big + environment.putAll(mapOf( + "BP_SPRING_CLOUD_BINDINGS_DISABLED" to "true", + "BP_JVM_VERSION" to "21", + "BP_JVM_TYPE" to (System.getenv("BP_JVM_TYPE") ?: project.findProperty("BP_JVM_TYPE")?.toString() ?: "JRE"), + "BP_JVM_PLATFORM" to "liberica" + )) + if (doDebug) { + environment.put("BPL_DEBUG_ENABLED", "true") + environment.put("BPL_DEBUG_PORT", "5005") + } +} + +tasks.named("integrationTest") { + description = "Runs UAA Integration Tests" + group = JavaBasePlugin.VERIFICATION_GROUP + + // 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()?.isNotEmpty() == true) { + systemProperty("integration.test.saml.url", samlUrl) + project.logger.warn("UAA - Overriding SAML Url:$samlUrl") + } + + doFirst { + if (System.getProperty("skipUaaAutoStart", "false").toBoolean()) { + logger.lifecycle("Skipping UAA auto-start (skipUaaAutoStart=true)") + return@doFirst + } + logger.lifecycle("Killing UAA before auto-start") + ProcessBuilder("bash", "scripts/kill_uaa.sh").directory(rootProject.projectDir).start().waitFor() + + val bootPidFile = rootProject.file("build/boot.pid") + val bootLogFile = rootProject.file("build/boot.log") + bootPidFile.delete() + bootLogFile.delete() + + logger.lifecycle("Starting UAA application for integration tests...") + + 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") + + val javaCmd = """nohup java \ + -DCLOUDFOUNDRY_CONFIG_PATH=${bootDir.absolutePath} \ + -DSECRETS_DIR=${bootDir.absolutePath} \ + -Dserver.servlet.context-path=/uaa \ + -Dlogging.config=scripts/boot/log4j2.properties \ + -Dsmtp.host=localhost \ + -Dsmtp.port=2525 \ + -Dspring.profiles.active=${springProfile} \ + -jar ${warFile.absolutePath} > ${bootLogFile.absolutePath} 2>&1 & echo $!""" + + val process = ProcessBuilder("bash", "-c", javaCmd).directory(rootProject.projectDir).start() + process.waitFor() + val pid = process.inputStream.bufferedReader().readText().trim() + bootPidFile.writeText(pid.toString()) + logger.lifecycle("UAA starting with PID: ${pid}, waiting for it to be ready...") + + val maxWaitSeconds = 120 + val startTime = System.currentTimeMillis() + while (System.currentTimeMillis() - startTime < maxWaitSeconds * 1000) { + if (bootLogFile.exists() && bootLogFile.readText().contains("Started UaaBootApplication")) { + val startUpTime = (System.currentTimeMillis() - startTime) / 1000 + logger.lifecycle("UAA is ready! ${startUpTime}s") + return@doFirst + } + Thread.sleep(1000) + } + + logger.lifecycle("Killing UAA after failed auto-start") + process.destroy(); + Thread.sleep(1000) + if (process.isAlive()) { + logger.lifecycle("Forcibly Killing") + process.destroyForcibly(); + } + throw org.gradle.api.GradleException("UAA failed to start within $maxWaitSeconds seconds. Check ${bootLogFile.absolutePath}") + } + + doLast { + if (System.getProperty("skipUaaAutoStart", "false").toBoolean()) { + logger.lifecycle("Skipping UAA auto-stop (skipUaaAutoStart=true)") + return@doLast + } + + logger.lifecycle("Stopping UAA application...") + ProcessBuilder("bash", "scripts/kill_uaa.sh").directory(rootProject.projectDir).start().waitFor() + rootProject.file("build/boot.pid").delete() + } + + dependsOn(tasks.named("bootWar")) +} + +tasks.named("bootRun") { + description = "Runs UAA as a boot application" + group = ApplicationPlugin.APPLICATION_GROUP + + 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")?.toString() ?: "5005" + // debugs = suspend and wait for debugger, debug = start immediately + val suspend = if (project.hasProperty("debugs")) "y" else "n" + jvmArgs.add("-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}") + } +}