blob: f38c685a4d6ed06f015f18c89f4232782037610b [file] [log] [blame]
/*
* 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.
*/
import at.bxm.gradleplugins.svntools.tasks.SvnCheckout
import org.apache.tools.ant.filters.ReplaceTokens
import org.asciidoctor.gradle.jvm.AsciidoctorTask
/* ========================================================
* Project setup
* ======================================================== */
plugins {
id 'application'
id 'groovy'
id 'eclipse'
id 'checkstyle'
id 'codenarc'
id 'maven-publish'
id 'at.bxm.svntools' version '3.1'
id 'org.asciidoctor.jvm.convert' version '3.3.2'
id 'org.asciidoctor.jvm.pdf' version '3.3.2'
id 'org.owasp.dependencycheck' version '7.4.4' apply false
id 'se.patrikerdes.use-latest-versions' version '0.2.18' apply false
id 'com.github.ben-manes.versions' version '0.42.0' apply false
id "com.github.ManifestClasspath" version "0.1.0-RELEASE"
id "com.github.jakemarsden.git-hooks" version "0.0.2"
id "com.github.node-gradle.node" version '3.5.1' apply false
}
/* OWASP plugin
*
* If project property "enableOwasp" is flagged then
* gradle will download required dependencies and
* activate Gradle's OWASP plugin and its related tasks.
*
* Syntax: gradlew -PenableOwasp dependencyCheckAnalyze
*/
if (project.hasProperty('enableOwasp')) {
apply plugin: 'org.owasp.dependencycheck'
}
/* DependencyUpdates plugin
*
* If project property "enableDependencyUpdates" is flagged then
* gradle will download required dependencies and
* activate Gradle's DependencyUpdates plugin and its related tasks.
*
* Syntax: gradlew -PenableDependencyUpdates dependencyUpdates -Drevision=release
*
* You may want to use the use-latest-versions plugin to help you in your work
* Syntax:
* Check only: gradlew -PenableDependencyUpdates useLatestVersions && gradlew -PenableDependencyUpdates useLatestVersionsCheck
* Automated update: gradlew -PenableDependencyUpdates useLatestVersions
* Beware that this is only a help.
* If you use it without check you will need to check things by yourself (can be as tedious as not using this plugin)
*/
if (project.hasProperty('enableDependencyUpdates')) {
apply plugin: 'com.github.ben-manes.versions'
apply plugin: 'se.patrikerdes.use-latest-versions'
}
apply from: 'common.gradle'
// global properties
ext.os = System.getProperty('os.name').toLowerCase()
ext.gradlew = os.contains('windows') ? 'gradlew.bat' : './gradlew'
ext.pluginsDir = "${rootDir}/plugins"
application {
mainClassName = 'org.apache.ofbiz.base.start.Start'
applicationDefaultJvmArgs = project.hasProperty('jvmArgs')
? jvmArgs.tokenize()
: ['-Xms128M','-Xmx1024M',
'-Djdk.serialFilter=maxarray=100000;maxdepth=20;maxrefs=1000;maxbytes=500000', // OFBIZ-12592 and OFBIZ-12716
'--add-exports=java.base/sun.util.calendar=ALL-UNNAMED', // OFBIZ-12721
'--add-opens=java.base/java.util=ALL-UNNAMED' // OFBIZ-12726
]
}
distributions.main.contents.from(rootDir) {
include 'framework/**', 'applications/**', 'themes/**', 'plugins/**'
}
javadoc {
title="OFBiz " + getCurrentGitBranch() + " API"
failOnError = true
options {
source '17'
encoding 'UTF-8'
charSet 'UTF-8'
// Those external Javadoc links should correspond to the actual
// versions declared in the 'dependencies' block.
links(
'https://docs.oracle.com/javase/17/docs/api',
'https://tomcat.apache.org/tomcat-9.0-doc/servletapi/',
'http://docs.groovy-lang.org/docs/groovy-3.0.13/html/api',
'https://commons.apache.org/proper/commons-cli/apidocs'
)
}
}
java {
sourceCompatibility(JavaVersion.VERSION_17)
targetCompatibility(JavaVersion.VERSION_17)
}
// Java compile options, syntax gradlew -PXlint:none build
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
if (!project.hasProperty('Xlint:none')) {
options.compilerArgs << '-Xlint:all'
// Exclude varargs warnings which are not silenced by @SafeVarargs.
options.compilerArgs << '-Xlint:-varargs'
}
}
// Only used for release branches
def getCurrentGitBranch() {
return "git branch --show-current".execute().text.trim()
}
// defines the footer files for git info
def File gitFooterFile = file("${rootDir}/runtime/GitInfo.ftl")
// root and subproject settings
defaultTasks 'jar', 'test'
allprojects {
repositories {
mavenCentral()
// the switch from jCenter to mavenCentral needs some additional repositories to be configured here
// this should be checked frequently to remove obsolete configurations if the artifacts are available
// on mavenCentral directly
maven {
// org.restlet and org.restlet.ext.servlet
url 'https://maven.restlet.talend.com'
}
maven {
// net.fortuna.ical4j:ical4j:1.0-rc3-atlassian-11
url 'https://packages.atlassian.com/maven-3rdparty/'
}
maven {
// org/milyn/flute/1.3/flute-1.3.jar
// need artifact only because of wrong pom metadata in maven central
// Required by: plugins:birt > org.eclipse.birt.runtime:viewservlets:4.5.0 > org.eclipse.birt.runtime:org.eclipse.birt.runtime:4.4.1
// TODO Maybe this will no longer needed wheh upgrading viewservlets to 4.9.0
url "https://repo1.maven.org/maven2"
metadataSources {
artifact()
}
}
maven {
url 'https://clojars.org/repo'
}
maven {
// org.cyberneko.html.parsers (used by UELFunctions, was in esapi before 2.3)
url "https://repository.ow2.org/nexus/content/repositories/public/"
}
}
}
subprojects {
configurations {
// compile-time plugin libraries
pluginLibsCompile
// runtime plugin libraries
pluginLibsRuntime
//compile-only libraries
pluginLibsCompileOnly
}
}
configurations {
junitReport {
description = 'libraries needed to run junitreport for OFBiz unit tests'
}
ofbizPlugins {
description = 'ofbiz plugin dependencies configuration'
transitive = true
}
}
configurations.all {
exclude group: 'log4j', module: 'log4j'
exclude group: 'org.slf4j', module: 'slf4j-log4j12'
exclude group: 'xml-apis', module: 'xml-apis'
exclude group: 'jaxen', module: 'jaxen'
exclude group: 'javax.xml.stream', module: 'stax-api'
exclude group: 'org.apache.geronimo.specs', module: 'geronimo-stax-api_1.0_spec'
exclude group: 'org.apache.geronimo.specs', module: 'geronimo-jta_1.1_spec'
exclude group: 'javax.transaction', module: 'jta'
}
dependencies {
implementation 'com.github.ben-manes.caffeine:caffeine:3.1.1'
implementation 'com.google.zxing:core:3.5.1'
implementation 'com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2'
implementation 'com.googlecode.ez-vcard:ez-vcard:0.11.3'
implementation 'com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20220608.1'
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.57'
implementation 'com.ibm.icu:icu4j:72.1'
implementation ('com.lowagie:itext:2.1.7') { // Don't update due to license change in newer versions, see OFBIZ-10455
exclude group: 'bouncycastle', module: 'bcmail-jdk14'
exclude group: 'bouncycastle', module: 'bcprov-jdk14'
exclude group: 'bouncycastle', module: 'bctsp-jdk14'
}
implementation 'com.sun.mail:javax.mail:1.6.2'
implementation 'com.rometools:rome:1.18.0'
implementation 'com.thoughtworks.xstream:xstream:1.4.19'
implementation 'commons-cli:commons-cli:1.5.0'
implementation 'commons-fileupload:commons-fileupload:1.5'
implementation 'commons-net:commons-net:3.8.0'
implementation 'commons-validator:commons-validator:1.7'
implementation 'de.odysseus.juel:juel-impl:2.2.7'
implementation 'javax.transaction:javax.transaction-api:1.3'
implementation 'net.fortuna.ical4j:ical4j:1.0-rc4-atlassian-12'
implementation 'net.lingala.zip4j:zip4j:2.11.2'
implementation 'org.apache.ant:ant-junit:1.10.12'
implementation 'org.apache.commons:commons-collections4:4.4'
implementation 'org.apache.commons:commons-csv:1.9.0'
implementation 'org.apache.commons:commons-dbcp2:2.9.0'
implementation 'org.apache.commons:commons-imaging:1.0-alpha3' // Alpha but OK, "Imaging was working and was used by a number of projects in production even before reaching its initial release as an Apache Commons component."
implementation 'org.apache.commons:commons-text:1.10.0'
implementation 'org.apache.geronimo.components:geronimo-transaction:3.1.5'
implementation 'org.apache.geronimo.specs:geronimo-jms_1.1_spec:1.1.1'
implementation 'org.apache.httpcomponents:httpclient-cache:4.5.13'
implementation 'org.apache.logging.log4j:log4j-api:2.19.0' // the API of log4j 2
implementation 'org.apache.logging.log4j:log4j-core:2.19.0' // Somehow needed by Buildbot to compile OFBizDynamicThresholdFilter.java
implementation 'org.apache.poi:poi:4.1.2' // poi-ooxml-schemas-5.0.0.pom'. Received status code 401 from server
implementation 'org.apache.pdfbox:pdfbox:2.0.27'
implementation 'org.apache.shiro:shiro-core:1.10.1'
implementation 'org.apache.sshd:sshd-core:2.9.1'
implementation 'org.apache.sshd:sshd-sftp:2.9.1'
implementation 'org.apache.tika:tika-core:2.5.0'
implementation 'org.apache.tika:tika-parsers:2.5.0'
implementation 'org.apache.tika:tika-parser-pdf-module:2.5.0'
implementation 'org.apache.cxf:cxf-rt-frontend-jaxrs:3.5.4'
implementation 'org.apache.tomcat:tomcat-catalina-ha:9.0.74' // Remember to change the version number (9 now) in javadoc block if needed.
implementation 'org.apache.tomcat:tomcat-jasper:9.0.74'
implementation 'org.apache.axis2:axis2-kernel:1.8.2'
implementation 'org.apache.xmlgraphics:batik-anim:1.14'
implementation 'org.apache.xmlgraphics:batik-util:1.14'
implementation 'org.apache.xmlgraphics:batik-bridge:1.14'
implementation 'org.apache.xmlgraphics:fop:2.3' // NOTE: since 2.4 dependencies are messed up. See https://github.com/moqui/moqui-fop/blob/master/build.gradle
implementation 'org.clojure:clojure:1.11.1'
implementation 'org.codehaus.groovy:groovy-all:3.0.13'
implementation 'org.freemarker:freemarker:2.3.32' // Remember to change the version number in FreeMarkerWorker class when upgrading. See OFBIZ-10019 if >= 2.4
implementation 'org.owasp.esapi:esapi:2.5.0.0'
implementation 'org.cyberneko:html:1.9.8'
implementation 'org.springframework:spring-test:5.3.23'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.4.2'
implementation 'oro:oro:2.0.8'
implementation 'wsdl4j:wsdl4j:1.6.3'
implementation 'com.auth0:java-jwt:4.2.1'
implementation 'org.jdom:jdom:1.1.3' // don't upgrade above 1.1.3, makes a lot of not obvious and useless complications, see last commits of OFBIZ-12092 for more
implementation 'com.google.re2j:re2j:1.7'
implementation 'xerces:xercesImpl:2.12.2'
testImplementation 'org.hamcrest:hamcrest-library:2.2' // Enable junit4 to not depend on hamcrest-1.3
testImplementation 'org.mockito:mockito-core:4.8.1'
testImplementation 'org.jmockit:jmockit:1.49'
testImplementation 'com.pholser:junit-quickcheck-generators:1.0'
runtimeOnly 'javax.xml.soap:javax.xml.soap-api:1.4.0'
runtimeOnly 'de.odysseus.juel:juel-spi:2.2.7'
runtimeOnly 'net.sf.barcode4j:barcode4j-fop-ext:2.1'
runtimeOnly 'net.sf.barcode4j:barcode4j:2.1'
runtimeOnly 'org.apache.axis2:axis2-transport-http:1.8.2'
runtimeOnly 'org.apache.axis2:axis2-transport-local:1.8.2'
runtimeOnly 'org.apache.derby:derby:10.14.2.0' // So far we did not update from 10.14.2.0 because of a runtime issue with 10.16.1.1: java.lang.ClassNotFoundException: org.apache.derby.jdbc.EmbeddedDriver
runtimeOnly 'org.apache.geronimo.specs:geronimo-jaxrpc_1.1_spec:2.1'
runtimeOnly 'org.apache.logging.log4j:log4j-1.2-api:2.19.0' // for external jars using the old log4j1.2: routes logging to log4j 2
runtimeOnly 'org.apache.logging.log4j:log4j-jul:2.19.0' // for external jars using the java.util.logging: routes logging to log4j 2
runtimeOnly 'org.apache.logging.log4j:log4j-slf4j-impl:2.19.0' // for external jars using slf4j: routes logging to log4j 2
runtimeOnly 'org.apache.logging.log4j:log4j-web:2.19.0' //???
runtimeOnly 'org.apache.logging.log4j:log4j-jcl:2.19.0' // need to constrain to version to avoid classpath conflict (ReflectionUtil)
// Dependencies defined by the plugins
subprojects.each { subProject ->
implementation project(path: subProject.path, configuration: 'pluginLibsCompile')
runtimeOnly project(path: subProject.path, configuration: 'pluginLibsRuntime')
compileOnly project(path: subProject.path, configuration: 'pluginLibsCompileOnly')
}
junitReport 'junit:junit:4.13.2'
junitReport 'org.apache.ant:ant-junit:1.10.12'
// Libraries downloaded manually
implementation fileTree(dir: file("${rootDir}/lib"), include: '**/*.jar')
getDirectoryInActiveComponentsIfExists('lib').each { libDir ->
implementation fileTree(dir: libDir, include: '**/*.jar')
}
// specify last codenarc version for java 17 compliance
codenarc('org.codenarc:CodeNarc:3.2.0')
}
def excludedJavaSources = [
'org/apache/ofbiz/accounting/thirdparty/cybersource/IcsPaymentServices.java',
'org/apache/ofbiz/accounting/thirdparty/orbital/OrbitalPaymentServices.java',
'org/apache/ofbiz/accounting/thirdparty/paypal/PayPalServices.java',
'org/apache/ofbiz/accounting/thirdparty/securepay/SecurePayPaymentServices.java',
'org/apache/ofbiz/accounting/thirdparty/securepay/SecurePayServiceTest.java',
'org/apache/ofbiz/accounting/thirdparty/verisign/PayflowPro.java',
'org/apache/ofbiz/order/thirdparty/taxware/TaxwareException.java',
'org/apache/ofbiz/order/thirdparty/taxware/TaxwareServices.java',
'org/apache/ofbiz/order/thirdparty/taxware/TaxwareUTL.java'
]
sourceSets {
main {
java {
srcDirs = getDirectoryInActiveComponentsIfExists('src/main/java')
exclude excludedJavaSources
}
groovy {
srcDirs = getDirectoryInActiveComponentsIfExists('src/main/groovy')
}
resources {
srcDirs = getDirectoryInActiveComponentsIfExists('src/main/resources')
srcDirs += getDirectoryInActiveComponentsIfExists('config')
srcDirs += getDirectoryInActiveComponentsIfExists('dtd')
}
}
test {
java {
srcDirs = getDirectoryInActiveComponentsIfExists('src/test/java')
}
groovy {
srcDirs = getDirectoryInActiveComponentsIfExists('src/test/groovy')
}
resources {
srcDirs = getDirectoryInActiveComponentsIfExists('src/test/resources')
}
}
groovyScripts {
groovy {
srcDirs += getDirectoryInActiveComponentsIfExists('groovyScripts')
compileClasspath += sourceSets.main.compileClasspath
compileClasspath += sourceSets.main.output
}
}
}
tasks.named('compileGroovyScriptsGroovy') {
// We don't want to build groovyScripts as they should be considered as standalone elements executed in
// isolation by ofbiz. Building them will result in numerous error due to duplicated classes.
enabled = false
}
jar.manifest.attributes(
'Implementation-Title': project.name,
'Main-Class': application.mainClassName,
'Class-Path': getJarClasspath()
)
// Checks OFBiz Java coding conventions.
checkstyle {
// Defining a maximum number of "tolerated" errors ensures that
// this number cannot increase in the future. It corresponds to
// the sum of errors found last time it was changed after using the
// 'checkstyle' tool present in the framework and in the official
// plugins.
tasks.checkstyleMain.maxErrors = 0
// Currently there are no errors so we can show new one when they appear
showViolations = true
}
gitHooks {
hooks = ['pre-push': 'checkstyleMain']
}
// Checks OFBiz Groovy coding conventions.
codenarc {
setConfigFile(new File('config/codenarc/codenarc.groovy'))
setMaxPriority1Violations(0)
setMaxPriority2Violations(448)
setMaxPriority3Violations(4396)
}
// Eclipse plugin settings
eclipse.classpath.file.whenMerged { classpath ->
/* The code inside this block removes unnecessary entries
* in the .classpath file which are generated automatically
* due to the settings in the sourceSets block
*/
def fileSep = System.getProperty('file.separator')
activeComponents().each { component ->
def componentName = component.toString() - rootDir.toString() - fileSep
def eclipseEntry = os.contains('windows') ? componentName.replaceAll("\\\\", '/') : componentName
classpath.entries.removeAll { entry ->
// remove any "src" entries in .classpath of the form /componentName
entry.kind == 'src' && !(entry.path ==~ 'framework/base/config' || entry.path ==~ 'framework/base/dtd') && (
entry.path ==~ '.*/+(' + componentName.tokenize(fileSep).last() + ')$' ||
entry.path ==~ /(\/+framework)$/ ||
entry.path ==~ /(\/+applications)$/ ||
entry.path ==~ /(\/+plugins)$/ ||
entry.path ==~ /(\/+themes)$/ ||
entry.path ==~ eclipseEntry + '/config' ||
entry.path ==~ eclipseEntry + '/dtd')
}
}
}
tasks.eclipse.dependsOn(cleanEclipse)
test {
jvmArgs "-javaagent:${classpath.find { it.name.contains('jmockit') }.absolutePath}"
}
/* ========================================================
* Tasks
* ======================================================== */
// ========== Task group labels ==========
def cleanupGroup = 'Cleaning'
def docsGroup = 'Documentation'
def ofbizServer = 'OFBiz Server'
def ofbizPlugin = 'OFBiz Plugin'
def sysadminGroup = 'System Administration'
// ========== OFBiz Server tasks ==========
task loadAll(group: ofbizServer) {
dependsOn 'ofbiz --load-data'
description 'Load default data; meant for OFBiz development, testing, and demo purposes'
}
task testIntegration(group: ofbizServer) {
dependsOn 'ofbiz --test'
description 'Run OFBiz integration tests; You must run loadAll before running this task'
}
task terminateOfbiz(group: ofbizServer,
description: 'Force termination of any running OFBiz servers, only use if \"--shutdown\" command fails') {
doLast {
if (os.contains('windows')) {
Runtime.getRuntime().exec("wmic process where \"CommandLine Like \'%org.apache.ofbiz.base.start.Start%\'\" Call Terminate")
} else {
def processOutput = new ByteArrayOutputStream()
exec {
commandLine 'ps', 'ax'
standardOutput = processOutput
}
processOutput.toString().split(System.lineSeparator()).each { line ->
if (line ==~ /.*org\.apache\.ofbiz\.base\.start\.Start.*/) {
exec { commandLine 'kill', '-9', line.tokenize().first() }
}
}
}
}
}
task loadAdminUserLogin(group: ofbizServer) {
description 'Create admin user with temporary password equal to ofbiz. You must provide userLoginId'
createOfbizCommandTask('executeLoadAdminUser',
['--load-data', 'file=/runtime/tmp/AdminUserLoginData.xml'])
executeLoadAdminUser.doFirst {
copy {
from ("${rootDir}/framework/resources/templates/AdminUserLoginData.xml") {
filter(ReplaceTokens, tokens: [userLoginId: userLoginId])
}
into "${rootDir}/runtime/tmp/"
}
}
dependsOn executeLoadAdminUser
doLast {
delete("${rootDir}/runtime/tmp/AdminUserLoginData.xml")
}
}
task loadTenant(group: ofbizServer, description: 'Load data using tenantId') {
createOfbizCommandTask('executeLoadTenant', [])
if (project.hasProperty('tenantId')) {
executeLoadTenant.args '--load-data', "delegator=default#${tenantId}"
}
if (project.hasProperty('tenantReaders')) {
executeLoadTenant.args '--load-data', "readers=${tenantReaders}"
}
if (project.hasProperty('tenantComponent')) {
executeLoadTenant.args '--load-data', "component=${tenantComponent}"
}
executeLoadTenant.doFirst {
if (!project.hasProperty('tenantId')) {
throw new GradleException('Missing project property tenantId')
}
}
dependsOn executeLoadTenant
}
task createTenant(group: ofbizServer, description: 'Create a new tenant in your environment') {
def databaseTemplateFile = "${rootDir}/framework/resources/templates/AdminNewTenantData-Derby.xml"
task prepareAndValidateTenantArguments {
doLast {
if (!project.hasProperty('tenantId')) {
throw new GradleException('Project property tenantId is missing')
}
// dbPlatform values: D(Derby), M(MySQL), O(Oracle), P(PostgreSQL) (default D)
if (project.hasProperty('dbPlatform')) {
if (dbPlatform == 'D') {
databaseTemplateFile = "${rootDir}/framework/resources/templates/AdminNewTenantData-Derby.xml"
} else if (dbPlatform == 'M') {
databaseTemplateFile = "${rootDir}/framework/resources/templates/AdminNewTenantData-MySQL.xml"
} else if (dbPlatform == 'O') {
databaseTemplateFile = "${rootDir}/framework/resources/templates/AdminNewTenantData-Oracle.xml"
} else if (dbPlatform == 'P') {
databaseTemplateFile = "${rootDir}/framework/resources/templates/AdminNewTenantData-PostgreSQL.xml"
} else {
throw new GradleException('Invalid value for property dbPlatform: ' + "${dbPlatform}")
}
}
}
}
task generateDatabaseTemplateFile(dependsOn: prepareAndValidateTenantArguments) {
doLast {
def filterTokens = ['tenantId': tenantId,
'tenantName': project.hasProperty('tenantName') ? tenantName : tenantId,
'domainName': project.hasProperty('domainName') ? domainName : 'org.apache.ofbiz',
'db-IP': project.hasProperty('dbIp') ? dbIp : '',
'db-User': project.hasProperty('dbUser') ? dbUser : '',
'db-Password': project.hasProperty('dbPassword') ? dbPassword : '']
generateFileFromTemplate(databaseTemplateFile, 'runtime/tmp',
filterTokens, 'tmpFilteredTenantData.xml')
}
}
task generateAdminUserTemplateFile(dependsOn: prepareAndValidateTenantArguments) {
doLast {
generateFileFromTemplate(
"${rootDir}/framework/resources/templates/AdminUserLoginData.xml",
'runtime/tmp',
['userLoginId': "${tenantId}-admin".toString()],
'tmpFilteredUserLogin.xml')
}
}
// Load the tenants master database
createOfbizCommandTask('loadTenantOnMasterTenantDb',
['--load-data', 'file=/runtime/tmp/tmpFilteredTenantData.xml',
'--load-data', 'readers=tenant'])
loadTenantOnMasterTenantDb.dependsOn(generateDatabaseTemplateFile, generateAdminUserTemplateFile)
// Load the actual tenant data
createOfbizCommandTask('loadTenantData', [])
loadTenantData.dependsOn(loadTenantOnMasterTenantDb)
// Load the tenant admin user account
createOfbizCommandTask('loadTenantAdminUserLogin', [])
loadTenantAdminUserLogin.dependsOn(loadTenantData)
/* pass arguments to tasks, must be done this way
* because we are in the configuration phase. We cannot
* set the parameters at the execution phase. */
if (project.hasProperty('tenantId')) {
loadTenantData.args '--load-data', "delegator=default#${tenantId}"
loadTenantAdminUserLogin.args(
'--load-data', "delegator=default#${tenantId}",
'--load-data', "file=${rootDir}/runtime/tmp/tmpFilteredUserLogin.xml"
)
}
if (project.hasProperty('tenantReaders')) {
loadTenantData.args '--load-data', "readers=${tenantReaders}"
}
dependsOn(loadTenantAdminUserLogin)
// cleanup
doLast {
delete("${rootDir}/runtime/tmp/tmpFilteredTenantData.xml")
delete("${rootDir}/runtime/tmp/tmpFilteredUserLogin.xml")
}
}
// ========== Documentation tasks ==========
tasks.withType(AsciidoctorTask) { task ->
inProcess = JAVA_EXEC
forkOptions {
jvmArgs("--add-opens","java.base/sun.nio.ch=ALL-UNNAMED","--add-opens","java.base/java.io=ALL-UNNAMED")
}
outputOptions {
// I hate we have to do this - but JRuby (asciidoctorj-pdf) and Windows don't mix well
if (System.properties['os.name'].toLowerCase().contains('windows')) {
backends = ['html5']
} else {
backends = ['html5', 'pdf']
}
}
attributes \
'doctype': 'book',
'revnumber': getCurrentGitBranch(),
'experimental': '',
'allow-uri-read': true,
'icons': 'font',
'sectnums': '',
'chapter-label': '',
'toc': 'left@',
'toclevels': '3'
}
task deleteOfbizDocumentation {
doFirst { delete "${buildDir}/asciidoc/ofbiz" }
}
task deletePluginDocumentation {
doFirst {
if (!project.hasProperty('pluginId')) {
throw new GradleException('Missing property \"pluginId\"')
}
if(!(activeComponents().stream().anyMatch { it.name == pluginId })) {
throw new GradleException("Could not find plugin with id ${pluginId}")
}
delete "${buildDir}/asciidoc/plugins/${pluginId}"
}
}
task deleteAllPluginsDocumentation {
doFirst { delete "${buildDir}/asciidoc/plugins" }
}
task generateReadmeFiles(group: docsGroup, type: AsciidoctorTask) {
doFirst { delete "${buildDir}/asciidoc/readme" }
description 'Generate OFBiz README files'
sourceDir "${rootDir}"
sources {
include 'README.adoc', 'CHANGELOG.adoc', 'CONTRIBUTING.adoc'
}
outputDir file("${buildDir}/asciidoc/readme/")
}
task generateOfbizDocumentation(group: docsGroup, type: AsciidoctorTask) {
dependsOn deleteOfbizDocumentation
description 'Generate OFBiz documentation manuals'
activeComponents().each { component ->
copy {
from "${component}/src/docs/asciidoc/images/${component.name}"
include '**/*.*'
into "${rootDir}/docs/asciidoc/images/${component.name}"
}
}
sourceDir "${rootDir}/docs/asciidoc"
outputDir file("${buildDir}/asciidoc/ofbiz")
doLast {
activeComponents().each { component ->
delete "${rootDir}/docs/asciidoc/images/${component.name}"
}
}
}
task generatePluginDocumentation(group: docsGroup) {
dependsOn deletePluginDocumentation
description 'Generate plugin documentation. Expects pluginId flag'
activeComponents()
.findAll { project.hasProperty('pluginId') && it.name == pluginId }
.each { component ->
def pluginAsciidoc = task "${component.name}Documentation" (type: AsciidoctorTask) {
def asciidocFolder = new File("${component}/src/docs/asciidoc")
if (asciidocFolder.exists()) {
copy {
from "${rootDir}/docs/asciidoc/images/OFBiz-Logo.svg"
into "${component}/src/docs/asciidoc/images"
}
sourceDir file("${component}/src/docs/asciidoc")
outputDir file("${buildDir}/asciidoc/plugins/${component.name}")
doLast { println "Documentation generated for plugin ${component.name}" }
} else {
println "No documentation found for plugin ${component.name}"
}
doLast { delete "${component}/src/docs/asciidoc/images/OFBiz-Logo.svg" }
mustRunAfter deletePluginDocumentation
}
dependsOn pluginAsciidoc
doLast { delete "${component}/src/docs/asciidoc/images/OFBiz-Logo.svg" }
}
}
task generateAllPluginsDocumentation(group: docsGroup,
description: 'Generate all plugins documentation.') {
dependsOn deleteAllPluginsDocumentation
file("${pluginsDir}").eachDir { plugin ->
activeComponents().each { component ->
if (component.name == plugin.name) {
if (subprojectExists(":plugins:${plugin.name}")) {
// Note: the "-" between "component.name" and "Documentation" allows to differentiate from
// the other inner task temporary created by the generatePluginDocumentation task
def pluginAsciidoc = task "${component.name}-Documentation" (type: AsciidoctorTask) {
def asciidocFolder = new File("${component}/src/docs/asciidoc")
doFirst {
if (asciidocFolder.exists()) {
copy {
from "${rootDir}/docs/asciidoc/images/OFBiz-Logo.svg"
into "${component}/src/docs/asciidoc/images"
}
}
}
if (asciidocFolder.exists()) {
sourceDir file("${component}/src/docs/asciidoc")
outputDir file("${buildDir}/asciidoc/plugins/${component.name}")
doLast { println "Documentation generated for plugin ${component.name}" }
}
mustRunAfter deleteAllPluginsDocumentation
doLast { delete "${component}/src/docs/asciidoc/images/OFBiz-Logo.svg" }
}
dependsOn pluginAsciidoc
}
}
}
}
}
// ========== System Administration tasks ==========
task createTestReports(group: sysadminGroup, description: 'Generate HTML reports from junit XML output') {
doLast {
ant.taskdef(name: 'junitreport',
classname: 'org.apache.tools.ant.taskdefs.optional.junit.XMLResultAggregator',
classpath: configurations.junitReport.asPath)
ant.junitreport(todir: './runtime/logs/test-results') {
fileset(dir: './runtime/logs/test-results') {
include(name: '*.xml')
}
report(format:'frames', todir:'./runtime/logs/test-results/html')
}
}
}
task gitInfoFooter(group: sysadminGroup, description: 'Update the Git Branch-revision info in the footer if Git is used') {
doLast {
def branch
def revision
def timestamp = new Date().format 'yyyy-MM-dd HH:mm:ss'
def gitFolder = rootProject.file('.git')
if (!gitFolder.exists()) {
println ('Git is not used')
return
}
def branchOutput = new ByteArrayOutputStream()
exec {
commandLine 'git', 'rev-parse', '--abbrev-ref', 'HEAD'
standardOutput = branchOutput
}
branch = branchOutput.toString()
def revisionOutput = new ByteArrayOutputStream()
exec {
commandLine 'git', 'rev-parse', 'HEAD'
standardOutput = revisionOutput
}
revision = revisionOutput.toString()
gitFooterFile.delete()
gitFooterFile.createNewFile()
gitFooterFile << System.lineSeparator()
gitFooterFile << '${uiLabelMap.CommonBranch} : ' + "${branch}" + System.lineSeparator()
gitFooterFile << '${uiLabelMap.CommonRevision} : ' + "${revision}" + System.lineSeparator()
gitFooterFile << '${uiLabelMap.CommonBuiltOn} : ' + "${timestamp}" + System.lineSeparator()
gitFooterFile << '${uiLabelMap.CommonJavaVersion} : ' + "${org.gradle.internal.jvm.Jvm.current()}"
}
}
// ========== OFBiz Plugin Management ==========
task createPlugin(group: ofbizPlugin, description: 'create a new plugin component based on specified templates') {
doLast {
if (!project.hasProperty('pluginResourceName')) {
ext.pluginResourceName = pluginId.capitalize()
}
if (!project.hasProperty('webappName')) {
ext.webappName = pluginId
}
if (!project.hasProperty('basePermission')) {
ext.basePermission = pluginId.toUpperCase()
}
def filterTokens = ['component-name': pluginId,
'component-resource-name': pluginResourceName,
'webapp-name': webappName,
'base-permission': basePermission]
def templateDir = "${rootDir}/framework/resources/templates"
def pluginDir = "${pluginsDir}/${pluginId}"
['config', 'dtd', 'entitydef', 'lib', 'patches/test', 'patches/qa',
'patches/production', 'groovyScripts', 'minilang', 'servicedef', 'src/main/java', 'src/test/java', 'testdef',
'widget', "webapp/${webappName}/error", "webapp/${webappName}/WEB-INF"].each {
mkdir pluginDir + '/' + it
}
[ [tempName:'ofbiz-component.xml', newName:'ofbiz-component.xml', location:''],
[tempName:'build.gradle', newName:'build.gradle', location:''],
[tempName:'TypeData.xml', newName:"${pluginResourceName}TypeData.xml", location:'data'],
[tempName:'SecurityPermissionSeedData.xml', newName:"${pluginResourceName}SecurityPermissionSeedData.xml", location:'data'],
[tempName:'SecurityGroupDemoData.xml', newName:"${pluginResourceName}SecurityGroupDemoData.xml", location:'data'],
[tempName:'DemoData.xml', newName:"${pluginResourceName}DemoData.xml", location:'data'],
[tempName:'HELP.adoc', newName:'HELP.adoc', location:''],
[tempName:'README.adoc', newName:'README.adoc', location:''],
[tempName:'entitymodel.xml', newName:'entitymodel.xml', location:'entitydef'],
[tempName:'services.xml', newName:'services.xml', location:'servicedef'],
[tempName:'Tests.xml', newName:"${pluginResourceName}Tests.xml", location:'testdef'],
[tempName:'UiLabels.xml', newName:"${pluginResourceName}UiLabels.xml", location:'config'],
[tempName:'index.jsp', newName:'index.jsp', location:"webapp/${webappName}"],
[tempName:'controller.xml', newName:'controller.xml', location:"webapp/${webappName}/WEB-INF"],
[tempName:'web.xml', newName:'web.xml', location:"webapp/${webappName}/WEB-INF"],
[tempName:'CommonScreens.xml', newName:'CommonScreens.xml', location:'widget'],
[tempName:'Screens.xml', newName:"${pluginResourceName}Screens.xml", location:'widget'],
[tempName:'Menus.xml', newName:"${pluginResourceName}Menus.xml", location:'widget'],
[tempName:'Forms.xml', newName:"${pluginResourceName}Forms.xml", location:'widget']
].each { tmpl ->
generateFileFromTemplate(templateDir + '/' + tmpl.tempName,
pluginDir + '/' + tmpl.location, filterTokens, tmpl.newName)
}
println "plugin successfully created in directory ${pluginsDir}/${pluginId}."
}
}
task installPlugin(group: ofbizPlugin, description: 'executes plugin install task if it exists') {
doFirst {
if (!project.hasProperty('pluginId')) {
throw new GradleException('Missing property \"pluginId\"')
}
}
if (project.hasProperty('pluginId')) {
activeComponents()
.findAll { it.name == pluginId }
.each { component ->
if (taskExistsInproject(":plugins:${pluginId}", 'install')) {
dependsOn ":plugins:${pluginId}:install"
doLast { println "installed plugin ${pluginId}" }
} else {
doLast { println "No install task defined for plugin ${pluginId}, nothing to do" }
}
}
}
}
task uninstallPlugin(group: ofbizPlugin, description: 'executes plugin uninstall task if it exists') {
doFirst {
if (!project.hasProperty('pluginId')) {
throw new GradleException('Missing property \"pluginId\"')
}
if (!subprojectExists(":plugins:${pluginId}")) {
throw new GradleException("Plugin \"${pluginId}\" does not exist")
}
}
if (project.hasProperty('pluginId') && taskExistsInproject(":plugins:${pluginId}", 'uninstall')) {
dependsOn ":plugins:${pluginId}:uninstall"
doLast { println "uninstalled plugin ${pluginId}" }
} else {
doLast { println "No uninstall task defined for plugin ${pluginId}" }
}
}
task removePlugin(group: ofbizPlugin, description: 'Uninstall a plugin and delete its files') {
if (project.hasProperty('pluginId') && subprojectExists(":plugins:${pluginId}")) {
dependsOn uninstallPlugin
}
doLast {
if (file("${pluginsDir}/${pluginId}").exists()) {
delete "${pluginsDir}/${pluginId}"
} else {
throw new GradleException("Directory not found: ${pluginsDir}/${pluginId}")
}
}
}
task pushPlugin(group: ofbizPlugin, description: 'push an existing plugin to local maven repository') {
if (project.hasProperty('pluginId')) {
doFirst {
if (!subprojectExists(":plugins:${pluginId}")) {
throw new GradleException("Plugin ${pluginId} does not exist, cannot publish")
}
}
task createPluginArchive(type: Zip) {
from "${pluginsDir}/${pluginId}"
}
publishing {
publications {
ofbizPluginPublication(MavenPublication) {
artifactId pluginId
groupId project.hasProperty('pluginGroup') ? pluginGroup : 'org.apache.ofbiz.plugin'
version project.hasProperty('pluginVersion') ? pluginVersion : '0.1.0-SNAPSHOT'
artifact createPluginArchive
pom.withXml {
if (project.hasProperty('pluginDescription')) {
asNode().appendNode('description', pluginDescription)
} else {
asNode().appendNode('description', "Publication of OFBiz plugin ${pluginId}")
}
}
}
}
}
if (subprojectExists(":plugins:${pluginId}")) {
dependsOn publishToMavenLocal
}
} else {
doFirst { throw new GradleException('Missing property \"pluginId\"') }
}
}
task pullPlugin(group: ofbizPlugin, description: 'Download and install a plugin with all dependencies') {
doLast {
if (!project.hasProperty('dependencyId')) {
throw new GradleException('You must pass the dependencyId of the plugin')
}
// Connect to a remote maven repository if defined
if (project.hasProperty('repoUrl')) {
repositories {
maven {
url repoUrl
if (project.hasProperty('repoUser') && project.hasProperty('repoPassword')) {
credentials {
username repoUser
password repoPassword
}
}
}
}
}
// download plugin and dependencies
dependencies {
ofbizPlugins dependencyId
}
// reverse the order of dependencies to install them before the plugin
def ofbizPluginArchives = new ArrayList(configurations.ofbizPlugins.files)
Collections.reverse(ofbizPluginArchives)
// Extract and install plugin and dependencies
ofbizPluginArchives.each { pluginArchive ->
ext.pluginId = dependencyId.tokenize(':').get(1)
println "installing plugin: ${pluginId}"
copy {
from zipTree(pluginArchive)
into "${pluginsDir}/${pluginId}"
}
gradlewSubprocess(['installPlugin', "-PpluginId=${pluginId}"])
}
}
}
task pullPluginSource(group: ofbizPlugin, description: 'Download and install a plugin from source control') {
if (project.hasProperty('pluginId')) {
// GitHub SVN feature https://docs.github.com/en/github/importing-your-projects-to-github/working-with-subversion-on-github/support-for-subversion-clients
task pullPluginFromSvn(type: SvnCheckout) {
def currentBranch = getCurrentGitBranch()
// Only used for release branches
if (currentBranch == "trunk") {
svnUrl = "https://github.com/apache/ofbiz-plugins/trunk/${pluginId}"
} else {
svnUrl = "https://github.com/apache/ofbiz-plugins/branches/" + currentBranch + "/${pluginId}"
}
workspaceDir = "${pluginsDir}/${pluginId}"
}
dependsOn pullPluginFromSvn
}
doLast {
gradlewSubprocess(['installPlugin', "-PpluginId=${pluginId}"])
}
}
task pullAllPluginsSource(group: ofbizPlugin,
description: 'Download and install all plugins from source control. Warning! deletes existing plugins') {
task deleteBeforePulling {
doLast { delete "${pluginsDir}" }
}
task pullPluginsFromSvn(type: SvnCheckout, dependsOn: deleteBeforePulling) {
// GitHub SVN feature https://docs.github.com/en/github/importing-your-projects-to-github/working-with-subversion-on-github/support-for-subversion-clients
def currentBranch = getCurrentGitBranch()
// Only used for release branches
if (currentBranch == "trunk") {
svnUrl = "https://github.com/apache/ofbiz-plugins/trunk"
} else {
svnUrl = "https://github.com/apache/ofbiz-plugins/branches/" + currentBranch
}
workspaceDir = "${pluginsDir}"
}
dependsOn pullPluginsFromSvn
task installAllPlugins {
subdirs(file("${pluginsDir}"))
.filter(this.isComponentEnabled)
.filter { taskExistsInproject(":plugins:${it.name}", 'install') }
.forEach({ plugin ->
dependsOn ":plugins:${plugin.name}:install"
doLast { println "installed plugin ${plugin.name}" }
})
}
doLast {
gradlewSubprocess(['installAllPlugins'])
}
}
// ========== Clean up tasks ==========
task cleanCatalina(group: cleanupGroup, description: 'Clean Catalina data in runtime/catalina/work') {
doLast { delete "${rootDir}/runtime/catalina/work" }
}
task cleanData(group: cleanupGroup, description: 'Clean all DB data (Derby) under runtime/data') {
doLast { deleteAllInDirWithExclusions("${rootDir}/runtime/data/", ['README', 'derby.properties']) }
}
task cleanDownloads(group: cleanupGroup, description: 'Clean all downloaded files') {
doLast {
delete fileTree(dir: "${rootDir}/framework/base/lib", includes: ['activemq-*.jar'])
delete fileTree(dir: "${rootDir}/framework/entity/lib/jdbc", includes: ['postgresql-*.jar'])
delete fileTree(dir: "${rootDir}/framework/entity/lib/jdbc", includes: ['mysql-*.jar'])
}
}
task cleanLogs(group: cleanupGroup, description: 'Clean all logs in runtime/logs') {
doLast { deleteAllInDirWithExclusions("${rootDir}/runtime/logs/", ['README']) }
}
task cleanOutput(group: cleanupGroup, description: 'Clean runtime/output directory') {
doLast { deleteAllInDirWithExclusions("${rootDir}/runtime/output/", ['README']) }
}
task cleanIndexes(group: cleanupGroup, description: 'Remove search indexes (e.g. Lucene) from runtime/indexes') {
doLast { deleteAllInDirWithExclusions("${rootDir}/runtime/indexes/", ['README', 'index.properties']) }
}
task cleanTempfiles(group: cleanupGroup, description: 'Remove file in runtime/tempfiles') {
doLast {
deleteAllInDirWithExclusions("${rootDir}/runtime/tempfiles/", ['README'])
deleteAllInDirWithExclusions("${rootDir}/runtime/tmp/", ['README'])
}
}
task cleanUploads(group: cleanupGroup, description: 'Remove uploaded files.') {
doLast { deleteAllInDirWithExclusions("${rootDir}/runtime/uploads/", []) }
}
task cleanXtra(group: cleanupGroup, description: 'Clean extra generated files like .rej, .DS_Store, etc.') {
doLast {
delete fileTree(dir: "${rootDir}",
includes: ['**/.nbattrs', '**/*~','**/.#*', '**/.DS_Store', '**/*.rej', '**/*.orig'])
}
}
task cleanFooterFiles(group: cleanupGroup, description: 'clean generated footer files') {
doLast {
delete gitFooterFile
}
}
/*
* Keep this task below all other clean tasks The location of
* declaration is important because it means that it will automatically
* run whenever the task cleanAll executes (dependency matched by regex)
*/
def cleanTasks = tasks.findAll { it.name ==~ /^clean.+/ }
task cleanAll(group: cleanupGroup, dependsOn: [cleanTasks, clean]) {
description 'Execute all cleaning tasks.'
}
/* ========================================================
* Rules-based OFBiz server commands
* ======================================================== */
tasks.addRule('Pattern: ofbiz <Commands>: Execute OFBiz startup commands') { String taskName ->
if (taskName ==~ /^ofbiz\s.*/ || taskName == 'ofbiz') {
def arguments = (taskName - 'ofbiz').tokenize(' ')
createOfbizCommandTask(taskName, arguments)
}
}
tasks.addRule('Pattern: ofbizBackground <Commands>: Execute OFBiz startup commands in background and output to console.log') { String taskName ->
if (taskName ==~ /^ofbizBackground\s.*/ || taskName == 'ofbizBackground') {
createOfbizBackgroundCommandTask(taskName)
}
}
/* ========================================================
* Helper Functions
* ======================================================== */
def createOfbizCommandTask(taskName, arguments) {
task(type: JavaExec, dependsOn: classes, taskName) {
jvmArgs(application.applicationDefaultJvmArgs)
classpath = sourceSets.main.runtimeClasspath
main = application.mainClassName
args arguments
if (taskName ==~ /^ofbiz.*(--test|-t).*/) {
finalizedBy(createTestReports)
}
}
}
def createOfbizBackgroundCommandTask(taskName) {
def sourceTask = taskName.tokenize().first()
def arguments = (taskName - sourceTask)
def serverCommand = "ofbiz ${arguments}".toString()
task (taskName) {
doLast {
spawnProcess([gradlew, '--no-daemon', serverCommand])
}
}
}
def spawnProcess(command) {
ProcessBuilder pb = new ProcessBuilder(command)
File consoleLog = file("${rootDir}/runtime/logs/console.log")
pb.directory(file("${rootDir}"))
pb.redirectErrorStream(true)
pb.redirectOutput(ProcessBuilder.Redirect.appendTo(consoleLog))
pb.start()
}
def getDirectoryInActiveComponentsIfExists(String dirName) {
activeComponents()
.collect { file(it.toString() + '/' + dirName) }
.findAll { it.exists() }
}
def deleteAllInDirWithExclusions(dirName, exclusions) {
ant.delete (includeEmptyDirs: 'true', verbose: 'on') {
fileset(dir: dirName, includes: '**/*', erroronmissingdir: 'false') {
exclusions.each { exclusion ->
exclude name: exclusion
}
}
}
}
def generateFileFromTemplate(templateFileInFullPath, targetDirectory, filterTokens, newFileName) {
copy {
from (templateFileInFullPath) {
filter ReplaceTokens, tokens: filterTokens
rename templateFileInFullPath.tokenize('/').last(), newFileName
}
into targetDirectory
}
}
def getJarClasspath() {
def mapper = os.contains('windows') ? { '\\' + "$it" } : { "$it" }
configurations.runtimeClasspath.collect(mapper).join(' ')
}
def subprojectExists(fullyQualifiedProject) {
subprojects.stream()
.anyMatch { it.path == fullyQualifiedProject.toString() }
}
def taskExistsInproject(fullyQualifiedProject, taskName) {
subprojects.stream()
.filter { it.path == fullyQualifiedProject.toString() }
.flatMap { it.tasks.stream() }
.anyMatch { it.name == taskName }
}
def gradlewSubprocess(commandList) {
exec { commandLine(gradlew, '--no-daemon', *commandList) }
}
def gradle = project.getGradle()
new File("${gradle.getGradleUserHomeDir().getAbsolutePath()}/daemon/${gradle.getGradleVersion()}").listFiles().each {
if (it.getName().endsWith('.out.log')) {
logger.debug("Cleaning up daemon log file $it")
it.delete()
}
}
tasks.startScripts {
doLast {
// Alter the start script for Unix systems.
unixScript.text =
unixScript.text.replace('CLASSPATH=$APP_HOME/lib','CLASSPATH=$APP_HOME/config/:$APP_HOME/lib-extra/*:$APP_HOME/lib')
// Alter the start script for Windows systems.
windowsScript.text =
windowsScript.text.replace('CLASSPATH=%APP_HOME%\\lib','CLASSPATH=%APP_HOME%\\conf\\;%APP_HOME%\\lib-extra\\*;%APP_HOME%\\lib')
}
}