/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ description = 'Fineract Provider' apply plugin: 'org.zeroturnaround.gradle.jrebel' apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot' apply plugin: 'com.gorylenko.gradle-git-properties' apply plugin: 'io.swagger.core.v3.swagger-gradle-plugin' apply plugin: 'com.google.cloud.tools.jib' apply plugin: 'org.springframework.boot' apply plugin: 'se.thinkcode.cucumber-runner' check.dependsOn('cucumber') tasks.register('staticWeaveJpa', JavaExec) { dependsOn classes def classesDir = layout.buildDirectory.dir("classes/java/main").get().asFile def outputDir = layout.buildDirectory.dir("weaved-classes").get().asFile def persistenceInfo = layout.buildDirectory.dir('static-weaving').get() copy { from file("src/main/resources/jpa/static-weaving/module/provider/persistence.xml") into "${persistenceInfo}/META-INF/" } mainClass.set("org.eclipse.persistence.tools.weaving.jpa.StaticWeave") classpath = sourceSets.main.runtimeClasspath args = [ '-persistenceinfo', persistenceInfo, classesDir.absolutePath, outputDir.absolutePath ] } tasks.register("weavedJar", Jar) { archiveClassifier.set("") dependsOn staticWeaveJpa duplicatesStrategy = DuplicatesStrategy.EXCLUDE from(layout.buildDirectory.dir("weaved-classes")) exclude("META-INF/persistence.xml") } tasks.register("weavedPlainJar", Jar) { archiveClassifier.set("plain") dependsOn staticWeaveJpa duplicatesStrategy = DuplicatesStrategy.EXCLUDE from(layout.buildDirectory.dir("weaved-classes")) exclude("META-INF/persistence.xml") } // Configure the default jar task to depend on weavedJar and use its output tasks.named('jar') { dependsOn weavedPlainJar enabled = false // Disable the default jar task // The weavedJar will be used as the primary artifact } // Make sure the assemble task depends on weavedJar tasks.named('assemble') { dependsOn weavedJar } tasks.named('staticWeaveJpa') { mustRunAfter tasks.named('resolve') } compileJava { dependsOn ':fineract-avro-schemas:buildJavaSdk' } // Configuration for Swagger documentation generation task // https://github.com/swagger-api/swagger-core/tree/master/modules/swagger-gradle-plugin import org.apache.tools.ant.filters.ReplaceTokens tasks.register('prepareInputYaml') { outputs.file("${buildDir}/tmp/swagger/fineract-input.yaml") doLast { copy { from file("${projectDir}/config/swagger/fineract-input.yaml.template") into file("${buildDir}/tmp/swagger") rename { String filename -> return 'fineract-input.yaml' } filter(ReplaceTokens, tokens: [VERSION: "${project.version}".toString()]) } } } resolve { logging.captureStandardOutput LogLevel.INFO outputFileName = 'fineract' outputFormat = 'JSONANDYAML' prettyPrint = false classpath = sourceSets.main.runtimeClasspath buildClasspath = classpath outputDir = file("${buildDir}/classes/java/main/static") openApiFile = file("${buildDir}/tmp/swagger/fineract-input.yaml") sortOutput = true dependsOn(prepareInputYaml) } configurations { providedRuntime // needed for Spring Boot executable WAR providedCompile implementation testImplementation cucumberRuntime { extendsFrom testImplementation } compile() { exclude module: 'hibernate-entitymanager' exclude module: 'hibernate-validator' exclude module: 'activation' exclude module: 'bcmail-jdk14' exclude module: 'bcprov-jdk14' exclude module: 'bctsp-jdk14' exclude module: 'c3p0' exclude module: 'stax-api' exclude module: 'jaxb-api' exclude module: 'jaxb-impl' exclude module: 'jboss-logging' exclude module: 'itext-rtf' exclude module: 'classworlds' } runtime } dependencies { implementation project(':fineract-core') implementation 'org.springframework.boot:spring-boot-starter-test' implementation 'org.mockito:mockito-core' implementation 'org.mockito:mockito-junit-jupiter' implementation 'org.junit.jupiter:junit-jupiter-api' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.liquibase:liquibase-core' } apply from: 'dependencies.gradle' // Configuration for the modernizer plugin // https://github.com/andygoossens/gradle-modernizer-plugin modernizer { ignoreClassNamePatterns = [ '.*AbstractPersistableCustom', '.*EntityTables', '.*domain.*' ] } // If we are running Gradle within Eclipse to enhance classes with OpenJPA, // set the classes directory to point to Eclipse's default build directory if (project.hasProperty('env') && project.getProperty('env') == 'eclipse') { sourceSets.main.java.outputDir = new File(rootProject.projectDir, "fineract-provider/bin/main") } eclipse { project { buildCommand([ LaunchConfigHandle: "/.externalToolBuilders/OpenJPA Enhance Builder.launch" ], 'org.eclipse.ui.externaltools.ExternalToolBuilder') } } if (!(project.hasProperty('env') && project.getProperty('env') == 'dev')) { sourceSets { test { java { exclude '**/core/boot/tests/**' } } } } // Configuration for SQL tasks // https://docs.groovy-lang.org/latest/html/api/groovy/sql/Sql.html import groovy.sql.Sql project.ext.mysqlUser='root' project.ext.mysqlPassword='mysql' project.ext.pgUser='root' project.ext.pgPassword='postgres' configurations { driver } dependencies { driver 'org.mariadb.jdbc:mariadb-java-client' driver 'org.postgresql:postgresql' driver 'com.mysql:mysql-connector-j' } URLClassLoader loader = GroovyObject.class.classLoader configurations.driver.each {File file -> loader.addURL(file.toURL()) } tasks.register('createDB') { description = "Creates the MariaDB Database. Needs database name to be passed (like: -PdbName=someDBname)" doLast { def sql = Sql.newInstance('jdbc:mariadb://localhost:3306/', mysqlUser, mysqlPassword, 'org.mariadb.jdbc.Driver') sql.execute('CREATE DATABASE ' + "`$dbName` CHARACTER SET utf8mb4") } } tasks.register('dropDB') { description = "Drops the specified MariaDB database. The database name has to be passed (like: -PdbName=someDBname)" doLast { def sql = Sql.newInstance('jdbc:mariadb://localhost:3306/', mysqlUser, mysqlPassword, 'org.mariadb.jdbc.Driver') sql.execute('DROP DATABASE ' + "`$dbName`") } } tasks.register('createPGDB') { description = "Creates the PostgreSQL Database. Needs database name to be passed (like: -PdbName=someDBname)" doLast { def sql = Sql.newInstance('jdbc:postgresql://localhost:5432/', pgUser, pgPassword, 'org.postgresql.Driver') sql.execute('create database ' + "$dbName") } } tasks.register('dropPGDB') { description = "Drops the specified PostgreSQL database. The database name has to be passed (like: -PdbName=someDBname)" doLast { def sql = Sql.newInstance('jdbc:postgresql://localhost:5432/', pgUser, pgPassword, 'org.postgresql.Driver') sql.execute('DROP DATABASE ' + "$dbName") } } tasks.register('createMySQLDB') { description = "Creates the MySQL Database. Needs database name to be passed (like: -PdbName=someDBname)" doLast { def sql = Sql.newInstance('jdbc:mysql://localhost:3306/', mysqlUser, mysqlPassword, 'com.mysql.cj.jdbc.Driver') sql.execute('CREATE DATABASE ' + "`$dbName` CHARACTER SET utf8mb4") } } tasks.register('dropMySQLDB') { description = "Drops the specified MySQL database. The database name has to be passed (like: -PdbName=someDBname)" doLast { def sql = Sql.newInstance('jdbc:mysql://localhost:3306/', mysqlUser, mysqlPassword, 'com.mysql.cj.jdbc.Driver') sql.execute('DROP DATABASE ' + "`$dbName`") } } tasks.register('setBlankPassword') { doLast { def sql = Sql.newInstance('jdbc:mariadb://localhost:3306/', mysqlUser, mysqlPassword, 'org.mariadb.jdbc.Driver') sql.execute('USE `fineract_tenants`') sql.execute('UPDATE fineract_tenants.tenants SET schema_server = \'localhost\', schema_server_port = \'3306\', schema_username = \'mifos\', schema_password = \'mysql\' WHERE id=1;') } } tasks.register("mergeWeavedOutput", Copy) { dependsOn staticWeaveJpa from layout.buildDirectory.dir("weaved-classes") into layout.buildDirectory.dir("merged-weaved-classes/java/main") // Optional: exclude files like persistence.xml exclude("META-INF/persistence.xml") } bootRun { dependsOn mergeWeavedOutput def weavedClasses = layout.buildDirectory.dir("weaved-classes").get().asFile classpath = files(weavedClasses) + sourceSets.main.runtimeClasspath.filter { file -> file.name.endsWith('.jar') } jvmArgs = [ "-Dspring.output.ansi.enabled=ALWAYS" ] dependencies { implementation 'org.mariadb.jdbc:mariadb-java-client' implementation 'org.postgresql:postgresql' } } springBoot { mainClass = 'org.apache.fineract.ServerApplication' } // Create a new weavedBootJar task that extends BootJar tasks.register("weavedBootJar", org.springframework.boot.gradle.tasks.bundling.BootJar) { group = "build" description = "Builds a bootable JAR with weaved classes" dependsOn staticWeaveJpa dependsOn resolve archiveClassifier.set("") duplicatesStrategy = DuplicatesStrategy.EXCLUDE //Required for bootable JAR mainClass.set("org.apache.fineract.ServerApplication") targetJavaVersion.set(JavaVersion.VERSION_21) // Replace default classes with weaved ones into("BOOT-INF/classes") { from(layout.buildDirectory.dir("weaved-classes")) } manifest { attributes( 'Main-Class': 'org.springframework.boot.loader.launch.PropertiesLauncher', 'Implementation-Title': 'Apache Fineract', 'Implementation-Version': project.version, 'Start-Class': 'org.apache.fineract.ServerApplication' ) } classpath configurations.runtimeClasspath } // Configure the bootJar task to include all dependencies tasks.withType(org.springframework.boot.gradle.tasks.bundling.BootJar) { // This ensures the launcher is included requiresUnpack '**/*.jar' // This ensures the launcher classes are included launchScript() } // Make assemble depend on weavedBootJar instead of bootJar tasks.named('assemble') { dependsOn 'weavedBootJar' } // Disable the default bootJar task bootJar { dependsOn weavedBootJar enabled = false } // Ensure jib uses the weaved classes tasks.named('jibDockerBuild') { dependsOn 'staticWeaveJpa' } // Ensure staticWeaveJpa runs before jib tasks.named('jib') { dependsOn 'staticWeaveJpa' } jib { from { image = 'azul/zulu-openjdk-alpine:21' platforms { platform { architecture = System.getProperty("os.arch").equals("aarch64")?"arm64":"amd64" os = 'linux' } } } to { image = 'fineract' tags = [ "${project.version}", 'latest' ] } container { creationTime = 'USE_CURRENT_TIMESTAMP' mainClass = 'org.apache.fineract.ServerApplication' extraClasspath = ['/app/plugins/*'] args = [ '-Duser.home=/tmp', '-Dfile.encoding=UTF-8', '-Duser.timezone=UTC', '-Djava.security.egd=file:/dev/./urandom' ] ports = ['8080/tcp', '8443/tcp'] labels = [maintainer: 'Aleksandar Vidakovic '] user = 'nobody:nogroup' } extraDirectories { paths { path { from = layout.buildDirectory.dir("weaved-classes").get().asFile into = '/app/classes' } } } allowInsecureRegistries = true dependencies { implementation 'org.mariadb.jdbc:mariadb-java-client' implementation 'org.postgresql:postgresql' } pluginExtensions { pluginExtension { implementation = 'com.google.cloud.tools.jib.gradle.extension.layerfilter.JibLayerFilterExtension' configuration { filters { filter { glob = '/app/resources/**' } } } } } } tasks.register('migrateDatabase') { doFirst { println 'Executing liquibase database migration to version ' + "$dbVersion" def dbUrl = 'jdbc:' + "$dbType" + '://' + "$dbHost" + ':' + "$dbPort" + '/' + "$dbName" def changeLogFilePath = 'fineract-provider/src/main/resources/db/changelog/tenant/upgrades/0000_upgrade_to_' + "$dbVersion" + '.xml' liquibase { activities { main { changeLogFile changeLogFilePath url dbUrl username project.ext.mysqlUser password project.ext.mysqlPassword } } } } } cucumber { tags = 'not @ignore' main = 'io.cucumber.core.cli.Main' shorten = 'argfile' plugin = [ 'pretty', 'html:build/reports/cucumber/report.html', 'json:build/reports/cucumber/report.json', 'junit:build/reports/cucumber/report.xml' ] } tasks.jibDockerBuild.dependsOn(bootJar) // Configuration for git properties gradle plugin // https://github.com/n0mer/gradle-git-properties gitProperties { gitPropertiesResourceDir = file("$buildDir/classes/java/main") dateFormat = "yyyy-MM-dd'T'HH:mmZ" dateFormatTimeZone = "GMT" failOnNoGitDirectory = false } // make sure the generateGitProperties task always executes (even when git.properties is not changed) generateGitProperties.outputs.upToDateWhen { false } // NOTE: Gradle suggested these dependencies jar.dependsOn resolve test.dependsOn resolve checkstyleMain.dependsOn resolve checkstyleTest.dependsOn resolve rat.dependsOn prepareInputYaml spotbugsTest.dependsOn resolve compileTestJava.dependsOn ':fineract-client:processResources', ':fineract-avro-schemas:processResources' resolveMainClassName.dependsOn resolve processResources.dependsOn compileJava javadoc { dependsOn resolve } tasks.register('devRun', org.springframework.boot.gradle.tasks.run.BootRun) { description = 'Runs the application quickly for development by skipping quality checks' group = 'Application' // Configure the build to skip quality checks gradle.taskGraph.whenReady { graph -> if (graph.hasTask(devRun)) { tasks.matching { task -> task.name in ['checkstyle', 'checkstyleMain', 'checkstyleTest', 'spotlessCheck', 'spotlessApply', 'spotbugsMain', 'spotbugsTest', 'javadoc', 'javadocJar', 'modernizer', 'testClasses'] }.configureEach { enabled = false } // Also disable error prone compilation flags tasks.withType(JavaCompile).configureEach { options.errorprone.enabled = false } } } // Inherit all bootRun settings classpath = bootRun.classpath mainClass = bootRun.mainClass jvmArgs = bootRun.jvmArgs doFirst { println "Running in development mode - quality checks are disabled" } } tasks.named("test") { dependsOn staticWeaveJpa def testClasses = layout.buildDirectory.dir("classes/java/test").get().asFile def weavedClasses = layout.buildDirectory.dir("weaved-classes").get().asFile def testResources = layout.buildDirectory.dir("resources/test").get().asFile classpath = files( weavedClasses, testClasses, testResources, configurations.testRuntimeClasspath ) } tasks.register("weavedCucumberTest", JavaExec) { group = "verification" description = "Runs Cucumber tests with weaved classes" dependsOn staticWeaveJpa, compileTestJava, processTestResources def weavedClasses = layout.buildDirectory.dir("weaved-classes").get().asFile def testClasses = layout.buildDirectory.dir("classes/java/test").get().asFile def testResources = layout.buildDirectory.dir("resources/test").get().asFile classpath = files( weavedClasses, testClasses, testResources, configurations.testRuntimeClasspath ) mainClass.set("io.cucumber.core.cli.Main") // Cucumber JUnit 5 CLI entrypoint args = [ '--glue', 'org.apache.fineract', // your step defs package 'src/test/resources/features' // your feature files path ] jvmArgs = ["-Dspring.output.ansi.enabled=ALWAYS"] } tasks.named("cucumber") { dependsOn weavedCucumberTest enabled = false }