Skip to content

Commit

Permalink
Java application plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
jjohannes committed Dec 4, 2023
1 parent 156881b commit d8c7f38
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 0 deletions.
7 changes: 7 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
plugins {
id("org.example.application")
}

application {
mainClass.set("org.example.product.app.Application")
}
9 changes: 9 additions & 0 deletions gradle/plugins/java-application-plugins/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
plugins {
`kotlin-dsl`
}

dependencies {
implementation(platform(project(":plugins-platform")))

implementation(project(":java-base-plugins"))
implementation("org.example:build-parameters-plugins")
implementation("org.owasp:dependency-check-gradle")
implementation("io.fuchs.gradle.classpath-collision-detector:classpath-collision-detector")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import org.example.application.tasks.MD5DirectoryChecksum
import org.example.application.tasks.VersionXml

plugins {
id("org.example.java")
id("application") // For stand-alone application packaging
id("jacoco-report-aggregation") // get and aggregated coverage report for all tests
id("test-report-aggregation") // get and aggregated result report for all tests
id("org.example.war") // For web application packaging/deployment
id("org.example.end2end-testing")
id("io.fuchs.gradle.classpath-collision-detector")
id("org.owasp.dependencycheck")
}

configurations.aggregateTestReportResults {
shouldResolveConsistentlyWith(configurations.appRuntimeClasspath.get())
}
configurations.aggregateCodeCoverageReportResults {
shouldResolveConsistentlyWith(configurations.appRuntimeClasspath.get())
}

// Generate additional resources required at application runtime
val generateVersionXml = tasks.register<VersionXml>("generateVersionXml") {
mainVersion.set(providers.fileContents(
rootProject.layout.projectDirectory.file("gradle/version.txt")).asText)
xmlFile.set(layout.buildDirectory.file("generated-resources/xml/version.xml"))
}
val resourcesChecksum = tasks.register<MD5DirectoryChecksum>("resourcesChecksum") {
inputDirectory.set(layout.projectDirectory.dir("src/main/resources"))
checksumFile.set(layout.buildDirectory.file("generated-resources/md5/resources.MD5"))
}

tasks.processResources {
from(generateVersionXml)
from(resourcesChecksum)
}

dependencyCheck {
scanConfigurations = listOf(configurations.runtimeClasspath.get().name)
autoUpdate = false
}

tasks.check {
dependsOn(tasks.testAggregateTestReport)
dependsOn(tasks.testCodeCoverageReport)
dependsOn(tasks.detectCollisions)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
plugins {
id("org.example.java")
}

// Specific API fixtures used for testing without live service
val mockApi = sourceSets.create("mockApi")
java.registerFeature(mockApi.name) {
usingSourceSet(mockApi)
}

// end-to-end tests located in the :app project
testing.suites.create<JvmTestSuite>("endToEndTest") {
useJUnitJupiter("")
targets.all {
testTask {
options {
this as JUnitPlatformOptions
excludeTags("slow")
}
}
tasks.check {
dependsOn(testTask)
}
}
}

// Add a second task for the endToEndTest suite (not yet supported by suites directly)
tasks.register<Test>("endToEndTestSlow") {
testClassesDirs = sourceSets["endToEndTest"].output.classesDirs
classpath = sourceSets["endToEndTest"].runtimeClasspath
group = LifecycleBasePlugin.VERIFICATION_GROUP
useJUnitPlatform { includeTags("slow") }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
plugins {
id("war")
id("org.example.java")
id("org.example.build-parameters")
}

// The war plugin used 'providedRuntime' / 'providedCompile' to resolve dependencies for packaging the WAR file
configurations.providedCompile {
shouldResolveConsistentlyWith(configurations.appRuntimeClasspath.get())
}
configurations.providedRuntime {
shouldResolveConsistentlyWith(configurations.appRuntimeClasspath.get())
}

tasks.register<Copy>("deployWebApp") {
group = "distribution"
description = "Deploy web app into local Tomcat found via CATALINA_HOME"
from(tasks.war) {
into("webapps")
}
into(buildParameters.catalina.home)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.example.application.tasks

import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import java.io.File
import java.nio.file.Files
import java.security.DigestInputStream
import java.security.MessageDigest

/**
* Gradle task based on 'Checksum' Ant Task but stripped down to what we need in this build.
*
* See: https://github.com/apache/ant/blob/master/src/main/org/apache/tools/ant/taskdefs/Checksum.java
*/
abstract class MD5DirectoryChecksum : DefaultTask() {

@get:InputDirectory
abstract val inputDirectory: DirectoryProperty

@get:OutputFile
abstract val checksumFile: RegularFileProperty

@TaskAction
fun generateChecksum() {
val messageDigest = MessageDigest.getInstance("MD5")

val allDigests = mutableMapOf<File, ByteArray>()
val bufSize = 8 * 1024
val buf = ByteArray(bufSize)

val folder = inputDirectory.get().asFile
folder.walkTopDown().filter { it.isFile }.forEach {
messageDigest.reset()
val fis = Files.newInputStream(it.toPath())
val dis = DigestInputStream(
fis,
messageDigest
)
while (dis.read(buf, 0, bufSize) != -1) {
// Empty statement
}
dis.close()
fis.close()
val fileDigest = messageDigest.digest()
allDigests[it] = fileDigest
}
// Calculate the total checksum
// Convert the keys (source files) into a sorted array.
val keyArray = allDigests.keys.sortedBy { it.relativeTo(folder).path }

// Loop over the checksums and generate a total hash.
messageDigest.reset()
keyArray.forEach {
// Add the digest for the file content
val digest = allDigests.getValue(it)
messageDigest.update(digest)

// Add the file path
val fileName = it.relativeTo(folder).path.replace(File.separatorChar, '/')
messageDigest.update(fileName.toByteArray())
}
checksumFile.get().asFile.writeText(createDigestString(messageDigest.digest()))
}

private fun createDigestString(fileDigest: ByteArray): String {
val byteMask = 0xFF
val checksumSb = StringBuilder()
for (digestByte in fileDigest) {
checksumSb.append(String.format("%02x", byteMask and digestByte.toInt()))
}
return checksumSb.toString()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.example.application.tasks

import org.gradle.api.DefaultTask
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction

/**
* Encodes the current product version in an XML format used at application runtime.
*/
abstract class VersionXml : DefaultTask() {

@get:Input
abstract val mainVersion: Property<String>

@get:OutputFile
abstract val xmlFile: RegularFileProperty

@TaskAction
fun generate() {
xmlFile.get().asFile.writeText(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?><exampleApp version=\"${mainVersion.get()}\"></exampleApp>"
)
}
}
2 changes: 2 additions & 0 deletions gradle/plugins/plugins-platform/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ plugins {

dependencies.constraints {
api("dev.jacomet.gradle.plugins:logging-capabilities:0.11.1")
api("io.fuchs.gradle.classpath-collision-detector:classpath-collision-detector:0.3")
api("org.gradlex:java-ecosystem-capabilities:1.3.1")
api("org.owasp:dependency-check-gradle:7.4.4")
}
1 change: 1 addition & 0 deletions gradle/version.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.2

0 comments on commit d8c7f38

Please sign in to comment.