[BUILD] Build improvements (#52)
* Each module has its own build.gradle * Functionality moved into plugins in buildSrc/ * Add Spring javaformat * Add release plugins (#20) Fixes #21
This commit is contained in:
2
.github/workflows/ci-pr.yml
vendored
2
.github/workflows/ci-pr.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
# GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
|
||||
# GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
run: |
|
||||
./gradlew clean build --rerun-tasks -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD"
|
||||
./gradlew clean build --continue --scan
|
||||
- name: Capture Test Results
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@@ -6,6 +6,8 @@ on:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '.github/**'
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
@@ -44,11 +46,17 @@ jobs:
|
||||
# GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
|
||||
# GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
run: |
|
||||
./gradlew clean build --refresh-dependencies --stacktrace -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD"
|
||||
./gradlew clean build --continue -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD"
|
||||
- name: Deploy artifacts
|
||||
# env:
|
||||
# ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
# ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_PASSPHRASE }}
|
||||
# OSSRH_TOKEN_USERNAME: ${{ secrets.OSSRH_S01_TOKEN_USERNAME }}
|
||||
# OSSRH_TOKEN_PASSWORD: ${{ secrets.OSSRH_S01_TOKEN_PASSWORD }}
|
||||
# ARTIFACTORY_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
# ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
# GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }}
|
||||
# GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
|
||||
# GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
run: |
|
||||
./gradlew artifactoryPublish --stacktrace -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD"
|
||||
./gradlew publishArtifact -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD" -PossrhUsername="$OSSRH_TOKEN_USERNAME" -PossrhPassword="$OSSRH_TOKEN_PASSWORD" --stacktrace
|
||||
|
||||
13
README.adoc
13
README.adoc
@@ -22,16 +22,16 @@ Most of the ideas in this project are borrowed from the Spring for Apache Kafka
|
||||
./gradlew clean build
|
||||
```
|
||||
|
||||
The build will produce two artifacts -- `spring-pulsar` and `spring-pulsar-boot-autoconfigure`
|
||||
The build will produce two artifacts -- `spring-pulsar` and `spring-pulsar-spring-boot-autoconfigure`
|
||||
|
||||
### Spring Boot Auto Configuration
|
||||
|
||||
We recommend using the library `spring-pulsar` in association with Spring Boot and therefore should also use `spring-pulsar-boot-autoconfigure`.
|
||||
If you simply use the module `spring-pulsar-boot-autoconfigure`, then that will transitively include `spring-pulsar`.
|
||||
We recommend using the library `spring-pulsar` in association with Spring Boot and therefore should also use `spring-pulsar-spring-boot-autoconfigure`.
|
||||
If you simply use the module `spring-pulsar-spring-boot-autoconfigure`, then that will transitively include `spring-pulsar`.
|
||||
|
||||
#### Pulsar Client
|
||||
|
||||
When using `spring-pulsar-boot-autoconfigure`, you get the `PulsarClient` auto-configured.
|
||||
When using `spring-pulsar-spring-boot-autoconfigure`, you get the `PulsarClient` auto-configured.
|
||||
This is done through a factory bean called `PulsarClientFactoryBean`, which takes a configuration object `PulsarClientConfiguration`.
|
||||
By default, the application tries to connect a local Pulsar instance available at `pulsar://localhost:6650`.
|
||||
|
||||
@@ -403,8 +403,3 @@ On the producer side also, for the Java primitive types, the framework can infer
|
||||
#### PulsarTemplate API details
|
||||
|
||||
### More support to come -- stay tuned...
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
382
build.gradle
382
build.gradle
@@ -1,15 +1,10 @@
|
||||
plugins {
|
||||
id 'base'
|
||||
id 'project-report'
|
||||
id 'idea'
|
||||
id 'org.sonarqube' version '2.8'
|
||||
id 'io.spring.nohttp'
|
||||
id 'org.springframework.pulsar.root-project'
|
||||
id 'org.ajoberstar.grgit' version '4.0.1' apply false
|
||||
id 'io.spring.nohttp' version '0.0.5.RELEASE'
|
||||
id 'io.spring.dependency-management' version '1.0.10.RELEASE' apply false
|
||||
id 'com.jfrog.artifactory' version '4.18.2' apply false
|
||||
}
|
||||
|
||||
apply plugin: 'io.spring.nohttp'
|
||||
description = 'Spring for Apache Pulsar'
|
||||
|
||||
def gitPresent = new File('.git').exists()
|
||||
|
||||
@@ -17,357 +12,64 @@ if (gitPresent) {
|
||||
apply plugin: 'org.ajoberstar.grgit'
|
||||
}
|
||||
|
||||
description = 'Spring for Apache Pulsar'
|
||||
|
||||
ext {
|
||||
linkHomepage = 'https://github.com/spring-projects-experimental/spring-pulsar'
|
||||
linkIssue = 'https://github.com/spring-projects-experimental/spring-pulsar/issues'
|
||||
linkScmUrl = 'https://github.com/spring-projects-experimental/spring-pulsar'
|
||||
linkScmConnection = 'https://github.com/spring-projects-experimental/spring-pulsar.git'
|
||||
linkScmDevConnection = 'git@github.com:spring-projects-experimental/spring-pulsar.git'
|
||||
|
||||
if (gitPresent) {
|
||||
modifiedFiles =
|
||||
files(grgit.status().unstaged.modified).filter{ f -> f.name.endsWith('.java') }
|
||||
modifiedFiles = files(grgit.status().unstaged.modified).filter{ f -> f.name.endsWith('.java') }
|
||||
}
|
||||
|
||||
assertjVersion = '3.22.0'
|
||||
awaitilityVersion = '4.2.0'
|
||||
googleJsr305Version = '3.0.2'
|
||||
hamcrestVersion = '2.2'
|
||||
hibernateValidationVersion = '7.0.4.Final'
|
||||
jacksonBomVersion = '2.13.3'
|
||||
jaywayJsonPathVersion = '2.6.0'
|
||||
junitJupiterVersion = '5.8.2'
|
||||
pulsarVersion = '2.10.0'
|
||||
log4jVersion = '2.17.2'
|
||||
mockitoVersion = '4.5.1'
|
||||
reactorVersion = '2020.0.17'
|
||||
springBootVersion = '3.0.0-SNAPSHOT' // docs module
|
||||
springRetryVersion = '1.3.3'
|
||||
springVersion = '6.0.0-SNAPSHOT'
|
||||
caffeineVersion = '3.1.1'
|
||||
idPrefix = 'pulsar'
|
||||
}
|
||||
|
||||
nohttp {
|
||||
source.include '**/src/**'
|
||||
source.exclude '**/*.gif', '**/*.ks'
|
||||
}
|
||||
|
||||
allprojects {
|
||||
group = 'org.springframework.pulsar'
|
||||
|
||||
apply plugin: 'io.spring.dependency-management'
|
||||
|
||||
dependencyManagement {
|
||||
resolutionStrategy {
|
||||
cacheChangingModulesFor 0, 'seconds'
|
||||
}
|
||||
applyMavenExclusions = false
|
||||
generatedPomCustomization {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
imports {
|
||||
mavenBom "com.fasterxml.jackson:jackson-bom:$jacksonBomVersion"
|
||||
mavenBom "org.junit:junit-bom:$junitJupiterVersion"
|
||||
mavenBom "org.springframework:spring-framework-bom:$springVersion"
|
||||
mavenBom "io.projectreactor:reactor-bom:$reactorVersion"
|
||||
mavenBom "org.apache.logging.log4j:log4j-bom:$log4jVersion"
|
||||
}
|
||||
configurations.all {
|
||||
resolutionStrategy.cacheChangingModulesFor 0, "minutes"
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url 'https://repo.spring.io/release' }
|
||||
maven { url 'https://repo.spring.io/milestone' }
|
||||
if (version.endsWith('SNAPSHOT')) {
|
||||
maven { url 'https://repo.spring.io/snapshot' }
|
||||
}
|
||||
// maven { url 'https://repository.apache.org/content/groups/staging/' }
|
||||
if (hasProperty('buildScan')) {
|
||||
buildScan {
|
||||
termsOfServiceUrl = 'https://gradle.com/terms-of-service'
|
||||
termsOfServiceAgree = 'yes'
|
||||
}
|
||||
}
|
||||
|
||||
nohttp {
|
||||
allowlistFile = project.file('src/nohttp/allowlist.lines')
|
||||
source.exclude "**/bin/**"
|
||||
source.exclude "**/build/**"
|
||||
source.exclude "**/out/**"
|
||||
source.exclude "**/target/**"
|
||||
}
|
||||
|
||||
check {
|
||||
dependsOn checkstyleNohttp
|
||||
}
|
||||
|
||||
subprojects { subproject ->
|
||||
if (!['spring-pulsar-docs'].contains(subproject.name)) {
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'java'
|
||||
apply from: "${rootProject.projectDir}/gradle/publish-artifactory.gradle"
|
||||
apply plugin: 'eclipse'
|
||||
apply plugin: 'idea'
|
||||
apply plugin: 'jacoco'
|
||||
apply plugin: 'checkstyle'
|
||||
|
||||
java {
|
||||
withJavadocJar()
|
||||
withSourcesJar()
|
||||
registerFeature('optional') {
|
||||
usingSourceSet(sourceSets.main)
|
||||
}
|
||||
registerFeature('provided') {
|
||||
usingSourceSet(sourceSets.main)
|
||||
}
|
||||
}
|
||||
|
||||
compileJava {
|
||||
sourceCompatibility = 17
|
||||
targetCompatibility = 17
|
||||
}
|
||||
|
||||
compileTestJava {
|
||||
sourceCompatibility = 17
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
|
||||
eclipse.project.natures += 'org.springframework.ide.eclipse.core.springnature'
|
||||
|
||||
jacoco {
|
||||
toolVersion = '0.8.7'
|
||||
}
|
||||
|
||||
// dependencies that are common across all java projects
|
||||
dependencies {
|
||||
implementation "com.google.code.findbugs:jsr305:$googleJsr305Version"
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-params'
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
|
||||
// To avoid compiler warnings about @API annotations in JUnit code
|
||||
testCompileOnly 'org.apiguardian:apiguardian-api:1.0.0'
|
||||
|
||||
testRuntimeOnly 'org.apache.logging.log4j:log4j-core'
|
||||
testRuntimeOnly 'org.apache.logging.log4j:log4j-jcl'
|
||||
|
||||
testImplementation("org.awaitility:awaitility:$awaitilityVersion") {
|
||||
exclude group: 'org.hamcrest'
|
||||
}
|
||||
testImplementation "org.hamcrest:hamcrest-core:$hamcrestVersion"
|
||||
optionalApi "org.assertj:assertj-core:$assertjVersion"
|
||||
|
||||
testImplementation "org.testcontainers:pulsar:1.17.2"
|
||||
|
||||
testImplementation "org.springframework:spring-test"
|
||||
|
||||
}
|
||||
|
||||
// enable all compiler warnings; individual projects may customize further
|
||||
[compileJava, compileTestJava]*.options*.compilerArgs = ['-Xlint:all,-options']
|
||||
|
||||
test {
|
||||
testLogging {
|
||||
events "skipped", "failed"
|
||||
showStandardStreams = project.hasProperty("showStandardStreams") ?: false
|
||||
showExceptions = true
|
||||
showStackTraces = true
|
||||
exceptionFormat = 'full'
|
||||
}
|
||||
|
||||
maxHeapSize = '1536m'
|
||||
// jvmArgs '-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=127.0.0.1:8111'
|
||||
jacoco {
|
||||
destinationFile = file("$buildDir/jacoco.exec")
|
||||
}
|
||||
useJUnitPlatform()
|
||||
|
||||
if (System.properties['sonar.host.url']) {
|
||||
finalizedBy jacocoTestReport
|
||||
}
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
configDirectory.set(rootProject.file("src/checkstyle"))
|
||||
toolVersion = '9.0'
|
||||
}
|
||||
|
||||
jacocoTestReport {
|
||||
reports {
|
||||
xml.enabled true
|
||||
csv.enabled false
|
||||
html.enabled false
|
||||
xml.destination file("${buildDir}/reports/jacoco/test/jacocoTestReport.xml")
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
suppressAllPomMetadataWarnings()
|
||||
from components.java
|
||||
pom.withXml {
|
||||
def pomDeps = asNode().dependencies.first()
|
||||
subproject.configurations.providedImplementation.allDependencies.each { dep ->
|
||||
pomDeps.remove(pomDeps.'*'.find { it.artifactId.text() == dep.name })
|
||||
pomDeps.appendNode('dependency').with {
|
||||
it.appendNode('groupId', dep.group)
|
||||
it.appendNode('artifactId', dep.name)
|
||||
it.appendNode('version', dep.version)
|
||||
it.appendNode('scope', 'provided')
|
||||
task updateCopyrights {
|
||||
onlyIf { gitPresent && !System.getenv('GITHUB_ACTION') }
|
||||
inputs.files(modifiedFiles.filter { f -> f.path.contains(subproject.name) })
|
||||
outputs.dir('build')
|
||||
doLast {
|
||||
def now = Calendar.instance.get(Calendar.YEAR) as String
|
||||
inputs.files.each { file ->
|
||||
def line
|
||||
file.withReader { reader ->
|
||||
while (line = reader.readLine()) {
|
||||
def matcher = line =~ /Copyright (20\d\d)-?(20\d\d)?/
|
||||
if (matcher.count) {
|
||||
def beginningYear = matcher[0][1]
|
||||
if (now != beginningYear && now != matcher[0][2]) {
|
||||
def years = "$beginningYear-$now"
|
||||
def sourceCode = file.text
|
||||
sourceCode = sourceCode.replaceFirst(/20\d\d(-20\d\d)?/, years)
|
||||
file.write(sourceCode)
|
||||
println "Copyright updated for file: $file"
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task updateCopyrights {
|
||||
onlyIf { gitPresent && !System.getenv('GITHUB_ACTION') }
|
||||
if (gitPresent) {
|
||||
inputs.files(modifiedFiles.filter { f -> f.path.contains(subproject.name) })
|
||||
}
|
||||
outputs.dir('build')
|
||||
|
||||
doLast {
|
||||
def now = Calendar.instance.get(Calendar.YEAR) as String
|
||||
inputs.files.each { file ->
|
||||
def line
|
||||
file.withReader { reader ->
|
||||
while (line = reader.readLine()) {
|
||||
def matcher = line =~ /Copyright (20\d\d)-?(20\d\d)?/
|
||||
if (matcher.count) {
|
||||
def beginningYear = matcher[0][1]
|
||||
if (now != beginningYear && now != matcher[0][2]) {
|
||||
def years = "$beginningYear-$now"
|
||||
def sourceCode = file.text
|
||||
sourceCode = sourceCode.replaceFirst(/20\d\d(-20\d\d)?/, years)
|
||||
file.write(sourceCode)
|
||||
println "Copyright updated for file: $file"
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes(
|
||||
'Implementation-Version': archiveVersion,
|
||||
'Created-By': "JDK ${System.properties['java.version']} (${System.properties['java.specification.vendor']})",
|
||||
'Implementation-Title': subproject.name,
|
||||
'Implementation-Vendor-Id': subproject.group,
|
||||
'Implementation-Vendor': 'Pivotal Software, Inc.',
|
||||
'Implementation-URL': linkHomepage,
|
||||
'Automatic-Module-Name': subproject.name.replace('-', '.') // for Jigsaw
|
||||
)
|
||||
}
|
||||
|
||||
from("${rootProject.projectDir}/src/dist") {
|
||||
include 'license.txt'
|
||||
include 'notice.txt'
|
||||
into 'META-INF'
|
||||
expand(copyright: new Date().format('yyyy'), version: project.version)
|
||||
}
|
||||
from("${rootProject.projectDir}") {
|
||||
include 'LICENSE.txt'
|
||||
into 'META-INF'
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(Javadoc) {
|
||||
options.addBooleanOption('Xdoclint:syntax', true) // only check syntax with doclint
|
||||
options.addBooleanOption('Werror', true) // fail build on Javadoc warnings
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
project ('spring-pulsar') {
|
||||
description = 'Spring Pulsar Support'
|
||||
|
||||
dependencies {
|
||||
api 'org.springframework:spring-context'
|
||||
api 'org.springframework:spring-messaging'
|
||||
api 'org.springframework:spring-tx'
|
||||
api ("org.springframework.retry:spring-retry:$springRetryVersion") {
|
||||
exclude group: 'org.springframework'
|
||||
}
|
||||
api "org.apache.pulsar:pulsar-client:$pulsarVersion"
|
||||
api "org.apache.pulsar:pulsar-client-admin:$pulsarVersion"
|
||||
api "org.apache.pulsar:pulsar-client-admin-api:$pulsarVersion"
|
||||
|
||||
api "com.github.ben-manes.caffeine:caffeine:$caffeineVersion"
|
||||
|
||||
optionalApi 'com.fasterxml.jackson.core:jackson-core'
|
||||
optionalApi 'com.fasterxml.jackson.core:jackson-databind'
|
||||
optionalApi 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8'
|
||||
optionalApi 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
|
||||
optionalApi 'com.fasterxml.jackson.datatype:jackson-datatype-joda'
|
||||
optionalApi ('com.fasterxml.jackson.module:jackson-module-kotlin') {
|
||||
exclude group: 'org.jetbrains.kotlin'
|
||||
}
|
||||
|
||||
optionalApi "com.jayway.jsonpath:json-path:$jaywayJsonPathVersion"
|
||||
|
||||
optionalApi 'io.projectreactor:reactor-core'
|
||||
|
||||
testImplementation 'io.projectreactor:reactor-test'
|
||||
testImplementation "org.mockito:mockito-junit-jupiter:$mockitoVersion"
|
||||
testImplementation "org.hibernate.validator:hibernate-validator:$hibernateValidationVersion"
|
||||
}
|
||||
}
|
||||
|
||||
project ('spring-pulsar-boot-autoconfigure') {
|
||||
description = 'Spring Boot Pulsar Autoconfiguration'
|
||||
|
||||
apply plugin: 'org.springframework.pulsar.configuration-properties'
|
||||
|
||||
dependencies {
|
||||
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor:$springBootVersion"
|
||||
|
||||
api "org.springframework.boot:spring-boot:$springBootVersion"
|
||||
api "org.springframework.boot:spring-boot-autoconfigure:$springBootVersion"
|
||||
api "org.springframework.boot:spring-boot-starter:$springBootVersion"
|
||||
api "org.springframework.boot:spring-boot-starter-validation:$springBootVersion"
|
||||
api project (':spring-pulsar')
|
||||
|
||||
testImplementation "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
|
||||
}
|
||||
}
|
||||
|
||||
project ('spring-pulsar-sample-apps') {
|
||||
description = 'Spring Pulsar Sample Applications'
|
||||
|
||||
dependencies {
|
||||
api project (':spring-pulsar-boot-autoconfigure')
|
||||
}
|
||||
|
||||
project.afterEvaluate {
|
||||
project.tasks.artifactoryPublish.enabled(false)
|
||||
}
|
||||
}
|
||||
|
||||
sonarqube {
|
||||
properties {
|
||||
property 'sonar.links.homepage', linkHomepage
|
||||
property 'sonar.links.ci', linkCi
|
||||
property 'sonar.links.issue', linkIssue
|
||||
property 'sonar.links.scm', linkScmUrl
|
||||
property 'sonar.links.scm_dev', linkScmDevConnection
|
||||
}
|
||||
}
|
||||
|
||||
// skip publishing the root module
|
||||
artifactoryPublish.skip = true
|
||||
|
||||
if (project.hasProperty('artifactoryUsername')) {
|
||||
artifactory {
|
||||
contextUrl = 'https://repo.spring.io'
|
||||
publish {
|
||||
repository {
|
||||
repoKey = 'libs-snapshot-local'
|
||||
username = "${artifactoryUsername}"
|
||||
password = "${artifactoryPassword}"
|
||||
}
|
||||
defaults {
|
||||
publications('mavenJava')
|
||||
properties {
|
||||
mavenJava '*:*:*:docs@zip', 'zip.name': 'spring-pulsar', 'zip.displayname': 'Spring Pulsar', 'zip.type': 'docs', 'zip.deployed': false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id "java-gradle-plugin"
|
||||
id "io.spring.javaformat" version "${javaFormatVersion}"
|
||||
id "checkstyle"
|
||||
id "java"
|
||||
id "groovy"
|
||||
}
|
||||
|
||||
repositories {
|
||||
@@ -12,43 +12,98 @@ repositories {
|
||||
if (version.endsWith('SNAPSHOT')) {
|
||||
maven { url 'https://repo.spring.io/snapshot' }
|
||||
}
|
||||
//
|
||||
// maven { url 'https://repo.spring.io/plugins-release/' }
|
||||
// maven { url 'https://plugins.gradle.org/m2/' }
|
||||
}
|
||||
|
||||
sourceCompatibility = 17
|
||||
targetCompatibility = 17
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java { srcDirs = [] }
|
||||
groovy { srcDirs += ['src/main/java'] }
|
||||
}
|
||||
}
|
||||
|
||||
configurations {
|
||||
implementation {
|
||||
exclude module: 'groovy-all'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:${javaFormatVersion}"
|
||||
implementation("org.asciidoctor:asciidoctor-gradle-jvm:3.3.2")
|
||||
implementation(platform("org.springframework.boot:spring-boot-dependencies:${springBootVersion}"))
|
||||
implementation("org.springframework:spring-core")
|
||||
implementation("org.springframework:spring-web")
|
||||
implementation("com.fasterxml.jackson.core:jackson-databind")
|
||||
implementation("org.gradle:test-retry-gradle-plugin:${testRetryVersion}")
|
||||
implementation localGroovy()
|
||||
implementation 'commons-codec:commons-codec'
|
||||
implementation 'com.fasterxml.jackson.core:jackson-databind'
|
||||
implementation 'io.github.gradle-nexus:publish-plugin:1.1.0'
|
||||
implementation("io.spring.javaformat:spring-javaformat-gradle-plugin:${javaFormatVersion}")
|
||||
testImplementation("org.assertj:assertj-core")
|
||||
testImplementation("org.apache.logging.log4j:log4j-core")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
implementation 'io.spring.nohttp:nohttp-gradle:0.0.10'
|
||||
implementation "org.apache.maven:maven-embedder:3.6.2"
|
||||
implementation "org.asciidoctor:asciidoctor-gradle-jvm:3.3.2"
|
||||
implementation 'org.codehaus.groovy:groovy-all:2.5.17'
|
||||
implementation 'org.jfrog.buildinfo:build-info-extractor-gradle:4.29.0'
|
||||
|
||||
implementation "org.gradle:test-retry-gradle-plugin:${testRetryVersion}"
|
||||
implementation 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1'
|
||||
implementation(platform("org.springframework.boot:spring-boot-dependencies:${springBootVersion}"))
|
||||
implementation 'org.springframework:spring-core'
|
||||
implementation 'org.springframework:spring-web'
|
||||
|
||||
testImplementation 'org.assertj:assertj-core'
|
||||
testImplementation 'org.apache.logging.log4j:log4j-core'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "${checkstyleToolVersion}"
|
||||
tasks.named('test', Test).configure {
|
||||
onlyIf { !project.hasProperty("buildSrc.skipTests") }
|
||||
useJUnitPlatform()
|
||||
jvmArgs(
|
||||
'--add-opens', 'java.base/java.lang=ALL-UNNAMED',
|
||||
'--add-opens', 'java.base/java.util=ALL-UNNAMED'
|
||||
)
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
configurationProperties {
|
||||
id = 'org.springframework.pulsar.configuration-properties'
|
||||
implementationClass = 'org.springframework.pulsar.build.docs.configprops.ConfigurationPropertiesPlugin'
|
||||
configurationPropertiesPlugin {
|
||||
id = "org.springframework.pulsar.configuration-properties"
|
||||
implementationClass = 'org.springframework.pulsar.gradle.docs.configprops.ConfigurationPropertiesPlugin'
|
||||
}
|
||||
conventionsPlugin {
|
||||
id = "org.springframework.pulsar.conventions"
|
||||
implementationClass = "org.springframework.pulsar.build.ConventionsPlugin"
|
||||
jacocoConventionsPlugin {
|
||||
id = "org.springframework.pulsar.jacoco"
|
||||
implementationClass = "org.springframework.pulsar.gradle.JacocoConventionsPlugin"
|
||||
}
|
||||
optionalDependenciesPlugin {
|
||||
id = "org.springframework.pulsar.optional-dependencies"
|
||||
implementationClass = "org.springframework.boot.gradle.optional.OptionalDependenciesPlugin"
|
||||
}
|
||||
rootProjectPlugin {
|
||||
id = "org.springframework.pulsar.root-project"
|
||||
implementationClass = "org.springframework.pulsar.gradle.RootProjectPlugin"
|
||||
}
|
||||
sonarQubeConventionsPlugin {
|
||||
id = "org.springframework.pulsar.sonarqube"
|
||||
implementationClass = "org.springframework.pulsar.gradle.SonarQubeConventionsPlugin"
|
||||
}
|
||||
springDocsModulePlugin {
|
||||
id = "org.springframework.pulsar.spring-docs-module"
|
||||
implementationClass = "org.springframework.pulsar.gradle.SpringDocsModulePlugin"
|
||||
}
|
||||
springModulePlugin {
|
||||
id = "org.springframework.pulsar.spring-module"
|
||||
implementationClass = "org.springframework.pulsar.gradle.SpringModulePlugin"
|
||||
}
|
||||
// groovy plugins
|
||||
artifactoryPlugin {
|
||||
id = "io.spring.convention.artfiactory"
|
||||
implementationClass = "io.spring.gradle.convention.ArtifactoryPlugin"
|
||||
}
|
||||
repositoryConventionPlugin {
|
||||
id = "io.spring.convention.repository"
|
||||
implementationClass = "io.spring.gradle.convention.RepositoryConventionPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
apolloGraphqlVersion=2.4.5
|
||||
checkstyleToolVersion=8.11
|
||||
javaFormatVersion=0.0.34
|
||||
testRetryVersion=1.4.0
|
||||
springBootVersion=3.0.0-M4
|
||||
testRetryVersion=1.4.0
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
package io.spring.gradle.convention
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
|
||||
import org.springframework.pulsar.gradle.ProjectUtils
|
||||
|
||||
class ArtifactoryPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
void apply(Project project) {
|
||||
project.plugins.apply('com.jfrog.artifactory')
|
||||
boolean isSnapshot = ProjectUtils.isSnapshot(project);
|
||||
boolean isMilestone = ProjectUtils.isMilestone(project);
|
||||
project.artifactory {
|
||||
contextUrl = 'https://repo.spring.io'
|
||||
publish {
|
||||
repository {
|
||||
repoKey = isSnapshot ? 'libs-snapshot-local' :
|
||||
(isMilestone ? 'libs-milestone-local' : 'libs-release-local')
|
||||
if(project.hasProperty('artifactoryUsername')) {
|
||||
username = artifactoryUsername
|
||||
password = artifactoryPassword
|
||||
}
|
||||
}
|
||||
defaults {
|
||||
publications('mavenJava')
|
||||
properties {
|
||||
mavenJava '*:*:*:docs@zip', 'zip.name': 'spring-pulsar', 'zip.displayname': 'Spring Pulsar', 'zip.type': 'docs', 'zip.deployed': false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2016-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package io.spring.gradle.convention;
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
|
||||
import org.springframework.pulsar.gradle.ProjectUtils
|
||||
|
||||
class RepositoryConventionPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
void apply(Project project) {
|
||||
String[] forceMavenRepositories = ((String) project.findProperty("forceMavenRepositories"))?.split(',')
|
||||
boolean isImplicitSnapshotRepository = forceMavenRepositories == null && ProjectUtils.isSnapshot(project)
|
||||
boolean isImplicitMilestoneRepository = forceMavenRepositories == null && ProjectUtils.isMilestone(project)
|
||||
boolean isSnapshot = isImplicitSnapshotRepository || forceMavenRepositories?.contains('snapshot')
|
||||
boolean isMilestone = isImplicitMilestoneRepository || forceMavenRepositories?.contains('milestone')
|
||||
|
||||
project.repositories {
|
||||
if (forceMavenRepositories?.contains('local')) {
|
||||
mavenLocal()
|
||||
}
|
||||
mavenCentral()
|
||||
if (isSnapshot) {
|
||||
maven {
|
||||
name = 'artifactory-snapshot'
|
||||
if (project.hasProperty('artifactoryUsername')) {
|
||||
credentials {
|
||||
username project.artifactoryUsername
|
||||
password project.artifactoryPassword
|
||||
}
|
||||
}
|
||||
url = 'https://repo.spring.io/snapshot/'
|
||||
}
|
||||
}
|
||||
if (isSnapshot || isMilestone) {
|
||||
maven {
|
||||
name = 'artifactory-milestone'
|
||||
if (project.hasProperty('artifactoryUsername')) {
|
||||
credentials {
|
||||
username project.artifactoryUsername
|
||||
password project.artifactoryPassword
|
||||
}
|
||||
}
|
||||
url = 'https://repo.spring.io/milestone/'
|
||||
}
|
||||
}
|
||||
maven {
|
||||
name = 'artifactory-release'
|
||||
if (project.hasProperty('artifactoryUsername')) {
|
||||
credentials {
|
||||
username project.artifactoryUsername
|
||||
password project.artifactoryPassword
|
||||
}
|
||||
}
|
||||
url = 'https://repo.spring.io/release/'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.gradle;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask;
|
||||
import org.asciidoctor.gradle.jvm.AsciidoctorJExtension;
|
||||
import org.asciidoctor.gradle.jvm.AsciidoctorJPlugin;
|
||||
import org.asciidoctor.gradle.jvm.AsciidoctorTask;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.Sync;
|
||||
|
||||
import org.springframework.boot.gradle.classpath.artifactory.ArtifactoryRepository;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Conventions that are applied in the presence of the {@link AsciidoctorJPlugin}. When
|
||||
* the plugin is applied:
|
||||
*
|
||||
* <ul>
|
||||
* <li>The {@code https://repo.spring.io/release} Maven repository is configured and
|
||||
* limited to dependencies in the following groups:
|
||||
* <ul>
|
||||
* <li>{@code io.spring.asciidoctor}
|
||||
* <li>{@code io.spring.asciidoctor.backends}
|
||||
* <li>{@code io.spring.docresources}
|
||||
* </ul>
|
||||
* <li>All warnings are made fatal.
|
||||
* <li>The version of AsciidoctorJ is upgraded to 2.4.3.
|
||||
* <li>An {@code asciidoctorExtensions} configuration is created.
|
||||
* <li>For each {@link AsciidoctorTask} (HTML only):
|
||||
* <ul>
|
||||
* <li>A task is created to sync the documentation resources to its output directory.
|
||||
* <li>{@code doctype} {@link AsciidoctorTask#options(Map) option} is configured.
|
||||
* <li>The {@code backend} is configured.
|
||||
* </ul>
|
||||
* <li>For each {@link AbstractAsciidoctorTask} (HTML and PDF):
|
||||
* <ul>
|
||||
* <li>{@link AsciidoctorTask#attributes(Map) Attributes} are configured to enable
|
||||
* warnings for references to missing attributes, the GitHub tag, the Artifactory repo for
|
||||
* the current version, etc.
|
||||
* <li>{@link AbstractAsciidoctorTask#baseDirFollowsSourceDir() baseDirFollowsSourceDir()}
|
||||
* is enabled.
|
||||
* <li>{@code asciidoctorExtensions} is added to the task's configurations.
|
||||
* </ul>
|
||||
* </ul>
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class AsciidoctorConventionsPlugin implements Plugin<Project> {
|
||||
|
||||
private static final String ASCIIDOCTORJ_VERSION = "2.4.3";
|
||||
|
||||
private static final String EXTENSIONS_CONFIGURATION_NAME = "asciidoctorExtensions";
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().withType(AsciidoctorJPlugin.class, (asciidoctorPlugin) -> {
|
||||
configureDocumentationDependenciesRepository(project);
|
||||
makeAllWarningsFatal(project);
|
||||
upgradeAsciidoctorJVersion(project);
|
||||
createAsciidoctorExtensionsConfiguration(project);
|
||||
project.getTasks().withType(AbstractAsciidoctorTask.class,
|
||||
(asciidoctorTask) -> configureAsciidoctorTask(project, asciidoctorTask));
|
||||
});
|
||||
}
|
||||
|
||||
private void configureDocumentationDependenciesRepository(Project project) {
|
||||
project.getRepositories().maven((mavenRepo) -> {
|
||||
mavenRepo.setUrl(URI.create("https://repo.spring.io/release"));
|
||||
mavenRepo.mavenContent((mavenContent) -> {
|
||||
mavenContent.includeGroup("io.spring.asciidoctor");
|
||||
mavenContent.includeGroup("io.spring.asciidoctor.backends");
|
||||
mavenContent.includeGroup("io.spring.docresources");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void makeAllWarningsFatal(Project project) {
|
||||
project.getExtensions().getByType(AsciidoctorJExtension.class).fatalWarnings(".*");
|
||||
}
|
||||
|
||||
private void upgradeAsciidoctorJVersion(Project project) {
|
||||
project.getExtensions().getByType(AsciidoctorJExtension.class).setVersion(ASCIIDOCTORJ_VERSION);
|
||||
}
|
||||
|
||||
private void createAsciidoctorExtensionsConfiguration(Project project) {
|
||||
project.getConfigurations().create(EXTENSIONS_CONFIGURATION_NAME, (configuration) -> {
|
||||
project.getConfigurations().matching((candidate) -> "dependencyManagement".equals(candidate.getName()))
|
||||
.all(configuration::extendsFrom);
|
||||
configuration.getDependencies().add(project.getDependencies()
|
||||
.create("io.spring.asciidoctor.backends:spring-asciidoctor-backends:0.0.3"));
|
||||
configuration.getDependencies()
|
||||
.add(project.getDependencies().create("org.asciidoctor:asciidoctorj-pdf:1.5.3"));
|
||||
});
|
||||
}
|
||||
|
||||
private void configureAsciidoctorTask(Project project, AbstractAsciidoctorTask asciidoctorTask) {
|
||||
asciidoctorTask.configurations(EXTENSIONS_CONFIGURATION_NAME);
|
||||
configureCommonAttributes(project, asciidoctorTask);
|
||||
configureOptions(asciidoctorTask);
|
||||
asciidoctorTask.baseDirFollowsSourceDir();
|
||||
createSyncDocumentationSourceTask(project, asciidoctorTask);
|
||||
if (asciidoctorTask instanceof AsciidoctorTask task) {
|
||||
boolean pdf = task.getName().toLowerCase().contains("pdf");
|
||||
String backend = (!pdf) ? "spring-html" : "spring-pdf";
|
||||
task.outputOptions((outputOptions) -> outputOptions.backends(backend));
|
||||
}
|
||||
}
|
||||
|
||||
private void configureCommonAttributes(Project project, AbstractAsciidoctorTask asciidoctorTask) {
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put("attribute-missing", "warn");
|
||||
attributes.put("github-tag", determineGitHubTag(project));
|
||||
attributes.put("spring-boot-artifactory-repo", ArtifactoryRepository.forProject(project));
|
||||
attributes.put("revnumber", null);
|
||||
asciidoctorTask.attributes(attributes);
|
||||
}
|
||||
|
||||
private String determineGitHubTag(Project project) {
|
||||
String version = "v" + project.getVersion();
|
||||
return (version.endsWith("-SNAPSHOT")) ? "main" : version;
|
||||
}
|
||||
|
||||
private void configureOptions(AbstractAsciidoctorTask asciidoctorTask) {
|
||||
asciidoctorTask.options(Collections.singletonMap("doctype", "book"));
|
||||
}
|
||||
|
||||
private Sync createSyncDocumentationSourceTask(Project project, AbstractAsciidoctorTask asciidoctorTask) {
|
||||
Sync syncDocumentationSource = project.getTasks()
|
||||
.create("syncDocumentationSourceFor" + StringUtils.capitalize(asciidoctorTask.getName()), Sync.class);
|
||||
File syncedSource = new File(project.getBuildDir(), "docs/src/" + asciidoctorTask.getName());
|
||||
syncDocumentationSource.setDestinationDir(syncedSource);
|
||||
syncDocumentationSource.from("src/docs/");
|
||||
asciidoctorTask.dependsOn(syncDocumentationSource);
|
||||
asciidoctorTask.getInputs().dir(syncedSource).withPathSensitivity(PathSensitivity.RELATIVE)
|
||||
.withPropertyName("synced source");
|
||||
asciidoctorTask.setSourceDir(project.relativePath(new File(syncedSource, "asciidoc/")));
|
||||
return syncDocumentationSource;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.gradle;
|
||||
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.file.DirectoryProperty;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.OutputDirectory;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.PropertyPlaceholderHelper;
|
||||
|
||||
/**
|
||||
* {@link Task} to extract resources from the classpath and write them to disk.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ExtractResources extends DefaultTask {
|
||||
|
||||
private final PropertyPlaceholderHelper propertyPlaceholderHelper = new PropertyPlaceholderHelper("${", "}");
|
||||
|
||||
private final Map<String, String> properties = new HashMap<>();
|
||||
|
||||
private final DirectoryProperty destinationDirectory;
|
||||
|
||||
private List<String> resourceNames = new ArrayList<>();
|
||||
|
||||
public ExtractResources() {
|
||||
this.destinationDirectory = getProject().getObjects().directoryProperty();
|
||||
}
|
||||
|
||||
@Input
|
||||
public List<String> getResourceNames() {
|
||||
return this.resourceNames;
|
||||
}
|
||||
|
||||
public void setResourcesNames(List<String> resourceNames) {
|
||||
this.resourceNames = resourceNames;
|
||||
}
|
||||
|
||||
@OutputDirectory
|
||||
public DirectoryProperty getDestinationDirectory() {
|
||||
return this.destinationDirectory;
|
||||
}
|
||||
|
||||
public void property(String name, String value) {
|
||||
this.properties.put(name, value);
|
||||
}
|
||||
|
||||
@Input
|
||||
public Map<String, String> getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void extractResources() throws IOException {
|
||||
for (String resourceName : this.resourceNames) {
|
||||
InputStream resourceStream = getClass().getClassLoader().getResourceAsStream(resourceName);
|
||||
if (resourceStream == null) {
|
||||
throw new GradleException("Resource '" + resourceName + "' does not exist");
|
||||
}
|
||||
String resource = FileCopyUtils.copyToString(new InputStreamReader(resourceStream, StandardCharsets.UTF_8));
|
||||
resource = this.propertyPlaceholderHelper.replacePlaceholders(resource, this.properties::get);
|
||||
FileCopyUtils.copy(resource,
|
||||
new FileWriter(this.destinationDirectory.file(resourceName).get().getAsFile()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.gradle;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.spring.javaformat.gradle.SpringJavaFormatPlugin;
|
||||
import io.spring.javaformat.gradle.tasks.CheckFormat;
|
||||
import io.spring.javaformat.gradle.tasks.Format;
|
||||
import org.gradle.api.JavaVersion;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.ConfigurationContainer;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
import org.gradle.api.artifacts.DependencySet;
|
||||
import org.gradle.api.plugins.JavaBasePlugin;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.plugins.quality.Checkstyle;
|
||||
import org.gradle.api.plugins.quality.CheckstyleExtension;
|
||||
import org.gradle.api.plugins.quality.CheckstylePlugin;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.SourceSetContainer;
|
||||
import org.gradle.api.tasks.bundling.Jar;
|
||||
import org.gradle.api.tasks.compile.JavaCompile;
|
||||
import org.gradle.api.tasks.javadoc.Javadoc;
|
||||
import org.gradle.api.tasks.testing.Test;
|
||||
import org.gradle.api.tasks.testing.logging.TestExceptionFormat;
|
||||
import org.gradle.api.tasks.testing.logging.TestLogEvent;
|
||||
import org.gradle.external.javadoc.CoreJavadocOptions;
|
||||
import org.gradle.testretry.TestRetryPlugin;
|
||||
import org.gradle.testretry.TestRetryTaskExtension;
|
||||
|
||||
import org.springframework.boot.gradle.classpath.CheckClasspathForProhibitedDependencies;
|
||||
import org.springframework.boot.gradle.optional.OptionalDependenciesPlugin;
|
||||
import org.springframework.boot.gradle.testing.TestFailuresPlugin;
|
||||
import org.springframework.boot.gradle.toolchain.ToolchainPlugin;
|
||||
import org.springframework.pulsar.gradle.classpath.LenientCheckClasspathForProhibitedDependencies;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Conventions that are applied in the presence of the {@link JavaBasePlugin}. When the
|
||||
* plugin is applied:
|
||||
*
|
||||
* <ul>
|
||||
* <li>The project is configured with source and target compatibility of 17
|
||||
* <li>{@link SpringJavaFormatPlugin Spring Java Format}, {@link CheckstylePlugin
|
||||
* Checkstyle}, {@link TestFailuresPlugin Test Failures}, and {@link TestRetryPlugin Test
|
||||
* Retry} plugins are applied
|
||||
* <li>{@link Test} tasks are configured:
|
||||
* <ul>
|
||||
* <li>to use JUnit Platform
|
||||
* <li>with a max heap of 1024M
|
||||
* <li>to run after any Checkstyle and format checking tasks
|
||||
* </ul>
|
||||
* <li>A {@code testRuntimeOnly} dependency upon
|
||||
* {@code org.junit.platform:junit-platform-launcher} is added to projects with the
|
||||
* {@link JavaPlugin} applied
|
||||
* <li>{@link JavaCompile}, {@link Javadoc}, and {@link Format} tasks are configured to
|
||||
* use UTF-8 encoding
|
||||
* <li>{@link JavaCompile} tasks are configured to:
|
||||
* <ul>
|
||||
* <li>Use {@code -parameters}.
|
||||
* <li>Treat warnings as errors
|
||||
* <li>Enable {@code unchecked}, {@code deprecation}, {@code rawtypes}, and {@code varags}
|
||||
* warnings
|
||||
* </ul>
|
||||
* <li>{@link Jar} tasks are configured to produce jars with LICENSE.txt and NOTICE.txt
|
||||
* files and the following manifest entries:
|
||||
* <ul>
|
||||
* <li>{@code Automatic-Module-Name}
|
||||
* <li>{@code Build-Jdk-Spec}
|
||||
* <li>{@code Built-By}
|
||||
* <li>{@code Implementation-Title}
|
||||
* <li>{@code Implementation-Version}
|
||||
* </ul>
|
||||
* <li>{@code spring-pulsar-dependencies} is used for dependency management</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p/>
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Christoph Dreis
|
||||
* @author Mike Smithson
|
||||
* @author Scott Frederick
|
||||
* @author Chris Bono
|
||||
*/
|
||||
public class JavaConventionsPlugin implements Plugin<Project> {
|
||||
|
||||
private static final String SOURCE_AND_TARGET_COMPATIBILITY = "17";
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().withType(JavaBasePlugin.class, (java) -> {
|
||||
project.getPlugins().apply(TestFailuresPlugin.class);
|
||||
configureSpringJavaFormat(project);
|
||||
configureJavaConventions(project);
|
||||
configureJavadocConventions(project);
|
||||
configureTestConventions(project);
|
||||
configureJarManifestConventions(project);
|
||||
configureDependencyManagement(project);
|
||||
configureToolchain(project);
|
||||
configureProhibitedDependencyChecks(project);
|
||||
});
|
||||
}
|
||||
|
||||
private void configureJarManifestConventions(Project project) {
|
||||
ExtractResources extractLegalResources = project.getTasks().create("extractLegalResources",
|
||||
ExtractResources.class);
|
||||
extractLegalResources.getDestinationDirectory().set(project.getLayout().getBuildDirectory().dir("legal"));
|
||||
extractLegalResources.setResourcesNames(Arrays.asList("LICENSE.txt", "NOTICE.txt"));
|
||||
extractLegalResources.property("version", project.getVersion().toString());
|
||||
SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
|
||||
Set<String> sourceJarTaskNames = sourceSets.stream().map(SourceSet::getSourcesJarTaskName)
|
||||
.collect(Collectors.toSet());
|
||||
Set<String> javadocJarTaskNames = sourceSets.stream().map(SourceSet::getJavadocJarTaskName)
|
||||
.collect(Collectors.toSet());
|
||||
project.getTasks().withType(Jar.class, (jar) -> project.afterEvaluate((evaluated) -> {
|
||||
jar.metaInf((metaInf) -> metaInf.from(extractLegalResources));
|
||||
jar.manifest((manifest) -> {
|
||||
Map<String, Object> attributes = new TreeMap<>();
|
||||
attributes.put("Automatic-Module-Name", project.getName().replace("-", "."));
|
||||
attributes.put("Build-Jdk-Spec", SOURCE_AND_TARGET_COMPATIBILITY);
|
||||
attributes.put("Built-By", "Spring");
|
||||
attributes.put("Implementation-Title",
|
||||
determineImplementationTitle(project, sourceJarTaskNames, javadocJarTaskNames, jar));
|
||||
attributes.put("Implementation-Version", project.getVersion());
|
||||
manifest.attributes(attributes);
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
private String determineImplementationTitle(Project project, Set<String> sourceJarTaskNames,
|
||||
Set<String> javadocJarTaskNames, Jar jar) {
|
||||
if (sourceJarTaskNames.contains(jar.getName())) {
|
||||
return "Source for " + project.getName();
|
||||
}
|
||||
if (javadocJarTaskNames.contains(jar.getName())) {
|
||||
return "Javadoc for " + project.getName();
|
||||
}
|
||||
return project.getDescription();
|
||||
}
|
||||
|
||||
private void configureTestConventions(Project project) {
|
||||
project.getTasks().withType(Test.class, (test) -> {
|
||||
test.useJUnitPlatform();
|
||||
test.setMaxHeapSize("1024M");
|
||||
test.testLogging(testLoggingContainer -> {
|
||||
testLoggingContainer.setEvents(Set.of(TestLogEvent.SKIPPED, TestLogEvent.FAILED));
|
||||
testLoggingContainer.setShowStandardStreams(project.hasProperty("showStandardStreams"));
|
||||
testLoggingContainer.setShowExceptions(true);
|
||||
testLoggingContainer.setShowStackTraces(true);
|
||||
testLoggingContainer.setExceptionFormat(TestExceptionFormat.FULL);
|
||||
});
|
||||
project.getTasks().withType(Checkstyle.class, test::mustRunAfter);
|
||||
project.getTasks().withType(CheckFormat.class, test::mustRunAfter);
|
||||
});
|
||||
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> project.getDependencies()
|
||||
.add(JavaPlugin.TEST_RUNTIME_ONLY_CONFIGURATION_NAME, "org.junit.platform:junit-platform-launcher"));
|
||||
project.getPlugins().apply(TestRetryPlugin.class);
|
||||
project.getTasks().withType(Test.class,
|
||||
(test) -> project.getPlugins().withType(TestRetryPlugin.class, (testRetryPlugin) -> {
|
||||
TestRetryTaskExtension testRetry = test.getExtensions().getByType(TestRetryTaskExtension.class);
|
||||
testRetry.getFailOnPassedAfterRetry().set(true);
|
||||
testRetry.getMaxRetries().set(isCi() ? 3 : 0);
|
||||
}));
|
||||
}
|
||||
|
||||
private boolean isCi() {
|
||||
return Boolean.parseBoolean(System.getenv("CI"));
|
||||
}
|
||||
|
||||
private void configureJavadocConventions(Project project) {
|
||||
project.getTasks().withType(Javadoc.class, (javadoc) -> {
|
||||
CoreJavadocOptions options = (CoreJavadocOptions) javadoc.getOptions();
|
||||
options.source("17");
|
||||
options.encoding("UTF-8");
|
||||
options.addStringOption("Xdoclint:none", "-quiet");
|
||||
});
|
||||
}
|
||||
|
||||
private void configureJavaConventions(Project project) {
|
||||
if (!project.hasProperty("toolchainVersion")) {
|
||||
JavaPluginExtension javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class);
|
||||
javaPluginExtension.setSourceCompatibility(JavaVersion.toVersion(SOURCE_AND_TARGET_COMPATIBILITY));
|
||||
}
|
||||
project.getTasks().withType(JavaCompile.class, (compile) -> {
|
||||
compile.getOptions().setEncoding("UTF-8");
|
||||
List<String> args = compile.getOptions().getCompilerArgs();
|
||||
if (!args.contains("-parameters")) {
|
||||
args.add("-parameters");
|
||||
}
|
||||
if (project.hasProperty("toolchainVersion")) {
|
||||
compile.setSourceCompatibility(SOURCE_AND_TARGET_COMPATIBILITY);
|
||||
compile.setTargetCompatibility(SOURCE_AND_TARGET_COMPATIBILITY);
|
||||
}
|
||||
else if (buildingWithJava17(project)) {
|
||||
args.addAll(Arrays.asList("-Werror", "-Xlint:unchecked", "-Xlint:deprecation", "-Xlint:rawtypes",
|
||||
"-Xlint:varargs"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean buildingWithJava17(Project project) {
|
||||
return !project.hasProperty("toolchainVersion") && JavaVersion.current() == JavaVersion.VERSION_17;
|
||||
}
|
||||
|
||||
private void configureSpringJavaFormat(Project project) {
|
||||
project.getPlugins().apply(SpringJavaFormatPlugin.class);
|
||||
project.getTasks().withType(Format.class, (Format) -> Format.setEncoding("UTF-8"));
|
||||
project.getPlugins().apply(CheckstylePlugin.class);
|
||||
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
|
||||
checkstyle.setToolVersion("8.45.1");
|
||||
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
|
||||
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
|
||||
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
|
||||
checkstyleDependencies
|
||||
.add(project.getDependencies().create("io.spring.javaformat:spring-javaformat-checkstyle:" + version));
|
||||
}
|
||||
|
||||
private void configureDependencyManagement(Project project) {
|
||||
ConfigurationContainer configurations = project.getConfigurations();
|
||||
Configuration dependencyManagement = configurations.create("dependencyManagement", (configuration) -> {
|
||||
configuration.setVisible(false);
|
||||
configuration.setCanBeConsumed(false);
|
||||
configuration.setCanBeResolved(false);
|
||||
});
|
||||
configurations
|
||||
.matching((c) -> c.getName().endsWith("Classpath") || c.getName().toLowerCase().endsWith("annotationprocessor"))
|
||||
.all((c) -> c.extendsFrom(dependencyManagement));
|
||||
Dependency springBootParent = project.getDependencies().enforcedPlatform(project.getDependencies()
|
||||
.project(Collections.singletonMap("path", ":spring-pulsar-dependencies")));
|
||||
dependencyManagement.getDependencies().add(springBootParent);
|
||||
project.getPlugins().withType(OptionalDependenciesPlugin.class, (optionalDependencies) -> configurations
|
||||
.getByName(OptionalDependenciesPlugin.OPTIONAL_CONFIGURATION_NAME).extendsFrom(dependencyManagement));
|
||||
}
|
||||
|
||||
private void configureToolchain(Project project) {
|
||||
project.getPlugins().apply(ToolchainPlugin.class);
|
||||
}
|
||||
|
||||
private void configureProhibitedDependencyChecks(Project project) {
|
||||
SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
|
||||
sourceSets.all((sourceSet) -> createProhibitedDependenciesChecks(project,
|
||||
sourceSet.getCompileClasspathConfigurationName(), sourceSet.getRuntimeClasspathConfigurationName()));
|
||||
}
|
||||
|
||||
private void createProhibitedDependenciesChecks(Project project, String... configurationNames) {
|
||||
ConfigurationContainer configurations = project.getConfigurations();
|
||||
for (String configurationName : configurationNames) {
|
||||
Configuration configuration = configurations.getByName(configurationName);
|
||||
createProhibitedDependenciesCheck(configuration, project);
|
||||
}
|
||||
}
|
||||
|
||||
private void createProhibitedDependenciesCheck(Configuration classpath, Project project) {
|
||||
CheckClasspathForProhibitedDependencies checkClasspathForProhibitedDependencies = project.getTasks().create(
|
||||
"check" + StringUtils.capitalize(classpath.getName() + "ForProhibitedDependencies"),
|
||||
LenientCheckClasspathForProhibitedDependencies.class);
|
||||
checkClasspathForProhibitedDependencies.setClasspath(classpath);
|
||||
project.getTasks().getByName(JavaBasePlugin.CHECK_TASK_NAME).dependsOn(checkClasspathForProhibitedDependencies);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.gradle.autoconfigure;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import org.springframework.asm.ClassReader;
|
||||
import org.springframework.asm.Opcodes;
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link Task} for generating metadata describing a project's auto-configuration
|
||||
* classes.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class AutoConfigurationMetadata extends DefaultTask {
|
||||
|
||||
private static final String COMMENT_START = "#";
|
||||
|
||||
private SourceSet sourceSet;
|
||||
|
||||
private File outputFile;
|
||||
|
||||
public AutoConfigurationMetadata() {
|
||||
getInputs()
|
||||
.file((Callable<File>) () -> new File(this.sourceSet.getOutput().getResourcesDir(),
|
||||
"META-INF/spring.factories"))
|
||||
.withPathSensitivity(PathSensitivity.RELATIVE).withPropertyName("spring.factories");
|
||||
getInputs()
|
||||
.file((Callable<File>) () -> new File(this.sourceSet.getOutput().getResourcesDir(),
|
||||
"META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports"))
|
||||
.withPathSensitivity(PathSensitivity.RELATIVE)
|
||||
.withPropertyName("org.springframework.boot.autoconfigure.AutoConfiguration");
|
||||
|
||||
dependsOn((Callable<String>) () -> this.sourceSet.getProcessResourcesTaskName());
|
||||
getProject().getConfigurations()
|
||||
.maybeCreate(AutoConfigurationPlugin.AUTO_CONFIGURATION_METADATA_CONFIGURATION_NAME);
|
||||
}
|
||||
|
||||
public void setSourceSet(SourceSet sourceSet) {
|
||||
this.sourceSet = sourceSet;
|
||||
}
|
||||
|
||||
@OutputFile
|
||||
public File getOutputFile() {
|
||||
return this.outputFile;
|
||||
}
|
||||
|
||||
public void setOutputFile(File outputFile) {
|
||||
this.outputFile = outputFile;
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void documentAutoConfiguration() throws IOException {
|
||||
Properties autoConfiguration = readAutoConfiguration();
|
||||
getOutputFile().getParentFile().mkdirs();
|
||||
try (FileWriter writer = new FileWriter(getOutputFile())) {
|
||||
autoConfiguration.store(writer, null);
|
||||
}
|
||||
}
|
||||
|
||||
private Properties readAutoConfiguration() throws IOException {
|
||||
Properties autoConfiguration = CollectionFactory.createSortedProperties(true);
|
||||
Set<String> classNames = new LinkedHashSet<>();
|
||||
classNames.addAll(readSpringFactories());
|
||||
classNames.addAll(readAutoConfigurationsFile());
|
||||
Set<String> publicClassNames = new LinkedHashSet<>();
|
||||
for (String className : classNames) {
|
||||
File classFile = findClassFile(className);
|
||||
if (classFile == null) {
|
||||
throw new IllegalStateException("Auto-configuration class '" + className + "' not found.");
|
||||
}
|
||||
try (InputStream in = new FileInputStream(classFile)) {
|
||||
int access = new ClassReader(in).getAccess();
|
||||
if ((access & Opcodes.ACC_PUBLIC) == Opcodes.ACC_PUBLIC) {
|
||||
publicClassNames.add(className);
|
||||
}
|
||||
}
|
||||
}
|
||||
autoConfiguration.setProperty("autoConfigurationClassNames", String.join(",", publicClassNames));
|
||||
autoConfiguration.setProperty("module", getProject().getName());
|
||||
return autoConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads auto-configurations from META-INF/spring.factories.
|
||||
* @return auto-configurations
|
||||
*/
|
||||
private Set<String> readSpringFactories() throws IOException {
|
||||
File file = new File(this.sourceSet.getOutput().getResourcesDir(), "META-INF/spring.factories");
|
||||
if (!file.exists()) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Properties springFactories = readSpringFactories(file);
|
||||
String enableAutoConfiguration = springFactories
|
||||
.getProperty("org.springframework.boot.autoconfigure.EnableAutoConfiguration");
|
||||
return StringUtils.commaDelimitedListToSet(enableAutoConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads auto-configurations from
|
||||
* META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.
|
||||
* @return auto-configurations
|
||||
*/
|
||||
private List<String> readAutoConfigurationsFile() throws IOException {
|
||||
File file = new File(this.sourceSet.getOutput().getResourcesDir(),
|
||||
"META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports");
|
||||
if (!file.exists()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// Nearly identical copy of
|
||||
// org.springframework.boot.context.annotation.ImportCandidates.load
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)))) {
|
||||
List<String> autoConfigurations = new ArrayList<>();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
line = stripComment(line);
|
||||
line = line.trim();
|
||||
if (line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
autoConfigurations.add(line);
|
||||
}
|
||||
return autoConfigurations;
|
||||
}
|
||||
}
|
||||
|
||||
private String stripComment(String line) {
|
||||
int commentStart = line.indexOf(COMMENT_START);
|
||||
if (commentStart == -1) {
|
||||
return line;
|
||||
}
|
||||
return line.substring(0, commentStart);
|
||||
}
|
||||
|
||||
private File findClassFile(String className) {
|
||||
String classFileName = className.replace(".", "/") + ".class";
|
||||
for (File classesDir : this.sourceSet.getOutput().getClassesDirs()) {
|
||||
File classFile = new File(classesDir, classFileName);
|
||||
if (classFile.isFile()) {
|
||||
return classFile;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Properties readSpringFactories(File file) throws IOException {
|
||||
Properties springFactories = new Properties();
|
||||
try (Reader in = new FileReader(file)) {
|
||||
springFactories.load(in);
|
||||
}
|
||||
return springFactories;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.gradle.autoconfigure;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
|
||||
import org.springframework.boot.gradle.context.properties.ConfigurationPropertiesPlugin;
|
||||
|
||||
/**
|
||||
* {@link Plugin} for projects that define auto-configuration. It reacts to the presence of the
|
||||
* {@link JavaPlugin} by:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Applying the {@link ConfigurationPropertiesPlugin}.
|
||||
* <li>Adding a dependency on the auto-configuration annotation processor.
|
||||
* <li>Defining a task that produces metadata describing the auto-configuration. The
|
||||
* metadata is made available as an artifact in the
|
||||
* </ul>
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class AutoConfigurationPlugin implements Plugin<Project> {
|
||||
|
||||
/**
|
||||
* Name of the {@link Configuration} that holds the auto-configuration metadata
|
||||
* artifact.
|
||||
*/
|
||||
public static final String AUTO_CONFIGURATION_METADATA_CONFIGURATION_NAME = "autoConfigurationMetadata";
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
|
||||
project.getPlugins().apply(ConfigurationPropertiesPlugin.class);
|
||||
Configuration annotationProcessors = project.getConfigurations()
|
||||
.getByName(JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME);
|
||||
annotationProcessors.getDependencies()
|
||||
.add(project.getDependencies().project(Collections.singletonMap("path",
|
||||
":spring-boot-project:spring-boot-tools:spring-boot-autoconfigure-processor")));
|
||||
annotationProcessors.getDependencies()
|
||||
.add(project.getDependencies().project(Collections.singletonMap("path",
|
||||
":spring-boot-project:spring-boot-tools:spring-boot-configuration-processor")));
|
||||
project.getTasks().create("autoConfigurationMetadata", AutoConfigurationMetadata.class, (task) -> {
|
||||
SourceSet main = project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets()
|
||||
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||
task.setSourceSet(main);
|
||||
task.dependsOn(main.getClassesTaskName());
|
||||
task.setOutputFile(new File(project.getBuildDir(), "auto-configuration-metadata.properties"));
|
||||
project.getArtifacts().add(AutoConfigurationPlugin.AUTO_CONFIGURATION_METADATA_CONFIGURATION_NAME,
|
||||
project.provider((Callable<File>) task::getOutputFile), (artifact) -> artifact.builtBy(task));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.gradle.autoconfigure;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.OutputDirectory;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link Task} used to document auto-configuration classes.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class DocumentAutoConfigurationClasses extends DefaultTask {
|
||||
|
||||
private FileCollection autoConfiguration;
|
||||
|
||||
private File outputDir;
|
||||
|
||||
@InputFiles
|
||||
public FileCollection getAutoConfiguration() {
|
||||
return this.autoConfiguration;
|
||||
}
|
||||
|
||||
public void setAutoConfiguration(FileCollection autoConfiguration) {
|
||||
this.autoConfiguration = autoConfiguration;
|
||||
}
|
||||
|
||||
@OutputDirectory
|
||||
public File getOutputDir() {
|
||||
return this.outputDir;
|
||||
}
|
||||
|
||||
public void setOutputDir(File outputDir) {
|
||||
this.outputDir = outputDir;
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void documentAutoConfigurationClasses() throws IOException {
|
||||
for (File metadataFile : this.autoConfiguration) {
|
||||
Properties metadata = new Properties();
|
||||
try (Reader reader = new FileReader(metadataFile)) {
|
||||
metadata.load(reader);
|
||||
}
|
||||
AutoConfiguration autoConfiguration = new AutoConfiguration(metadata.getProperty("module"), new TreeSet<>(
|
||||
StringUtils.commaDelimitedListToSet(metadata.getProperty("autoConfigurationClassNames"))));
|
||||
writeTable(autoConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeTable(AutoConfiguration autoConfigurationClasses) throws IOException {
|
||||
this.outputDir.mkdirs();
|
||||
try (PrintWriter writer = new PrintWriter(
|
||||
new FileWriter(new File(this.outputDir, autoConfigurationClasses.module + ".adoc")))) {
|
||||
writer.println("[cols=\"4,1\"]");
|
||||
writer.println("|===");
|
||||
writer.println("| Configuration Class | Links");
|
||||
|
||||
for (AutoConfigurationClass autoConfigurationClass : autoConfigurationClasses.classes) {
|
||||
writer.println();
|
||||
writer.printf("| {spring-boot-code}/spring-boot-project/%s/src/main/java/%s.java[`%s`]%n",
|
||||
autoConfigurationClasses.module, autoConfigurationClass.path, autoConfigurationClass.name);
|
||||
writer.printf("| {spring-boot-api}/%s.html[javadoc]%n", autoConfigurationClass.path);
|
||||
}
|
||||
|
||||
writer.println("|===");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class AutoConfiguration {
|
||||
|
||||
private final String module;
|
||||
|
||||
private final SortedSet<AutoConfigurationClass> classes;
|
||||
|
||||
private AutoConfiguration(String module, Set<String> classNames) {
|
||||
this.module = module;
|
||||
this.classes = classNames.stream().map((className) -> {
|
||||
String path = className.replace('.', '/');
|
||||
String name = className.substring(className.lastIndexOf('.') + 1);
|
||||
return new AutoConfigurationClass(name, path);
|
||||
}).collect(Collectors.toCollection(TreeSet::new));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class AutoConfigurationClass implements Comparable<AutoConfigurationClass> {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final String path;
|
||||
|
||||
private AutoConfigurationClass(String name, String path) {
|
||||
this.name = name;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(AutoConfigurationClass other) {
|
||||
return this.name.compareTo(other.name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.gradle.classpath;
|
||||
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.ModuleVersionIdentifier;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.tasks.Classpath;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
/**
|
||||
* A {@link Task} for checking the classpath for prohibited dependencies.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Chris Bono
|
||||
*/
|
||||
public class CheckClasspathForProhibitedDependencies extends DefaultTask {
|
||||
|
||||
private Configuration classpath;
|
||||
|
||||
public CheckClasspathForProhibitedDependencies() {
|
||||
getOutputs().upToDateWhen((task) -> true);
|
||||
}
|
||||
|
||||
public void setClasspath(Configuration classpath) {
|
||||
this.classpath = classpath;
|
||||
}
|
||||
|
||||
@Classpath
|
||||
public FileCollection getClasspath() {
|
||||
return this.classpath;
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
public void checkForProhibitedDependencies() {
|
||||
TreeSet<String> prohibited = this.classpath.getResolvedConfiguration().getResolvedArtifacts().stream()
|
||||
.map((artifact) -> artifact.getModuleVersion().getId()).filter(this::prohibited)
|
||||
.map((id) -> id.getGroup() + ":" + id.getName()).collect(Collectors.toCollection(TreeSet::new));
|
||||
if (!prohibited.isEmpty()) {
|
||||
StringBuilder message = new StringBuilder(String.format("Found prohibited dependencies:%n"));
|
||||
for (String dependency : prohibited) {
|
||||
message.append(String.format(" %s%n", dependency));
|
||||
}
|
||||
throw new GradleException(message.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean prohibited(ModuleVersionIdentifier id) {
|
||||
return prohibitedByDefault(id) ? !overrideProhibited(id) : false;
|
||||
}
|
||||
|
||||
private boolean prohibitedByDefault(ModuleVersionIdentifier id) {
|
||||
String group = id.getGroup();
|
||||
if (group.equals("javax.batch")) {
|
||||
return false;
|
||||
}
|
||||
if (group.equals("javax.cache")) {
|
||||
return false;
|
||||
}
|
||||
if (group.equals("javax.money")) {
|
||||
return false;
|
||||
}
|
||||
if (group.startsWith("javax")) {
|
||||
return true;
|
||||
}
|
||||
if (group.equals("org.codehaus.groovy")) {
|
||||
return true;
|
||||
}
|
||||
if (group.equals("org.eclipse.jetty.toolchain")) {
|
||||
return true;
|
||||
}
|
||||
if (group.equals("commons-logging")) {
|
||||
return true;
|
||||
}
|
||||
if (group.equals("org.slf4j") && id.getName().equals("jcl-over-slf4j")) {
|
||||
return true;
|
||||
}
|
||||
if (group.startsWith("org.jboss.spec")) {
|
||||
return true;
|
||||
}
|
||||
if (group.equals("org.apache.geronimo.specs")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean overrideProhibited(ModuleVersionIdentifier id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.gradle.classpath.artifactory;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
|
||||
/**
|
||||
* An Artifactory repository to which a build of Spring Boot can be published.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public final class ArtifactoryRepository {
|
||||
|
||||
private final String name;
|
||||
|
||||
private ArtifactoryRepository(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public static ArtifactoryRepository forProject(Project project) {
|
||||
return new ArtifactoryRepository(determineArtifactoryRepo(project));
|
||||
}
|
||||
|
||||
private static String determineArtifactoryRepo(Project project) {
|
||||
String version = project.getVersion().toString();
|
||||
int modifierIndex = version.lastIndexOf('-');
|
||||
if (modifierIndex == -1) {
|
||||
return "release";
|
||||
}
|
||||
String type = version.substring(modifierIndex + 1);
|
||||
if (type.startsWith("M") || type.startsWith("RC")) {
|
||||
return "milestone";
|
||||
}
|
||||
return "snapshot";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.build.docs.configprops;
|
||||
package org.springframework.boot.gradle.context.properties;
|
||||
|
||||
/**
|
||||
* Simple builder to help construct Asciidoc markup.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.build.docs.configprops;
|
||||
package org.springframework.boot.gradle.context.properties;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.build.docs.configprops;
|
||||
package org.springframework.boot.gradle.context.properties;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.build.docs.configprops;
|
||||
package org.springframework.boot.gradle.context.properties;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -14,8 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.build.docs.configprops;
|
||||
package org.springframework.boot.gradle.context.properties;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
@@ -66,19 +67,19 @@ public class ConfigurationPropertiesPlugin implements Plugin<Project> {
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
|
||||
// TODO addConfigurationProcessorDependency(project);
|
||||
addConfigurationProcessorDependency(project);
|
||||
configureAdditionalMetadataLocationsCompilerArgument(project);
|
||||
registerCheckAdditionalMetadataTask(project);
|
||||
addMetadataArtifact(project);
|
||||
});
|
||||
}
|
||||
|
||||
// private void addConfigurationProcessorDependency(Project project) {
|
||||
// Configuration annotationProcessors = project.getConfigurations()
|
||||
// .getByName(JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME);
|
||||
// annotationProcessors.getDependencies().add(project.getDependencies().project(Collections.singletonMap("path",
|
||||
// ":spring-boot-project:spring-boot-tools:spring-boot-configuration-processor")));
|
||||
// }
|
||||
private void addConfigurationProcessorDependency(Project project) {
|
||||
Configuration annotationProcessors = project.getConfigurations()
|
||||
.getByName(JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME);
|
||||
annotationProcessors.getDependencies().add(project.getDependencies().project(Collections.singletonMap("path",
|
||||
":spring-boot-project:spring-boot-tools:spring-boot-configuration-processor")));
|
||||
}
|
||||
|
||||
private void addMetadataArtifact(Project project) {
|
||||
SourceSet mainSourceSet = project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets()
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.build.docs.configprops;
|
||||
package org.springframework.boot.gradle.context.properties;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.gradle.context.properties;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.OutputDirectory;
|
||||
import org.gradle.api.tasks.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import org.springframework.boot.gradle.context.properties.Snippet.Config;
|
||||
|
||||
/**
|
||||
* {@link Task} used to document auto-configuration classes.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class DocumentConfigurationProperties extends DefaultTask {
|
||||
|
||||
private FileCollection configurationPropertyMetadata;
|
||||
|
||||
private File outputDir;
|
||||
|
||||
@InputFiles
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
public FileCollection getConfigurationPropertyMetadata() {
|
||||
return this.configurationPropertyMetadata;
|
||||
}
|
||||
|
||||
public void setConfigurationPropertyMetadata(FileCollection configurationPropertyMetadata) {
|
||||
this.configurationPropertyMetadata = configurationPropertyMetadata;
|
||||
}
|
||||
|
||||
@OutputDirectory
|
||||
public File getOutputDir() {
|
||||
return this.outputDir;
|
||||
}
|
||||
|
||||
public void setOutputDir(File outputDir) {
|
||||
this.outputDir = outputDir;
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void documentConfigurationProperties() throws IOException {
|
||||
Snippets snippets = new Snippets(this.configurationPropertyMetadata);
|
||||
snippets.add("application-properties.core", "Core Properties", this::corePrefixes);
|
||||
snippets.add("application-properties.cache", "Cache Properties", this::cachePrefixes);
|
||||
snippets.add("application-properties.mail", "Mail Properties", this::mailPrefixes);
|
||||
snippets.add("application-properties.json", "JSON Properties", this::jsonPrefixes);
|
||||
snippets.add("application-properties.data", "Data Properties", this::dataPrefixes);
|
||||
snippets.add("application-properties.transaction", "Transaction Properties", this::transactionPrefixes);
|
||||
snippets.add("application-properties.data-migration", "Data Migration Properties", this::dataMigrationPrefixes);
|
||||
snippets.add("application-properties.integration", "Integration Properties", this::integrationPrefixes);
|
||||
snippets.add("application-properties.web", "Web Properties", this::webPrefixes);
|
||||
snippets.add("application-properties.templating", "Templating Properties", this::templatePrefixes);
|
||||
snippets.add("application-properties.server", "Server Properties", this::serverPrefixes);
|
||||
snippets.add("application-properties.security", "Security Properties", this::securityPrefixes);
|
||||
snippets.add("application-properties.rsocket", "RSocket Properties", this::rsocketPrefixes);
|
||||
snippets.add("application-properties.actuator", "Actuator Properties", this::actuatorPrefixes);
|
||||
snippets.add("application-properties.devtools", "Devtools Properties", this::devtoolsPrefixes);
|
||||
snippets.add("application-properties.testing", "Testing Properties", this::testingPrefixes);
|
||||
snippets.writeTo(this.outputDir.toPath());
|
||||
}
|
||||
|
||||
private void corePrefixes(Config config) {
|
||||
config.accept("debug");
|
||||
config.accept("trace");
|
||||
config.accept("logging");
|
||||
config.accept("spring.aop");
|
||||
config.accept("spring.application");
|
||||
config.accept("spring.autoconfigure");
|
||||
config.accept("spring.banner");
|
||||
config.accept("spring.beaninfo");
|
||||
config.accept("spring.codec");
|
||||
config.accept("spring.config");
|
||||
config.accept("spring.info");
|
||||
config.accept("spring.jmx");
|
||||
config.accept("spring.lifecycle");
|
||||
config.accept("spring.main");
|
||||
config.accept("spring.messages");
|
||||
config.accept("spring.pid");
|
||||
config.accept("spring.profiles");
|
||||
config.accept("spring.quartz");
|
||||
config.accept("spring.reactor");
|
||||
config.accept("spring.task");
|
||||
config.accept("spring.mandatory-file-encoding");
|
||||
config.accept("info");
|
||||
config.accept("spring.output.ansi.enabled");
|
||||
}
|
||||
|
||||
private void cachePrefixes(Config config) {
|
||||
config.accept("spring.cache");
|
||||
}
|
||||
|
||||
private void mailPrefixes(Config config) {
|
||||
config.accept("spring.mail");
|
||||
config.accept("spring.sendgrid");
|
||||
}
|
||||
|
||||
private void jsonPrefixes(Config config) {
|
||||
config.accept("spring.jackson");
|
||||
config.accept("spring.gson");
|
||||
}
|
||||
|
||||
private void dataPrefixes(Config config) {
|
||||
config.accept("spring.couchbase");
|
||||
config.accept("spring.elasticsearch");
|
||||
config.accept("spring.h2");
|
||||
config.accept("spring.influx");
|
||||
config.accept("spring.ldap");
|
||||
config.accept("spring.mongodb");
|
||||
config.accept("spring.neo4j");
|
||||
config.accept("spring.redis");
|
||||
config.accept("spring.dao");
|
||||
config.accept("spring.data");
|
||||
config.accept("spring.datasource");
|
||||
config.accept("spring.jooq");
|
||||
config.accept("spring.jdbc");
|
||||
config.accept("spring.jpa");
|
||||
config.accept("spring.r2dbc");
|
||||
config.accept("spring.datasource.oracleucp",
|
||||
"Oracle UCP specific settings bound to an instance of Oracle UCP's PoolDataSource");
|
||||
config.accept("spring.datasource.dbcp2",
|
||||
"Commons DBCP2 specific settings bound to an instance of DBCP2's BasicDataSource");
|
||||
config.accept("spring.datasource.tomcat",
|
||||
"Tomcat datasource specific settings bound to an instance of Tomcat JDBC's DataSource");
|
||||
config.accept("spring.datasource.hikari",
|
||||
"Hikari specific settings bound to an instance of Hikari's HikariDataSource");
|
||||
|
||||
}
|
||||
|
||||
private void transactionPrefixes(Config prefix) {
|
||||
prefix.accept("spring.jta");
|
||||
prefix.accept("spring.transaction");
|
||||
}
|
||||
|
||||
private void dataMigrationPrefixes(Config prefix) {
|
||||
prefix.accept("spring.flyway");
|
||||
prefix.accept("spring.liquibase");
|
||||
prefix.accept("spring.sql.init");
|
||||
}
|
||||
|
||||
private void integrationPrefixes(Config prefix) {
|
||||
prefix.accept("spring.activemq");
|
||||
prefix.accept("spring.artemis");
|
||||
prefix.accept("spring.batch");
|
||||
prefix.accept("spring.integration");
|
||||
prefix.accept("spring.jms");
|
||||
prefix.accept("spring.kafka");
|
||||
prefix.accept("spring.rabbitmq");
|
||||
prefix.accept("spring.hazelcast");
|
||||
prefix.accept("spring.webservices");
|
||||
}
|
||||
|
||||
private void webPrefixes(Config prefix) {
|
||||
prefix.accept("spring.hateoas");
|
||||
prefix.accept("spring.http");
|
||||
prefix.accept("spring.servlet");
|
||||
prefix.accept("spring.mvc");
|
||||
prefix.accept("spring.netty");
|
||||
prefix.accept("spring.resources");
|
||||
prefix.accept("spring.session");
|
||||
prefix.accept("spring.web");
|
||||
prefix.accept("spring.webflux");
|
||||
}
|
||||
|
||||
private void templatePrefixes(Config prefix) {
|
||||
prefix.accept("spring.freemarker");
|
||||
prefix.accept("spring.groovy");
|
||||
prefix.accept("spring.mustache");
|
||||
prefix.accept("spring.thymeleaf");
|
||||
prefix.accept("spring.groovy.template.configuration", "See GroovyMarkupConfigurer");
|
||||
}
|
||||
|
||||
private void serverPrefixes(Config prefix) {
|
||||
prefix.accept("server");
|
||||
}
|
||||
|
||||
private void securityPrefixes(Config prefix) {
|
||||
prefix.accept("spring.security");
|
||||
}
|
||||
|
||||
private void rsocketPrefixes(Config prefix) {
|
||||
prefix.accept("spring.rsocket");
|
||||
}
|
||||
|
||||
private void actuatorPrefixes(Config prefix) {
|
||||
prefix.accept("management");
|
||||
}
|
||||
|
||||
private void devtoolsPrefixes(Config prefix) {
|
||||
prefix.accept("spring.devtools");
|
||||
}
|
||||
|
||||
private void testingPrefixes(Config prefix) {
|
||||
prefix.accept("spring.test");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.build.docs.configprops;
|
||||
package org.springframework.boot.gradle.context.properties;
|
||||
|
||||
/**
|
||||
* Abstract class for rows in {@link Table}.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.build.docs.configprops;
|
||||
package org.springframework.boot.gradle.context.properties;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.build.docs.configprops;
|
||||
package org.springframework.boot.gradle.context.properties;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.build.docs.configprops;
|
||||
package org.springframework.boot.gradle.context.properties;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.build.docs.configprops;
|
||||
package org.springframework.boot.gradle.context.properties;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.gradle.optional;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.tasks.SourceSetContainer;
|
||||
|
||||
/**
|
||||
* A {@code Plugin} that adds support for Maven-style optional dependencies. Creates a new
|
||||
* {@code optional} configuration. The {@code optional} configuration is part of the
|
||||
* project's compile and runtime classpaths but does not affect the classpath of dependent
|
||||
* projects.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class OptionalDependenciesPlugin implements Plugin<Project> {
|
||||
|
||||
/**
|
||||
* Name of the {@code optional} configuration.
|
||||
*/
|
||||
public static final String OPTIONAL_CONFIGURATION_NAME = "optional";
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
Configuration optional = project.getConfigurations().create("optional");
|
||||
optional.setCanBeConsumed(false);
|
||||
optional.setCanBeResolved(false);
|
||||
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
|
||||
SourceSetContainer sourceSets = project.getExtensions().getByType(JavaPluginExtension.class)
|
||||
.getSourceSets();
|
||||
sourceSets.all((sourceSet) -> {
|
||||
project.getConfigurations().getByName(sourceSet.getCompileClasspathConfigurationName())
|
||||
.extendsFrom(optional);
|
||||
project.getConfigurations().getByName(sourceSet.getRuntimeClasspathConfigurationName())
|
||||
.extendsFrom(optional);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.gradle.test;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.SourceSetContainer;
|
||||
import org.gradle.api.tasks.testing.Test;
|
||||
import org.gradle.language.base.plugins.LifecycleBasePlugin;
|
||||
import org.gradle.plugins.ide.eclipse.EclipsePlugin;
|
||||
import org.gradle.plugins.ide.eclipse.model.EclipseModel;
|
||||
|
||||
/**
|
||||
* A {@link Plugin} to configure integration testing support in a {@link Project}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class IntegrationTestPlugin implements Plugin<Project> {
|
||||
|
||||
/**
|
||||
* Name of the {@code intTest} task.
|
||||
*/
|
||||
public static String INT_TEST_TASK_NAME = "intTest";
|
||||
|
||||
/**
|
||||
* Name of the {@code intTest} source set.
|
||||
*/
|
||||
public static String INT_TEST_SOURCE_SET_NAME = "intTest";
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> configureIntegrationTesting(project));
|
||||
}
|
||||
|
||||
private void configureIntegrationTesting(Project project) {
|
||||
SourceSet intTestSourceSet = createSourceSet(project);
|
||||
Test intTest = createTestTask(project, intTestSourceSet);
|
||||
project.getTasks().getByName(LifecycleBasePlugin.CHECK_TASK_NAME).dependsOn(intTest);
|
||||
project.getPlugins().withType(EclipsePlugin.class, (eclipsePlugin) -> {
|
||||
EclipseModel eclipse = project.getExtensions().getByType(EclipseModel.class);
|
||||
eclipse.classpath((classpath) -> classpath.getPlusConfigurations().add(
|
||||
project.getConfigurations().getByName(intTestSourceSet.getRuntimeClasspathConfigurationName())));
|
||||
});
|
||||
}
|
||||
|
||||
private SourceSet createSourceSet(Project project) {
|
||||
SourceSetContainer sourceSets = project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets();
|
||||
SourceSet intTestSourceSet = sourceSets.create(INT_TEST_SOURCE_SET_NAME);
|
||||
SourceSet main = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||
intTestSourceSet.setCompileClasspath(intTestSourceSet.getCompileClasspath().plus(main.getOutput()));
|
||||
intTestSourceSet.setRuntimeClasspath(intTestSourceSet.getRuntimeClasspath().plus(main.getOutput()));
|
||||
return intTestSourceSet;
|
||||
}
|
||||
|
||||
private Test createTestTask(Project project, SourceSet intTestSourceSet) {
|
||||
Test intTest = project.getTasks().create(INT_TEST_TASK_NAME, Test.class);
|
||||
intTest.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP);
|
||||
intTest.setDescription("Runs integration tests.");
|
||||
intTest.setTestClassesDirs(intTestSourceSet.getOutput().getClassesDirs());
|
||||
intTest.setClasspath(intTestSourceSet.getRuntimeClasspath());
|
||||
intTest.shouldRunAfter(JavaPlugin.TEST_TASK_NAME);
|
||||
return intTest;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.gradle.test;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.specs.Spec;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.SourceSetContainer;
|
||||
import org.gradle.api.tasks.testing.Test;
|
||||
import org.gradle.language.base.plugins.LifecycleBasePlugin;
|
||||
import org.gradle.plugins.ide.eclipse.EclipsePlugin;
|
||||
import org.gradle.plugins.ide.eclipse.model.EclipseModel;
|
||||
|
||||
/**
|
||||
* A {@link Plugin} to configure system testing support in a {@link Project}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Scott Frederick
|
||||
*/
|
||||
public class SystemTestPlugin implements Plugin<Project> {
|
||||
|
||||
private static final Spec<Task> NEVER = (task) -> false;
|
||||
|
||||
/**
|
||||
* Name of the {@code systemTest} task.
|
||||
*/
|
||||
public static String SYSTEM_TEST_TASK_NAME = "systemTest";
|
||||
|
||||
/**
|
||||
* Name of the {@code systemTest} source set.
|
||||
*/
|
||||
public static String SYSTEM_TEST_SOURCE_SET_NAME = "systemTest";
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> configureSystemTesting(project));
|
||||
}
|
||||
|
||||
private void configureSystemTesting(Project project) {
|
||||
SourceSet systemTestSourceSet = createSourceSet(project);
|
||||
createTestTask(project, systemTestSourceSet);
|
||||
project.getPlugins().withType(EclipsePlugin.class, (eclipsePlugin) -> {
|
||||
EclipseModel eclipse = project.getExtensions().getByType(EclipseModel.class);
|
||||
eclipse.classpath((classpath) -> classpath.getPlusConfigurations().add(
|
||||
project.getConfigurations().getByName(systemTestSourceSet.getRuntimeClasspathConfigurationName())));
|
||||
});
|
||||
}
|
||||
|
||||
private SourceSet createSourceSet(Project project) {
|
||||
SourceSetContainer sourceSets = project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets();
|
||||
SourceSet systemTestSourceSet = sourceSets.create(SYSTEM_TEST_SOURCE_SET_NAME);
|
||||
SourceSet mainSourceSet = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||
systemTestSourceSet
|
||||
.setCompileClasspath(systemTestSourceSet.getCompileClasspath().plus(mainSourceSet.getOutput()));
|
||||
systemTestSourceSet
|
||||
.setRuntimeClasspath(systemTestSourceSet.getRuntimeClasspath().plus(mainSourceSet.getOutput()));
|
||||
return systemTestSourceSet;
|
||||
}
|
||||
|
||||
private void createTestTask(Project project, SourceSet systemTestSourceSet) {
|
||||
Test systemTest = project.getTasks().create(SYSTEM_TEST_TASK_NAME, Test.class);
|
||||
systemTest.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP);
|
||||
systemTest.setDescription("Runs system tests.");
|
||||
systemTest.setTestClassesDirs(systemTestSourceSet.getOutput().getClassesDirs());
|
||||
systemTest.setClasspath(systemTestSourceSet.getRuntimeClasspath());
|
||||
systemTest.shouldRunAfter(JavaPlugin.TEST_TASK_NAME);
|
||||
if (isCi()) {
|
||||
systemTest.getOutputs().upToDateWhen(NEVER);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCi() {
|
||||
return Boolean.parseBoolean(System.getenv("CI"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.gradle.testing;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.provider.Provider;
|
||||
import org.gradle.api.tasks.testing.Test;
|
||||
import org.gradle.api.tasks.testing.TestDescriptor;
|
||||
import org.gradle.api.tasks.testing.TestListener;
|
||||
import org.gradle.api.tasks.testing.TestResult;
|
||||
|
||||
/**
|
||||
* Plugin for recording test failures and reporting them at the end of the build.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class TestFailuresPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
Provider<TestResultsOverview> testResultsOverview = project.getGradle().getSharedServices()
|
||||
.registerIfAbsent("testResultsOverview", TestResultsOverview.class, (spec) -> {
|
||||
});
|
||||
project.getTasks().withType(Test.class,
|
||||
(test) -> test.addTestListener(new FailureRecordingTestListener(testResultsOverview, test)));
|
||||
}
|
||||
|
||||
private final class FailureRecordingTestListener implements TestListener {
|
||||
|
||||
private final List<TestDescriptor> failures = new ArrayList<>();
|
||||
|
||||
private final Provider<TestResultsOverview> testResultsOverview;
|
||||
|
||||
private final Test test;
|
||||
|
||||
private FailureRecordingTestListener(Provider<TestResultsOverview> testResultOverview, Test test) {
|
||||
this.testResultsOverview = testResultOverview;
|
||||
this.test = test;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSuite(TestDescriptor descriptor, TestResult result) {
|
||||
if (!this.failures.isEmpty()) {
|
||||
this.testResultsOverview.get().addFailures(this.test, this.failures);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTest(TestDescriptor descriptor, TestResult result) {
|
||||
if (result.getFailedTestCount() > 0) {
|
||||
this.failures.add(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeSuite(TestDescriptor descriptor) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTest(TestDescriptor descriptor) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.gradle.testing;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.gradle.api.services.BuildService;
|
||||
import org.gradle.api.services.BuildServiceParameters;
|
||||
import org.gradle.api.tasks.testing.Test;
|
||||
import org.gradle.api.tasks.testing.TestDescriptor;
|
||||
import org.gradle.tooling.events.FinishEvent;
|
||||
import org.gradle.tooling.events.OperationCompletionListener;
|
||||
|
||||
/**
|
||||
* {@link BuildService} that provides an overview of all the test failures in the build.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public abstract class TestResultsOverview
|
||||
implements BuildService<BuildServiceParameters.None>, OperationCompletionListener, AutoCloseable {
|
||||
|
||||
private final Map<Test, List<TestFailure>> testFailures = new TreeMap<>(
|
||||
(one, two) -> one.getPath().compareTo(two.getPath()));
|
||||
|
||||
private final Object monitor = new Object();
|
||||
|
||||
void addFailures(Test test, List<TestDescriptor> failureDescriptors) {
|
||||
List<TestFailure> testFailures = failureDescriptors.stream().map(TestFailure::new).sorted()
|
||||
.collect(Collectors.toList());
|
||||
synchronized (this.monitor) {
|
||||
this.testFailures.put(test, testFailures);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish(FinishEvent event) {
|
||||
// OperationCompletionListener is implemented to defer close until the build ends
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
synchronized (this.monitor) {
|
||||
if (this.testFailures.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
System.err.println();
|
||||
System.err.println("Found test failures in " + this.testFailures.size() + " test task"
|
||||
+ ((this.testFailures.size() == 1) ? ":" : "s:"));
|
||||
this.testFailures.forEach((task, failures) -> {
|
||||
System.err.println();
|
||||
System.err.println(task.getPath());
|
||||
failures.forEach((failure) -> System.err
|
||||
.println(" " + failure.descriptor.getClassName() + " > " + failure.descriptor.getName()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static final class TestFailure implements Comparable<TestFailure> {
|
||||
|
||||
private final TestDescriptor descriptor;
|
||||
|
||||
private TestFailure(TestDescriptor descriptor) {
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(TestFailure other) {
|
||||
int comparison = this.descriptor.getClassName().compareTo(other.descriptor.getClassName());
|
||||
if (comparison == 0) {
|
||||
comparison = this.descriptor.getName().compareTo(other.descriptor.getName());
|
||||
}
|
||||
return comparison;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.gradle.toolchain;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.provider.ListProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.jvm.toolchain.JavaLanguageVersion;
|
||||
|
||||
/**
|
||||
* DSL extension for {@link ToolchainPlugin}.
|
||||
*
|
||||
* @author Christoph Dreis
|
||||
*/
|
||||
public class ToolchainExtension {
|
||||
|
||||
private final Property<JavaLanguageVersion> maximumCompatibleJavaVersion;
|
||||
|
||||
private final ListProperty<String> testJvmArgs;
|
||||
|
||||
private final JavaLanguageVersion javaVersion;
|
||||
|
||||
public ToolchainExtension(Project project) {
|
||||
this.maximumCompatibleJavaVersion = project.getObjects().property(JavaLanguageVersion.class);
|
||||
this.testJvmArgs = project.getObjects().listProperty(String.class);
|
||||
String toolchainVersion = (String) project.findProperty("toolchainVersion");
|
||||
this.javaVersion = (toolchainVersion != null) ? JavaLanguageVersion.of(toolchainVersion) : null;
|
||||
}
|
||||
|
||||
public Property<JavaLanguageVersion> getMaximumCompatibleJavaVersion() {
|
||||
return this.maximumCompatibleJavaVersion;
|
||||
}
|
||||
|
||||
public ListProperty<String> getTestJvmArgs() {
|
||||
return this.testJvmArgs;
|
||||
}
|
||||
|
||||
JavaLanguageVersion getJavaVersion() {
|
||||
return this.javaVersion;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.gradle.toolchain;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.tasks.compile.JavaCompile;
|
||||
import org.gradle.api.tasks.javadoc.Javadoc;
|
||||
import org.gradle.api.tasks.testing.Test;
|
||||
import org.gradle.jvm.toolchain.JavaLanguageVersion;
|
||||
import org.gradle.jvm.toolchain.JavaToolchainSpec;
|
||||
|
||||
/**
|
||||
* {@link Plugin} for customizing Gradle's toolchain support.
|
||||
*
|
||||
* @author Christoph Dreis
|
||||
*/
|
||||
public class ToolchainPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
configureToolchain(project);
|
||||
}
|
||||
|
||||
private void configureToolchain(Project project) {
|
||||
ToolchainExtension toolchain = project.getExtensions().create("toolchain", ToolchainExtension.class, project);
|
||||
JavaLanguageVersion toolchainVersion = toolchain.getJavaVersion();
|
||||
if (toolchainVersion != null) {
|
||||
project.afterEvaluate((evaluated) -> configure(evaluated, toolchain));
|
||||
}
|
||||
}
|
||||
|
||||
private void configure(Project project, ToolchainExtension toolchain) {
|
||||
if (!isJavaVersionSupported(toolchain, toolchain.getJavaVersion())) {
|
||||
disableToolchainTasks(project);
|
||||
}
|
||||
else {
|
||||
JavaToolchainSpec toolchainSpec = project.getExtensions().getByType(JavaPluginExtension.class)
|
||||
.getToolchain();
|
||||
toolchainSpec.getLanguageVersion().set(toolchain.getJavaVersion());
|
||||
configureTestToolchain(project, toolchain);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isJavaVersionSupported(ToolchainExtension toolchain, JavaLanguageVersion toolchainVersion) {
|
||||
return toolchain.getMaximumCompatibleJavaVersion().map((version) -> version.canCompileOrRun(toolchainVersion))
|
||||
.getOrElse(true);
|
||||
}
|
||||
|
||||
private void disableToolchainTasks(Project project) {
|
||||
project.getTasks().withType(JavaCompile.class, (task) -> task.setEnabled(false));
|
||||
project.getTasks().withType(Javadoc.class, (task) -> task.setEnabled(false));
|
||||
project.getTasks().withType(Test.class, (task) -> task.setEnabled(false));
|
||||
}
|
||||
|
||||
private void configureTestToolchain(Project project, ToolchainExtension toolchain) {
|
||||
List<String> jvmArgs = new ArrayList<>();
|
||||
jvmArgs.addAll(toolchain.getTestJvmArgs().getOrElse(Collections.emptyList()));
|
||||
project.getTasks().withType(Test.class, (test) -> test.jvmArgs(jvmArgs));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.build;
|
||||
|
||||
import org.asciidoctor.gradle.jvm.AsciidoctorJPlugin;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
|
||||
import org.springframework.pulsar.build.docs.asciidoc.AsciidoctorConventions;
|
||||
|
||||
/**
|
||||
* Plugin to apply conventions to projects that are part of Spring Pulsar's build.
|
||||
* Conventions are applied in response to various plugins being applied.
|
||||
*
|
||||
* When the {@link AsciidoctorJPlugin} is applied, the conventions in
|
||||
* {@link AsciidoctorConventions} are applied.
|
||||
*
|
||||
* @author Chris Bono
|
||||
*/
|
||||
public class ConventionsPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
new AsciidoctorConventions().apply(project);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.springframework.pulsar.gradle;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.tasks.testing.Test;
|
||||
import org.gradle.testing.jacoco.plugins.JacocoPlugin;
|
||||
import org.gradle.testing.jacoco.plugins.JacocoPluginExtension;
|
||||
import org.gradle.testing.jacoco.tasks.JacocoReport;
|
||||
|
||||
/**
|
||||
* Adds a version of jacoco to use and makes check depend on jacocoTestReport.
|
||||
*
|
||||
* @author Chris Bono
|
||||
*/
|
||||
public class JacocoConventionsPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(final Project project) {
|
||||
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
|
||||
project.getPluginManager().apply(JacocoPlugin.class);
|
||||
project.getExtensions().configure(JacocoPluginExtension.class, (jacocoExtension) -> {
|
||||
jacocoExtension.setToolVersion("0.8.7");
|
||||
});
|
||||
project.getTasks().withType(Test.class, test -> {
|
||||
project.getTasks().withType(JacocoReport.class, jacocoReport -> {
|
||||
test.finalizedBy(jacocoReport);
|
||||
jacocoReport.dependsOn(test);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.springframework.pulsar.gradle;
|
||||
|
||||
public enum ProjectLinks {
|
||||
|
||||
HOMEPAGE("https://github.com/spring-projects-experimental/spring-pulsar"),
|
||||
ISSUES("https://github.com/spring-projects-experimental/spring-pulsar/issues"),
|
||||
CI("https://github.com/spring-projects-experimental/spring-pulsar/actions"),
|
||||
SCM_URL("https://github.com/spring-projects-experimental/spring-pulsar"),
|
||||
SCM_CONNECTION("https://github.com/spring-projects-experimental/spring-pulsar.git"),
|
||||
SCM_DEV_CONNECTION("git@github.com:spring-projects-experimental/spring-pulsar.git");
|
||||
|
||||
private final String link;
|
||||
|
||||
ProjectLinks(String link) {
|
||||
this.link = link;
|
||||
}
|
||||
|
||||
public String link() {
|
||||
return this.link;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.springframework.pulsar.gradle;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
|
||||
public final class ProjectUtils {
|
||||
|
||||
private ProjectUtils() {
|
||||
}
|
||||
|
||||
public static boolean isSnapshot(Project project) {
|
||||
return projectVersion(project).endsWith("-SNAPSHOT");
|
||||
}
|
||||
|
||||
public static boolean isMilestone(Project project) {
|
||||
String projectVersion = projectVersion(project);
|
||||
return projectVersion.matches("^.*[.-]M\\d+$") || projectVersion.matches("^.*[.-]RC\\d+$");
|
||||
}
|
||||
|
||||
public static boolean isRelease(Project project) {
|
||||
return !(isSnapshot(project) || isMilestone(project));
|
||||
}
|
||||
|
||||
private static String projectVersion(Project project) {
|
||||
return String.valueOf(project.getVersion());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2022-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.gradle;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.plugins.BasePlugin;
|
||||
import org.gradle.api.plugins.PluginManager;
|
||||
|
||||
import org.springframework.pulsar.gradle.publish.SpringNexusPublishPlugin;
|
||||
|
||||
/**
|
||||
* @author Chris Bono
|
||||
*/
|
||||
public class RootProjectPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(final Project project) {
|
||||
PluginManager pluginManager = project.getPluginManager();
|
||||
pluginManager.apply(BasePlugin.class);
|
||||
pluginManager.apply(SpringNexusPublishPlugin.class);
|
||||
project.getRepositories().mavenCentral();
|
||||
pluginManager.apply(SonarQubeConventionsPlugin.class);
|
||||
|
||||
Task finalizeDeployArtifacts = project.task("finalizeDeployArtifacts");
|
||||
if (ProjectUtils.isRelease(project) && project.hasProperty("ossrhUsername")) {
|
||||
finalizeDeployArtifacts.dependsOn(project.getTasks().findByName("closeAndReleaseOssrhStagingRepository"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.springframework.pulsar.gradle;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.sonarqube.gradle.SonarQubeExtension;
|
||||
import org.sonarqube.gradle.SonarQubePlugin;
|
||||
|
||||
/**
|
||||
* Adds a version of SonarQube to use and configures it.
|
||||
*
|
||||
* @author Chris Bono
|
||||
*/
|
||||
public class SonarQubeConventionsPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(final Project project) {
|
||||
project.getPluginManager().apply(SonarQubePlugin.class);
|
||||
project.getExtensions().configure(SonarQubeExtension.class, (sonarExtension) ->
|
||||
sonarExtension.properties((sonarProperties) -> {
|
||||
sonarProperties.property("sonar.projectName", project.getName());
|
||||
sonarProperties.property("sonar.jacoco.reportPath", project.getBuildDir().getName() + "/jacoco.exec");
|
||||
sonarProperties.property("sonar.links.homepage", ProjectLinks.HOMEPAGE.link());
|
||||
sonarProperties.property("sonar.links.ci", ProjectLinks.CI.link());
|
||||
sonarProperties.property("sonar.links.issue", ProjectLinks.ISSUES.link());
|
||||
sonarProperties.property("sonar.links.scm", ProjectLinks.SCM_CONNECTION.link());
|
||||
sonarProperties.property("sonar.links.scm_dev", ProjectLinks.SCM_DEV_CONNECTION.link());
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2022-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.gradle;
|
||||
|
||||
import io.spring.gradle.convention.RepositoryConventionPlugin;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.plugins.JavaLibraryPlugin;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.PluginManager;
|
||||
|
||||
import org.springframework.boot.gradle.JavaConventionsPlugin;
|
||||
import org.springframework.boot.gradle.optional.OptionalDependenciesPlugin;
|
||||
import org.springframework.pulsar.gradle.docs.asciidoc.AsciidoctorConventionsPlugin;
|
||||
import org.springframework.pulsar.gradle.publish.SpringPublishPlugin;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
* @author Chris Bono
|
||||
*/
|
||||
public class SpringDocsModulePlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(final Project project) {
|
||||
PluginManager pluginManager = project.getPluginManager();
|
||||
pluginManager.apply(JavaPlugin.class);
|
||||
pluginManager.apply(RepositoryConventionPlugin.class);
|
||||
pluginManager.apply(JavaLibraryPlugin.class);
|
||||
pluginManager.apply(JavaConventionsPlugin.class);
|
||||
pluginManager.apply(AsciidoctorConventionsPlugin.class);
|
||||
pluginManager.apply(SpringPublishPlugin.class);
|
||||
pluginManager.apply(OptionalDependenciesPlugin.class);
|
||||
|
||||
Task deployArtifacts = project.task("deployArtifacts");
|
||||
deployArtifacts.setGroup("Deploy tasks");
|
||||
deployArtifacts.setDescription("Deploys the artifacts to either Artifactory or Maven Central");
|
||||
if (!ProjectUtils.isRelease(project)) {
|
||||
deployArtifacts.dependsOn(project.getTasks().getByName("artifactoryPublish"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2022-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.gradle;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.PluginManager;
|
||||
|
||||
import org.springframework.pulsar.gradle.publish.PublishAllJavaComponentsPlugin;
|
||||
|
||||
/**
|
||||
* @author Chris Bono
|
||||
*/
|
||||
public class SpringModulePlugin extends SpringDocsModulePlugin {
|
||||
|
||||
@Override
|
||||
public void apply(final Project project) {
|
||||
super.apply(project);
|
||||
PluginManager pluginManager = project.getPluginManager();
|
||||
pluginManager.apply(PublishAllJavaComponentsPlugin.class);
|
||||
pluginManager.apply(JacocoConventionsPlugin.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2022-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.gradle.classpath;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.artifacts.ModuleVersionIdentifier;
|
||||
|
||||
import org.springframework.boot.gradle.classpath.CheckClasspathForProhibitedDependencies;
|
||||
|
||||
/**
|
||||
* Extends the Spring Boot {@link Task} for checking the classpath for prohibited dependencies in a more lenient fashion
|
||||
* and allows the PulsarClient to bring in some of the {@code javax.*} dependencies.
|
||||
*
|
||||
* @author Chris Bono
|
||||
*/
|
||||
public class LenientCheckClasspathForProhibitedDependencies extends CheckClasspathForProhibitedDependencies {
|
||||
|
||||
private static Set<String> OVERRIDE_PROHIBITED_DEPENDENCIES = Set.of(
|
||||
"javax.validation:validation-api",
|
||||
"javax.ws.rs:javax.ws.rs-api",
|
||||
"javax.xml.bind:jaxb-api",
|
||||
"commons-logging:commons-logging");
|
||||
|
||||
@Override
|
||||
protected boolean overrideProhibited(ModuleVersionIdentifier id) {
|
||||
return OVERRIDE_PROHIBITED_DEPENDENCIES.contains(id.getGroup() + ":" + id.getName());
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.build.docs.asciidoc;
|
||||
package org.springframework.pulsar.gradle.docs.asciidoc;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
@@ -26,6 +26,7 @@ import org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask;
|
||||
import org.asciidoctor.gradle.jvm.AsciidoctorJExtension;
|
||||
import org.asciidoctor.gradle.jvm.AsciidoctorJPlugin;
|
||||
import org.asciidoctor.gradle.jvm.AsciidoctorTask;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.Sync;
|
||||
@@ -66,12 +67,13 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class AsciidoctorConventions {
|
||||
public class AsciidoctorConventionsPlugin implements Plugin<Project> {
|
||||
|
||||
private static final String ASCIIDOCTORJ_VERSION = "2.4.3";
|
||||
|
||||
private static final String EXTENSIONS_CONFIGURATION_NAME = "asciidoctorExtensions";
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().withType(AsciidoctorJPlugin.class, (asciidoctorPlugin) -> {
|
||||
configureDocumentationDependenciesRepository(project);
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.gradle.docs.configprops;
|
||||
|
||||
/**
|
||||
* Simple builder to help construct Asciidoc markup.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class Asciidoc {
|
||||
|
||||
private final StringBuilder content;
|
||||
|
||||
Asciidoc() {
|
||||
this.content = new StringBuilder();
|
||||
}
|
||||
|
||||
Asciidoc appendWithHardLineBreaks(Object... items) {
|
||||
for (Object item : items) {
|
||||
appendln("`+", item, "+` +");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Asciidoc appendln(Object... items) {
|
||||
return append(items).newLine();
|
||||
}
|
||||
|
||||
Asciidoc append(Object... items) {
|
||||
for (Object item : items) {
|
||||
this.content.append(item);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Asciidoc newLine() {
|
||||
return append(System.lineSeparator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.content.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.gradle.docs.configprops;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.file.FileTree;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.SourceTask;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
/**
|
||||
* {@link SourceTask} that checks additional Spring configuration metadata files.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class CheckAdditionalSpringConfigurationMetadata extends SourceTask {
|
||||
|
||||
private final RegularFileProperty reportLocation;
|
||||
|
||||
public CheckAdditionalSpringConfigurationMetadata() {
|
||||
this.reportLocation = getProject().getObjects().fileProperty();
|
||||
}
|
||||
|
||||
@OutputFile
|
||||
public RegularFileProperty getReportLocation() {
|
||||
return this.reportLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
@InputFiles
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
public FileTree getSource() {
|
||||
return super.getSource();
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void check() throws JsonParseException, IOException {
|
||||
Report report = createReport();
|
||||
File reportFile = getReportLocation().get().getAsFile();
|
||||
Files.write(reportFile.toPath(), report, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
if (report.hasProblems()) {
|
||||
throw new GradleException(
|
||||
"Problems found in additional Spring configuration metadata. See " + reportFile + " for details.");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Report createReport() throws IOException, JsonParseException, JsonMappingException {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
Report report = new Report();
|
||||
for (File file : getSource().getFiles()) {
|
||||
Analysis analysis = report.analysis(getProject().getProjectDir().toPath().relativize(file.toPath()));
|
||||
Map<String, Object> json = objectMapper.readValue(file, Map.class);
|
||||
check("groups", json, analysis);
|
||||
check("properties", json, analysis);
|
||||
check("hints", json, analysis);
|
||||
}
|
||||
return report;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void check(String key, Map<String, Object> json, Analysis analysis) {
|
||||
List<Map<String, Object>> groups = (List<Map<String, Object>>) json.get(key);
|
||||
List<String> names = groups.stream().map((group) -> (String) group.get("name")).collect(Collectors.toList());
|
||||
List<String> sortedNames = sortedCopy(names);
|
||||
for (int i = 0; i < names.size(); i++) {
|
||||
String actual = names.get(i);
|
||||
String expected = sortedNames.get(i);
|
||||
if (!actual.equals(expected)) {
|
||||
analysis.problems.add("Wrong order at $." + key + "[" + i + "].name - expected '" + expected
|
||||
+ "' but found '" + actual + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> sortedCopy(Collection<String> original) {
|
||||
List<String> copy = new ArrayList<>(original);
|
||||
Collections.sort(copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
private static final class Report implements Iterable<String> {
|
||||
|
||||
private final List<Analysis> analyses = new ArrayList<>();
|
||||
|
||||
private Analysis analysis(Path path) {
|
||||
Analysis analysis = new Analysis(path);
|
||||
this.analyses.add(analysis);
|
||||
return analysis;
|
||||
}
|
||||
|
||||
private boolean hasProblems() {
|
||||
for (Analysis analysis : this.analyses) {
|
||||
if (!analysis.problems.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> iterator() {
|
||||
List<String> lines = new ArrayList<>();
|
||||
for (Analysis analysis : this.analyses) {
|
||||
lines.add(analysis.source.toString());
|
||||
lines.add("");
|
||||
if (analysis.problems.isEmpty()) {
|
||||
lines.add("No problems found.");
|
||||
}
|
||||
else {
|
||||
lines.addAll(analysis.problems);
|
||||
}
|
||||
lines.add("");
|
||||
}
|
||||
return lines.iterator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class Analysis {
|
||||
|
||||
private final List<String> problems = new ArrayList<>();
|
||||
|
||||
private final Path source;
|
||||
|
||||
private Analysis(Path source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.gradle.docs.configprops;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Table row regrouping a list of configuration properties sharing the same description.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class CompoundRow extends Row {
|
||||
|
||||
private final Set<String> propertyNames;
|
||||
|
||||
private final String description;
|
||||
|
||||
CompoundRow(Snippet snippet, String prefix, String description) {
|
||||
super(snippet, prefix);
|
||||
this.description = description;
|
||||
this.propertyNames = new TreeSet<>();
|
||||
}
|
||||
|
||||
void addProperty(ConfigurationProperty property) {
|
||||
this.propertyNames.add(property.getDisplayName());
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(Asciidoc asciidoc) {
|
||||
asciidoc.append("|");
|
||||
asciidoc.append("[[" + getAnchor() + "]]");
|
||||
asciidoc.append("<<" + getAnchor() + ",");
|
||||
this.propertyNames.forEach(asciidoc::appendWithHardLineBreaks);
|
||||
asciidoc.appendln(">>");
|
||||
asciidoc.appendln("|+++", this.description, "+++");
|
||||
asciidoc.appendln("|");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.gradle.docs.configprops;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* Configuration properties read from one or more
|
||||
* {@code META-INF/spring-configuration-metadata.json} files.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
final class ConfigurationProperties {
|
||||
|
||||
private final Map<String, ConfigurationProperty> byName;
|
||||
|
||||
private ConfigurationProperties(List<ConfigurationProperty> properties) {
|
||||
Map<String, ConfigurationProperty> byName = new LinkedHashMap<>();
|
||||
for (ConfigurationProperty property : properties) {
|
||||
byName.put(property.getName(), property);
|
||||
}
|
||||
this.byName = Collections.unmodifiableMap(byName);
|
||||
}
|
||||
|
||||
ConfigurationProperty get(String propertyName) {
|
||||
return this.byName.get(propertyName);
|
||||
}
|
||||
|
||||
Stream<ConfigurationProperty> stream() {
|
||||
return this.byName.values().stream();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static ConfigurationProperties fromFiles(Iterable<File> files) {
|
||||
try {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
List<ConfigurationProperty> properties = new ArrayList<>();
|
||||
for (File file : files) {
|
||||
Map<String, Object> json = objectMapper.readValue(file, Map.class);
|
||||
for (Map<String, Object> property : (List<Map<String, Object>>) json.get("properties")) {
|
||||
properties.add(ConfigurationProperty.fromJsonProperties(property));
|
||||
}
|
||||
}
|
||||
return new ConfigurationProperties(properties);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new RuntimeException("Failed to load configuration metadata", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.gradle.docs.configprops;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
import org.gradle.api.tasks.compile.JavaCompile;
|
||||
import org.gradle.language.base.plugins.LifecycleBasePlugin;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link Plugin} for projects that define {@code @ConfigurationProperties}. When applied,
|
||||
* the plugin reacts to the presence of the {@link JavaPlugin} by:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Adding a dependency on the configuration properties annotation processor.
|
||||
* <li>Configuring the additional metadata locations annotation processor compiler
|
||||
* argument.
|
||||
* <li>Adding the outputs of the processResources task as inputs of the compileJava task
|
||||
* to ensure that the additional metadata is available when the annotation processor runs.
|
||||
* <li>Registering a {@link CheckAdditionalSpringConfigurationMetadata} task and
|
||||
* configuring the {@code check} task to depend upon it.
|
||||
* <li>Defining an artifact for the resulting configuration property metadata so that it
|
||||
* can be consumed by downstream projects.
|
||||
* </ul>
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Chris Bono
|
||||
*/
|
||||
public class ConfigurationPropertiesPlugin implements Plugin<Project> {
|
||||
|
||||
// TODO extend the one in boot and delete most of this
|
||||
|
||||
/**
|
||||
* Name of the {@link Configuration} that holds the configuration property metadata
|
||||
* artifact.
|
||||
*/
|
||||
public static final String CONFIGURATION_PROPERTIES_METADATA_CONFIGURATION_NAME = "configurationPropertiesMetadata";
|
||||
|
||||
/**
|
||||
* Name of the {@link CheckAdditionalSpringConfigurationMetadata} task.
|
||||
*/
|
||||
public static final String CHECK_ADDITIONAL_SPRING_CONFIGURATION_METADATA_TASK_NAME = "checkAdditionalSpringConfigurationMetadata";
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
|
||||
addConfigurationProcessorDependency(project);
|
||||
configureAdditionalMetadataLocationsCompilerArgument(project);
|
||||
registerCheckAdditionalMetadataTask(project);
|
||||
addMetadataArtifact(project);
|
||||
});
|
||||
}
|
||||
|
||||
private void addConfigurationProcessorDependency(Project project) {
|
||||
Configuration annotationProcessors = project.getConfigurations()
|
||||
.getByName(JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME);
|
||||
// Object bootVersion = project.findProperty("springBootVersion");
|
||||
// if (bootVersion == null) {
|
||||
// bootVersion = project.findProperty("spring-boot.version");
|
||||
// }
|
||||
// Assert.notNull(bootVersion, "Unable to determine Spring Boot version");
|
||||
annotationProcessors.getDependencies().add(project.getDependencies().create(
|
||||
"org.springframework.boot:spring-boot-configuration-processor"));
|
||||
}
|
||||
|
||||
private void addMetadataArtifact(Project project) {
|
||||
SourceSet mainSourceSet = project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets()
|
||||
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||
project.getConfigurations().maybeCreate(CONFIGURATION_PROPERTIES_METADATA_CONFIGURATION_NAME);
|
||||
project.afterEvaluate((evaluatedProject) -> evaluatedProject.getArtifacts().add(
|
||||
CONFIGURATION_PROPERTIES_METADATA_CONFIGURATION_NAME,
|
||||
mainSourceSet.getJava().getDestinationDirectory().dir("META-INF/spring-configuration-metadata.json"),
|
||||
(artifact) -> artifact
|
||||
.builtBy(evaluatedProject.getTasks().getByName(mainSourceSet.getClassesTaskName()))));
|
||||
}
|
||||
|
||||
private void configureAdditionalMetadataLocationsCompilerArgument(Project project) {
|
||||
JavaCompile compileJava = project.getTasks().withType(JavaCompile.class)
|
||||
.getByName(JavaPlugin.COMPILE_JAVA_TASK_NAME);
|
||||
((Task) compileJava).getInputs().files(project.getTasks().getByName(JavaPlugin.PROCESS_RESOURCES_TASK_NAME))
|
||||
.withPathSensitivity(PathSensitivity.RELATIVE).withPropertyName("processed resources");
|
||||
SourceSet mainSourceSet = project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets()
|
||||
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||
compileJava.getOptions().getCompilerArgs()
|
||||
.add("-Aorg.springframework.boot.configurationprocessor.additionalMetadataLocations=" + StringUtils
|
||||
.collectionToCommaDelimitedString(mainSourceSet.getResources().getSourceDirectories().getFiles()
|
||||
.stream().map(project.getRootProject()::relativePath).collect(Collectors.toSet())));
|
||||
}
|
||||
|
||||
private void registerCheckAdditionalMetadataTask(Project project) {
|
||||
TaskProvider<CheckAdditionalSpringConfigurationMetadata> checkConfigurationMetadata = project.getTasks()
|
||||
.register(CHECK_ADDITIONAL_SPRING_CONFIGURATION_METADATA_TASK_NAME,
|
||||
CheckAdditionalSpringConfigurationMetadata.class);
|
||||
checkConfigurationMetadata.configure((check) -> {
|
||||
SourceSet mainSourceSet = project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets()
|
||||
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||
check.setSource(mainSourceSet.getResources());
|
||||
check.include("META-INF/additional-spring-configuration-metadata.json");
|
||||
check.getReportLocation().set(project.getLayout().getBuildDirectory()
|
||||
.file("reports/additional-spring-configuration-metadata/check.txt"));
|
||||
});
|
||||
project.getTasks().named(LifecycleBasePlugin.CHECK_TASK_NAME)
|
||||
.configure((check) -> check.dependsOn(checkConfigurationMetadata));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.gradle.docs.configprops;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A configuration property.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class ConfigurationProperty {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final String type;
|
||||
|
||||
private final Object defaultValue;
|
||||
|
||||
private final String description;
|
||||
|
||||
private final boolean deprecated;
|
||||
|
||||
ConfigurationProperty(String name, String type) {
|
||||
this(name, type, null, null, false);
|
||||
}
|
||||
|
||||
ConfigurationProperty(String name, String type, Object defaultValue, String description, boolean deprecated) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.defaultValue = defaultValue;
|
||||
this.description = description;
|
||||
this.deprecated = deprecated;
|
||||
}
|
||||
|
||||
String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
String getDisplayName() {
|
||||
return (getType() != null && getType().startsWith("java.util.Map")) ? getName() + ".*" : getName();
|
||||
}
|
||||
|
||||
String getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
Object getDefaultValue() {
|
||||
return this.defaultValue;
|
||||
}
|
||||
|
||||
String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
boolean isDeprecated() {
|
||||
return this.deprecated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ConfigurationProperty [name=" + this.name + ", type=" + this.type + "]";
|
||||
}
|
||||
|
||||
static ConfigurationProperty fromJsonProperties(Map<String, Object> property) {
|
||||
String name = (String) property.get("name");
|
||||
String type = (String) property.get("type");
|
||||
Object defaultValue = property.get("defaultValue");
|
||||
String description = (String) property.get("description");
|
||||
boolean deprecated = property.containsKey("deprecated");
|
||||
return new ConfigurationProperty(name, type, defaultValue, description, deprecated);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.build.docs.configprops;
|
||||
package org.springframework.pulsar.gradle.docs.configprops;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.gradle.docs.configprops;
|
||||
|
||||
/**
|
||||
* Abstract class for rows in {@link Table}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
abstract class Row implements Comparable<Row> {
|
||||
|
||||
private final Snippet snippet;
|
||||
|
||||
private final String id;
|
||||
|
||||
protected Row(Snippet snippet, String id) {
|
||||
this.snippet = snippet;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Row other = (Row) obj;
|
||||
return this.id.equals(other.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.id.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Row other) {
|
||||
return this.id.compareTo(other.id);
|
||||
}
|
||||
|
||||
String getAnchor() {
|
||||
return this.snippet.getAnchor() + "." + this.id;
|
||||
}
|
||||
|
||||
abstract void write(Asciidoc asciidoc);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.gradle.docs.configprops;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Table row containing a single configuration property.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class SingleRow extends Row {
|
||||
|
||||
private final String displayName;
|
||||
|
||||
private final String description;
|
||||
|
||||
private final String defaultValue;
|
||||
|
||||
SingleRow(Snippet snippet, ConfigurationProperty property) {
|
||||
super(snippet, property.getName());
|
||||
this.displayName = property.getDisplayName();
|
||||
this.description = property.getDescription();
|
||||
this.defaultValue = getDefaultValue(property.getDefaultValue());
|
||||
}
|
||||
|
||||
private String getDefaultValue(Object defaultValue) {
|
||||
if (defaultValue == null) {
|
||||
return null;
|
||||
}
|
||||
if (defaultValue.getClass().isArray()) {
|
||||
return Arrays.stream((Object[]) defaultValue).map(Object::toString)
|
||||
.collect(Collectors.joining("," + System.lineSeparator()));
|
||||
}
|
||||
return defaultValue.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(Asciidoc asciidoc) {
|
||||
asciidoc.append("|");
|
||||
asciidoc.append("[[" + getAnchor() + "]]");
|
||||
asciidoc.appendln("<<" + getAnchor() + ",`+", this.displayName, "+`>>");
|
||||
writeDescription(asciidoc);
|
||||
writeDefaultValue(asciidoc);
|
||||
}
|
||||
|
||||
private void writeDescription(Asciidoc builder) {
|
||||
if (this.description == null || this.description.isEmpty()) {
|
||||
builder.appendln("|");
|
||||
}
|
||||
else {
|
||||
String cleanedDescription = this.description.replace("|", "\\|").replace("<", "<").replace(">", ">");
|
||||
builder.appendln("|+++", cleanedDescription, "+++");
|
||||
}
|
||||
}
|
||||
|
||||
private void writeDefaultValue(Asciidoc builder) {
|
||||
String defaultValue = (this.defaultValue != null) ? this.defaultValue : "";
|
||||
if (defaultValue.isEmpty()) {
|
||||
builder.appendln("|");
|
||||
}
|
||||
else {
|
||||
defaultValue = defaultValue.replace("\\", "\\\\").replace("|", "\\|");
|
||||
builder.appendln("|`+", defaultValue, "+`");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.gradle.docs.configprops;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A configuration properties snippet.
|
||||
*
|
||||
* @author Brian Clozed
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class Snippet {
|
||||
|
||||
private final String anchor;
|
||||
|
||||
private final String title;
|
||||
|
||||
private final Set<String> prefixes;
|
||||
|
||||
private final Map<String, String> overrides;
|
||||
|
||||
Snippet(String anchor, String title, Consumer<Config> config) {
|
||||
Set<String> prefixes = new LinkedHashSet<>();
|
||||
Map<String, String> overrides = new LinkedHashMap<>();
|
||||
if (config != null) {
|
||||
config.accept(new Config() {
|
||||
|
||||
@Override
|
||||
public void accept(String prefix) {
|
||||
prefixes.add(prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(String prefix, String description) {
|
||||
overrides.put(prefix, description);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
this.anchor = anchor;
|
||||
this.title = title;
|
||||
this.prefixes = prefixes;
|
||||
this.overrides = overrides;
|
||||
}
|
||||
|
||||
String getAnchor() {
|
||||
return this.anchor;
|
||||
}
|
||||
|
||||
String getTitle() {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
void forEachPrefix(Consumer<String> action) {
|
||||
this.prefixes.forEach(action);
|
||||
}
|
||||
|
||||
void forEachOverride(BiConsumer<String, String> action) {
|
||||
this.overrides.forEach(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to configure the snippet.
|
||||
*/
|
||||
interface Config {
|
||||
|
||||
/**
|
||||
* Accept the given prefix using the meta-data description.
|
||||
* @param prefix the prefix to accept
|
||||
*/
|
||||
void accept(String prefix);
|
||||
|
||||
/**
|
||||
* Accept the given prefix with a defined description.
|
||||
* @param prefix the prefix to accept
|
||||
* @param description the description to use
|
||||
*/
|
||||
void accept(String prefix, String description);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.gradle.docs.configprops;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.gradle.api.file.FileCollection;
|
||||
|
||||
/**
|
||||
* Configuration properties snippets.
|
||||
*
|
||||
* @author Brian Clozed
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class Snippets {
|
||||
|
||||
private final ConfigurationProperties properties;
|
||||
|
||||
private final List<Snippet> snippets = new ArrayList<>();
|
||||
|
||||
Snippets(FileCollection configurationPropertyMetadata) {
|
||||
this.properties = ConfigurationProperties.fromFiles(configurationPropertyMetadata);
|
||||
}
|
||||
|
||||
void add(String anchor, String title, Consumer<Snippet.Config> config) {
|
||||
this.snippets.add(new Snippet(anchor, title, config));
|
||||
}
|
||||
|
||||
void writeTo(Path outputDirectory) throws IOException {
|
||||
createDirectory(outputDirectory);
|
||||
Set<String> remaining = this.properties.stream().filter((property) -> !property.isDeprecated())
|
||||
.map(ConfigurationProperty::getName).collect(Collectors.toSet());
|
||||
for (Snippet snippet : this.snippets) {
|
||||
Set<String> written = writeSnippet(outputDirectory, snippet, remaining);
|
||||
remaining.removeAll(written);
|
||||
}
|
||||
if (!remaining.isEmpty()) {
|
||||
throw new IllegalStateException(
|
||||
"The following keys were not written to the documentation: " + String.join(", ", remaining));
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> writeSnippet(Path outputDirectory, Snippet snippet, Set<String> remaining) throws IOException {
|
||||
Table table = new Table();
|
||||
Set<String> added = new HashSet<>();
|
||||
snippet.forEachOverride((prefix, description) -> {
|
||||
CompoundRow row = new CompoundRow(snippet, prefix, description);
|
||||
remaining.stream().filter((candidate) -> candidate.startsWith(prefix)).forEach((name) -> {
|
||||
if (added.add(name)) {
|
||||
row.addProperty(this.properties.get(name));
|
||||
}
|
||||
});
|
||||
table.addRow(row);
|
||||
});
|
||||
snippet.forEachPrefix((prefix) -> {
|
||||
remaining.stream().filter((candidate) -> candidate.startsWith(prefix)).forEach((name) -> {
|
||||
if (added.add(name)) {
|
||||
table.addRow(new SingleRow(snippet, this.properties.get(name)));
|
||||
}
|
||||
});
|
||||
});
|
||||
Asciidoc asciidoc = getAsciidoc(snippet, table);
|
||||
writeAsciidoc(outputDirectory, snippet, asciidoc);
|
||||
return added;
|
||||
}
|
||||
|
||||
private Asciidoc getAsciidoc(Snippet snippet, Table table) {
|
||||
Asciidoc asciidoc = new Asciidoc();
|
||||
// We have to prepend 'appendix.' as a section id here, otherwise the
|
||||
// spring-asciidoctor-extensions:section-id asciidoctor extension complains
|
||||
asciidoc.appendln("[[appendix." + snippet.getAnchor() + "]]");
|
||||
asciidoc.appendln("== ", snippet.getTitle());
|
||||
table.write(asciidoc);
|
||||
return asciidoc;
|
||||
}
|
||||
|
||||
private void writeAsciidoc(Path outputDirectory, Snippet snippet, Asciidoc asciidoc) throws IOException {
|
||||
String[] parts = (snippet.getAnchor()).split("\\.");
|
||||
Path path = outputDirectory;
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
String name = (i < parts.length - 1) ? parts[i] : parts[i] + ".adoc";
|
||||
path = path.resolve(name);
|
||||
}
|
||||
createDirectory(path.getParent());
|
||||
Files.deleteIfExists(path);
|
||||
try (OutputStream outputStream = Files.newOutputStream(path)) {
|
||||
outputStream.write(asciidoc.toString().getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
private void createDirectory(Path path) throws IOException {
|
||||
assertValidOutputDirectory(path);
|
||||
if (!Files.exists(path)) {
|
||||
Files.createDirectory(path);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertValidOutputDirectory(Path path) {
|
||||
if (path == null) {
|
||||
throw new IllegalArgumentException("Directory path should not be null");
|
||||
}
|
||||
if (Files.exists(path) && !Files.isDirectory(path)) {
|
||||
throw new IllegalArgumentException("Path already exists and is not a directory");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.gradle.docs.configprops;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Asciidoctor table listing configuration properties sharing to a common theme.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class Table {
|
||||
|
||||
private final Set<Row> rows = new TreeSet<>();
|
||||
|
||||
void addRow(Row row) {
|
||||
this.rows.add(row);
|
||||
}
|
||||
|
||||
void write(Asciidoc asciidoc) {
|
||||
asciidoc.appendln("[cols=\"4,3,3\", options=\"header\"]");
|
||||
asciidoc.appendln("|===");
|
||||
asciidoc.appendln("|Name|Description|Default Value");
|
||||
asciidoc.appendln();
|
||||
this.rows.forEach((entry) -> {
|
||||
entry.write(asciidoc);
|
||||
asciidoc.appendln();
|
||||
});
|
||||
asciidoc.appendln("|===");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.gradle.publish;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.attributes.Usage;
|
||||
import org.gradle.api.component.AdhocComponentWithVariants;
|
||||
import org.gradle.api.component.ConfigurationVariantDetails;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.publish.PublishingExtension;
|
||||
import org.gradle.api.publish.VariantVersionMappingStrategy;
|
||||
import org.gradle.api.publish.maven.MavenPom;
|
||||
import org.gradle.api.publish.maven.MavenPomDeveloperSpec;
|
||||
import org.gradle.api.publish.maven.MavenPomIssueManagement;
|
||||
import org.gradle.api.publish.maven.MavenPomLicenseSpec;
|
||||
import org.gradle.api.publish.maven.MavenPomOrganization;
|
||||
import org.gradle.api.publish.maven.MavenPomScm;
|
||||
import org.gradle.api.publish.maven.MavenPublication;
|
||||
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
|
||||
|
||||
import org.springframework.pulsar.gradle.ProjectLinks;
|
||||
|
||||
/**
|
||||
* Conventions that are applied in the presence of the {@link MavenPublishPlugin}. When
|
||||
* the plugin is applied:
|
||||
*
|
||||
* <ul>
|
||||
* <li>If the {@code deploymentRepository} property has been set, a Maven artifact repository
|
||||
* is configured to publish to it
|
||||
* it.
|
||||
* <li>The poms of all {@link MavenPublication Maven publications} are customized to meet
|
||||
* Maven Central's requirements.
|
||||
* <li>If the {@link JavaPlugin Java plugin} has also been applied:
|
||||
* <ul>
|
||||
* <li>Creation of Javadoc and source jars is enabled.
|
||||
* <li>Publication metadata (poms and Gradle module metadata) is configured to use
|
||||
* resolved versions.
|
||||
* </ul>
|
||||
* </ul>
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Christoph Dreis
|
||||
* @author Mike Smithson
|
||||
* @author Chris Bono
|
||||
*/
|
||||
class MavenPublishingConventionsPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().withType(MavenPublishPlugin.class).all((mavenPublish) -> {
|
||||
PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);
|
||||
if (project.hasProperty("deploymentRepository")) {
|
||||
publishing.getRepositories().maven((mavenRepository) -> {
|
||||
mavenRepository.setUrl(project.property("deploymentRepository"));
|
||||
mavenRepository.setName("deployment");
|
||||
});
|
||||
}
|
||||
publishing.getPublications().withType(MavenPublication.class)
|
||||
.all((mavenPublication) -> customizeMavenPublication(mavenPublication, project));
|
||||
project.getPlugins().withType(JavaPlugin.class).all((javaPlugin) -> {
|
||||
JavaPluginExtension extension = project.getExtensions().getByType(JavaPluginExtension.class);
|
||||
extension.withJavadocJar();
|
||||
extension.withSourcesJar();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void customizeMavenPublication(MavenPublication publication, Project project) {
|
||||
customizePom(publication.getPom(), project);
|
||||
project.getPlugins().withType(JavaPlugin.class)
|
||||
.all((javaPlugin) -> customizeJavaMavenPublication(publication, project));
|
||||
suppressMavenOptionalFeatureWarnings(publication);
|
||||
}
|
||||
|
||||
private void customizePom(MavenPom pom, Project project) {
|
||||
pom.getUrl().set(ProjectLinks.HOMEPAGE.link());
|
||||
pom.getName().set(project.provider(project::getName));
|
||||
pom.getDescription().set(project.provider(project::getDescription));
|
||||
pom.organization(this::customizeOrganization);
|
||||
pom.licenses(this::customizeLicences);
|
||||
pom.developers(this::customizeDevelopers);
|
||||
pom.scm(this::customizeScm);
|
||||
pom.issueManagement(this::customizeIssueManagement);
|
||||
}
|
||||
|
||||
private void customizeJavaMavenPublication(MavenPublication publication, Project project) {
|
||||
addMavenOptionalFeature(project);
|
||||
publication.versionMapping((strategy) -> strategy.usage(Usage.JAVA_API, (mappingStrategy) -> mappingStrategy
|
||||
.fromResolutionOf(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME)));
|
||||
publication.versionMapping(
|
||||
(strategy) -> strategy.usage(Usage.JAVA_RUNTIME, VariantVersionMappingStrategy::fromResolutionResult));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a feature that allows maven plugins to declare optional dependencies that
|
||||
* appear in the POM. This is required to make m2e in Eclipse happy.
|
||||
* @param project the project to add the feature to
|
||||
*/
|
||||
private void addMavenOptionalFeature(Project project) {
|
||||
JavaPluginExtension extension = project.getExtensions().getByType(JavaPluginExtension.class);
|
||||
extension.registerFeature("mavenOptional",
|
||||
(feature) -> feature.usingSourceSet(extension.getSourceSets().getByName("main")));
|
||||
AdhocComponentWithVariants javaComponent = (AdhocComponentWithVariants) project.getComponents()
|
||||
.findByName("java");
|
||||
javaComponent.addVariantsFromConfiguration(
|
||||
project.getConfigurations().findByName("mavenOptionalRuntimeElements"),
|
||||
ConfigurationVariantDetails::mapToOptional);
|
||||
}
|
||||
|
||||
private void suppressMavenOptionalFeatureWarnings(MavenPublication publication) {
|
||||
publication.suppressPomMetadataWarningsFor("mavenOptionalApiElements");
|
||||
publication.suppressPomMetadataWarningsFor("mavenOptionalRuntimeElements");
|
||||
}
|
||||
|
||||
private void customizeOrganization(MavenPomOrganization organization) {
|
||||
organization.getName().set("Pivotal Software, Inc.");
|
||||
organization.getUrl().set("https://spring.io");
|
||||
}
|
||||
|
||||
private void customizeLicences(MavenPomLicenseSpec licences) {
|
||||
licences.license((licence) -> {
|
||||
licence.getName().set("Apache License, Version 2.0");
|
||||
licence.getUrl().set("https://www.apache.org/licenses/LICENSE-2.0");
|
||||
});
|
||||
}
|
||||
|
||||
private void customizeDevelopers(MavenPomDeveloperSpec developers) {
|
||||
developers.developer((developer) -> {
|
||||
developer.getId().set("schacko");
|
||||
developer.getName().set("Soby Chacko");
|
||||
developer.getEmail().set("chackos@vmware.com");
|
||||
});
|
||||
developers.developer((developer) -> {
|
||||
developer.getId().set("onobc");
|
||||
developer.getName().set("Chris Bono");
|
||||
developer.getEmail().set("cbono@vmware.com");
|
||||
});
|
||||
}
|
||||
|
||||
private void customizeScm(MavenPomScm scm) {
|
||||
scm.getConnection().set(ProjectLinks.SCM_CONNECTION.link());
|
||||
scm.getDeveloperConnection().set(ProjectLinks.SCM_DEV_CONNECTION.link());
|
||||
scm.getUrl().set(ProjectLinks.SCM_URL.link());
|
||||
}
|
||||
|
||||
private void customizeIssueManagement(MavenPomIssueManagement issueManagement) {
|
||||
issueManagement.getSystem().set("GitHub");
|
||||
issueManagement.getUrl().set(ProjectLinks.ISSUES.link());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.springframework.pulsar.gradle.publish;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.JavaPlatformPlugin;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.publish.PublishingExtension;
|
||||
import org.gradle.api.publish.maven.MavenPublication;
|
||||
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
|
||||
|
||||
public class PublishAllJavaComponentsPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().withType(MavenPublishPlugin.class).all((mavenPublish) -> {
|
||||
PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);
|
||||
publishing.getPublications().create("mavenJava", MavenPublication.class, maven -> {
|
||||
project.getPlugins().withType(JavaPlugin.class, (plugin) -> {
|
||||
maven.from(project.getComponents().getByName("java"));
|
||||
});
|
||||
project.getPlugins().withType(JavaPlatformPlugin.class, (plugin) -> {
|
||||
maven.from(project.getComponents().getByName("javaPlatform"));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.springframework.pulsar.gradle.publish;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
|
||||
import org.springframework.pulsar.gradle.ProjectUtils;
|
||||
|
||||
public class PublishArtifactsPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getTasks().register("publishArtifacts", publishArtifacts -> {
|
||||
publishArtifacts.setGroup("Publishing");
|
||||
publishArtifacts.setDescription("Publish the artifacts to either Artifactory or Maven Central based on the version");
|
||||
if (ProjectUtils.isRelease(project)) {
|
||||
publishArtifacts.dependsOn("publishToOssrh");
|
||||
}
|
||||
else {
|
||||
publishArtifacts.dependsOn("artifactoryPublish");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.springframework.pulsar.gradle.publish;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.publish.PublishingExtension;
|
||||
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
|
||||
|
||||
public class PublishLocalPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().withType(MavenPublishPlugin.class).all(mavenPublish -> {
|
||||
PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);
|
||||
publishing.getRepositories().maven(maven -> {
|
||||
maven.setName("local");
|
||||
maven.setUrl(new File(project.getRootProject().getBuildDir(), "publications/repos"));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.springframework.pulsar.gradle.publish;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
|
||||
import io.github.gradlenexus.publishplugin.NexusPublishExtension;
|
||||
import io.github.gradlenexus.publishplugin.NexusPublishPlugin;
|
||||
import io.github.gradlenexus.publishplugin.NexusRepository;
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
|
||||
public class SpringNexusPublishPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().apply(NexusPublishPlugin.class);
|
||||
NexusPublishExtension nexusPublishing = project.getExtensions().findByType(NexusPublishExtension.class);
|
||||
nexusPublishing.getRepositories().create("ossrh", nexusRepository -> {
|
||||
nexusRepository.getNexusUrl().set(URI.create("https://s01.oss.sonatype.org/service/local/"));
|
||||
nexusRepository.getSnapshotRepositoryUrl().set(
|
||||
URI.create("https://s01.oss.sonatype.org/content/repositories/snapshots/"));
|
||||
});
|
||||
nexusPublishing.getConnectTimeout().set(Duration.ofMinutes(3));
|
||||
nexusPublishing.getClientTimeout().set(Duration.ofMinutes(3));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.springframework.pulsar.gradle.publish;
|
||||
|
||||
import io.spring.gradle.convention.ArtifactoryPlugin;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.PluginManager;
|
||||
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
|
||||
|
||||
public class SpringPublishPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
PluginManager pluginManager = project.getPluginManager();
|
||||
pluginManager.apply(MavenPublishPlugin.class);
|
||||
pluginManager.apply(SpringSigningPlugin.class);
|
||||
pluginManager.apply(MavenPublishingConventionsPlugin.class);
|
||||
pluginManager.apply(PublishLocalPlugin.class);
|
||||
pluginManager.apply(PublishArtifactsPlugin.class);
|
||||
pluginManager.apply(ArtifactoryPlugin.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2016-2019 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.gradle.publish;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.publish.Publication;
|
||||
import org.gradle.api.publish.PublishingExtension;
|
||||
import org.gradle.plugins.signing.SigningExtension;
|
||||
import org.gradle.plugins.signing.SigningPlugin;
|
||||
|
||||
public class SpringSigningPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getPluginManager().apply(SigningPlugin.class);
|
||||
project.getPlugins().withType(SigningPlugin.class).all(signingPlugin -> {
|
||||
boolean hasSigningKey = project.hasProperty("signing.keyId") || project.hasProperty("signingKey");
|
||||
if (hasSigningKey) {
|
||||
sign(project);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sign(Project project) {
|
||||
SigningExtension signing = project.getExtensions().findByType(SigningExtension.class);
|
||||
signing.setRequired((Callable<Boolean>) () -> project.getGradle().getTaskGraph().hasTask("publishArtifacts"));
|
||||
String signingKeyId = (String) project.findProperty("signingKeyId");
|
||||
String signingKey = (String) project.findProperty("signingKey");
|
||||
String signingPassword = (String) project.findProperty("signingPassword");
|
||||
if (signingKeyId != null) {
|
||||
signing.useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword);
|
||||
}
|
||||
else {
|
||||
signing.useInMemoryPgpKeys(signingKey, signingPassword);
|
||||
}
|
||||
project.getPlugins().withType(PublishAllJavaComponentsPlugin.class)
|
||||
.all(publishingPlugin -> {
|
||||
PublishingExtension publishing = project.getExtensions().findByType(PublishingExtension.class);
|
||||
Publication maven = publishing.getPublications().getByName("mavenJava");
|
||||
signing.sign(maven);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.build.docs.configprops;
|
||||
package org.springframework.pulsar.gradle.docs.configprops;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.build.docs.configprops;
|
||||
package org.springframework.pulsar.gradle.docs.configprops;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.build.docs.configprops;
|
||||
package org.springframework.pulsar.gradle.docs.configprops;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.pulsar.build.docs.configprops;
|
||||
package org.springframework.pulsar.gradle.docs.configprops;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -22,6 +22,7 @@ settings.gradle.projectsLoaded {
|
||||
rootProject.name = 'spring-pulsar-dist'
|
||||
|
||||
include 'spring-pulsar'
|
||||
include 'spring-pulsar-boot-autoconfigure'
|
||||
include 'spring-pulsar-dependencies'
|
||||
include 'spring-pulsar-spring-boot-autoconfigure'
|
||||
include 'spring-pulsar-sample-apps'
|
||||
include 'spring-pulsar-docs'
|
||||
|
||||
62
spring-pulsar-dependencies/build.gradle
Normal file
62
spring-pulsar-dependencies/build.gradle
Normal file
@@ -0,0 +1,62 @@
|
||||
plugins {
|
||||
id 'java-platform'
|
||||
}
|
||||
|
||||
javaPlatform {
|
||||
allowDependencies()
|
||||
}
|
||||
|
||||
ext {
|
||||
assertjVersion = '3.22.0'
|
||||
awaitilityVersion = '4.2.0'
|
||||
googleJsr305Version = '3.0.2'
|
||||
hamcrestVersion = '2.2'
|
||||
hibernateValidationVersion = '7.0.4.Final'
|
||||
jacksonBomVersion = '2.13.3'
|
||||
jaywayJsonPathVersion = '2.6.0'
|
||||
junitJupiterVersion = '5.8.2'
|
||||
pulsarVersion = '2.10.0'
|
||||
log4jVersion = '2.17.2'
|
||||
mockitoVersion = '4.5.1'
|
||||
reactorVersion = '2020.0.17'
|
||||
pulsarTestcontainersVersion = '1.17.2'
|
||||
springBootVersion = '3.0.0-SNAPSHOT'
|
||||
springRetryVersion = '1.3.3'
|
||||
springVersion = '6.0.0-SNAPSHOT'
|
||||
caffeineVersion = '3.1.1'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api platform("org.springframework:spring-framework-bom:$springVersion")
|
||||
api platform("io.projectreactor:reactor-bom:$reactorVersion")
|
||||
api platform("org.junit:junit-bom:$junitJupiterVersion")
|
||||
api platform("com.fasterxml.jackson:jackson-bom:$jacksonBomVersion")
|
||||
api platform("org.apache.logging.log4j:log4j-bom:$log4jVersion")
|
||||
|
||||
constraints {
|
||||
// spring-pulsar
|
||||
api "com.github.ben-manes.caffeine:caffeine:$caffeineVersion"
|
||||
api "org.apache.pulsar:pulsar-client:$pulsarVersion"
|
||||
api "org.apache.pulsar:pulsar-client-admin:$pulsarVersion"
|
||||
api "org.apache.pulsar:pulsar-client-admin-api:$pulsarVersion"
|
||||
api "org.springframework.retry:spring-retry:$springRetryVersion"
|
||||
api "com.jayway.jsonpath:json-path:$jaywayJsonPathVersion"
|
||||
api "org.mockito:mockito-junit-jupiter:$mockitoVersion"
|
||||
api "org.hibernate.validator:hibernate-validator:$hibernateValidationVersion"
|
||||
// spring-pulsar-spring-boot-autoconfigure
|
||||
api "org.springframework.boot:spring-boot-autoconfigure-processor:$springBootVersion"
|
||||
api "org.springframework.boot:spring-boot-configuration-processor:$springBootVersion"
|
||||
api "org.springframework.boot:spring-boot:$springBootVersion"
|
||||
api "org.springframework.boot:spring-boot-autoconfigure:$springBootVersion"
|
||||
api "org.springframework.boot:spring-boot-starter:$springBootVersion"
|
||||
api "org.springframework.boot:spring-boot-starter-validation:$springBootVersion"
|
||||
api "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
|
||||
// Common to all
|
||||
api "com.google.code.findbugs:jsr305:$googleJsr305Version"
|
||||
api 'org.apiguardian:apiguardian-api:1.0.0'
|
||||
api "org.awaitility:awaitility:$awaitilityVersion"
|
||||
api "org.hamcrest:hamcrest-core:$hamcrestVersion"
|
||||
api "org.assertj:assertj-core:$assertjVersion"
|
||||
api "org.testcontainers:pulsar:$pulsarTestcontainersVersion"
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,8 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'java-library'
|
||||
id 'org.springframework.pulsar.conventions'
|
||||
id 'org.springframework.pulsar.spring-docs-module'
|
||||
id 'org.asciidoctor.jvm.convert'
|
||||
}
|
||||
|
||||
apply from: "${rootProject.projectDir}/gradle/publish-artifactory.gradle"
|
||||
|
||||
description = 'Spring Pulsar Docs'
|
||||
|
||||
configurations {
|
||||
@@ -14,9 +10,9 @@ configurations {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api "org.springframework.boot:spring-boot-starter:$springBootVersion"
|
||||
api 'org.springframework.boot:spring-boot-starter'
|
||||
api project (':spring-pulsar')
|
||||
configurationProperties(project(path: ":spring-pulsar-boot-autoconfigure", configuration: "configurationPropertiesMetadata"))
|
||||
configurationProperties(project(path: ":spring-pulsar-spring-boot-autoconfigure", configuration: "configurationPropertiesMetadata"))
|
||||
}
|
||||
|
||||
task aggregatedJavadoc(type: Javadoc) {
|
||||
@@ -52,7 +48,7 @@ task aggregatedJavadoc(type: Javadoc) {
|
||||
}
|
||||
}
|
||||
|
||||
task documentConfigurationProperties(type: org.springframework.pulsar.build.docs.configprops.DocumentConfigurationProperties) {
|
||||
task documentConfigurationProperties(type: org.springframework.pulsar.gradle.docs.configprops.DocumentConfigurationProperties) {
|
||||
configurationPropertyMetadata = configurations.configurationProperties
|
||||
outputDir = file("${buildDir}/docs/generated/")
|
||||
}
|
||||
@@ -71,7 +67,7 @@ asciidoctor {
|
||||
|
||||
task asciidoctorPdf(type: org.asciidoctor.gradle.jvm.AsciidoctorTask) {
|
||||
sources {
|
||||
include "*.singleadoc"
|
||||
include "*.adoc"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ For a quick but less detailed introduction, see <<index.adoc#quick-intro>>.
|
||||
[[pulsar-client]]
|
||||
=== Pulsar Client
|
||||
|
||||
When using `spring-pulsar-boot-autoconfigure`, you get the `PulsarClient` auto-configured.
|
||||
When using `spring-pulsar-spring-boot-autoconfigure`, you get the `PulsarClient` auto-configured.
|
||||
This is done through a factory bean called `PulsarClientFactoryBean`, which takes a configuration object `PulsarClientConfiguration`.
|
||||
By default, the application tries to connect a local Pulsar instance available at `pulsar://localhost:6650`.
|
||||
|
||||
@@ -130,7 +130,7 @@ Note that, when using a `MessageRouter, you must set the `spring.pulsar.producer
|
||||
=== Pulsar Producer Factory
|
||||
|
||||
`PulsarTemplate` uses a `PulsarProducerFactory` for creating the underlying Pulsar producer.
|
||||
When using Spring Boot through `spring-pulsar-boot-autoconfigure`, then it automatically autoconfigures a `PulsarProducerFactory`.
|
||||
When using Spring Boot through `spring-pulsar-spring-boot-autoconfigure`, then it automatically autoconfigures a `PulsarProducerFactory`.
|
||||
Any producer properties mentioned above (using the prefix, `spring.pulsar.producer`) is passed along to the backing producer factory implementation along with a Pulsar client.
|
||||
You can disable the autoconfigured producer factory by providing your own bean definition for `DefaultPulsarProducerFactory` in the application.
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ Use the following command to do a full build of the project.
|
||||
The build will produce the following artifacts.
|
||||
|
||||
* spring-pulsar
|
||||
* spring-pulsar-boot-autoconfigure
|
||||
* spring-pulsar-spring-boot-autoconfigure
|
||||
|
||||
### Maven Coordinates
|
||||
|
||||
@@ -102,4 +102,3 @@ Behind the scenes, it creates a message listener container which creates and man
|
||||
As with a regular Pulsar consumer, the default subscription type when using `PulsarListener` is the `Exclusive` mode.
|
||||
As records are published in to the `hello-pulsar` topic, the `Pulsarlistener` consumes them and prints them on the console.
|
||||
Here also, the framework infers the schema type used from the data type that the `PulsarListner` method uses as the payload - `String` in this case.
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package foo;
|
||||
|
||||
public class Bar {
|
||||
}
|
||||
14
spring-pulsar-sample-apps/build.gradle
Normal file
14
spring-pulsar-sample-apps/build.gradle
Normal file
@@ -0,0 +1,14 @@
|
||||
plugins {
|
||||
id 'org.springframework.pulsar.spring-module'
|
||||
}
|
||||
|
||||
description = 'Spring Pulsar Sample Applications'
|
||||
|
||||
dependencies {
|
||||
api project(':spring-pulsar-spring-boot-autoconfigure')
|
||||
implementation 'com.google.code.findbugs:jsr305'
|
||||
}
|
||||
|
||||
project.afterEvaluate {
|
||||
project.tasks.artifactoryPublish.enabled(false)
|
||||
}
|
||||
@@ -40,8 +40,8 @@ public class SpringPulsarBootApp {
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic publisher using PulsarTemplate<String> and a PulsarListener using
|
||||
* an exclusive subscription to consume.
|
||||
* Basic publisher using PulsarTemplate<String> and a PulsarListener using an
|
||||
* exclusive subscription to consume.
|
||||
*/
|
||||
@Bean
|
||||
public ApplicationRunner runner1(PulsarTemplate<String> pulsarTemplate) {
|
||||
@@ -61,8 +61,8 @@ public class SpringPulsarBootApp {
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic publisher using PulsarTemplate<Integer> and a PulsarListener using
|
||||
* an exclusive subscription to consume.
|
||||
* Basic publisher using PulsarTemplate<Integer> and a PulsarListener using an
|
||||
* exclusive subscription to consume.
|
||||
*/
|
||||
@Bean
|
||||
public ApplicationRunner runner2(PulsarTemplate<Integer> pulsarTemplate) {
|
||||
@@ -82,8 +82,8 @@ public class SpringPulsarBootApp {
|
||||
}
|
||||
|
||||
/*
|
||||
* Demonstrating more complex types for publishing using JSON schema and the associated
|
||||
* PulsarListener using an exclusive subscription.
|
||||
* Demonstrating more complex types for publishing using JSON schema and the
|
||||
* associated PulsarListener using an exclusive subscription.
|
||||
*/
|
||||
@Bean
|
||||
public ApplicationRunner runner3(PulsarTemplate<Foo> pulsarTemplate) {
|
||||
@@ -97,7 +97,8 @@ public class SpringPulsarBootApp {
|
||||
};
|
||||
}
|
||||
|
||||
@PulsarListener(subscriptionName = "subscription-3", topics = "hello-pulsar-exclusive-3", schemaType = SchemaType.JSON)
|
||||
@PulsarListener(subscriptionName = "subscription-3", topics = "hello-pulsar-exclusive-3",
|
||||
schemaType = SchemaType.JSON)
|
||||
public void listen3(Foo message) {
|
||||
this.logger.info("Message received :" + message);
|
||||
}
|
||||
@@ -117,7 +118,8 @@ public class SpringPulsarBootApp {
|
||||
};
|
||||
}
|
||||
|
||||
@PulsarListener(subscriptionName = "subscription-4", topics = "hello-pulsar-exclusive-4", schemaType = SchemaType.JSON, batch = true)
|
||||
@PulsarListener(subscriptionName = "subscription-4", topics = "hello-pulsar-exclusive-4",
|
||||
schemaType = SchemaType.JSON, batch = true)
|
||||
public void listen4(List<Foo> messages) {
|
||||
this.logger.info("records received :" + messages.size());
|
||||
for (Foo message : messages) {
|
||||
@@ -128,10 +130,7 @@ public class SpringPulsarBootApp {
|
||||
record Foo(String foo, String bar) {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Foo{" +
|
||||
"foo='" + this.foo + '\'' +
|
||||
", bar='" + this.bar + '\'' +
|
||||
'}';
|
||||
return "Foo{" + "foo='" + this.foo + '\'' + ", bar='" + this.bar + '\'' + '}';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,22 +53,26 @@ public class FailoverConsumerApp {
|
||||
};
|
||||
}
|
||||
|
||||
@PulsarListener(subscriptionName = "failover-subscription-demo", topics = "failover-demo-topic", subscriptionType = "failover")
|
||||
@PulsarListener(subscriptionName = "failover-subscription-demo", topics = "failover-demo-topic",
|
||||
subscriptionType = "failover")
|
||||
public void listen1(String foo) {
|
||||
this.logger.info("failover-listen1 : " + foo);
|
||||
}
|
||||
|
||||
@PulsarListener(subscriptionName = "failover-subscription-demo", topics = "failover-demo-topic", subscriptionType = "failover")
|
||||
@PulsarListener(subscriptionName = "failover-subscription-demo", topics = "failover-demo-topic",
|
||||
subscriptionType = "failover")
|
||||
public void listen2(String foo) {
|
||||
this.logger.info("failover-listen2 : " + foo);
|
||||
}
|
||||
|
||||
@PulsarListener(subscriptionName = "failover-subscription-demo", topics = "failover-demo-topic", subscriptionType = "failover")
|
||||
@PulsarListener(subscriptionName = "failover-subscription-demo", topics = "failover-demo-topic",
|
||||
subscriptionType = "failover")
|
||||
public void listen(String foo) {
|
||||
this.logger.info("failover-listen3 : " + foo);
|
||||
}
|
||||
|
||||
static class FooRouter implements MessageRouter {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -1L;
|
||||
|
||||
@@ -76,9 +80,11 @@ public class FailoverConsumerApp {
|
||||
public int choosePartition(Message<?> msg, TopicMetadata metadata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class BarRouter implements MessageRouter {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -1L;
|
||||
|
||||
@@ -86,9 +92,11 @@ public class FailoverConsumerApp {
|
||||
public int choosePartition(Message<?> msg, TopicMetadata metadata) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class BuzzRouter implements MessageRouter {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -1L;
|
||||
|
||||
@@ -96,5 +104,7 @@ public class FailoverConsumerApp {
|
||||
public int choosePartition(Message<?> msg, TopicMetadata metadata) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
28
spring-pulsar-spring-boot-autoconfigure/build.gradle
Normal file
28
spring-pulsar-spring-boot-autoconfigure/build.gradle
Normal file
@@ -0,0 +1,28 @@
|
||||
plugins {
|
||||
id 'org.springframework.pulsar.spring-module'
|
||||
id 'org.springframework.pulsar.configuration-properties'
|
||||
}
|
||||
|
||||
description = 'Spring Pulsar Spring Boot Auto-configuration'
|
||||
|
||||
dependencies {
|
||||
annotationProcessor 'org.springframework.boot:spring-boot-autoconfigure-processor'
|
||||
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
|
||||
|
||||
api project (':spring-pulsar')
|
||||
api 'org.springframework.boot:spring-boot'
|
||||
api 'org.springframework.boot:spring-boot-autoconfigure'
|
||||
api 'org.springframework.boot:spring-boot-starter'
|
||||
api 'org.springframework.boot:spring-boot-starter-validation'
|
||||
implementation 'com.google.code.findbugs:jsr305'
|
||||
|
||||
// TODO remove unused dependencies
|
||||
|
||||
// TODO do I really need this??? To avoid compiler warnings about @API annotations in JUnit code
|
||||
testCompileOnly 'org.apiguardian:apiguardian-api'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
testImplementation 'org.testcontainers:pulsar'
|
||||
// TODO why do I care about these?
|
||||
testRuntimeOnly 'org.apache.logging.log4j:log4j-core'
|
||||
testRuntimeOnly 'org.apache.logging.log4j:log4j-jcl'
|
||||
}
|
||||
@@ -93,4 +93,5 @@ public class PulsarAutoConfiguration {
|
||||
public PulsarConsumerFactory<?> pulsarConsumerFactory(PulsarClient pulsarClient) {
|
||||
return new DefaultPulsarConsumerFactory<>(pulsarClient, this.properties.buildConsumerProperties());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -328,11 +328,11 @@ public class PulsarProperties {
|
||||
map.from(this::getTopicsPattern).as(Pattern::compile).to(properties.in("topicsPattern"));
|
||||
map.from(this::getSubscriptionName).to(properties.in("subscriptionName"));
|
||||
map.from(this::getSubscriptionType).to(properties.in("subscriptionType"));
|
||||
map.from(this::getReceiverQueueSize)
|
||||
.to(properties.in("receiverQueueSize"));
|
||||
map.from(this::getReceiverQueueSize).to(properties.in("receiverQueueSize"));
|
||||
map.from(this::getAcknowledgementsGroupTimeMicros).to(properties.in("acknowledgementsGroupTimeMicros"));
|
||||
map.from(this::getNegativeAckRedeliveryDelayMicros).to(properties.in("negativeAckRedeliveryDelayMicros"));
|
||||
map.from(this::getMaxTotalReceiverQueueSizeAcrossPartitions).to(properties.in("maxTotalReceiverQueueSizeAcrossPartitions"));
|
||||
map.from(this::getMaxTotalReceiverQueueSizeAcrossPartitions)
|
||||
.to(properties.in("maxTotalReceiverQueueSizeAcrossPartitions"));
|
||||
map.from(this::getConsumerName).to(properties.in("consumerName"));
|
||||
map.from(this::getAckTimeoutMillis).to(properties.in("ackTimeoutMillis"));
|
||||
map.from(this::getTickDurationMillis).to(properties.in("tickDurationMillis"));
|
||||
@@ -345,9 +345,11 @@ public class PulsarProperties {
|
||||
map.from(this::getRegexSubscriptionMode).to(properties.in("regexSubscriptionMode"));
|
||||
map.from(this::isAutoUpdatePartitions).to(properties.in("autoUpdatePartitions"));
|
||||
map.from(this::isReplicateSubscriptionState).to(properties.in("replicateSubscriptionState"));
|
||||
map.from(this::isAutoAckOldestChunkedMessageOnQueueFull).to(properties.in("autoAckOldestChunkedMessageOnQueueFull"));
|
||||
map.from(this::isAutoAckOldestChunkedMessageOnQueueFull)
|
||||
.to(properties.in("autoAckOldestChunkedMessageOnQueueFull"));
|
||||
map.from(this::getMaxPendingChunkedMessage).to(properties.in("maxPendingChunkedMessage"));
|
||||
map.from(this::getExpireTimeOfIncompleteChunkedMessageMillis).to(properties.in("expireTimeOfIncompleteChunkedMessageMillis"));
|
||||
map.from(this::getExpireTimeOfIncompleteChunkedMessageMillis)
|
||||
.to(properties.in("expireTimeOfIncompleteChunkedMessageMillis"));
|
||||
return properties;
|
||||
}
|
||||
|
||||
@@ -531,7 +533,8 @@ public class PulsarProperties {
|
||||
map.from(this::getSendTimeoutMs).to(properties.in("sendTimeoutMs"));
|
||||
map.from(this::isBlockIfQueueFull).to(properties.in("blockIfQueueFull"));
|
||||
map.from(this::getMaxPendingMessages).to(properties.in("maxPendingMessages"));
|
||||
map.from(this::getMaxPendingMessagesAcrossPartitions).to(properties.in("maxPendingMessagesAcrossPartitions"));
|
||||
map.from(this::getMaxPendingMessagesAcrossPartitions)
|
||||
.to(properties.in("maxPendingMessagesAcrossPartitions"));
|
||||
map.from(this::getMessageRoutingMode).to(properties.in("messageRoutingMode"));
|
||||
map.from(this::getHashingScheme).to(properties.in("hashingScheme"));
|
||||
map.from(this::getCryptoFailureAction).to(properties.in("cryptoFailureAction"));
|
||||
@@ -582,6 +585,7 @@ public class PulsarProperties {
|
||||
public void setInitialCapacity(Integer initialCapacity) {
|
||||
this.initialCapacity = initialCapacity;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Client {
|
||||
@@ -794,7 +798,8 @@ public class PulsarProperties {
|
||||
map.from(this::isTlsHostnameVerificationEnable).to(properties.in("tlsHostnameVerificationEnable"));
|
||||
map.from(this::getConcurrentLookupRequest).to(properties.in("concurrentLookupRequest"));
|
||||
map.from(this::getMaxLookupRequest).to(properties.in("maxLookupRequest"));
|
||||
map.from(this::getMaxNumberOfRejectedRequestPerConnection).to(properties.in("maxNumberOfRejectedRequestPerConnection"));
|
||||
map.from(this::getMaxNumberOfRejectedRequestPerConnection)
|
||||
.to(properties.in("maxNumberOfRejectedRequestPerConnection"));
|
||||
map.from(this::getKeepAliveIntervalSeconds).to(properties.in("keepAliveIntervalSeconds"));
|
||||
map.from(this::getConnectionTimeoutMs).to(properties.in("connectionTimeoutMs"));
|
||||
map.from(this::getRequestTimeoutMs).to(properties.in("requestTimeoutMs"));
|
||||
@@ -803,6 +808,7 @@ public class PulsarProperties {
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Listener {
|
||||
@@ -826,6 +832,7 @@ public class PulsarProperties {
|
||||
public void setSchemaType(SchemaType schemaType) {
|
||||
this.schemaType = schemaType;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@@ -834,5 +841,7 @@ public class PulsarProperties {
|
||||
<V> java.util.function.Consumer<V> in(String key) {
|
||||
return (value) -> put(key, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -57,8 +57,7 @@ class PulsarAutoConfigurationTests {
|
||||
@Test
|
||||
void autoConfigurationSkippedWhenPulsarTemplateNotOnClasspath() {
|
||||
this.contextRunner.withClassLoader(new FilteredClassLoader(PulsarTemplate.class))
|
||||
.run((context) -> assertThat(context).hasNotFailed()
|
||||
.doesNotHaveBean(PulsarAutoConfiguration.class));
|
||||
.run((context) -> assertThat(context).hasNotFailed().doesNotHaveBean(PulsarAutoConfiguration.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -71,8 +70,8 @@ class PulsarAutoConfigurationTests {
|
||||
@Test
|
||||
void bootstrapConfigurationSkippedWhenCustomPulsarListenerAnnotationProcessorDefined() {
|
||||
this.contextRunner
|
||||
.withBean("org.springframework.pulsar.config.internalPulsarListenerAnnotationProcessor",
|
||||
String.class, () -> "someFauxBean")
|
||||
.withBean("org.springframework.pulsar.config.internalPulsarListenerAnnotationProcessor", String.class,
|
||||
() -> "someFauxBean")
|
||||
.run((context) -> assertThat(context).hasNotFailed()
|
||||
.doesNotHaveBean(PulsarBootstrapConfiguration.class));
|
||||
}
|
||||
@@ -80,28 +79,27 @@ class PulsarAutoConfigurationTests {
|
||||
@Test
|
||||
void defaultBeansAreAutoConfigured() {
|
||||
this.contextRunner.run((context) -> assertThat(context).hasNotFailed()
|
||||
.hasSingleBean(PulsarClientConfiguration.class)
|
||||
.hasSingleBean(PulsarClientFactoryBean.class)
|
||||
.hasSingleBean(PulsarProducerFactory.class)
|
||||
.hasSingleBean(PulsarTemplate.class)
|
||||
.hasSingleBean(PulsarConsumerFactory.class)
|
||||
.hasSingleBean(DefaultPulsarListenerContainerFactory.class)
|
||||
.hasSingleBean(PulsarClientConfiguration.class).hasSingleBean(PulsarClientFactoryBean.class)
|
||||
.hasSingleBean(PulsarProducerFactory.class).hasSingleBean(PulsarTemplate.class)
|
||||
.hasSingleBean(PulsarConsumerFactory.class).hasSingleBean(DefaultPulsarListenerContainerFactory.class)
|
||||
.hasSingleBean(PulsarListenerAnnotationBeanPostProcessor.class)
|
||||
.hasSingleBean(PulsarListenerEndpointRegistry.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void customPulsarClientConfigurationIsRespected() {
|
||||
PulsarClientConfiguration clientConfig = new PulsarClientConfiguration(new PulsarProperties().buildClientProperties());
|
||||
PulsarClientConfiguration clientConfig = new PulsarClientConfiguration(
|
||||
new PulsarProperties().buildClientProperties());
|
||||
this.contextRunner
|
||||
.withBean("customPulsarClientConfiguration", PulsarClientConfiguration.class, () -> clientConfig)
|
||||
.run((context) -> assertThat(context).hasNotFailed()
|
||||
.getBean(PulsarClientConfiguration.class).isSameAs(clientConfig));
|
||||
.run((context) -> assertThat(context).hasNotFailed().getBean(PulsarClientConfiguration.class)
|
||||
.isSameAs(clientConfig));
|
||||
}
|
||||
|
||||
@Test
|
||||
void customPulsarClientFactoryBeanIsRespected() {
|
||||
PulsarClientConfiguration clientConfig = new PulsarClientConfiguration(new PulsarProperties().buildClientProperties());
|
||||
PulsarClientConfiguration clientConfig = new PulsarClientConfiguration(
|
||||
new PulsarProperties().buildClientProperties());
|
||||
PulsarClientFactoryBean clientFactoryBean = new PulsarClientFactoryBean(clientConfig);
|
||||
this.contextRunner
|
||||
.withBean("customPulsarClientFactoryBean", PulsarClientFactoryBean.class, () -> clientFactoryBean)
|
||||
@@ -113,47 +111,47 @@ class PulsarAutoConfigurationTests {
|
||||
@Test
|
||||
void customPulsarProducerFactoryIsRespected() {
|
||||
PulsarProducerFactory<String> producerFactory = mock(PulsarProducerFactory.class);
|
||||
this.contextRunner
|
||||
.withBean("customPulsarProducerFactory", PulsarProducerFactory.class, () -> producerFactory)
|
||||
.run((context) -> assertThat(context).hasNotFailed()
|
||||
.getBean(PulsarProducerFactory.class).isSameAs(producerFactory));
|
||||
this.contextRunner.withBean("customPulsarProducerFactory", PulsarProducerFactory.class, () -> producerFactory)
|
||||
.run((context) -> assertThat(context).hasNotFailed().getBean(PulsarProducerFactory.class)
|
||||
.isSameAs(producerFactory));
|
||||
}
|
||||
|
||||
@Test
|
||||
void customPulsarTemplateIsRespected() {
|
||||
PulsarTemplate<String> template = mock(PulsarTemplate.class);
|
||||
this.contextRunner
|
||||
.withBean("customPulsarTemplate", PulsarTemplate.class, () -> template)
|
||||
.run((context) -> assertThat(context).hasNotFailed()
|
||||
.getBean(PulsarTemplate.class).isSameAs(template));
|
||||
this.contextRunner.withBean("customPulsarTemplate", PulsarTemplate.class, () -> template)
|
||||
.run((context) -> assertThat(context).hasNotFailed().getBean(PulsarTemplate.class).isSameAs(template));
|
||||
}
|
||||
|
||||
@Test
|
||||
void customPulsarConsumerFactoryIsRespected() {
|
||||
PulsarConsumerFactory<String> consumerFactory = mock(PulsarConsumerFactory.class);
|
||||
this.contextRunner
|
||||
.withBean("customPulsarConsumerFactory", PulsarConsumerFactory.class, () -> consumerFactory)
|
||||
.run((context) -> assertThat(context).hasNotFailed()
|
||||
.getBean(PulsarConsumerFactory.class).isSameAs(consumerFactory));
|
||||
this.contextRunner.withBean("customPulsarConsumerFactory", PulsarConsumerFactory.class, () -> consumerFactory)
|
||||
.run((context) -> assertThat(context).hasNotFailed().getBean(PulsarConsumerFactory.class)
|
||||
.isSameAs(consumerFactory));
|
||||
}
|
||||
|
||||
@Test
|
||||
void customPulsarListenerContainerFactoryIsRespected() {
|
||||
PulsarListenerContainerFactory<DefaultPulsarMessageListenerContainer<String>> listenerContainerFactory = mock(PulsarListenerContainerFactory.class);
|
||||
PulsarListenerContainerFactory<DefaultPulsarMessageListenerContainer<String>> listenerContainerFactory = mock(
|
||||
PulsarListenerContainerFactory.class);
|
||||
this.contextRunner
|
||||
.withBean("pulsarListenerContainerFactory", PulsarListenerContainerFactory.class, () -> listenerContainerFactory)
|
||||
.run((context) -> assertThat(context).hasNotFailed()
|
||||
.getBean(PulsarListenerContainerFactory.class).isSameAs(listenerContainerFactory));
|
||||
.withBean("pulsarListenerContainerFactory", PulsarListenerContainerFactory.class,
|
||||
() -> listenerContainerFactory)
|
||||
.run((context) -> assertThat(context).hasNotFailed().getBean(PulsarListenerContainerFactory.class)
|
||||
.isSameAs(listenerContainerFactory));
|
||||
}
|
||||
|
||||
@Test
|
||||
void customPulsarListenerAnnotationBeanPostProcessorIsRespected() {
|
||||
PulsarListenerAnnotationBeanPostProcessor<String, String> listenerAnnotationBeanPostProcessor = mock(PulsarListenerAnnotationBeanPostProcessor.class);
|
||||
PulsarListenerAnnotationBeanPostProcessor<String, String> listenerAnnotationBeanPostProcessor = mock(
|
||||
PulsarListenerAnnotationBeanPostProcessor.class);
|
||||
this.contextRunner
|
||||
.withBean("org.springframework.pulsar.config.internalPulsarListenerAnnotationProcessor",
|
||||
PulsarListenerAnnotationBeanPostProcessor.class, () -> listenerAnnotationBeanPostProcessor)
|
||||
.run((context) -> assertThat(context).hasNotFailed()
|
||||
.getBean(PulsarListenerAnnotationBeanPostProcessor.class).isSameAs(listenerAnnotationBeanPostProcessor));
|
||||
.getBean(PulsarListenerAnnotationBeanPostProcessor.class)
|
||||
.isSameAs(listenerAnnotationBeanPostProcessor));
|
||||
}
|
||||
|
||||
@Nested
|
||||
@@ -178,24 +176,22 @@ class PulsarAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
void cachingProducerFactoryCanBeConfigured() {
|
||||
contextRunner.withPropertyValues(
|
||||
"spring.pulsar.producer.cache.expire-after-access=100s",
|
||||
contextRunner
|
||||
.withPropertyValues("spring.pulsar.producer.cache.expire-after-access=100s",
|
||||
"spring.pulsar.producer.cache.maximum-size=5150",
|
||||
"spring.pulsar.producer.cache.initial-capacity=200")
|
||||
.run((context -> assertThat(context)
|
||||
.hasNotFailed()
|
||||
.getBean(PulsarProducerFactory.class)
|
||||
.extracting("producerCache")
|
||||
.extracting("cache")
|
||||
.run((context -> assertThat(context).hasNotFailed().getBean(PulsarProducerFactory.class)
|
||||
.extracting("producerCache").extracting("cache")
|
||||
.hasFieldOrPropertyWithValue("maximum", 5150L)
|
||||
.hasFieldOrPropertyWithValue("expiresAfterAccessNanos", TimeUnit.SECONDS.toNanos(100))));
|
||||
}
|
||||
|
||||
private void assertHasProducerFactoryOfType(Class<?> producerFactoryType, AssertableApplicationContext context) {
|
||||
assertThat(context).hasNotFailed()
|
||||
.hasSingleBean(PulsarProducerFactory.class).getBean(PulsarProducerFactory.class)
|
||||
.isExactlyInstanceOf(producerFactoryType);
|
||||
private void assertHasProducerFactoryOfType(Class<?> producerFactoryType,
|
||||
AssertableApplicationContext context) {
|
||||
assertThat(context).hasNotFailed().hasSingleBean(PulsarProducerFactory.class)
|
||||
.getBean(PulsarProducerFactory.class).isExactlyInstanceOf(producerFactoryType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -48,7 +48,8 @@ class PulsarListenerTests extends AbstractContainerBaseTests {
|
||||
SpringApplication app = new SpringApplication(BasicListenerConfig.class);
|
||||
app.setWebApplicationType(WebApplicationType.NONE);
|
||||
|
||||
try (ConfigurableApplicationContext context = app.run("--spring.pulsar.client.serviceUrl=" + AbstractContainerBaseTests.getPulsarBrokerUrl())) {
|
||||
try (ConfigurableApplicationContext context = app
|
||||
.run("--spring.pulsar.client.serviceUrl=" + AbstractContainerBaseTests.getPulsarBrokerUrl())) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final PulsarTemplate<String> pulsarTemplate = context.getBean(PulsarTemplate.class);
|
||||
pulsarTemplate.send("hello-pulsar-exclusive", "John Doe");
|
||||
@@ -62,7 +63,8 @@ class PulsarListenerTests extends AbstractContainerBaseTests {
|
||||
SpringApplication app = new SpringApplication(BatchListenerConfig.class);
|
||||
app.setWebApplicationType(WebApplicationType.NONE);
|
||||
|
||||
try (ConfigurableApplicationContext context = app.run("--spring.pulsar.client.serviceUrl=" + AbstractContainerBaseTests.getPulsarBrokerUrl())) {
|
||||
try (ConfigurableApplicationContext context = app
|
||||
.run("--spring.pulsar.client.serviceUrl=" + AbstractContainerBaseTests.getPulsarBrokerUrl())) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final PulsarTemplate<String> pulsarTemplate = context.getBean(PulsarTemplate.class);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
@@ -81,6 +83,7 @@ class PulsarListenerTests extends AbstractContainerBaseTests {
|
||||
public void listen(String foo) {
|
||||
latch1.countDown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -91,5 +94,7 @@ class PulsarListenerTests extends AbstractContainerBaseTests {
|
||||
public void listen(List<String> foo) {
|
||||
foo.forEach(t -> latch2.countDown());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
50
spring-pulsar/build.gradle
Normal file
50
spring-pulsar/build.gradle
Normal file
@@ -0,0 +1,50 @@
|
||||
plugins {
|
||||
id 'org.springframework.pulsar.spring-module'
|
||||
}
|
||||
|
||||
description = 'Spring Pulsar Support'
|
||||
|
||||
dependencies {
|
||||
api 'org.springframework:spring-context'
|
||||
api 'org.springframework:spring-messaging'
|
||||
api 'org.springframework:spring-tx'
|
||||
api ('org.springframework.retry:spring-retry') {
|
||||
exclude group: 'org.springframework'
|
||||
}
|
||||
api 'org.apache.pulsar:pulsar-client'
|
||||
api 'org.apache.pulsar:pulsar-client-admin'
|
||||
api 'org.apache.pulsar:pulsar-client-admin-api'
|
||||
api 'com.github.ben-manes.caffeine:caffeine'
|
||||
|
||||
optional 'com.fasterxml.jackson.core:jackson-core'
|
||||
optional 'com.fasterxml.jackson.core:jackson-databind'
|
||||
optional 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8'
|
||||
optional 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
|
||||
optional 'com.fasterxml.jackson.datatype:jackson-datatype-joda'
|
||||
optional 'com.jayway.jsonpath:json-path'
|
||||
optional 'io.projectreactor:reactor-core'
|
||||
|
||||
testImplementation 'io.projectreactor:reactor-test'
|
||||
testImplementation 'org.mockito:mockito-junit-jupiter'
|
||||
testImplementation 'org.hibernate.validator:hibernate-validator'
|
||||
|
||||
// TODO remove unused dependencies
|
||||
|
||||
// Common to all ??
|
||||
implementation 'com.google.code.findbugs:jsr305'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-params'
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
// To avoid compiler warnings about @API annotations in JUnit code
|
||||
testCompileOnly 'org.apiguardian:apiguardian-api'
|
||||
testRuntimeOnly 'org.apache.logging.log4j:log4j-core'
|
||||
testRuntimeOnly 'org.apache.logging.log4j:log4j-jcl'
|
||||
testImplementation("org.awaitility:awaitility") {
|
||||
exclude group: 'org.hamcrest'
|
||||
}
|
||||
testImplementation 'org.hamcrest:hamcrest-core'
|
||||
testImplementation 'org.assertj:assertj-core'
|
||||
testImplementation 'org.testcontainers:pulsar'
|
||||
testImplementation 'org.springframework:spring-test'
|
||||
}
|
||||
@@ -33,4 +33,5 @@ public class PulsarException extends NestedRuntimeException {
|
||||
public PulsarException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ import java.lang.annotation.Target;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Enables detection of {@link PulsarListener} annotations on any Spring-managed bean in the container.
|
||||
* Enables detection of {@link PulsarListener} annotations on any Spring-managed bean in
|
||||
* the container.
|
||||
*
|
||||
* @author Soby Chacko
|
||||
* @author Chris Bono
|
||||
@@ -35,4 +36,5 @@ import org.springframework.context.annotation.Import;
|
||||
@Documented
|
||||
@Import(PulsarListenerConfigurationSelector.class)
|
||||
public @interface EnablePulsar {
|
||||
|
||||
}
|
||||
|
||||
@@ -24,16 +24,17 @@ import org.springframework.pulsar.config.PulsarListenerBeanNames;
|
||||
import org.springframework.pulsar.config.PulsarListenerEndpointRegistry;
|
||||
|
||||
/**
|
||||
* An {@link ImportBeanDefinitionRegistrar} class that registers a {@link PulsarListenerAnnotationBeanPostProcessor}
|
||||
* bean capable of processing Spring's @{@link PulsarListener} annotation. Also register
|
||||
* a default {@link PulsarListenerEndpointRegistry}.
|
||||
* An {@link ImportBeanDefinitionRegistrar} class that registers a
|
||||
* {@link PulsarListenerAnnotationBeanPostProcessor} bean capable of processing
|
||||
* Spring's @{@link PulsarListener} annotation. Also register a default
|
||||
* {@link PulsarListenerEndpointRegistry}.
|
||||
*
|
||||
* <p>This configuration class is automatically imported when using the @{@link EnablePulsar}
|
||||
* <p>
|
||||
* This configuration class is automatically imported when using the @{@link EnablePulsar}
|
||||
* annotation.
|
||||
*
|
||||
* @author Soby Chacko
|
||||
* @author Chris Bono
|
||||
*
|
||||
* @see PulsarListenerAnnotationBeanPostProcessor
|
||||
* @see PulsarListenerEndpointRegistry
|
||||
* @see EnablePulsar
|
||||
|
||||
@@ -33,10 +33,10 @@ import org.springframework.pulsar.config.PulsarListenerEndpointRegistry;
|
||||
* specified topics.
|
||||
*
|
||||
* The {@link #containerFactory()} identifies the
|
||||
* {@link org.springframework.pulsar.config.PulsarListenerContainerFactory} to use to build the Pulsar listener container.
|
||||
* If not set, a <em>default</em> container factory is assumed to be available with a bean name
|
||||
* of {@code pulsarListenerContainerFactory} unless an explicit default has been provided
|
||||
* through configuration.
|
||||
* {@link org.springframework.pulsar.config.PulsarListenerContainerFactory} to use to
|
||||
* build the Pulsar listener container. If not set, a <em>default</em> container factory
|
||||
* is assumed to be available with a bean name of {@code pulsarListenerContainerFactory}
|
||||
* unless an explicit default has been provided through configuration.
|
||||
*
|
||||
* <p>
|
||||
* Processing of {@code @PulsarListener} annotations is performed by registering a
|
||||
@@ -55,8 +55,10 @@ public @interface PulsarListener {
|
||||
|
||||
/**
|
||||
* The unique identifier of the container for this listener.
|
||||
* <p>If none is specified an auto-generated id is used.
|
||||
* <p>SpEL {@code #{...}} and property place holders {@code ${...}} are supported.
|
||||
* <p>
|
||||
* If none is specified an auto-generated id is used.
|
||||
* <p>
|
||||
* SpEL {@code #{...}} and property place holders {@code ${...}} are supported.
|
||||
* @return the {@code id} for the container managing for this endpoint.
|
||||
* @see PulsarListenerEndpointRegistry#getListenerContainer(String)
|
||||
*/
|
||||
@@ -77,8 +79,8 @@ public @interface PulsarListener {
|
||||
SchemaType schemaType() default SchemaType.NONE;
|
||||
|
||||
/**
|
||||
* The bean name of the {@link PulsarListenerContainerFactory}
|
||||
* to use to create the message listener container responsible to serve this endpoint.
|
||||
* The bean name of the {@link PulsarListenerContainerFactory} to use to create the
|
||||
* message listener container responsible to serve this endpoint.
|
||||
* <p>
|
||||
* If not specified, the default container factory is used, if any. If a SpEL
|
||||
* expression is provided ({@code #{...}}), the expression can either evaluate to a
|
||||
@@ -89,14 +91,12 @@ public @interface PulsarListener {
|
||||
|
||||
/**
|
||||
* Topics to listen to.
|
||||
*
|
||||
* @return a comma separated list of topics to listen from.
|
||||
*/
|
||||
String[] topics() default {};
|
||||
|
||||
/**
|
||||
* Topic patten to listen to.
|
||||
*
|
||||
* @return topic pattern to listen to.
|
||||
*/
|
||||
String topicPattern() default "";
|
||||
@@ -106,23 +106,22 @@ public @interface PulsarListener {
|
||||
* be a property placeholder or SpEL expression that evaluates to a {@link Boolean} or
|
||||
* a {@link String}, in which case the {@link Boolean#parseBoolean(String)} is used to
|
||||
* obtain the value.
|
||||
* <p>SpEL {@code #{...}} and property place holders {@code ${...}} are supported.
|
||||
* <p>
|
||||
* SpEL {@code #{...}} and property place holders {@code ${...}} are supported.
|
||||
* @return true to auto start, false to not auto start.
|
||||
*/
|
||||
String autoStartup() default "";
|
||||
|
||||
/**
|
||||
* Activate batch consumption.
|
||||
*
|
||||
* @return whether this listener is in batch mode or not.
|
||||
*/
|
||||
boolean batch() default false;
|
||||
|
||||
/**
|
||||
* A pseudo bean name used in SpEL expressions within this annotation to reference
|
||||
* the current bean within which this listener is defined. This allows access to
|
||||
* properties and methods within the enclosing bean.
|
||||
* Default '__listener'.
|
||||
* A pseudo bean name used in SpEL expressions within this annotation to reference the
|
||||
* current bean within which this listener is defined. This allows access to
|
||||
* properties and methods within the enclosing bean. Default '__listener'.
|
||||
* <p>
|
||||
* @return the pseudo bean name.
|
||||
*/
|
||||
@@ -130,23 +129,27 @@ public @interface PulsarListener {
|
||||
|
||||
/**
|
||||
* Pulsar consumer properties; they will supersede any properties with the same name
|
||||
* defined in the consumer factory (if the consumer factory supports property overrides).
|
||||
* defined in the consumer factory (if the consumer factory supports property
|
||||
* overrides).
|
||||
* <p>
|
||||
* <b>Supported Syntax</b>
|
||||
* <p>The supported syntax for key-value pairs is the same as the
|
||||
* syntax defined for entries in a Java
|
||||
* {@linkplain java.util.Properties#load(java.io.Reader) properties file}:
|
||||
* <p>
|
||||
* The supported syntax for key-value pairs is the same as the syntax defined for
|
||||
* entries in a Java {@linkplain java.util.Properties#load(java.io.Reader) properties
|
||||
* file}:
|
||||
* <ul>
|
||||
* <li>{@code key=value}</li>
|
||||
* <li>{@code key:value}</li>
|
||||
* <li>{@code key value}</li>
|
||||
* </ul>
|
||||
* {@code group.id} and {@code client.id} are ignored.
|
||||
* <p>SpEL {@code #{...}} and property place holders {@code ${...}} are supported.
|
||||
* SpEL expressions must resolve to a {@link String}, a @{link String[]} or a
|
||||
* <p>
|
||||
* SpEL {@code #{...}} and property place holders {@code ${...}} are supported. SpEL
|
||||
* expressions must resolve to a {@link String}, a @{link String[]} or a
|
||||
* {@code Collection<String>} where each member of the array or collection is a
|
||||
* property name + value with the above formats.
|
||||
* @return the properties.
|
||||
*/
|
||||
String[] properties() default {};
|
||||
|
||||
}
|
||||
|
||||
@@ -89,27 +89,26 @@ import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
/**
|
||||
* Bean post-processor that registers methods annotated with {@link PulsarListener}
|
||||
* to be invoked by a Pulsar message listener container created under the covers
|
||||
* by a {@link PulsarListenerContainerFactory}
|
||||
* according to the parameters of the annotation.
|
||||
* Bean post-processor that registers methods annotated with {@link PulsarListener} to be
|
||||
* invoked by a Pulsar message listener container created under the covers by a
|
||||
* {@link PulsarListenerContainerFactory} according to the parameters of the annotation.
|
||||
*
|
||||
* <p>Annotated methods can use flexible arguments as defined by {@link PulsarListener}.
|
||||
* <p>
|
||||
* Annotated methods can use flexible arguments as defined by {@link PulsarListener}.
|
||||
*
|
||||
* <p>This post-processor is automatically registered by the {@link EnablePulsar}
|
||||
* annotation.
|
||||
* <p>
|
||||
* This post-processor is automatically registered by the {@link EnablePulsar} annotation.
|
||||
*
|
||||
* <p>Auto-detect any {@link PulsarListenerConfigurer} instances in the container,
|
||||
* allowing for customization of the registry to be used, the default container
|
||||
* factory or for fine-grained control over endpoints registration. See
|
||||
* {@link EnablePulsar} Javadoc for complete usage details.
|
||||
* <p>
|
||||
* Auto-detect any {@link PulsarListenerConfigurer} instances in the container, allowing
|
||||
* for customization of the registry to be used, the default container factory or for
|
||||
* fine-grained control over endpoints registration. See {@link EnablePulsar} Javadoc for
|
||||
* complete usage details.
|
||||
*
|
||||
* @param <K> the key type.
|
||||
* @param <V> the value type.
|
||||
*
|
||||
* @author Soby Chacko
|
||||
* @author Chris Bono
|
||||
*
|
||||
* @see PulsarListener
|
||||
* @see EnablePulsar
|
||||
* @see PulsarListenerConfigurer
|
||||
@@ -118,12 +117,14 @@ import org.springframework.validation.Validator;
|
||||
* @see PulsarListenerEndpoint
|
||||
* @see MethodPulsarListenerEndpoint
|
||||
*/
|
||||
public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPostProcessor, Ordered, ApplicationContextAware, InitializingBean, SmartInitializingSingleton {
|
||||
public class PulsarListenerAnnotationBeanPostProcessor<K, V>
|
||||
implements BeanPostProcessor, Ordered, ApplicationContextAware, InitializingBean, SmartInitializingSingleton {
|
||||
|
||||
private final LogAccessor logger = new LogAccessor(LogFactory.getLog(getClass()));
|
||||
|
||||
/**
|
||||
* The bean name of the default {@link org.springframework.pulsar.config.PulsarListenerContainerFactory}.
|
||||
* The bean name of the default
|
||||
* {@link org.springframework.pulsar.config.PulsarListenerContainerFactory}.
|
||||
*/
|
||||
public static final String DEFAULT_PULSAR_LISTENER_CONTAINER_FACTORY_BEAN_NAME = "pulsarListenerContainerFactory";
|
||||
|
||||
@@ -157,12 +158,10 @@ public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPost
|
||||
|
||||
private final ListenerScope listenerScope = new ListenerScope();
|
||||
|
||||
|
||||
private AnnotationEnhancer enhancer;
|
||||
|
||||
private final AtomicInteger counter = new AtomicInteger();
|
||||
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return LOWEST_PRECEDENCE;
|
||||
@@ -189,9 +188,7 @@ public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPost
|
||||
private void buildEnhancer() {
|
||||
if (this.applicationContext != null) {
|
||||
List<AnnotationEnhancer> enhancers = this.applicationContext
|
||||
.getBeanProvider(AnnotationEnhancer.class, false)
|
||||
.orderedStream()
|
||||
.toList();
|
||||
.getBeanProvider(AnnotationEnhancer.class, false).orderedStream().toList();
|
||||
if (!enhancers.isEmpty()) {
|
||||
this.enhancer = (attrs, element) -> {
|
||||
for (AnnotationEnhancer enh : enhancers) {
|
||||
@@ -225,7 +222,8 @@ public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPost
|
||||
this.registrar.setContainerFactoryBeanName(this.defaultContainerFactoryBeanName);
|
||||
}
|
||||
|
||||
// Set the custom handler method factory once resolved by the configurer - otherwise register default formatters
|
||||
// Set the custom handler method factory once resolved by the configurer -
|
||||
// otherwise register default formatters
|
||||
MessageHandlerMethodFactory handlerMethodFactory = this.registrar.getMessageHandlerMethodFactory();
|
||||
if (handlerMethodFactory != null) {
|
||||
this.messageHandlerMethodFactory.setHandlerMethodFactory(handlerMethodFactory);
|
||||
@@ -283,8 +281,8 @@ public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPost
|
||||
this.listenerScope.removeListener(beanRef);
|
||||
}
|
||||
|
||||
protected void processListener(MethodPulsarListenerEndpoint<?> endpoint, PulsarListener PulsarListener,
|
||||
Object bean, String beanName, String[] topics) {
|
||||
protected void processListener(MethodPulsarListenerEndpoint<?> endpoint, PulsarListener PulsarListener, Object bean,
|
||||
String beanName, String[] topics) {
|
||||
|
||||
processPulsarListenerAnnotation(endpoint, PulsarListener, bean, topics);
|
||||
|
||||
@@ -297,7 +295,7 @@ public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPost
|
||||
|
||||
@Nullable
|
||||
private PulsarListenerContainerFactory<?> resolveContainerFactory(PulsarListener PulsarListener,
|
||||
Object factoryTarget, String beanName) {
|
||||
Object factoryTarget, String beanName) {
|
||||
|
||||
String containerFactory = PulsarListener.containerFactory();
|
||||
if (!StringUtils.hasText(containerFactory)) {
|
||||
@@ -310,17 +308,15 @@ public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPost
|
||||
if (resolved instanceof PulsarListenerContainerFactory) {
|
||||
return (PulsarListenerContainerFactory<?>) resolved;
|
||||
}
|
||||
String containerFactoryBeanName = resolveExpressionAsString(containerFactory,
|
||||
"containerFactory");
|
||||
String containerFactoryBeanName = resolveExpressionAsString(containerFactory, "containerFactory");
|
||||
if (StringUtils.hasText(containerFactoryBeanName)) {
|
||||
assertBeanFactory();
|
||||
try {
|
||||
factory = this.beanFactory.getBean(containerFactoryBeanName, PulsarListenerContainerFactory.class);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
throw new BeanInitializationException(
|
||||
noBeanFoundMessage(factoryTarget, beanName, containerFactoryBeanName,
|
||||
PulsarListenerContainerFactory.class), ex);
|
||||
throw new BeanInitializationException(noBeanFoundMessage(factoryTarget, beanName,
|
||||
containerFactoryBeanName, PulsarListenerContainerFactory.class), ex);
|
||||
}
|
||||
}
|
||||
return factory;
|
||||
@@ -331,15 +327,15 @@ public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPost
|
||||
}
|
||||
|
||||
protected String noBeanFoundMessage(Object target, String listenerBeanName, String requestedBeanName,
|
||||
Class<?> expectedClass) {
|
||||
Class<?> expectedClass) {
|
||||
|
||||
return "Could not register Pulsar listener endpoint on ["
|
||||
+ target + "] for bean " + listenerBeanName + ", no '" + expectedClass.getSimpleName() + "' with id '"
|
||||
+ requestedBeanName + "' was found in the application context";
|
||||
return "Could not register Pulsar listener endpoint on [" + target + "] for bean " + listenerBeanName + ", no '"
|
||||
+ expectedClass.getSimpleName() + "' with id '" + requestedBeanName
|
||||
+ "' was found in the application context";
|
||||
}
|
||||
|
||||
private void processPulsarListenerAnnotation(MethodPulsarListenerEndpoint<?> endpoint,
|
||||
PulsarListener pulsarListener, Object bean, String[] topics) {
|
||||
PulsarListener pulsarListener, Object bean, String[] topics) {
|
||||
|
||||
endpoint.setBean(bean);
|
||||
endpoint.setMessageHandlerMethodFactory(this.messageHandlerMethodFactory);
|
||||
@@ -397,8 +393,8 @@ public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPost
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("'properties' must resolve to a String, a String[] or "
|
||||
+ "Collection<String>");
|
||||
throw new IllegalStateException(
|
||||
"'properties' must resolve to a String, a String[] or " + "Collection<String>");
|
||||
}
|
||||
}
|
||||
endpoint.setConsumerProperties(properties);
|
||||
@@ -452,8 +448,8 @@ public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPost
|
||||
return (String) resolved;
|
||||
}
|
||||
else if (resolved != null) {
|
||||
throw new IllegalStateException(THE_LEFT + attribute + "] must resolve to a String. "
|
||||
+ RESOLVED_TO_LEFT + resolved.getClass() + RIGHT_FOR_LEFT + value + "]");
|
||||
throw new IllegalStateException(THE_LEFT + attribute + "] must resolve to a String. " + RESOLVED_TO_LEFT
|
||||
+ resolved.getClass() + RIGHT_FOR_LEFT + value + "]");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -497,8 +493,8 @@ public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPost
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"@PulsarListener can't resolve '%s' as a String", resolvedValue));
|
||||
throw new IllegalArgumentException(
|
||||
String.format("@PulsarListener can't resolve '%s' as a String", resolvedValue));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,7 +502,8 @@ public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPost
|
||||
Method method = methodArg;
|
||||
if (AopUtils.isJdkDynamicProxy(bean)) {
|
||||
try {
|
||||
// Found a @PulsarListener method on the target class for this JDK proxy ->
|
||||
// Found a @PulsarListener method on the target class for this JDK proxy
|
||||
// ->
|
||||
// is it also present on the proxy itself?
|
||||
method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
|
||||
Class<?>[] proxiedInterfaces = ((Advised) bean).getProxiedInterfaces();
|
||||
@@ -525,12 +522,11 @@ public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPost
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"@PulsarListener method '%s' found on bean target class '%s', " +
|
||||
"but not found in any interface(s) for bean JDK proxy. Either " +
|
||||
"pull the method up to an interface or switch to subclass (CGLIB) " +
|
||||
"proxies by setting proxy-target-class/proxyTargetClass " +
|
||||
"attribute to 'true'", method.getName(),
|
||||
method.getDeclaringClass().getSimpleName()), ex);
|
||||
"@PulsarListener method '%s' found on bean target class '%s', "
|
||||
+ "but not found in any interface(s) for bean JDK proxy. Either "
|
||||
+ "pull the method up to an interface or switch to subclass (CGLIB) "
|
||||
+ "proxies by setting proxy-target-class/proxyTargetClass " + "attribute to 'true'",
|
||||
method.getName(), method.getDeclaringClass().getSimpleName()), ex);
|
||||
}
|
||||
}
|
||||
return method;
|
||||
@@ -545,9 +541,8 @@ public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPost
|
||||
}
|
||||
PulsarListeners anns = AnnotationUtils.findAnnotation(clazz, PulsarListeners.class);
|
||||
if (anns != null) {
|
||||
listeners.addAll(Arrays.stream(anns.value())
|
||||
.map(anno -> enhance(clazz, anno))
|
||||
.collect(Collectors.toList()));
|
||||
listeners
|
||||
.addAll(Arrays.stream(anns.value()).map(anno -> enhance(clazz, anno)).collect(Collectors.toList()));
|
||||
}
|
||||
return listeners;
|
||||
}
|
||||
@@ -561,9 +556,8 @@ public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPost
|
||||
}
|
||||
PulsarListeners anns = AnnotationUtils.findAnnotation(method, PulsarListeners.class);
|
||||
if (anns != null) {
|
||||
listeners.addAll(Arrays.stream(anns.value())
|
||||
.map(anno -> enhance(method, anno))
|
||||
.collect(Collectors.toList()));
|
||||
listeners.addAll(
|
||||
Arrays.stream(anns.value()).map(anno -> enhance(method, anno)).collect(Collectors.toList()));
|
||||
}
|
||||
return listeners;
|
||||
}
|
||||
@@ -574,11 +568,11 @@ public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPost
|
||||
}
|
||||
else {
|
||||
return AnnotationUtils.synthesizeAnnotation(
|
||||
this.enhancer.apply(AnnotationUtils.getAnnotationAttributes(ann), element), PulsarListener.class, null);
|
||||
this.enhancer.apply(AnnotationUtils.getAnnotationAttributes(ann), element), PulsarListener.class,
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void addFormatters(FormatterRegistry registry) {
|
||||
this.beanFactory.getBeanProvider(Converter.class).forEach(registry::addConverter);
|
||||
this.beanFactory.getBeanProvider(GenericConverter.class).forEach(registry::addConverter);
|
||||
@@ -607,8 +601,7 @@ public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPost
|
||||
|
||||
private class PulsarHandlerMethodFactoryAdapter implements MessageHandlerMethodFactory {
|
||||
|
||||
private final DefaultFormattingConversionService defaultFormattingConversionService =
|
||||
new DefaultFormattingConversionService();
|
||||
private final DefaultFormattingConversionService defaultFormattingConversionService = new DefaultFormattingConversionService();
|
||||
|
||||
private MessageHandlerMethodFactory handlerMethodFactory;
|
||||
|
||||
@@ -635,17 +628,19 @@ public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPost
|
||||
defaultFactory.setValidator(validator);
|
||||
}
|
||||
defaultFactory.setBeanFactory(PulsarListenerAnnotationBeanPostProcessor.this.beanFactory);
|
||||
this.defaultFormattingConversionService.addConverter(
|
||||
new BytesToStringConverter(PulsarListenerAnnotationBeanPostProcessor.this.charset));
|
||||
this.defaultFormattingConversionService
|
||||
.addConverter(new BytesToStringConverter(PulsarListenerAnnotationBeanPostProcessor.this.charset));
|
||||
this.defaultFormattingConversionService.addConverter(new BytesToNumberConverter());
|
||||
defaultFactory.setConversionService(this.defaultFormattingConversionService);
|
||||
GenericMessageConverter messageConverter = new GenericMessageConverter(this.defaultFormattingConversionService);
|
||||
GenericMessageConverter messageConverter = new GenericMessageConverter(
|
||||
this.defaultFormattingConversionService);
|
||||
defaultFactory.setMessageConverter(messageConverter);
|
||||
|
||||
List<HandlerMethodArgumentResolver> customArgumentsResolver =
|
||||
new ArrayList<>(PulsarListenerAnnotationBeanPostProcessor.this.registrar.getCustomMethodArgumentResolvers());
|
||||
List<HandlerMethodArgumentResolver> customArgumentsResolver = new ArrayList<>(
|
||||
PulsarListenerAnnotationBeanPostProcessor.this.registrar.getCustomMethodArgumentResolvers());
|
||||
// Has to be at the end - look at PayloadMethodArgumentResolver documentation
|
||||
//customArgumentsResolver.add(new PulsarNullAwarePayloadArgumentResolver(messageConverter, validator));
|
||||
// customArgumentsResolver.add(new
|
||||
// PulsarNullAwarePayloadArgumentResolver(messageConverter, validator));
|
||||
defaultFactory.setCustomArgumentResolvers(customArgumentsResolver);
|
||||
|
||||
defaultFactory.afterPropertiesSet();
|
||||
@@ -657,7 +652,6 @@ public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPost
|
||||
|
||||
private static class BytesToStringConverter implements Converter<byte[], String> {
|
||||
|
||||
|
||||
private final Charset charset;
|
||||
|
||||
BytesToStringConverter(Charset charset) {
|
||||
@@ -770,10 +764,8 @@ public class PulsarListenerAnnotationBeanPostProcessor<K, V> implements BeanPost
|
||||
|
||||
}
|
||||
|
||||
|
||||
public interface AnnotationEnhancer extends BiFunction<Map<String, Object>, AnnotatedElement, Map<String, Object>> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -22,26 +22,25 @@ import org.springframework.pulsar.config.PulsarListenerEndpointRegistrar;
|
||||
import org.springframework.pulsar.config.PulsarListenerEndpointRegistry;
|
||||
|
||||
/**
|
||||
* Optional interface to be implemented by Spring managed bean willing to
|
||||
* customize how Pulsar listener endpoints are configured. Typically used
|
||||
* to define the default {@link PulsarListenerContainerFactory} to use or
|
||||
* for registering Pulsar endpoints in a <em>programmatic</em> fashion as
|
||||
* opposed to the <em>declarative</em> approach of using the
|
||||
* {@link PulsarListener} annotation.
|
||||
* Optional interface to be implemented by Spring managed bean willing to customize how
|
||||
* Pulsar listener endpoints are configured. Typically used to define the default
|
||||
* {@link PulsarListenerContainerFactory} to use or for registering Pulsar endpoints in a
|
||||
* <em>programmatic</em> fashion as opposed to the <em>declarative</em> approach of using
|
||||
* the {@link PulsarListener} annotation.
|
||||
*
|
||||
* @author Soby Chacko
|
||||
* @author Chris Bono
|
||||
*
|
||||
* @see PulsarListenerEndpointRegistrar
|
||||
*/
|
||||
public interface PulsarListenerConfigurer {
|
||||
|
||||
/**
|
||||
* Callback allowing a {@link PulsarListenerEndpointRegistry} and specific
|
||||
* {@link PulsarListenerEndpoint} instances to be registered against the
|
||||
* given {@link PulsarListenerEndpointRegistrar}. The default
|
||||
* {@link PulsarListenerEndpoint} instances to be registered against the given
|
||||
* {@link PulsarListenerEndpointRegistrar}. The default
|
||||
* {@link PulsarListenerContainerFactory} can also be customized.
|
||||
* @param registrar the registrar to be configured
|
||||
*/
|
||||
void configurePulsarListeners(PulsarListenerEndpointRegistrar registrar);
|
||||
|
||||
}
|
||||
|
||||
@@ -25,10 +25,10 @@ import java.lang.annotation.Target;
|
||||
/**
|
||||
* Container annotation that aggregates several {@link PulsarListener} annotations.
|
||||
* <p>
|
||||
* Can be used natively, declaring several nested {@link PulsarListener} annotations.
|
||||
* Can also be used in conjunction with Java 8's support for repeatable annotations,
|
||||
* where {@link PulsarListener} can simply be declared several times on the same method
|
||||
* (or class), implicitly generating this container annotation.
|
||||
* Can be used natively, declaring several nested {@link PulsarListener} annotations. Can
|
||||
* also be used in conjunction with Java 8's support for repeatable annotations, where
|
||||
* {@link PulsarListener} can simply be declared several times on the same method (or
|
||||
* class), implicitly generating this container annotation.
|
||||
*
|
||||
* @author Soby Chacko
|
||||
* @author Chris Bono
|
||||
|
||||
@@ -37,14 +37,14 @@ import org.springframework.pulsar.support.MessageConverter;
|
||||
*
|
||||
* @param <C> the {@link AbstractPulsarMessageListenerContainer} implementation type.
|
||||
* @param <T> Message payload type.
|
||||
*
|
||||
* @author Soby Chacko
|
||||
*/
|
||||
public abstract class AbstractPulsarListenerContainerFactory<C extends AbstractPulsarMessageListenerContainer<T>, T>
|
||||
implements PulsarListenerContainerFactory<C>, ApplicationEventPublisherAware, InitializingBean,
|
||||
ApplicationContextAware {
|
||||
|
||||
protected final LogAccessor logger = new LogAccessor(LogFactory.getLog(getClass())); // NOSONAR protected
|
||||
protected final LogAccessor logger = new LogAccessor(LogFactory.getLog(getClass())); // NOSONAR
|
||||
// protected
|
||||
|
||||
private final PulsarContainerProperties containerProperties = new PulsarContainerProperties();
|
||||
|
||||
@@ -67,7 +67,6 @@ public abstract class AbstractPulsarListenerContainerFactory<C extends AbstractP
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
|
||||
public void setPulsarConsumerFactory(PulsarConsumerFactory<? super T> consumerFactory) {
|
||||
this.consumerFactory = consumerFactory;
|
||||
}
|
||||
@@ -76,12 +75,10 @@ public abstract class AbstractPulsarListenerContainerFactory<C extends AbstractP
|
||||
return this.consumerFactory;
|
||||
}
|
||||
|
||||
|
||||
public void setAutoStartup(Boolean autoStartup) {
|
||||
this.autoStartup = autoStartup;
|
||||
}
|
||||
|
||||
|
||||
public void setPhase(int phase) {
|
||||
this.phase = phase;
|
||||
}
|
||||
@@ -94,12 +91,10 @@ public abstract class AbstractPulsarListenerContainerFactory<C extends AbstractP
|
||||
this.messageConverter = messageConverter;
|
||||
}
|
||||
|
||||
|
||||
public Boolean isBatchListener() {
|
||||
return this.batchListener;
|
||||
}
|
||||
|
||||
|
||||
public void setBatchListener(Boolean batchListener) {
|
||||
this.batchListener = batchListener;
|
||||
}
|
||||
@@ -109,7 +104,6 @@ public abstract class AbstractPulsarListenerContainerFactory<C extends AbstractP
|
||||
this.applicationEventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
|
||||
public PulsarContainerProperties getContainerProperties() {
|
||||
return this.containerProperties;
|
||||
}
|
||||
@@ -123,15 +117,14 @@ public abstract class AbstractPulsarListenerContainerFactory<C extends AbstractP
|
||||
@Override
|
||||
public C createListenerContainer(PulsarListenerEndpoint endpoint) {
|
||||
C instance = createContainerInstance(endpoint);
|
||||
JavaUtils.INSTANCE
|
||||
.acceptIfNotNull(endpoint.getSubscriptionName(), instance::setBeanName);
|
||||
JavaUtils.INSTANCE.acceptIfNotNull(endpoint.getSubscriptionName(), instance::setBeanName);
|
||||
if (endpoint instanceof AbstractPulsarListenerEndpoint) {
|
||||
configureEndpoint((AbstractPulsarListenerEndpoint<C>) endpoint);
|
||||
}
|
||||
|
||||
endpoint.setupListenerContainer(instance, this.messageConverter);
|
||||
initializeContainer(instance, endpoint);
|
||||
//customizeContainer(instance);
|
||||
// customizeContainer(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -140,20 +133,19 @@ public abstract class AbstractPulsarListenerContainerFactory<C extends AbstractP
|
||||
private void configureEndpoint(AbstractPulsarListenerEndpoint<C> aplEndpoint) {
|
||||
|
||||
if (aplEndpoint.getBatchListener() == null) {
|
||||
JavaUtils.INSTANCE
|
||||
.acceptIfNotNull(this.batchListener, aplEndpoint::setBatchListener);
|
||||
JavaUtils.INSTANCE.acceptIfNotNull(this.batchListener, aplEndpoint::setBatchListener);
|
||||
}
|
||||
}
|
||||
|
||||
protected void initializeContainer(C instance, PulsarListenerEndpoint endpoint) {
|
||||
PulsarContainerProperties properties = instance.getPulsarContainerProperties();
|
||||
// BeanUtils.copyProperties(this.containerProperties, properties, "topics", "messageListener",
|
||||
// "batchListener", "subscriptionName", "subscriptionType", "schema");
|
||||
// BeanUtils.copyProperties(this.containerProperties, properties, "topics",
|
||||
// "messageListener",
|
||||
// "batchListener", "subscriptionName", "subscriptionType", "schema");
|
||||
if (properties.getSchema() == null) {
|
||||
properties.setSchema(Schema.BYTES);
|
||||
}
|
||||
|
||||
|
||||
Boolean autoStart = endpoint.getAutoStartup();
|
||||
if (autoStart != null) {
|
||||
instance.setAutoStartup(autoStart);
|
||||
@@ -162,8 +154,7 @@ public abstract class AbstractPulsarListenerContainerFactory<C extends AbstractP
|
||||
instance.setAutoStartup(this.autoStartup);
|
||||
}
|
||||
|
||||
JavaUtils.INSTANCE
|
||||
.acceptIfNotNull(this.phase, instance::setPhase)
|
||||
JavaUtils.INSTANCE.acceptIfNotNull(this.phase, instance::setPhase)
|
||||
.acceptIfNotNull(this.applicationContext, instance::setApplicationContext)
|
||||
.acceptIfNotNull(this.applicationEventPublisher, instance::setApplicationEventPublisher);
|
||||
}
|
||||
|
||||
@@ -46,10 +46,10 @@ import org.springframework.util.Assert;
|
||||
* Base implementation for {@link PulsarListenerEndpoint}.
|
||||
*
|
||||
* @param <K> Message payload type.
|
||||
*
|
||||
* @author Soby Chacko
|
||||
*/
|
||||
public abstract class AbstractPulsarListenerEndpoint<K> implements PulsarListenerEndpoint, BeanFactoryAware, InitializingBean {
|
||||
public abstract class AbstractPulsarListenerEndpoint<K>
|
||||
implements PulsarListenerEndpoint, BeanFactoryAware, InitializingBean {
|
||||
|
||||
private final LogAccessor logger = new LogAccessor(LogFactory.getLog(getClass()));
|
||||
|
||||
@@ -72,7 +72,9 @@ public abstract class AbstractPulsarListenerEndpoint<K> implements PulsarListene
|
||||
private BeanResolver beanResolver;
|
||||
|
||||
private Boolean autoStartup;
|
||||
|
||||
private Properties consumerProperties;
|
||||
|
||||
private Boolean batchListener;
|
||||
|
||||
@Override
|
||||
@@ -156,25 +158,24 @@ public abstract class AbstractPulsarListenerEndpoint<K> implements PulsarListene
|
||||
|
||||
@Override
|
||||
public void setupListenerContainer(PulsarMessageListenerContainer listenerContainer,
|
||||
@Nullable MessageConverter messageConverter) {
|
||||
@Nullable MessageConverter messageConverter) {
|
||||
|
||||
setupMessageListener(listenerContainer, messageConverter);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void setupMessageListener(PulsarMessageListenerContainer container,
|
||||
@Nullable MessageConverter messageConverter) {
|
||||
@Nullable MessageConverter messageConverter) {
|
||||
|
||||
PulsarMessagingMessageListenerAdapter<K> adapter = createMessageListener(container, messageConverter);
|
||||
Object messageListener = adapter;
|
||||
boolean isBatchListener = isBatchListener();
|
||||
Assert.state(messageListener != null,
|
||||
() -> "Endpoint [" + this + "] must provide a non null message listener");
|
||||
Assert.state(messageListener != null, () -> "Endpoint [" + this + "] must provide a non null message listener");
|
||||
container.setupMessageListener(messageListener);
|
||||
}
|
||||
|
||||
protected abstract PulsarMessagingMessageListenerAdapter<K> createMessageListener(PulsarMessageListenerContainer container,
|
||||
@Nullable MessageConverter messageConverter);
|
||||
protected abstract PulsarMessagingMessageListenerAdapter<K> createMessageListener(
|
||||
PulsarMessageListenerContainer container, @Nullable MessageConverter messageConverter);
|
||||
|
||||
public void setConsumerProperties(Properties consumerProperties) {
|
||||
this.consumerProperties = consumerProperties;
|
||||
@@ -185,7 +186,6 @@ public abstract class AbstractPulsarListenerEndpoint<K> implements PulsarListene
|
||||
return this.batchListener;
|
||||
}
|
||||
|
||||
|
||||
public void setBatchListener(boolean batchListener) {
|
||||
this.batchListener = batchListener;
|
||||
}
|
||||
@@ -194,7 +194,6 @@ public abstract class AbstractPulsarListenerEndpoint<K> implements PulsarListene
|
||||
return this.batchListener == null ? false : this.batchListener;
|
||||
}
|
||||
|
||||
|
||||
public SubscriptionType getSubscriptionType() {
|
||||
return this.subscriptionType;
|
||||
}
|
||||
@@ -210,4 +209,5 @@ public abstract class AbstractPulsarListenerEndpoint<K> implements PulsarListene
|
||||
public void setSchemaType(SchemaType schemaType) {
|
||||
this.schemaType = schemaType;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @param <C> container implementation type.
|
||||
* @param <T> message type in the listener.
|
||||
*
|
||||
* @author Soby Chacko
|
||||
* @author Chris Bono
|
||||
*/
|
||||
public class DefaultPulsarListenerContainerFactory<C, T> extends AbstractPulsarListenerContainerFactory<DefaultPulsarMessageListenerContainer<T>, T> {
|
||||
public class DefaultPulsarListenerContainerFactory<C, T>
|
||||
extends AbstractPulsarListenerContainerFactory<DefaultPulsarMessageListenerContainer<T>, T> {
|
||||
|
||||
@Override
|
||||
protected DefaultPulsarMessageListenerContainer<T> createContainerInstance(PulsarListenerEndpoint endpoint) {
|
||||
@@ -67,7 +67,7 @@ public class DefaultPulsarListenerContainerFactory<C, T> extends AbstractPulsarL
|
||||
|
||||
@Override
|
||||
protected void initializeContainer(DefaultPulsarMessageListenerContainer<T> instance,
|
||||
PulsarListenerEndpoint endpoint) {
|
||||
PulsarListenerEndpoint endpoint) {
|
||||
|
||||
super.initializeContainer(instance, endpoint);
|
||||
}
|
||||
@@ -84,7 +84,8 @@ public class DefaultPulsarListenerContainerFactory<C, T> extends AbstractPulsarL
|
||||
};
|
||||
DefaultPulsarMessageListenerContainer<T> container = createContainerInstance(endpoint);
|
||||
initializeContainer(container, endpoint);
|
||||
//customizeContainer(container);
|
||||
// customizeContainer(container);
|
||||
return container;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -51,21 +51,20 @@ import org.springframework.pulsar.support.converter.PulsarRecordMessageConverter
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link PulsarListenerEndpoint} providing the method to invoke to process
|
||||
* an incoming message for this endpoint.
|
||||
* A {@link PulsarListenerEndpoint} providing the method to invoke to process an incoming
|
||||
* message for this endpoint.
|
||||
*
|
||||
* @param <V> Message payload type
|
||||
*
|
||||
* @author Soby Chacko
|
||||
*/
|
||||
public class MethodPulsarListenerEndpoint<V> extends AbstractPulsarListenerEndpoint<V> {
|
||||
|
||||
|
||||
private final LogAccessor logger = new LogAccessor(LogFactory.getLog(getClass()));
|
||||
|
||||
private Object bean;
|
||||
|
||||
private Method method;
|
||||
|
||||
private MessageHandlerMethodFactory messageHandlerMethodFactory;
|
||||
|
||||
private SmartMessageConverter messagingConverter;
|
||||
@@ -80,7 +79,6 @@ public class MethodPulsarListenerEndpoint<V> extends AbstractPulsarListenerEndpo
|
||||
|
||||
/**
|
||||
* Set the method to invoke to process a message managed by this endpoint.
|
||||
*
|
||||
* @param method the target method for the {@link #bean}.
|
||||
*/
|
||||
public void setMethod(Method method) {
|
||||
@@ -97,30 +95,33 @@ public class MethodPulsarListenerEndpoint<V> extends AbstractPulsarListenerEndpo
|
||||
|
||||
@Override
|
||||
protected PulsarMessagingMessageListenerAdapter<V> createMessageListener(PulsarMessageListenerContainer container,
|
||||
@Nullable MessageConverter messageConverter) {
|
||||
@Nullable MessageConverter messageConverter) {
|
||||
Assert.state(this.messageHandlerMethodFactory != null,
|
||||
"Could not create message listener - MessageHandlerMethodFactory not set");
|
||||
PulsarMessagingMessageListenerAdapter<V> messageListener = createMessageListenerInstance(messageConverter);
|
||||
final HandlerAdapter handlerMethod = configureListenerAdapter(messageListener);
|
||||
messageListener.setHandlerMethod(handlerMethod);
|
||||
|
||||
//Since we have access to the handler method here, check if we can type infer the Schema used.
|
||||
// Since we have access to the handler method here, check if we can type infer the
|
||||
// Schema used.
|
||||
|
||||
//TODO: filter out the payload type by excluding Consumer, Message, Messages etc.
|
||||
// TODO: filter out the payload type by excluding Consumer, Message, Messages etc.
|
||||
|
||||
final MethodParameter[] methodParameters = handlerMethod.getInvokerHandlerMethod().getMethodParameters();
|
||||
MethodParameter methodParameter = null;
|
||||
final Optional<MethodParameter> parameter = Arrays.stream(methodParameters).filter(
|
||||
methodParameter1 -> !methodParameter1.getParameterType().equals(Consumer.class)
|
||||
|| !methodParameter1.getParameterType().equals(Acknowledgement.class)).findFirst();
|
||||
final long count = Arrays.stream(methodParameters).filter(methodParameter1 -> !methodParameter1.getParameterType().equals(Consumer.class)
|
||||
&& !methodParameter1.getParameterType().equals(Acknowledgement.class)).count();
|
||||
final Optional<MethodParameter> parameter = Arrays.stream(methodParameters)
|
||||
.filter(methodParameter1 -> !methodParameter1.getParameterType().equals(Consumer.class)
|
||||
|| !methodParameter1.getParameterType().equals(Acknowledgement.class))
|
||||
.findFirst();
|
||||
final long count = Arrays.stream(methodParameters)
|
||||
.filter(methodParameter1 -> !methodParameter1.getParameterType().equals(Consumer.class)
|
||||
&& !methodParameter1.getParameterType().equals(Acknowledgement.class))
|
||||
.count();
|
||||
Assert.isTrue(count == 1, "More than 1 expected payload types found");
|
||||
if (parameter.isPresent()) {
|
||||
methodParameter = parameter.get();
|
||||
}
|
||||
|
||||
|
||||
final DefaultPulsarMessageListenerContainer<?> containerInstance = (DefaultPulsarMessageListenerContainer<?>) container;
|
||||
final PulsarContainerProperties pulsarContainerProperties = containerInstance.getPulsarContainerProperties();
|
||||
final SchemaType schemaType = pulsarContainerProperties.getSchemaType();
|
||||
@@ -187,7 +188,8 @@ public class MethodPulsarListenerEndpoint<V> extends AbstractPulsarListenerEndpo
|
||||
return messageListener;
|
||||
}
|
||||
|
||||
private Schema<?> getRequiredSchema(MethodParameter methodParameter, PulsarContainerProperties pulsarContainerProperties) {
|
||||
private Schema<?> getRequiredSchema(MethodParameter methodParameter,
|
||||
PulsarContainerProperties pulsarContainerProperties) {
|
||||
ResolvableType resolvableType = ResolvableType.forMethodParameter(methodParameter);
|
||||
final Class<?> rawClass = resolvableType.getRawClass();
|
||||
if (rawClass != null && isContainerType(rawClass)) {
|
||||
@@ -198,16 +200,17 @@ public class MethodPulsarListenerEndpoint<V> extends AbstractPulsarListenerEndpo
|
||||
}
|
||||
|
||||
private boolean isContainerType(Class<?> rawClass) {
|
||||
return rawClass.isAssignableFrom(List.class) || rawClass.isAssignableFrom(Message.class) || rawClass.isAssignableFrom(Messages.class);
|
||||
return rawClass.isAssignableFrom(List.class) || rawClass.isAssignableFrom(Message.class)
|
||||
|| rawClass.isAssignableFrom(Messages.class);
|
||||
}
|
||||
|
||||
protected HandlerAdapter configureListenerAdapter(PulsarMessagingMessageListenerAdapter<V> messageListener) {
|
||||
InvocableHandlerMethod invocableHandlerMethod =
|
||||
this.messageHandlerMethodFactory.createInvocableHandlerMethod(getBean(), getMethod());
|
||||
InvocableHandlerMethod invocableHandlerMethod = this.messageHandlerMethodFactory
|
||||
.createInvocableHandlerMethod(getBean(), getMethod());
|
||||
return new HandlerAdapter(invocableHandlerMethod);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
protected PulsarMessagingMessageListenerAdapter<V> createMessageListenerInstance(
|
||||
@Nullable MessageConverter messageConverter) {
|
||||
|
||||
|
||||
@@ -43,4 +43,5 @@ public class PulsarClientConfiguration {
|
||||
public Map<String, Object> getConfigs() {
|
||||
return this.configs;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,9 +46,7 @@ public class PulsarClientFactoryBean extends AbstractFactoryBean<PulsarClient> {
|
||||
|
||||
@Override
|
||||
protected PulsarClient createInstance() throws Exception {
|
||||
return PulsarClient.builder()
|
||||
.loadConf(this.pulsarClientConfiguration.getConfigs())
|
||||
.build();
|
||||
return PulsarClient.builder().loadConf(this.pulsarClientConfiguration.getConfigs()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -27,13 +27,11 @@ public abstract class PulsarListenerBeanNames {
|
||||
/**
|
||||
* The bean name of the internally managed Pulsar listener annotation processor.
|
||||
*/
|
||||
public static final String PULSAR_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME =
|
||||
"org.springframework.pulsar.config.internalPulsarListenerAnnotationProcessor";
|
||||
public static final String PULSAR_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.pulsar.config.internalPulsarListenerAnnotationProcessor";
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed Pulsar listener endpoint registry.
|
||||
*/
|
||||
public static final String PULSAR_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME =
|
||||
"org.springframework.pulsar.config.internalPulsarListenerEndpointRegistry";
|
||||
public static final String PULSAR_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME = "org.springframework.pulsar.config.internalPulsarListenerEndpointRegistry";
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user