Search code examples
spring-bootkotlincxf

minimal Apache CXF, Jakarta, Spring Boot 3, Kotlin, Gradle config fails


I try to setup a minimal Apache CXF webapp using SpringBoot3, Kotlin and Gradle, but the single endpoint simply isn't available. All tutorials for Apache CXF & SpringBoot were pretty old, even the official documentation is stuck on Apache CXF 3.1 (current version is 4.0), but according to what i could find, my code looks reasonable.

SpringKotlinInterfaceTestApplication.kt:

import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.datatype.joda.JodaModule
import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider
import jakarta.ws.rs.*
import org.apache.cxf.Bus
import org.apache.cxf.endpoint.Server
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.stereotype.Component

@Component
@Consumes("application/json")
@Produces("application/json")
class PreconditionApiImpl {
    @GET @Path("/pingx")
    fun pingx(): String = "{status: OK}"
}

@SpringBootApplication
class SpringKotlinInterfaceTestApplication

fun main(args: Array<String>) {
    runApplication<SpringKotlinInterfaceTestApplication>(*args){}
}

@Configuration
class RsConfig {
    @Bean
    fun getRsServer(bus: Bus, api: PreconditionApiImpl): Server = JAXRSServerFactoryBean().apply {
        setBus(bus)
        address = "/"
        setServiceBeans(listOf(api))
        setProvider(
            JacksonJsonProvider().apply {
                setMapper(ObjectMapper().apply {
                    registerModule(JodaModule())
                    setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
                })
            }
        )
    }.create()
}

gradle.build.kts

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    id("org.springframework.boot") version "3.0.9"
    id("io.spring.dependency-management") version "1.0.15.RELEASE"
    val kotlinVersion = "1.9.0"
    kotlin("jvm") version kotlinVersion
    kotlin("plugin.spring") version kotlinVersion
}

group = "com.example"
version = "0.0.1-SNAPSHOT"

java {
    sourceCompatibility = JavaVersion.VERSION_17
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.springframework.boot:spring-boot-starter-actuator")
    implementation("org.jetbrains.kotlin:kotlin-reflect")

    val jacksonVersion = "2.14.3"
    implementation("com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-json-provider:$jacksonVersion")
    implementation("com.fasterxml.jackson.datatype:jackson-datatype-joda:$jacksonVersion")

    val cxfVersion = "4.0.2"
    implementation("org.apache.cxf:cxf-spring-boot-starter-jaxrs:$cxfVersion")
    implementation("org.apache.cxf:cxf-rt-rs-client:$cxfVersion")
    implementation("org.apache.cxf:cxf-rt-transports-http:$cxfVersion")
    implementation("org.apache.cxf:cxf-rt-frontend-jaxws:$cxfVersion")
}

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs += "-Xjsr305=strict"
        jvmTarget = "17"
    }
}

Yet a GET to http://localhost:8080/pingx returns a 404.

What am I missing? (maybe cxf-spring-boot-starter-jaxrs doesn't do all the basic wiring after all??)


Solution

  • I figured it out! the configuration is fine! The url was wrong! I have to put an "/services" into it, so it becomes: http://localhost:8080/services/pingx

    If I had used a different configuration for the Server address

        @Bean fun getRsServer(bus: Bus, api: PreconditionApiImpl): Server = JAXRSServerFactoryBean().apply {
            setBus(bus)
            address = "/rs"
            ...
    

    It would be: http://localhost:8080/services/rs/pingx