/* * 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.AsciidoctorTask /* ======================================================== * Project setup * ======================================================== */ buildscript { repositories { mavenCentral() maven { url "https://plugins.gradle.org/m2/" } } dependencies { classpath 'at.bxm.gradleplugins:gradle-svntools-plugin:2.2.1' classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.8.1' // Rather than using 1.5.9.2 see OFBIZ-10693 classpath 'org.asciidoctor:asciidoctorj-pdf:1.5.0-alpha.16' } } apply plugin: 'java' apply plugin: 'groovy' apply plugin: 'eclipse' apply plugin: 'maven-publish' apply plugin: "at.bxm.svntools" apply plugin: 'org.asciidoctor.convert' apply plugin: 'checkstyle' apply from: 'common.gradle' // global properties ext.os = System.getProperty('os.name').toLowerCase() ext.pluginsDir = "${rootDir}/plugins" // java settings List jvmArguments = ['-Xms128M', '-Xmx1024M'] if (project.hasProperty('jvmArgs')) { jvmArguments = jvmArgs.tokenize() } ext.ofbizMainClass = 'org.apache.ofbiz.base.start.Start' javadoc { title='OFBiz R18.12 API' failOnError = true options { source '8' 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/8/docs/api", "https://docs.oracle.com/javaee/7/api", "http://docs.groovy-lang.org/docs/groovy-2.4.13/html/api", "https://commons.apache.org/proper/commons-cli/apidocs" ) } } // 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 that were present before introducing the // ‘checkstyle’ tool present in the framework and in the official // plugins. // Because of Windows EOL difference with *nix, despite the number // for maxErrors being more around 39600+ we temporarily put 50000 // here to avoid issue when releasing. // Just increasing the number of lines is the problem. maxErrors = 50000 // Currently there are a lot of errors so we need to temporarily // hide them to avoid polluting the terminal output. showViolations = false } sourceCompatibility = '1.8' targetCompatibility = '1.8' // 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" } } // defines the footer files for svn and git info def File gitFooterFile = file("${rootDir}/runtime/GitInfo.ftl") def File svnFooterFile = file("${rootDir}/runtime/SvnInfo.ftl") // root and subproject settings defaultTasks 'build' 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 { // apache-xerces:xercesImpl:2.9.1 url "https://maven.repository.redhat.com/ga/" } maven { // net.fortuna.ical4j:ical4j:1.0-rc4-atlassian-12 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 url "https://repo1.maven.org/maven2" metadataSources { artifact() } } } } 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 } } dependencies { // ofbiz compile libs compile 'apache-xerces:xercesImpl:2.9.1' compile 'com.google.zxing:core:3.3.3' compile 'com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2' compile 'com.googlecode.ez-vcard:ez-vcard:0.9.10' compile 'com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20180219.1' compile 'com.googlecode.libphonenumber:libphonenumber:8.9.16' compile 'com.ibm.icu:icu4j:63.1' compile 'com.lowagie:itext:2.1.7' // Don't update due to license change in newer versions, see OFBIZ-10455 compile 'com.sun.mail:javax.mail:1.6.2' compile 'com.rometools:rome:1.16.0' compile 'com.thoughtworks.xstream:xstream:1.4.11.1' compile 'commons-cli:commons-cli:1.4' compile 'commons-net:commons-net:3.6' compile 'commons-validator:commons-validator:1.6' compile 'commons-fileupload:commons-fileupload:1.3.3' compile 'de.odysseus.juel:juel-impl:2.2.7' compile 'javax.el:javax.el-api:3.0.1-b06' compile 'javax.servlet:javax.servlet-api:4.0.1' compile 'javax.servlet.jsp:javax.servlet.jsp-api:2.3.3' compile 'junit:junit-dep:4.11' compile 'net.fortuna.ical4j:ical4j:1.0-rc4-atlassian-12' compile 'org.apache.ant:ant-junit:1.10.5' compile 'org.apache.axis2:axis2-kernel:1.7.8' compile 'org.apache.commons:commons-collections4:4.2' compile 'org.apache.commons:commons-csv:1.6' compile 'org.apache.commons:commons-dbcp2:2.5.0' compile 'org.apache.commons:commons-text:1.6' compile 'org.apache.geronimo.components:geronimo-transaction:3.1.4' compile 'org.apache.geronimo.specs:geronimo-jms_1.1_spec:1.1.1' compile 'org.apache.httpcomponents:httpclient-cache:4.5.6' compile 'org.apache.logging.log4j:log4j-api:2.17.1' // the API of log4j 2 compile 'org.apache.logging.log4j:log4j-web:2.17.1' //??? compile 'org.apache.poi:poi:3.17' compile 'org.apache.pdfbox:pdfbox:2.0.24' compile 'org.apache.shiro:shiro-core:1.4.0' compile 'org.apache.sshd:sshd-core:1.7.0' compile 'org.apache.tika:tika-core:1.28' compile 'org.apache.tika:tika-parsers:1.28' compile 'org.apache.tomcat:tomcat-catalina-ha:9.0.54' compile 'org.apache.tomcat:tomcat-catalina:9.0.54' compile 'org.apache.tomcat:tomcat-jasper:9.0.54' compile 'org.apache.tomcat:tomcat-tribes:9.0.54' compile 'org.apache.xmlgraphics:fop:2.3' compile 'org.apache.xmlrpc:xmlrpc-client:3.1.3' compile 'org.apache.xmlrpc:xmlrpc-server:3.1.3' compile 'org.codehaus.groovy:groovy-all:2.4.13' // Remember to change the version number in javadoc.options block compile 'org.freemarker:freemarker:2.3.31' // Remember to change the version number in FreeMarkerWorker class when upgrading compile 'org.hamcrest:hamcrest-all:1.3' compile 'org.owasp.esapi:esapi:2.1.0.1' compile 'org.springframework:spring-test:5.1.2.RELEASE' compile 'org.zapodot:jackson-databind-java-optional:2.6.1' compile 'oro:oro:2.0.8' compile 'wsdl4j:wsdl4j:1.6.3' compile 'org.jsoup:jsoup:1.11.3' compile 'com.auth0:java-jwt:3.8.2' compile '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 compile 'net.lingala.zip4j:zip4j:2.6.4' compile 'org.apache.commons:commons-imaging:1.0-alpha2' // 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." compile 'batik:batik-svg-dom:1.6-1' compile 'org.postgresql:postgresql:42.3.3' // ofbiz unit-test compile libs testCompile 'org.mockito:mockito-core:2.23.0' // ofbiz runtime libs runtime 'javax.xml.soap:javax.xml.soap-api:1.4.0' runtime 'de.odysseus.juel:juel-spi:2.2.7' runtime 'net.sf.barcode4j:barcode4j-fop-ext:2.1' runtime 'net.sf.barcode4j:barcode4j:2.1' runtime 'org.apache.axis2:axis2-transport-http:1.7.8' runtime 'org.apache.axis2:axis2-transport-local:1.7.8' runtime 'org.apache.derby:derby:10.14.2.0' runtime 'org.apache.geronimo.specs:geronimo-jaxrpc_1.1_spec:1.1' runtime 'org.apache.logging.log4j:log4j-1.2-api:2.17.1' // for external jars using the old log4j1.2: routes logging to log4j 2 runtime 'org.apache.logging.log4j:log4j-core:2.17.1' // the implementation of the log4j 2 API runtime 'org.apache.logging.log4j:log4j-jul:2.17.1' // for external jars using the java.util.logging: routes logging to log4j 2 runtime 'org.apache.logging.log4j:log4j-slf4j-impl:2.17.1' // for external jars using slf4j: routes logging to log4j 2 runtime 'org.apache.logging.log4j:log4j-jcl:2.17.1' // need to constrain to version to avoid classpath conflict (ReflectionUtil) runtime 'org.codeartisans.thirdparties.swing:batik-all:1.8pre-r1084380' // plugin libs subprojects.each { subProject -> compile project(path: subProject.path, configuration: 'pluginLibsCompile') runtime project(path: subProject.path, configuration: 'pluginLibsRuntime') compileOnly project(path: subProject.path, configuration: 'pluginLibsCompileOnly') } // libs needed for junitreport junitReport 'junit:junit:4.12' junitReport 'org.apache.ant:ant-junit:1.10.5' // bug workaround - see OFBIZ-9873 asciidoctor 'org.jruby:jruby-complete:9.2.4.0' // local libs getDirectoryInActiveComponentsIfExists('lib').each { libDir -> compile fileTree(dir: libDir, include: '**/*.jar') } compile fileTree(dir: file("${rootDir}/lib"), include: '**/*.jar') } 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' ] // Files and directories present in config directories that should not be included in ofbiz.jar (see OFBIZ-8321). def excludedConfigFiles = [ 'README', 'APACHE2_HEADER_FOR_XML', '*.txt', '*.jks', 'fop.xconf', 'MiniLang.xslt', 'AutoImportTemplate.ftl', 'axis2', 'barcode' ] sourceSets { main { java { srcDirs = getDirectoryInActiveComponentsIfExists('src/main/java') exclude excludedJavaSources } groovy { srcDirs = getDirectoryInActiveComponentsIfExists('src/main/groovy') } resources { srcDirs = getDirectoryInActiveComponentsIfExists('src/main/java') srcDirs += getDirectoryInActiveComponentsIfExists('config') srcDirs += getDirectoryInActiveComponentsIfExists('dtd') exclude excludedJavaSources exclude excludedConfigFiles // Below are necessary for unit tests run by Gradle and integration tests exclude { FileTreeElement elem -> elem.getName().contains('Labels.xml') } exclude { FileTreeElement elem -> elem.getName().contains('.properties') && !elem.getName().contains('start.properties') && !elem.getName().contains('load-data.properties') && !elem.getName().contains('debug.properties') && !elem.getName().contains('cache.properties') && !elem.getName().contains('test.properties') && !elem.getName().contains('rmi.properties') } } } test { java { srcDirs = getDirectoryInActiveComponentsIfExists('src/test/java') } groovy { srcDirs = getDirectoryInActiveComponentsIfExists('src/test/groovy') } resources { srcDirs = getDirectoryInActiveComponentsIfExists('src/test/java') } } } jar { manifest { attributes( "Implementation-Title": project.name, "Main-Class": ofbizMainClass, "Class-Path": getJarManifestClasspathForCurrentOs() ) } } // 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") iterateOverActiveComponents { 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) /* 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 */ buildscript { if (project.hasProperty('enableOwasp')) { dependencies { classpath 'org.owasp:dependency-check-gradle:3.0.2' } } } if (project.hasProperty('enableOwasp')) { apply plugin: 'org.owasp.dependencycheck' } /* ======================================================== * 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'], jvmArguments, false) 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', [], jvmArguments, false) if (project.hasProperty('tenantId')) { executeLoadTenant.args '--load-data' executeLoadTenant.args "delegator=default#${tenantId}" } if (project.hasProperty('tenantReaders')) { executeLoadTenant.args '--load-data' executeLoadTenant.args "readers=${tenantReaders}" } if (project.hasProperty('tenantComponent')) { executeLoadTenant.args '--load-data' executeLoadTenant.args "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'], jvmArguments, false) loadTenantOnMasterTenantDb.dependsOn(generateDatabaseTemplateFile, generateAdminUserTemplateFile) // Load the actual tenant data createOfbizCommandTask('loadTenantData', [], jvmArguments, false) loadTenantData.dependsOn(loadTenantOnMasterTenantDb) // Load the tenant admin user account createOfbizCommandTask('loadTenantAdminUserLogin', [], jvmArguments, false) 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' loadTenantData.args "delegator=default#${tenantId}" loadTenantAdminUserLogin.args '--load-data' loadTenantAdminUserLogin.args "delegator=default#${tenantId}" loadTenantAdminUserLogin.args '--load-data' loadTenantAdminUserLogin.args "file=${rootDir}/runtime/tmp/tmpFilteredUserLogin.xml" } if (project.hasProperty('tenantReaders')) { loadTenantData.args '--load-data' loadTenantData.args "readers=${tenantReaders}" } dependsOn(loadTenantAdminUserLogin) // cleanup doLast { delete("${rootDir}/runtime/tmp/tmpFilteredTenantData.xml") delete("${rootDir}/runtime/tmp/tmpFilteredUserLogin.xml") } } // ========== Documentation tasks ========== tasks.withType(AsciidoctorTask) { task -> backends 'html5', 'pdf' attributes \ 'doctype': 'book', 'experimental': '', 'icons': 'font', 'sectnums': '', 'chapter-label': '', 'toc': 'left@', 'toclevels': '3' } task deleteOfbizDocumentation { doFirst { delete "${buildDir}/asciidoc/ofbiz" } } task deletePluginDocumentation { def activeComponents = [] iterateOverActiveComponents { component -> activeComponents.add(component.name) } doFirst { if (!project.hasProperty('pluginId')) { throw new GradleException('Missing property \"pluginId\"') } if(!activeComponents.contains(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' sourceDir "${rootDir}/docs/asciidoc" outputDir file("${buildDir}/asciidoc/ofbiz") } task generatePluginDocumentation(group: docsGroup) { dependsOn deletePluginDocumentation description 'Generate plugin documentation. Expects pluginId flag' iterateOverActiveComponents { component -> if (project.hasProperty('pluginId') && component.name == pluginId) { 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 -> iterateOverActiveComponents { 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 = new 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()}" } } task svnInfoFooter(group: sysadminGroup, description: 'Update the Subversion revision info in the footer if Subversion is used') { doLast { def timestamp = new Date().format 'yyyy-MM-dd HH:mm:ss' def svnOutput = new ByteArrayOutputStream() def svnFolder = new File('.svn') if (!svnFolder.exists()) { println ("Subversion is not used") return } exec{ commandLine 'svn', 'info', '--xml' standardOutput = svnOutput } def info = new XmlParser().parseText(svnOutput.toString()) svnFooterFile.delete() svnFooterFile.createNewFile() svnFooterFile << System.lineSeparator() svnFooterFile << '${uiLabelMap.CommonBranch} : ' + "${info.entry.url.text()}" + System.lineSeparator() svnFooterFile << '${uiLabelMap.CommonRevision} : ' + "${info.entry.commit.@revision}" + System.lineSeparator() svnFooterFile << '${uiLabelMap.CommonBuiltOn} : ' + "${timestamp}" + System.lineSeparator() svnFooterFile << '${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', 'data/helpdata', 'dtd', 'documents', '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.xml', newName:"HELP_${pluginResourceName}.xml", location:'data/helpdata'], [tempName:'document.xml', newName:"${pluginResourceName}.xml", location:'documents'], [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:'error.jsp', newName:'error.jsp', location:"webapp/${webappName}/error"], [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')) { iterateOverActiveComponents { component -> if (component.name == pluginId) { if (subprojectExists(":plugins:${pluginId}")) { 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')) { task pullPluginFromSvn(type: SvnCheckout) { svnUrl = "https://github.com/apache/ofbiz-plugins/branches/release18.12/${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) { svnUrl = "https://github.com/apache/ofbiz-plugins/branches/release18.12" workspaceDir = "${pluginsDir}" } dependsOn pullPluginsFromSvn task installAllPlugins { file("${pluginsDir}").eachDir { plugin -> iterateOverActiveComponents { component -> if (component.name == plugin.name) { if (subprojectExists(":plugins:${plugin.name}")) { if (taskExistsInproject(":plugins:${plugin.name}", 'install')) { 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 delete svnFooterFile } } /* * 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 : Execute OFBiz startup commands') { String taskName -> if (taskName ==~ /^ofbiz\s.*/ || taskName == 'ofbiz') { def arguments = (taskName - 'ofbiz').tokenize(' ') createOfbizCommandTask(taskName, arguments, jvmArguments, false) } } tasks.addRule('Pattern: ofbizDebug : Execute OFBiz startup commands in remote debug mode') { String taskName -> if (taskName ==~ /^ofbizDebug\s.*/ || taskName == 'ofbizDebug') { def arguments = (taskName - 'ofbizDebug').tokenize(' ') createOfbizCommandTask(taskName, arguments, jvmArguments, true) } } tasks.addRule('Pattern: ofbizBackground : 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, jvmArguments, isDebugMode) { def ofbizJarName = buildDir.toString()+'/libs/'+project.name+'.jar' task(type: JavaExec, dependsOn: build, taskName) { jvmArgs(jvmArguments) debug = isDebugMode classpath = files(ofbizJarName) main = ofbizMainClass arguments.each { argument -> args argument } if (taskName ==~ /^ofbiz.*--test.*/ || taskName ==~ /^ofbiz.*-t.*/) { finalizedBy(createTestReports) } } } def createOfbizBackgroundCommandTask(taskName) { def sourceTask = taskName.tokenize().first() def arguments = (taskName - sourceTask) def serverCommand = "ofbiz ${arguments}".toString() def gradleRunner = os.contains('windows') ? 'gradlew.bat' : './gradlew' task (taskName) { doLast { spawnProcess([gradleRunner, "--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) { def dirInComponents = [] iterateOverActiveComponents { component -> def subDir = file(component.toString() + '/' + dirName) if (subDir.exists()) { dirInComponents.add subDir } } return dirInComponents } 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 getJarManifestClasspathForCurrentOs() { def osClassPath = '' if (os.contains('windows')) { configurations.runtime.files.each { cpEntry -> osClassPath += '\\' + cpEntry.toString() + ' ' } } else { osClassPath = configurations.runtime.files.collect { "$it" }.join(' ') } return osClassPath } def subprojectExists(fullyQualifiedProject) { subprojects.stream() .filter { it.path == fullyQualifiedProject.toString() } .findAny() .isPresent() } def taskExistsInproject(fullyQualifiedProject, taskName) { subprojects.stream() .filter { it.path == fullyQualifiedProject.toString() } .flatMap { it.tasks.stream() } .anyMatch { it.name == taskName } } def gradlewSubprocess(commandList) { def gradlew = os.contains('windows') ? 'gradlew.bat' : './gradlew' 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() } }