This is an automated email from the ASF dual-hosted git repository. udo pushed a commit to branch feature/GEODE-3864 in repository https://gitbox.apache.org/repos/asf/geode.git
The following commit(s) were added to refs/heads/feature/GEODE-3864 by this push: new 9ed6a78 GEODE-3864: Added protobuf message file archiving 9ed6a78 is described below commit 9ed6a789529f19b128d8f74ad0542d84dce4e804 Author: kohlmu-pivotal <ukohlme...@pivotal.io> AuthorDate: Wed Oct 18 14:28:35 2017 -0700 GEODE-3864: Added protobuf message file archiving --- geode-assembly/build.gradle | 677 ++++++++++++++++++++++---------------------- geode-protobuf/build.gradle | 1 - 2 files changed, 336 insertions(+), 342 deletions(-) diff --git a/geode-assembly/build.gradle b/geode-assembly/build.gradle index 80dc870..48a5cfa 100755 --- a/geode-assembly/build.gradle +++ b/geode-assembly/build.gradle @@ -21,227 +21,222 @@ apply plugin: 'distribution' // disable artifact generation for this project jar.enabled = false extraArchive { - sources = false - javadoc = false - tests = false + sources = false + javadoc = false + tests = false } // Gradle doesn't automatically remove the jar artifact even though we disabled it // this causes publishing to fail. So we nuke all the disabled artifacts from all configurations. configurations.all { - artifacts.removeAll artifacts.findAll { - it instanceof ArchivePublishArtifact && !it.archiveTask.enabled - } + artifacts.removeAll artifacts.findAll { it instanceof ArchivePublishArtifact && !it.archiveTask.enabled } } -gradle.taskGraph.whenReady({ graph -> - tasks.withType(Tar).each { tar -> - tar.compression = Compression.GZIP - tar.extension = 'tar.gz' - } - tasks.srcDistZip { - classifier 'src' - } - tasks.srcDistTar { - classifier 'src' - } +gradle.taskGraph.whenReady( { graph -> + tasks.withType(Tar).each { tar -> + tar.compression = Compression.GZIP + tar.extension = 'tar.gz' + } + tasks.srcDistZip { + classifier 'src' + } + tasks.srcDistTar { + classifier 'src' + } }) configurations { - bundled { - description 'A dependency that is shipped with geode, but is not required to compile' - } - gfshDependencies + bundled { + description 'A dependency that is shipped with geode, but is not required to compile' + } + gfshDependencies } dependencies { - provided project(':geode-core') - - archives project(':geode-common') - archives project(':geode-json') - archives project(':geode-core') - archives project(':geode-lucene') - archives project(':geode-old-client-support') - archives project(':geode-protobuf') - archives project(':geode-web') - archives project(':geode-web-api') - archives project(':geode-wan') - archives project(':geode-cq') - archives project(':geode-rebalancer') - - testCompile project(':geode-junit') - testCompile project(':geode-pulse') - testCompile files(project(':geode-core').sourceSets.test.output) - testCompile('org.springframework:spring-web:' + project.'springframework.version') { - exclude module: 'aopalliance' - exclude module: 'spring-aop' - } - - testCompile 'org.apache.httpcomponents:httpclient:' + project.'httpclient.version' - testCompile 'org.apache.httpcomponents:httpcore:' + project.'httpcore.version' - testCompile 'com.google.guava:guava:' + project.'guava.version' - testCompile 'org.apache.commons:commons-exec:' + project.'commons-exec.version' - - testCompile group: 'org.codehaus.cargo', name: 'cargo-core-uberjar', version: '1.6.3' - - testCompile(project(':extensions/geode-modules')) { - // Remove everything related to Tomcat - exclude group: 'org.apache.tomcat' - } - testCompile(project(':extensions/geode-modules-session-internal')) { - // Remove everything related to Tomcat - exclude group: 'org.apache.tomcat' - } - - testCompile project(':extensions/session-testing-war') - - testRuntime files("${System.getProperty('java.home')}/../lib/tools.jar") - testRuntime files("$buildDir/install/${distributions.main.baseName}/lib/geode-dependencies.jar") - - - gfshDependencies('org.springframework:spring-web:' + project.'springframework.version') { - exclude module: 'spring-core' - exclude module: 'commons-logging' - } - - testCompile project(':geode-old-versions') + provided project(':geode-core') + + archives project(':geode-common') + archives project(':geode-json') + archives project(':geode-core') + archives project(':geode-lucene') + archives project(':geode-old-client-support') + archives project(':geode-protobuf') + archives project(':geode-web') + archives project(':geode-web-api') + archives project(':geode-wan') + archives project(':geode-cq') + archives project(':geode-rebalancer') + + testCompile project(':geode-junit') + testCompile project(':geode-pulse') + testCompile files(project(':geode-core').sourceSets.test.output) + testCompile ('org.springframework:spring-web:' + project.'springframework.version') { + exclude module: 'aopalliance' + exclude module: 'spring-aop' + } + + testCompile 'org.apache.httpcomponents:httpclient:' + project.'httpclient.version' + testCompile 'org.apache.httpcomponents:httpcore:' + project.'httpcore.version' + testCompile 'com.google.guava:guava:' + project.'guava.version' + testCompile 'org.apache.commons:commons-exec:' + project.'commons-exec.version' + + testCompile group: 'org.codehaus.cargo', name: 'cargo-core-uberjar', version: '1.6.3' + + testCompile (project(':extensions/geode-modules')) { + // Remove everything related to Tomcat + exclude group: 'org.apache.tomcat' + } + testCompile (project(':extensions/geode-modules-session-internal')) { + // Remove everything related to Tomcat + exclude group: 'org.apache.tomcat' + } + + testCompile project(':extensions/session-testing-war') + + testRuntime files("${System.getProperty('java.home')}/../lib/tools.jar") + testRuntime files("$buildDir/install/${distributions.main.baseName}/lib/geode-dependencies.jar") + + + gfshDependencies ('org.springframework:spring-web:' + project.'springframework.version'){ + exclude module: 'spring-core' + exclude module: 'commons-logging' + } + + testCompile project(':geode-old-versions') } sourceSets { - // need to remove this since we use the dependencies jar out of the install dir - //test.runtimeClasspath -= configurations.provided + // need to remove this since we use the dependencies jar out of the install dir + //test.runtimeClasspath -= configurations.provided } test { - // test from the actual classpath not the gradle classpath - dependsOn installDist - // @TODO: this doesn't seem to be working need to get basename first. - classpath += files "$buildDir/install/${distributions.main.baseName}/lib/geode-dependencies.jar" + // test from the actual classpath not the gradle classpath + dependsOn installDist + // @TODO: this doesn't seem to be working need to get basename first. + classpath += files "$buildDir/install/${distributions.main.baseName}/lib/geode-dependencies.jar" } -tasks.withType(Test) { - environment 'GEODE_HOME', "$buildDir/install/${distributions.main.baseName}" +tasks.withType(Test){ + environment 'GEODE_HOME', "$buildDir/install/${distributions.main.baseName}" } task defaultDistributionConfig(type: JavaExec, dependsOn: classes) { - outputs.file file("$buildDir/gemfire.properties") - main 'org.apache.geode.distributed.internal.DefaultPropertiesGenerator' - classpath project(':geode-core').sourceSets.main.runtimeClasspath - workingDir buildDir - - doFirst { - buildDir.mkdirs() - } + outputs.file file("$buildDir/gemfire.properties") + main 'org.apache.geode.distributed.internal.DefaultPropertiesGenerator' + classpath project(':geode-core').sourceSets.main.runtimeClasspath + workingDir buildDir + + doFirst { + buildDir.mkdirs() + } } task defaultCacheConfig(type: JavaExec, dependsOn: classes) { - outputs.file file("$buildDir/cache.xml") - main 'org.apache.geode.internal.cache.xmlcache.CacheXmlGenerator' - classpath project(':geode-core').sourceSets.main.runtimeClasspath - workingDir buildDir - - doFirst { - buildDir.mkdirs() - } + outputs.file file("$buildDir/cache.xml") + main 'org.apache.geode.internal.cache.xmlcache.CacheXmlGenerator' + classpath project(':geode-core').sourceSets.main.runtimeClasspath + workingDir buildDir + + doFirst { + buildDir.mkdirs() + } } // This closure sets the gemfire classpath. If we add another jar to the classpath it must // be included in the filter logic below. def cp = { - // first add all the dependent project jars - def jars = configurations.archives.dependencies.collect { it.dependencyProject } - .findAll { !(it.name.contains('web') || it.name.contains('pulse')) } - .collect { it.jar.archiveName } - .join(' ') - - // then add all the dependencies of the dependent jars - jars += ' ' + configurations.archives.dependencies.collect { - it.dependencyProject.findAll { !(it.name.contains('web-api') || it.name.contains('pulse')) } - .collect { - it.configurations.runtime.collect { it.getName() }.findAll { - // depedencies from geode-core - it.contains('antlr') || - it.contains('commons-io') || - it.contains('commons-collections') || - it.contains('commons-lang') || - it.contains('commons-logging') || - it.contains('commons-validator') || - it.contains('fast-classpath-scanner') || - it.contains('fastutil') || - it.contains('jackson-annotations') || - it.contains('jackson-core') || - it.contains('jackson-databind') || - it.contains('jansi') || - it.contains('javax.resource-api') || - it.contains('javax.servlet-api') || - it.contains('javax.transaction-api') || - it.contains('jetty-http') || - it.contains('jetty-io') || - it.contains('jetty-security') || - it.contains('jetty-server') || - it.contains('jetty-servlet') || - it.contains('jetty-webapp') || - it.contains('jetty-util') || - it.contains('jetty-xml') || - it.contains('jline') || - it.contains('jna') || - it.contains('jopt-simple') || - it.contains('log4j-api') || - it.contains('log4j-core') || - it.contains('log4j-jcl') || - it.contains('log4j-jul') || - it.contains('log4j-slf4j-impl') || - it.contains('shiro') || - it.contains('slf4j-api') || - it.contains('spring-core') || - it.contains('spring-shell') || - it.contains('snappy') || - it.contains('jgroups') || - it.contains('netty') || - - // dependencies from geode-lucene - it.contains('lucene-analyzers-common') || - it.contains('lucene-core') || - it.contains('lucene-queries') || - it.contains('lucene-queryparser') || - it.contains('lucene-analyzers-phonetic') || - - // dependencies from geode-protobuf - it.contains('protobuf-java') - } - } - }.flatten().unique().join(' ') + // first add all the dependent project jars + def jars = configurations.archives.dependencies.collect { it.dependencyProject } + .findAll { !(it.name.contains('web') || it.name.contains('pulse')) } + .collect { it.jar.archiveName } + .join(' ') + + // then add all the dependencies of the dependent jars + jars += ' ' + configurations.archives.dependencies.collect { + it.dependencyProject.findAll { !(it.name.contains('web-api') || it.name.contains('pulse')) } + .collect { it.configurations.runtime.collect { it.getName() }.findAll { + // depedencies from geode-core + it.contains('antlr') || + it.contains('commons-io') || + it.contains('commons-collections') || + it.contains('commons-lang') || + it.contains('commons-logging') || + it.contains('commons-validator') || + it.contains('fast-classpath-scanner') || + it.contains('fastutil') || + it.contains('jackson-annotations') || + it.contains('jackson-core') || + it.contains('jackson-databind') || + it.contains('jansi') || + it.contains('javax.resource-api') || + it.contains('javax.servlet-api') || + it.contains('javax.transaction-api') || + it.contains('jetty-http') || + it.contains('jetty-io') || + it.contains('jetty-security') || + it.contains('jetty-server') || + it.contains('jetty-servlet') || + it.contains('jetty-webapp') || + it.contains('jetty-util') || + it.contains('jetty-xml') || + it.contains('jline') || + it.contains('jna') || + it.contains('jopt-simple') || + it.contains('log4j-api') || + it.contains('log4j-core') || + it.contains('log4j-jcl') || + it.contains('log4j-jul') || + it.contains('log4j-slf4j-impl') || + it.contains('shiro') || + it.contains('slf4j-api') || + it.contains('spring-core') || + it.contains('spring-shell') || + it.contains('snappy') || + it.contains('jgroups') || + it.contains('netty') || + + // dependencies from geode-lucene + it.contains('lucene-analyzers-common') || + it.contains('lucene-core') || + it.contains('lucene-queries') || + it.contains('lucene-queryparser') || + it.contains('lucene-analyzers-phonetic') || + + // dependencies from geode-protobuf + it.contains('protobuf-java') + } + } + }.flatten().unique().join(' ') - return jars + return jars } // Note: this dependency doesn't work if you change a library version from // a dependent project. Please fix me. -task depsJar(type: Jar, dependsOn: ':geode-core:classes') { - description 'Assembles the jar archive that defines the gemfire classpath.' - archiveName 'geode-dependencies.jar' - doFirst { - manifest { - attributes("Class-Path": cp()) - } +task depsJar (type: Jar, dependsOn: ':geode-core:classes') { + description 'Assembles the jar archive that defines the gemfire classpath.' + archiveName 'geode-dependencies.jar' + doFirst { + manifest { + attributes("Class-Path": cp()) } + } } // Note: this dependency doesn't work if you change a library version from // a dependent project. Please fix me. -task gfshDepsJar(type: Jar, dependsOn: ':geode-core:classes') { - description 'Assembles the jar archive that defines the gfsh classpath.' - archiveName 'gfsh-dependencies.jar' - doFirst { - manifest { - attributes("Class-Path": cp() + - ' ' + project(':geode-core').webJar.archiveName + - ' ' + configurations.gfshDependencies.collect { - it.getName() - }.flatten().join(' ') - ) - } +task gfshDepsJar (type: Jar, dependsOn: ':geode-core:classes') { + description 'Assembles the jar archive that defines the gfsh classpath.' + archiveName 'gfsh-dependencies.jar' + doFirst { + manifest { + attributes("Class-Path": cp() + + ' ' + project(':geode-core').webJar.archiveName + + ' ' + configurations.gfshDependencies.collect{ it.getName() }.flatten().join(' ') + ) } + } } @@ -250,7 +245,7 @@ task docs(type: Javadoc) { options.addStringOption('Xdoclint:none', '-quiet') options.links("https://lucene.apache.org/core/6_4_1/core/") options.links("https://lucene.apache.org/core/6_4_1/queryparser/") - options.encoding = 'UTF-8' + options.encoding='UTF-8' source parent.subprojects*.javadoc*.source classpath = files(parent.subprojects*.javadoc*.classpath) title = "${productName} ${project.version}" @@ -263,166 +258,166 @@ task docs(type: Javadoc) { destinationDir = docsDir doLast { - parent.subprojects.each { project -> - copy { - from project.sourceSets.main.resources.srcDirs - into docsDir - include 'javadoc-images/*' - } + parent.subprojects.each { project -> + copy { + from project.sourceSets.main.resources.srcDirs + into docsDir + include 'javadoc-images/*' } - } + } + } } task writeBuildInfo { - def buildInfo = file "$buildDir/.buildinfo" - outputs.file buildInfo - doLast { - buildInfo.getParentFile().mkdirs(); - new FileOutputStream(buildInfo).withStream { fos -> - project(':geode-core').readScmInfo().store(fos, '') - } + def buildInfo = file "$buildDir/.buildinfo" + outputs.file buildInfo + doLast { + buildInfo.getParentFile().mkdirs(); + new FileOutputStream(buildInfo).withStream { fos -> + project(':geode-core').readScmInfo().store(fos, '') } + } } -gradle.taskGraph.whenReady({ graph -> - tasks.withType(AbstractArchiveTask).findAll { - it.name.toLowerCase().contains("dist") - }.each { archive -> - archive.doLast { - ant.checksum file: "${archive.archivePath}", algorithm: "md5", format: 'MD5SUM' - ant.checksum file: "${archive.archivePath}", algorithm: "sha-256", format: 'MD5SUM', fileext: '.sha256' - } +gradle.taskGraph.whenReady( { graph -> + tasks.withType(AbstractArchiveTask).findAll { + it.name.toLowerCase().contains("dist") + }.each { archive -> + archive.doLast { + ant.checksum file:"${archive.archivePath}", algorithm:"md5", format: 'MD5SUM' + ant.checksum file:"${archive.archivePath}", algorithm:"sha-256", format: 'MD5SUM', fileext: '.sha256' } + } }) distributions { - src { - baseName = 'apache-geode' - contents { - from writeBuildInfo - from(rootDir) { - exclude 'KEYS' - exclude '**/gradlew' - exclude '**/gradlew.bat' - exclude '**/gradle/wrapper/gradle-wrapper.jar' - exclude '**/.gradle' - exclude '**/build/**' - exclude '**/.project' - exclude '**/.classpath' - exclude '**/.settings/**' - exclude '**/build-eclipse/**' - exclude '**/.idea/**' - exclude '**/*.iml' - exclude '**/*.ipr' - exclude '**/*.iws' - exclude '**/.travis.yml' - exclude '**/tags' - - //These directories are generated on the jenkins server by gradle - exclude 'caches' - exclude 'daemon' - exclude 'native' - exclude 'wrapper' - } + src { + baseName = 'apache-geode' + contents { + from writeBuildInfo + from (rootDir) { + exclude 'KEYS' + exclude '**/gradlew' + exclude '**/gradlew.bat' + exclude '**/gradle/wrapper/gradle-wrapper.jar' + exclude '**/.gradle' + exclude '**/build/**' + exclude '**/.project' + exclude '**/.classpath' + exclude '**/.settings/**' + exclude '**/build-eclipse/**' + exclude '**/.idea/**' + exclude '**/*.iml' + exclude '**/*.ipr' + exclude '**/*.iws' + exclude '**/.travis.yml' + exclude '**/tags' + + //These directories are generated on the jenkins server by gradle + exclude 'caches' + exclude 'daemon' + exclude 'native' + exclude 'wrapper' + } - } } + } + + main { + baseName = 'apache-geode' //TODO rootProject.name + contents { + duplicatesStrategy 'exclude' + exclude '*.asc' + + exclude '*.asc' + exclude '*-sources.jar' + exclude '*-javadoc.jar' + + from rootProject.file( 'README.md' ) + + into ('config') { + from defaultCacheConfig + from defaultDistributionConfig + from (project(':geode-core').sourceSets.main.resources.files.find { + it.name == 'log4j2.xml' + }) + } + + into ('lib') { + from project(":geode-common").configurations.runtime + from project(":geode-common").configurations.archives.allArtifacts.files + + from project(":geode-json").configurations.runtime + from project(":geode-json").configurations.archives.allArtifacts.files + + from project(":geode-wan").configurations.runtime + from project(":geode-wan").configurations.archives.allArtifacts.files + + from project(":geode-cq").configurations.runtime + from project(":geode-cq").configurations.archives.allArtifacts.files + + from project(":geode-core").configurations.runtime + from project(":geode-core").configurations.archives.allArtifacts.files + + from project(":geode-lucene").configurations.runtime + from project(":geode-lucene").configurations.archives.allArtifacts.files + + from project(":geode-old-client-support").configurations.runtime + from project(":geode-old-client-support").configurations.archives.allArtifacts.files + + from project(":geode-protobuf").configurations.runtime + from project(":geode-protobuf").configurations.archives.allArtifacts.files + + from project(":geode-rebalancer").configurations.runtime + from project(":geode-rebalancer").configurations.archives.allArtifacts.files + + from configurations.bundled + from configurations.gfshDependencies + + //These tasks are included as closures (wrapped in {}) because gradle may evaluate + //this CopySpec before it evaluates the geode-core build file. + from { project(":geode-core").webJar } + from { project(":geode-core").raJar } + from { project(":geode-core").jcaJar } + + // dependency jars + from depsJar + from gfshDepsJar + } + + into ('tools/Extensions') { + from (project(":geode-web").configurations.archives.allArtifacts.files) { + exclude '*.jar' + } + from (project(":geode-web-api").configurations.archives.allArtifacts.files) { + exclude '*.jar' + } + } - main { - baseName = 'apache-geode' //TODO rootProject.name - contents { - duplicatesStrategy 'exclude' - exclude '*.asc' - - exclude '*.asc' - exclude '*-sources.jar' - exclude '*-javadoc.jar' - - from rootProject.file('README.md') - - into('config') { - from defaultCacheConfig - from defaultDistributionConfig - from(project(':geode-core').sourceSets.main.resources.files.find { - it.name == 'log4j2.xml' - }) - } - - into('lib') { - from project(":geode-common").configurations.runtime - from project(":geode-common").configurations.archives.allArtifacts.files - - from project(":geode-json").configurations.runtime - from project(":geode-json").configurations.archives.allArtifacts.files - - from project(":geode-wan").configurations.runtime - from project(":geode-wan").configurations.archives.allArtifacts.files - - from project(":geode-cq").configurations.runtime - from project(":geode-cq").configurations.archives.allArtifacts.files - - from project(":geode-core").configurations.runtime - from project(":geode-core").configurations.archives.allArtifacts.files - - from project(":geode-lucene").configurations.runtime - from project(":geode-lucene").configurations.archives.allArtifacts.files - - from project(":geode-old-client-support").configurations.runtime - from project(":geode-old-client-support").configurations.archives.allArtifacts.files - - from project(":geode-protobuf").configurations.runtime - from project(":geode-protobuf").configurations.archives.allArtifacts.files - - from project(":geode-rebalancer").configurations.runtime - from project(":geode-rebalancer").configurations.archives.allArtifacts.files - - from configurations.bundled - from configurations.gfshDependencies - - //These tasks are included as closures (wrapped in {}) because gradle may evaluate - //this CopySpec before it evaluates the geode-core build file. - from { project(":geode-core").webJar } - from { project(":geode-core").raJar } - from { project(":geode-core").jcaJar } - - // dependency jars - from depsJar - from gfshDepsJar - } - - into('tools/Extensions') { - from(project(":geode-web").configurations.archives.allArtifacts.files) { - exclude '*.jar' - } - from(project(":geode-web-api").configurations.archives.allArtifacts.files) { - exclude '*.jar' - } - } - - into('tools/ClientProtocol') { - from {project(":geode-protobuf").zip.outputs.files} - } + into('tools/ClientProtocol') { + from {project(":geode-protobuf").zip.outputs.files} + } - into('javadoc') { - from docs - } + into ('javadoc') { + from docs + } - into('tools/Pulse') { - from(project(":geode-pulse").configurations.archives.allArtifacts.files) - } + into ('tools/Pulse') { + from (project(":geode-pulse").configurations.archives.allArtifacts.files) + } - into('tools/Modules') { - from(project(':extensions/geode-modules-assembly').configurations.moduleDistOutputs.files) - } - } + into ('tools/Modules') { + from (project(':extensions/geode-modules-assembly').configurations.moduleDistOutputs.files) + } } + } } // Create a configuration closure to configure test targets with the install directory def dependOnInstalledProduct = { - dependsOn installDist - def install = file("$buildDir/install/${distributions.main.baseName}") - environment('GEODE_HOME', install) + dependsOn installDist + def install = file("$buildDir/install/${distributions.main.baseName}") + environment ('GEODE_HOME', install) } // Add the configuration closure to the test targets so they depend on the install directory @@ -441,31 +436,31 @@ distributedTest.dependsOn ':geode-old-versions:build' /**Print the names of all jar files in a fileTree */ def printJars(tree) { - tree.matching { include("**/*.jar") }.visit { file -> - if (!file.isDirectory()) { - println file.name - } - }; + tree.matching {include("**/*.jar")}.visit{ file -> + if(!file.isDirectory()) { + println file.name + } + }; } task dumpInstalledJars(dependsOn: installDist) { - doLast { - description "Dump a list of all of the jars shipped with the binary distribution, for validation purposes" - - FileTree installDir = fileTree(dir: installDist.destinationDir) - - println("Jars in the binary install") - println("==========================") - printJars(installDir) - - installDir.include("**/*.war").visit { file -> - if (!file.isDirectory()) { - FileTree warContents = zipTree(file.file) - println "" - println file.name - println("==========================") - printJars(warContents); - } - }; - } + doLast { + description "Dump a list of all of the jars shipped with the binary distribution, for validation purposes" + + FileTree installDir = fileTree(dir: installDist.destinationDir) + + println("Jars in the binary install") + println("==========================") + printJars(installDir) + + installDir.include("**/*.war").visit{ file -> + if(!file.isDirectory()) { + FileTree warContents = zipTree(file.file) + println "" + println file.name + println("==========================") + printJars(warContents); + } + }; + } } diff --git a/geode-protobuf/build.gradle b/geode-protobuf/build.gradle index 833a43b..7b605be 100644 --- a/geode-protobuf/build.gradle +++ b/geode-protobuf/build.gradle @@ -67,5 +67,4 @@ task zip(type: Zip) { destinationDir = file("$buildDir/libs") } -build.dependsOn 'zip' assemble.dependsOn 'zip' \ No newline at end of file -- To stop receiving notification emails like this one, please contact ['"commits@geode.apache.org" <commits@geode.apache.org>'].