plugins { id 'java' id 'groovy' id 'eclipse' id "de.undercouch.download" version "5.0.1" id 'war' // Handles the WAR layout mechanics natively } import java.nio.file.Files import java.nio.file.Path // Project Metadata group = 'org.thoughtframe' def buildVersion = findProperty('version') ?: 'local' version = buildVersion description = 'thoughtframe project' // Compile with JDK 25 regardless of the JDK running Gradle (foojay auto-provisions it). java { toolchain { languageVersion = JavaLanguageVersion.of(25) } } // Path Settings def srcDir = 'src' def testSrcDir = 'test' def resourcesDir = 'resources' def libDir = 'lib' def deployDir = 'deploy' def emDir = 'dependancies/entermedia' // def framebranch = findProperty('framebranch') ?: 'dev_' repositories { mavenLocal() // 🌟 Locates your new entermedia snapshots mavenCentral() // Pulls down modern open-source equivalents gradlePluginPortal() maven { url 'https://repo.gradle.org/gradle/libs-releases' } } dependencies { // 1. Core Framework Foundation api project(':runtime-api') implementation project(':dam') // Unified SLF4J 2.x Logging Ecosystem implementation 'org.slf4j:slf4j-api:2.0.12' implementation 'org.slf4j:slf4j-jdk14:2.0.12' // Runtime Request Interception Bridges implementation 'org.slf4j:jcl-over-slf4j:2.0.12' implementation 'org.slf4j:log4j-over-slf4j:2.0.12' // Embedded Jetty Engine for local launching/testing // 🔄 Update the group ID for the ee8-webapp module: implementation 'org.eclipse.jetty:jetty-server:12.0.35' implementation 'org.eclipse.jetty.ee8:jetty-ee8-webapp:12.0.35' // 🌟 FIXES: Unresolved dependency error! implementation 'org.eclipse.jetty.ee8.websocket:jetty-ee8-websocket-javax-server:12.0.35' implementation 'org.eclipse.jetty:jetty-util:12.0.35' // 2. Specialized Document Engines implementation 'com.itextpdf:itextpdf:5.5.7' implementation 'com.itextpdf:itext-pdfa:5.5.7' implementation 'com.itextpdf.tool:xmlworker:5.5.7' // 🌟 FIXES: xmlworker group ID coordinates // 🌟 FIXES: commons-imaging SNAPSHOT error by cleanly disabling it inside itext-xtra implementation('com.itextpdf:itext-xtra:5.5.7') { exclude group: 'org.apache.commons', module: 'commons-imaging' } // ODF & Document Translators implementation 'org.odftoolkit:odfdom-java:0.8.7' implementation 'fr.opensagres.xdocreport:org.odftoolkit.odfdom.converter.core:1.0.0' implementation 'fr.opensagres.xdocreport:org.odftoolkit.odfdom.converter.pdf:1.0.0' implementation 'fr.opensagres.xdocreport:org.odftoolkit.odfdom.converter.xhtml:1.0.0' // 3. Git Automation Suite implementation 'org.eclipse.jgit:org.eclipse.jgit:6.8.0.202311291450-r' implementation 'org.eclipse.jgit:org.eclipse.jgit.http.server:6.8.0.202311291450-r' // 4. Modern I/O Network Clients & Utilities implementation 'com.squareup.okhttp3:okhttp:4.12.0' implementation 'com.squareup.okhttp3:okhttp-sse:4.12.0' implementation 'com.squareup.okio:okio:3.6.0' implementation 'com.neovisionaries:nv-websocket-client:2.12' implementation 'com.jayway.jsonpath:json-path:2.7.0' // 5. Payment Gateways, Subscriptions, & Feeds implementation 'com.stripe:stripe-java:22.28.0' implementation 'com.paypal.sdk:rest-api-sdk:1.14.0' implementation 'org.python:jython-standalone:2.7.2' implementation 'org.apache.commons:commons-math4-core:4.0-beta1' // 6. Base Scripting, Mail & Platform Protocols compileOnly "org.apache.groovy:groovy-all:6.0.0" // testImplementation "org.apache.groovy:groovy-all:6.0.0" // implementation "org.eclipse.angus:angus-mail:2.0.3" // implementation "jakarta.mail:jakarta.mail-api:2.1.3" // implementation "jakarta.activation:jakarta.activation-api:2.1.3" // implementation 'org.gradle:gradle-tooling-api:8.10' implementation 'javax.websocket:javax.websocket-api:1.1' implementation 'commons-lang:commons-lang:2.6' // 7. Local Custom Jars ONLY (Including pngj to bypass coordinate headaches!) implementation fileTree(dir: 'lib', include: [ 'jcsAnetGWv1_02.jar', 'paypal_base.jar', 'openecho1.2.6.jar', 'store-reports.jar', 'tincan-1.1.0.jar', ]) // 8. Testing // `test` is JUnit 5 (Jupiter). The old suite is JUnit 3 (junit.framework.TestCase via // BaseTestCase/StoreTestCase) and lives in src/legacyTest - see the legacyTest task below. testImplementation 'org.junit.jupiter:junit-jupiter-api:5.12.0' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.12.0' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.12.0' testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.12.0' } // OpenSearch k-NN ships only as a plugin distribution zip (no consumable jar), so unpack it // and put its jars on the runtime classpath. The embedded node loads the plugin by classname // (node.xml opensearch.plugins) and the engine stages this runtime classpath, so shipping it // here is what puts it in eclipse/engine. Same shape as the product :damsel build. configurations { knnPlugin } def knnLibsDir = layout.buildDirectory.dir('knn-plugin').get().asFile tasks.register('unpackKnnPlugin', Copy) { description = 'Unpack the OpenSearch k-NN plugin zip onto the runtime classpath' from({ zipTree(configurations.knnPlugin.singleFile) }) into knnLibsDir } tasks.named('compileJava') { dependsOn 'unpackKnnPlugin' } dependencies { knnPlugin 'org.opensearch.plugin:opensearch-knn:3.8.0.0@zip' // The plugin expects these from the OpenSearch distribution's lib dir / SPI, which the // maven artifacts do not provide transitively (the same set :damsel declares). The // bundled slf4j is dropped - thoughtframe already provides slf4j-api. runtimeOnly fileTree(dir: knnLibsDir, include: '*.jar', exclude: 'slf4j-api-*.jar') runtimeOnly 'com.google.guava:guava:33.4.8-jre' runtimeOnly 'org.opensearch.plugin:transport-grpc:3.8.0' runtimeOnly 'org.opensearch.plugin:lang-painless:3.8.0' } // --------------------------------------------------------------------------- // Legacy JUnit 3 suite (src/legacyTest). Kept runnable so it can be rebuilt // incrementally, but NOT wired into `check`/`build` - it has pre-existing // failures against the current catalog fixture. Run with: // ./gradlew :thoughtframe:legacyTest // --------------------------------------------------------------------------- sourceSets { legacyTest { java.srcDir 'src/legacyTest/java' compileClasspath += sourceSets.main.output + configurations.testRuntimeClasspath runtimeClasspath += output + compileClasspath } } configurations { legacyTestImplementation.extendsFrom testImplementation legacyTestRuntimeOnly.extendsFrom testRuntimeOnly } dependencies { legacyTestImplementation 'junit:junit:4.13.2' legacyTestRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.12.0' } tasks.register('legacyTest', Test) { group = 'verification' description = 'Runs the legacy JUnit 3 suite in src/legacyTest (opt-in; has known failures).' testClassesDirs = sourceSets.legacyTest.output.classesDirs classpath = sourceSets.legacyTest.runtimeClasspath useJUnitPlatform() testLogging { events "failed", "skipped" } // Only the AllTests aggregators, otherwise every case runs twice (once via its // suite, once via direct class discovery). filter { includeTestsMatching '*AllTests' } shouldRunAfter tasks.named('test') } tasks.register("setupLinks") { group = "thoughtframe" description = "Creates the local development runtime." doLast { def runtime = projectDir.toPath().resolve("webapp") def base = runtime.resolve("WEB-INF/base") Files.createDirectories(base) // Stale generated symlinks from older setupLinks runs must not linger: they carry // machine-specific absolute targets and break createWar on any other host (e.g. CI). ["emfrontend", "emshare"].each { n -> def stale = base.resolve(n) if (Files.isSymbolicLink(stale) || Files.exists(stale)) { println "Removing stale ${stale}" Files.delete(stale) } } def links = [ [target: project(":dam").projectDir.toPath().resolve("webapp/WEB-INF/base/entermedia"), link: base.resolve("entermedia")], [target: project(":dam").projectDir.toPath().resolve("webapp/WEB-INF/base/emfrontend2"), link: base.resolve("emfrontend2")], [target: project(":dam").projectDir.toPath().resolve("webapp/WEB-INF/base/system"), link: base.resolve("system")], [target: project(":dam").projectDir.toPath().resolve("webapp/WEB-INF/base/manager"), link: base.resolve("manager")], [target: project(":dam").projectDir.toPath().resolve("webapp/WEB-INF/base/mediadb"), link: base.resolve("mediadb")], [target: project(":cms").projectDir.toPath().resolve("webapp/openedit"), link: base.resolve("openedit")] ] links.each { if (Files.exists(it.link) || Files.isSymbolicLink(it.link)) { Files.delete(it.link) } println "Linking ${it.link} -> ${it.target}" Files.createSymbolicLink(it.link, it.target) } } } tasks.named("war") { dependsOn("setupLinks") } // Compilation Hooks linked cleanly without inline class injection loops tasks.named('compileGroovy') { groovyOptions.encoding = 'UTF-8' // options.release = 25 // } tasks.named('compileJava') { options.encoding = 'UTF-8' // options.release = 25 // } tasks.named('test') { useJUnitPlatform() // testClassesDirs = sourceSets.test.output.classesDirs // testLogging { events "passed", "skipped", "failed" } // } tasks.named('clean') { delete deployDir } // jar { dependsOn 'classes' // archiveBaseName.set("${framebranch}thoughtframe") // archiveVersion.set(version) // destinationDirectory.set(file("$deployDir/builds")) // } // 📦 Streamlined production Web Deployment construction task createWar(type: War) { dependsOn jar, setupLinks // setupLinks for the local run; the WAR copies the bases directly archiveBaseName.set("ROOT") // archiveFileName.set("ROOT.war") // fixed name — the runtime bakes docker/runtime/ROOT.war destinationDirectory.set(file("$deployDir")) // duplicatesStrategy = DuplicatesStrategy.EXCLUDE // // Product code lives in WEB-INF/lib only. The War task otherwise routes the classpath // dirs into WEB-INF/classes, which shadow the jar and are never refreshed by the // runtime's overlay upgrade (entrypoint deletes them on first boot as a safety net). classpath = files() // 2. Automate Library Assemblies via Runtime Classpath (No manual listing needed!) from(configurations.runtimeClasspath) { into "WEB-INF/lib" } // // 3. Stage ThoughtFrame core components from("webapp") { // include "WEB-INF/base/thoughtframe/**" // include "WEB-INF/base/_site.xconf" // include "WEB-INF/bin/**" // include "WEB-INF/data/system/**" // include "WEB-INF/*.*" // include "*.*" // } from("webapp/WEB-INF/base/thoughtframe") { into "WEB-INF/base/thoughtframe" } // // Platform bases copied DIRECTLY from the sibling projects — not via the setupLinks // symlinks (host-specific, and dangling on a fresh checkout / CI). from(project(":dam").file("webapp/WEB-INF/base/entermedia")) { into "WEB-INF/base/entermedia" } from(project(":dam").file("webapp/WEB-INF/base/emfrontend2")) { into "WEB-INF/base/emfrontend2" } from(project(":dam").file("webapp/WEB-INF/base/system")) { into "WEB-INF/base/system" } from(project(":dam").file("webapp/WEB-INF/base/manager")) { into "WEB-INF/base/manager" } from(project(":dam").file("webapp/WEB-INF/base/mediadb")) { into "WEB-INF/base/mediadb" } from(project(":cms").file("webapp/openedit")) { into "WEB-INF/base/openedit" } // // 4. Fallback for files that are strictly local or un-mavenized from(libDir) { // include "jcsAnet*.jar" // include "openecho*.jar" // include "paypal*.jar" // into "WEB-INF/lib" // } // 5. Inject your compiled core application bundle from("$deployDir/builds") { // include "${framebranch}thoughtframe-${version}.jar" // into "WEB-INF/lib" // } manifest { attributes( "Built-By": "THOUGHTFRAME.AI", // "Implementation-Title": project.name, // "Implementation-Version": version, // "Implementation-Vendor": "ThoughtFrame.AI" // ) } } task buildAll { group = 'build' // description = 'Run the full build process' // dependsOn clean, jar, createWar // } defaultTasks 'buildAll' // // Updated configurations to force Eclipse execution priority // The Eclipse bin/ output tree is not a build input, but a stale resource copy inside it can // collide with the real one under src/ - Gradle 9 fails on duplicate archive entries where older // versions silently included them. Same strategy thoughtframe's createWar already sets. tasks.withType(Jar).configureEach { duplicatesStrategy = DuplicatesStrategy.EXCLUDE }