import javax.xml.transform.TransformerFactory description = 'The THREDDS Data Server (TDS) is a web server that provides catalog and data access services for ' + 'scientific data using OPeNDAP, OGC WCS and WMS, HTTP, and other remote-data-access protocols.' ext.title = 'THREDDS Data Server (TDS)' ext.url = 'https://www.unidata.ucar.edu/software/tds/' apply from: "$rootDir/gradle/any/dependencies.gradle" apply from: "$rootDir/gradle/any/war-published.gradle" apply from: "$rootDir/gradle/any/gretty.gradle" dependencies { implementation enforcedPlatform (project(':tds-platform')) testImplementation enforcedPlatform (project(':tds-testing-platform')) implementation 'edu.ucar:bufr' implementation 'edu.ucar:cdm-core' implementation 'edu.ucar:cdm-radial' implementation 'edu.ucar:cdm-misc' implementation 'edu.ucar:cdm-image' implementation 'edu.ucar:cdm-s3' implementation 'edu.ucar:cdm-zarr' implementation 'edu.ucar:grib' implementation 'edu.ucar:netcdf4' implementation 'edu.ucar:httpservices' implementation 'edu.ucar:opendap' implementation project(':opendap:opendap-servlet') implementation project(':tdcommon') implementation 'edu.ucar:cdm-mcidas' implementation 'edu.ucar:waterml' implementation project(':tds-ugrid') implementation 'net.openhft:chronicle-map' implementation 'jakarta.validation:jakarta.validation-api' // DAP4 Dependencies (technically forward) implementation 'edu.ucar:dap4' implementation project(':dap4:d4servlet') // Server stuff providedCompile "jakarta.servlet:jakarta.servlet-api:${depVersion.jakartaServletApi}" runtimeOnly "jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api" runtimeOnly "org.glassfish.web:jakarta.servlet.jsp.jstl" runtimeOnly 'org.apache.taglibs:taglibs-standard-spec' runtimeOnly 'org.apache.taglibs:taglibs-standard-impl' // Apache httpclient libraries implementation 'org.apache.httpcomponents:httpclient' implementation 'org.apache.httpcomponents:httpcore' implementation 'com.coverity.security:coverity-escapers' // todo: replace with google escapers? implementation 'org.jdom:jdom2' implementation 'org.quartz-scheduler:quartz' implementation 'com.google.code.findbugs:jsr305' implementation 'com.google.guava:guava' implementation 'joda-time:joda-time' implementation 'org.apache.commons:commons-lang3' // WaterML implementation 'org.apache.xmlbeans:xmlbeans' implementation 'org.n52.sensorweb:52n-xml-waterML-v20' implementation 'org.n52.sensorweb:52n-xml-om-v20' // Spring implementation 'org.springframework:spring-core' implementation 'org.springframework:spring-context' implementation 'org.springframework:spring-beans' implementation 'org.springframework:spring-web' implementation 'org.springframework:spring-webmvc' runtimeOnly 'org.springframework.security:spring-security-web' // Needed for FilterChainProxy in applicationContext.xml. runtimeOnly 'org.springframework.security:spring-security-config' // Needed for "xmlns:security" schema in applicationContext.xml. // Needed for XPath operations in mock tests testImplementation 'jaxen:jaxen' // Needed for XPaths in WmsDetailedConfig runtimeOnly 'jaxen:jaxen' // edal ncwms related libs implementation('uk.ac.rdg.resc:edal-common') implementation ('uk.ac.rdg.resc:edal-cdm') implementation ('uk.ac.rdg.resc:edal-graphics') implementation ('uk.ac.rdg.resc:edal-wms') implementation('uk.ac.rdg.resc:edal-godiva') // json writing 'org.json:json' // JSR 303 with Hibernate Validator, which is dragging in jboss logging runtimeOnly 'org.hibernate.validator:hibernate-validator' runtimeOnly 'org.glassfish:jakarta.el' annotationProcessor "org.hibernate.validator:hibernate-validator-annotation-processor:${depVersion.hibernateValidator}" // @Resource annotation (removed post Java 8) implementation 'jakarta.annotation:jakarta.annotation-api' implementation 'org.thymeleaf:thymeleaf-spring6' // Testing testImplementation "jakarta.servlet:jakarta.servlet-api:${depVersion.jakartaServletApi}" testImplementation 'org.springframework:spring-test' testImplementation 'org.hamcrest:hamcrest-core' testImplementation 'commons-io:commons-io' testImplementation 'pl.pragmatists:JUnitParams' testImplementation 'com.google.truth:truth' testImplementation 'junit:junit' testImplementation project(':tds-test-utils'); testImplementation 'edu.ucar:cdm-test-utils' // Contains stuff like the JUnit @Category classes. testImplementation 'edu.ucar:httpservices' testImplementation 'com.beust:jcommander' // Logging implementation 'org.slf4j:slf4j-api' runtimeOnly 'org.apache.logging.log4j:log4j-slf4j-impl' runtimeOnly 'org.apache.logging.log4j:log4j-jakarta-web' testRuntimeOnly 'ch.qos.logback:logback-classic' // This is for freshInstallTest testImplementation 'org.xmlunit:xmlunit-core' // For comparing catalog XML. } // "testRuntime" extends from "runtime", meaning that "testRuntime" will get the log4j dependencies declared in // "runtime". However, we want logback-classic to be the logger during tests, so exclude all of the log4j stuff. configurations.testRuntimeOnly { exclude group: 'org.apache.logging.log4j' } configurations.testRuntimeClasspath { resolutionStrategy { force "jakarta.servlet:jakarta.servlet-api:${depVersion.testJakartaServletApi}" } } task copyWebappFilesForTests(type: Copy) { // Tests expect for certain webapp files to be accessible from the classpath (e.g. WEB-INF/applicationContext.xml). from 'src/main/webapp' from 'src/main/webapp/WEB-INF/classes' into sourceSets.test.java.outputDir } processTestResources { dependsOn copyWebappFilesForTests } war { // Assert that no servlet-api JAR is slated for inclusion in the WAR. doFirst { File servletApiJar = classpath.find { it.name.contains('servlet-api') } if (servletApiJar) { // This will fail the build. throw new GradleException("Found a servlet-api JAR in the WAR classpath: ${servletApiJar.name}") } } // Replace '$projectVersion' and '$buildTimestamp' placeholders with the correct values. // Currently, we only use those placeholders in tds.properties and README.txt. def properties = [:] properties['projectVersion'] = project.version properties['buildTimestamp'] = project.buildTimestamp // Defined in root project. // War CopySpec already includes everything in 'src/main/webapp', which tds.properties lives within. // So, the from() and into() methods aren't needed. filesMatching('**/tds.properties') { expand properties } from('README.txt') { into 'docs' expand properties } } ////////////////////////////////////// Godiva 3 ////////////////////////////////////// // Adapted from https://blog.eveoh.nl/2012/01/using-google-web-toolkit-with-gradle/ // 'de.richsource.gradle.plugins:gwt-gradle-plugin:0.6' is also available, but it has problems. // First, it modifies the testRuntime configuration after project evaluation: // https://github.com/steffenschaefer/gwt-gradle-plugin/issues/89. In the case of TDS, this lead to an old version // of "validation-api" being present on the classpath, which caused hibernate-validator to fail. // // Second, it adds gwt-dev and gwt-user to the "compile" config and gwt-servlet to the "runtime" config. // None of those are necessary at runtime: http://stackoverflow.com/a/5135151/3874643. // Even worse, the JARs are *huge*, and inflated the size of tds.war by ~59 MB. configurations { gwt } dependencies { // These are needed by the compileGwt task but nowhere else, which is why we place them in their own config. gwt "com.google.gwt:gwt-user:${depVersion.gwt}" gwt "com.google.gwt:gwt-dev:${depVersion.gwt}" } ext { gwtDir = "${project.buildDir}/gwt" extraDir = "${project.buildDir}/extra" } task compileGwt (dependsOn: classes, type: JavaExec) { inputs.files(sourceSets.main.java.srcDirs).skipWhenEmpty() inputs.dir sourceSets.main.output.resourcesDir outputs.dir gwtDir doFirst { file(gwtDir).mkdirs() } main = 'com.google.gwt.dev.Compiler' classpath { [ configurations.gwt, // For com.google.gwt.dev.Compiler in "gwt-dev". sourceSets.main.compileClasspath, // For 'uk/ac/rdg/resc/godiva/Godiva.gwt.xml' in "edal-java". sourceSets.main.resources.srcDirs // For Godiva3.gwt.xml in 'tds/src/main/resources'. ] } args = [ 'Godiva3', // The GWT module, from edal-godiva. '-war', gwtDir, '-logLevel', 'WARN', // Only get log messages at level WARN or above. We don't want the spammy output. '-localWorkers', '2', '-compileReport', '-extra', extraDir, ] maxHeapSize = '512M' } war { dependsOn compileGwt from gwtDir destinationDirectory = new File(rootProject.getBuildDir(), "downloads") archiveFileName = "thredds-${project.getVersion()}.war" } jar { archiveClassifier = 'classes' } configurations.all { // STAX is already included in Java 1.6+; no need for a third-party dependency. /* ./gradlew -q tds:dependencyInsight --configuration runtime --dependency stax-api stax:stax-api:1.0.1 +--- org.apache.xmlbeans:xmlbeans:2.6.0 | ... \--- org.codehaus.jettison:jettison:1.3.7 \--- net.openhft:chronicle-map:2.4.15 ... */ exclude group: 'stax', module: 'stax-api' exclude group: 'org.slf4j', module: 'slf4j-log4j12' exclude group: 'net.openhft', module: 'chronicle-analytics' } ////////////////////////////////////// Integration Tests ////////////////////////////////////// import org.akhikhl.gretty.AppBeforeIntegrationTestTask import org.akhikhl.gretty.AppAfterIntegrationTestTask import java.nio.file.Paths test { jvmArgs = jvmArguments } gretty { httpPort = 8081 contextPath = '/thredds' jvmArgs = jvmArguments } def testInfomation = [[name: 'freshInstallTest', contentDir: file("$buildDir/tdsFreshContentDir"), description: description = 'Runs tests on a fresh installation of TDS (no existing catalog.xml).'], [name: 'integrationTests', contentDir: file("$projectDir/src/test/content"), description: description = 'Runs tds integration tests.']] def previousTestTask = test def previousTestTaskFinalizer = null testInfomation.forEach { testInfo -> sourceSets.create(testInfo.name) { resources.srcDirs = [file("src/${testInfo.name}/resources")] // Need 'sourceSets.test.output' because we use TestOnLocalServer in our test. compileClasspath += sourceSets.test.output + configurations.testCompileClasspath runtimeClasspath += output + sourceSets.test.output + configurations.testRuntimeClasspath } def thisTestTask = tasks.create("${testInfo.name}", Test) { group = 'verification' description testInfo.description as String testClassesDirs = sourceSets.getByName("${testInfo.name}").output classpath = sourceSets."${testInfo.name}".runtimeClasspath mustRunAfter previousTestTask // Use built-in Xalan XSLT instead of Saxon-HE. // This works around an error we were seeing in org.xmlunit.builder.DiffBuilder.build(): // java.lang.ClassCastException: net.sf.saxon.value.ObjectValue cannot be cast to net.sf.saxon.om.NodeInfo // ... // at thredds.tds.TestFreshTdsInstall.shouldReturnExpectedClientCatalog(TestFreshTdsInstall.java:72) // See buildSrc/build.gradle for another example of working around JAXP weirdness. systemProperty TransformerFactory.name, // See javax.xml.transform.TransformerFactory.newInstance(). 'com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl' // Replace the system property that was propagated from the Gradle process in any/testing.gradle. systemProperty 'tds.content.root.path',testInfo.contentDir.absolutePath } task ("before${testInfo.name.capitalize()}", type: AppBeforeIntegrationTestTask, group: 'gretty') { dependsOn assemble description = "Starts server before ${testInfo.name}." inplace = false integrationTestTask thisTestTask.getName() debug = false // Start the embedded sever in debug mode. // for integration tests that rely on gretty, make sure the embedded server is stopped before // moving on to next test set. if (previousTestTaskFinalizer != null) { mustRunAfter previousTestTaskFinalizer } prepareServerConfig { // The embedded TDS that this task launches will have a non-existent content root directory. systemProperty 'tds.content.root.path', testInfo.contentDir.absolutePath } doFirst { if(testInfo.name == "freshInstallTest") { assert testInfo.contentDir.deleteDir(): "Couldn't delete ${testInfo.contentDir}." } } } previousTestTaskFinalizer = tasks.create("after${testInfo.name.capitalize()}", AppAfterIntegrationTestTask) { group = "gretty" description = "Stops server after ${testInfo.name}." integrationTestTask thisTestTask.getName() } check.dependsOn thisTestTask previousTestTask = thisTestTask }