1. Overview

The goal of this document is to provide reference documentation for developers utilizing the CXF Codegen Gradle plugin.

1.1. What is CXF Codegen

CXF Codegen is a Gradle plugin port of the Maven plugin. The Gradle plugin offers an API similar to the one offered by the Maven plugin. The API is not a 1:1 port and leans heavily on Gradle idioms and conventions.

1.2. Getting Started

To get started with the plugin it needs to be applied to your project.

The plugin is published to Gradle’s plugin portal and can be applied using the plugins DSL block:

Kotlin
plugins {
    java
    id("io.mateo.cxf-codegen") version "2.4.0"
}
Groovy
plugins {
    id "java"
    id "io.mateo.cxf-codegen" version "2.4.0"
}

1.3. Supported Gradle Versions

The CXF Codegen Gradle plugin supports the following Gradle versions:

Gradle 8
  • 8.4

  • 8.5

  • 8.6

  • 8.7

The plugin is fully tested against the above versions to ensure compatibility. View the source of the functional tests for more details.

Gradle’s configuration cache is supported.

All examples are written for and are tested for Gradle 8.7. Depending on your Gradle version, you may need to adapt the example to a syntax that is compatible with your Gradle version.

1.3.1. Support Policy

Each major version of this plugin targets a specific Gradle major version. For example, 2.x.x targets Gradle 8.x and 3.x.x targets Gradle 9.x and so on.

2. Plugin Configuration

2.1. Extension

The plugin contributes a single extension to the project named cxfCodegen.

Kotlin
cxfCodegen { (1)

}
Groovy
cxfCodegen { (1)

}
1 Primary entry point to the plugin configuration.

For details on what configuration options are available, view the Javadoc for CxfCodegenExtension.

2.2. Dependency Configuration

The plugin creates a configuration named cxfCodegen which can be used to add additional dependencies to the classpath for code generation.

Out-of-the-box, the following dependencies are added (v4.0.4):

  • org.apache.cxf:cxf-core

  • org.apache.cxf:cxf-tools-common

  • org.apache.cxf:cxf-tools-wsdlto-core

  • org.apache.cxf:cxf-tools-wsdlto-databinding-jaxb

  • org.apache.cxf:cxf-tools-wsdlto-frontend-jaxws

  • org.apache.cxf:cxf-tools-wsdlto-frontend-javascript

    • org.apache.cxf:cxf-rt-frontend-simple is excluded.

These are the same dependencies defined in the Maven plugin’s POM.

2.2.1. Managing Dependency Versions

There are two ways to manage the contributed dependencies:

The extension provides a short and concise way to specify the version while using standard Gradle dependency management offers more rich configuration options.

The extension is preferred for simplicity, but if your project requires more complex dependency management, then use Gradle’s dependency management instead.

You will need to ensure the version of CXF you specify is compatible with the options used and your application’s version of Jakarta EE (Java EE).

Additionally, depending on the version of you CXF you specify, you will also need to ensure you have the appropriate dependencies for the cxfCodegen classpath and/or your application’s classpath.

Failure to do so can result in an error during code generation and/or compilation errors.

Extension

To change the version using the extension, specify the version using the cxfVersion property:

Kotlin
cxfCodegen {
    cxfVersion.set("3.2.0") // 3.3.0 breaks the build
}
Groovy
cxfCodegen {
    cxfVersion = "3.2.0" // 3.3.0 breaks the build
}
Standard Gradle Dependency Management

To change the version using Standard Gradle dependency management, a resolve rule can be used:

Kotlin
configurations.cxfCodegen {
    resolutionStrategy.eachDependency {
        if (requested.group == "org.apache.cxf") {
            useVersion("3.2.0")
            because("3.3.0 breaks the build")
        }
    }
}
Groovy
configurations.cxfCodegen {
    resolutionStrategy.eachDependency {
        if (requested.group == "org.apache.cxf") {
            useVersion "3.2.0"
            because "3.3.0 breaks the build"
        }
    }
}

3. Generating Java Sources

To generate Java sources from a WSDL, define task of type Wsdl2Java and configure the toolOptions. Note that the task is a subclass of JavaExec.

Incremental Build

The JavaExec supports up-to-date checks (aka incremental build). As mentioned above, Wsdl2Java is a subclass of JavaExec and as a result, also supports up-to-date checks.

However, depending on how your WSDLs are structured, you may encounter scenarios where tasks are out-of-date.

Additionally, when defining a task:

  1. For each task, the generated Java source (the task output) is added to the main source sets.

    1. This can be disabled when creating the task by using addToMainSourceSet.set(false)

  2. All Wsdl2Java task types are aggregated to a single task named wsdl2java.

3.1. Minimal Usage

The minimum requirement for generating Java is a single WSDL which can either be a path to a file or URL. A file example is shown below.

Kotlin
import io.mateo.cxf.codegen.wsdl2java.Wsdl2Java

// ...

tasks.register("example", Wsdl2Java::class) { (1)
    toolOptions { (2)
        wsdl.set(file("path/to/example.wsdl").toPath().toAbsolutePath().toString()) (3)
    }
    allJvmArgs = listOf("-Duser.language=fr", "-Duser.country=CA") (4)
}
Groovy
import io.mateo.cxf.codegen.wsdl2java.Wsdl2Java

// ...

tasks.register("example", Wsdl2Java) { (1)
    toolOptions { (2)
        wsdl.set(file("path/to/example.wsdl").toPath().toAbsolutePath().toString()) (3)
    }
    allJvmArgs = ["-Duser.language=fr", "-Duser.country=CA"] (4)
}
1 Creates a task named example of type Wsdl2Java.
2 Use the toolOptions to configure all available options for code generation.
3 Configure the WSDL to use for code generation. The value can be a path to some location on a filesystem or a URL to a remote location.
4 Configure a custom locale.
Remember that Wsdl2Java is a subclass of JavaExec. All configurations you can do to JavaExec can be done here as well.
For local files, if your WSDL imports a schema (XSD) or something else, consider adding that file to the task inputs as well so that it can be considered during Gradle’s up-to-date checks.

3.2. Options

There are quite a few options that can be specified that alter the generated Java. These are identical to ones offered by the Maven plugin.

Kotlin
tasks.register("example", Wsdl2Java::class) {
    toolOptions {
        wsdl.set(file("path/to/example.wsdl").toPath().toAbsolutePath().toString())
        outputDir.set(file("$buildDir/generated-java")) (1)
        markGenerated.set(true) (2)
        packageNames.set(listOf("com.example", "com.foo.bar")) (3)
        asyncMethods.set(listOf("foo", "bar")) (4)
    }
}
Groovy
tasks.register("example", Wsdl2Java) {
    toolOptions {
        wsdl.set(file("path/to/example.wsdl").toPath().toAbsolutePath().toString())
        outputDir.set(file("$buildDir/generated-java")) (1)
        markGenerated.set(true) (2)
        packageNames.set(["com.example", "com.foo.bar"]) (3)
        asyncMethods.set(["foo", "bar"]) (4)
    }
}
1 Change the directory the generated code files are written to.
2 Adds the @Generated annotation to classes.
3 Package names to use for the generated code.
4 Specifies subsequently generated Java class methods to allow for client-side asynchronous calls, similar to enableAsyncMapping in a JAX-WS binding file.

There are more options available than what is shown above. View the method summary section in the Javadoc for Wsdl2JavaOptions for more details.

3.3. Default Options

You may want to configure options that apply to all tasks. This is easily accomplished using native Gradle functionality.

First define some tasks:

Kotlin
tasks {
    register("first", Wsdl2Java::class) {
        toolOptions {
            wsdl.set(file("path/to/first.wsdl").toPath().toAbsolutePath().toString())
        }
    }
    register("second", Wsdl2Java::class) {
        toolOptions {
            wsdl.set(file("path/to/second.wsdl").toPath().toAbsolutePath().toString())
        }
    }
    register("third", Wsdl2Java::class) {
        toolOptions {
            wsdl.set(file("path/to/third.wsdl").toPath().toAbsolutePath().toString())
        }
    }
}
Groovy
tasks.register("first", Wsdl2Java) {
    toolOptions {
        wsdl.set(file("path/to/first.wsdl").toPath().toAbsolutePath().toString())
    }
}
tasks.register("second", Wsdl2Java) {
    toolOptions {
        wsdl.set(file("path/to/second.wsdl").toPath().toAbsolutePath().toString())
    }
}
tasks.register("third", Wsdl2Java) {
    toolOptions {
        wsdl.set(file("path/to/third.wsdl").toPath().toAbsolutePath().toString())
    }
}

Then configure each one using the configureEach method on the container:

Kotlin
tasks.withType(Wsdl2Java::class).configureEach {
    toolOptions {
        markGenerated.set(true)
    }
}
Groovy
tasks.withType(Wsdl2Java).configureEach {
    toolOptions {
        markGenerated.set(true)
    }
}

3.4. Logging

Apache CXF uses SLF4J for logging and by default, all logs are suppressed. This is accomplished by CXF Codegen Gradle plugin including the org.slf4j:slf4j-nop dependency in the cxfCodegen configuration.

If logs were not suppressed by default, when executing any of the created tasks, the following lines will be printed to the console:

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

The above logging provides no useful information in the build output and instead pollutes the build output. This is the reason why logging is suppressed by default.

3.4.1. Enable Logging

To enable logging for Apache CXF:

  1. Exclude the org.slf4j:slf4j-nop dependency

  2. Include a SLF4J provider (or binding)

  3. Include a logging library

    1. The library should implement the SLF4J API

In the following example, Logback is used which provides an implementation of the SLF4J API.

Kotlin
configurations.cxfCodegen {
    exclude(group = "org.slf4j", module = "slf4j-nop")
}

dependencies {
    cxfCodegen("ch.qos.logback:logback-classic:1.4.14")
}
Groovy
configurations {
    cxfCodegen {
        exclude group: "org.slf4j", module: "slf4j-nop"
    }
}

dependencies {
    cxfCodegen "ch.qos.logback:logback-classic:1.4.14"
}

With the above, you will see some logs from Apache Velocity:

11:48:25.024 [main] DEBUG org.apache.velocity -- Initializing Velocity, Calling init()...
11:48:25.025 [main] DEBUG org.apache.velocity -- Starting Apache Velocity v2.3
11:48:25.026 [main] DEBUG org.apache.velocity -- Default Properties resource: org/apache/velocity/runtime/defaults/velocity.properties

3.4.2. Verbose Logs

To see logs from Apache CXF, configure the logger for the specific task you want logs for.

The example below configures logging for all Wsdl2JavaTask tasks.
Kotlin
tasks.withType(Wsdl2Java::class).configureEach {
    jvmArgs = listOf("-Dorg.apache.cxf.Logger=org.apache.cxf.common.logging.Slf4jLogger")
}
Groovy
tasks.withType(Wsdl2Java).configureEach {
    jvmArgs = ["-Dorg.apache.cxf.Logger=org.apache.cxf.common.logging.Slf4jLogger"]
}

With the above configuration, you will see logs from Apache CXF:

11:00:38.266 [main] DEBUG org.apache.cxf.common.logging.LogUtils -- Using org.apache.cxf.common.logging.Slf4jLogger for logging.
11:00:38.319 [main] DEBUG org.apache.cxf.tools.wsdlto.core.PluginLoader -- Loading plugin jar:file:~/.gradle/caches/modules-2/files-2.1/org.apache.cxf/cxf-tools-wsdlto-databinding-jaxb/4.0.0/5876acee034617f4f6ed756105fe9a0dc50a750c/cxf-tools-wsdlto-databinding-jaxb-4.0.0.jar!/META-INF/tools-plugin.xml
11:00:38.349 [main] DEBUG org.apache.cxf.tools.wsdlto.core.PluginLoader -- Found 1 databindings in <jaxb> plugin.
11:00:38.349 [main] DEBUG org.apache.cxf.tools.wsdlto.core.PluginLoader -- Loading <jaxb> databinding from <jaxb> plugin.
11:00:38.349 [main] DEBUG org.apache.cxf.tools.wsdlto.core.PluginLoader -- Loading plugin jar:file:~/.gradle/caches/modules-2/files-2.1/org.apache.cxf/cxf-tools-wsdlto-frontend-jaxws/4.0.0/762d4f960e3e8778af050a271218785567bc29e1/cxf-tools-wsdlto-frontend-jaxws-4.0.0.jar!/META-INF/tools-plugin.xml

...and so on

4. Generating JavaScript Sources

To generate JavaScript sources from a WSDL, define task of type Wsdl2Js and configure the toolOptions. Note that the task is a subclass of JavaExec.

All Wsdl2Js task types are aggregated to a single task named wsdl2js.

4.1. Minimal Usage

The minimum requirement for generating JavaScript is a single WSDL file.

Kotlin
import io.mateo.cxf.codegen.wsdl2js.Wsdl2Js

// ...

tasks.register("example", Wsdl2Js::class) { (1)
    toolOptions { (2)
        wsdl.set(file("path/to/example.wsdl").absolutePath) (3)
    }
}
Groovy
import io.mateo.cxf.codegen.wsdl2js.Wsdl2Js

// ...

tasks.register("example", Wsdl2Js) { (1)
    toolOptions { (2)
        wsdl.set(file("path/to/example.wsdl").absolutePath) (3)
    }
}
1 Creates a task named example of type Wsdl2Js.
2 Use the toolOptions to configure all available options for code generation.
3 Configure the WSDL to use for code generation.

4.2. Options

There are quite a few options that can be specified that alter the generated JavaScript. These are identical to ones offered by the wsdl2js tool.

Kotlin
tasks.register("example", Wsdl2Js::class) {
    toolOptions {
        wsdl.set(file("path/to/example.wsdl").absolutePath)
        outputDir.set(file("$buildDir/example-generated-js")) (1)
        catalog.set(file("path/to/example-catalog.xml")) (2)
        packagePrefixes.set(listOf(UriPrefixPair("https://example.com", "example"))) (3)
        verbose.set(true) (4)
    }
}
Groovy
tasks.register("example", Wsdl2Js) {
    toolOptions {
        wsdl.set(file("path/to/example.wsdl").absolutePath)
        outputDir.set(file("$buildDir/example-generated-js")) (1)
        catalog.set(file("path/to/example-catalog.xml")) (2)
        packagePrefixes.set([new UriPrefixPair("https://example.com", "example")]) (3)
        verbose.set(true) (4)
    }
}
1 Change the directory the generated code files are written to.
2 Specifies the XML catalog to use for resolving imported schemas and WSDL documents.
3 Specifies a mapping between the namespaces used in the WSDL document and the prefixes used in the generated JavaScript.
4 Displays comments during the code generation process.

There are more options available than what is shown above. View the method summary section in the Javadoc for Wsdl2JsOptions for more details.

4.3. Default Options

You may want to configure options that apply to all tasks. This is can be accomplished using native Gradle functionality. Refer to the default options section in the documentation for generating Java sources for examples. Replace Wsdl2Java with Wsdl2Js in the examples.

4.4. Logging

Refer to the documentation for generating Java sources. The process for enabling or disabling logs is the same. Replace Wsdl2Java with Wsdl2Js.

5. Java Examples

This section provides Gradle examples translated from the Maven plugin examples.

You will need to ensure you have the appropriate dependencies when generating code. Additionally, there may be more dependencies the CXF Codegen tool requires. Review dependency configuration documentation on how to add additional dependencies to the CXF Codegen tool classpath.

In addition to the tool dependencies, your application may require additional dependencies as well.

All examples are written for and tested for Gradle 8.7. Depending on your Gradle version, you may need to adapt the example to a syntax that is compatible with your Gradle version.

The following examples have been omitted:

  1. Using wsdlRoot with includes/excludes patterns

    1. It is a design decision of the plugin to omit the functionality to define a wsdlRoot or "base path" to locate WSDL(s). It is responsibility of the user to provide the location a WSDL. This keeps the plugin codebase simpler.

  2. Using defaultOption to avoid repetition

    1. The defaultOptions configuration does not exist in this Gradle plugin. Instead, use native Gradle APIs to accomplish the same thing. Refer to the default options section for details.

5.1. JAX-WS Binding File

Kotlin
import io.mateo.cxf.codegen.wsdl2java.Wsdl2Java

// ...

tasks.register("calculator", Wsdl2Java::class) {
    toolOptions {
        wsdl.set(layout.projectDirectory.file("calculator.wsdl").asFile.toPath().toAbsolutePath().toString())
        bindingFiles.add(layout.projectDirectory.file("async-binding.xml").asFile.absolutePath) (1)
    }
}
Groovy
import io.mateo.cxf.codegen.wsdl2java.Wsdl2Java

// ...

tasks.register("calculator", Wsdl2Java) {
    toolOptions {
        wsdl.set(layout.projectDirectory.file("calculator.wsdl").asFile.toPath().toAbsolutePath().toString())
        bindingFiles.add(layout.projectDirectory.file("async-binding.xml").asFile.absolutePath) (1)
    }
}
1 Add the path of the binding file to the list property of binding files.

5.2. Specify Data Binding

Kotlin
import io.mateo.cxf.codegen.wsdl2java.Wsdl2Java

dependencies {
    cxfCodegen("org.apache.cxf:cxf-rt-databinding-jibx:3.1.18") (1)
}

// ...

tasks.register("calculator", Wsdl2Java::class) {
    toolOptions {
        wsdl.set(layout.projectDirectory.file("calculator.wsdl").asFile.toPath().toAbsolutePath().toString())
        extraArgs.addAll(listOf("-databinding", "jibx")) (2)
    }
}
Groovy
import io.mateo.cxf.codegen.wsdl2java.Wsdl2Java

dependencies {
    cxfCodegen "org.apache.cxf:cxf-rt-databinding-jibx:3.1.18" (1)
}

// ...

tasks.register("calculator", Wsdl2Java) {
    toolOptions {
        wsdl.set(layout.projectDirectory.file("calculator.wsdl").asFile.toPath().toAbsolutePath().toString())
        extraArgs.addAll(["-databinding", "jibx"]) (2)
    }
}
1 Add required dependency to tool classpath for JiBX data binding.
2 Specify JiBX data binding.

5.3. Specify Service to Generate Artifacts For

Kotlin
import io.mateo.cxf.codegen.wsdl2java.Wsdl2Java

// ...

tasks.register("calculator", Wsdl2Java::class) {
    toolOptions {
        wsdl.set(layout.projectDirectory.file("calculator.wsdl").asFile.toPath().toAbsolutePath().toString())
        serviceName.set("Calculator") (1)
    }
}
Groovy
import io.mateo.cxf.codegen.wsdl2java.Wsdl2Java

// ...

tasks.register("calculator", Wsdl2Java) {
    toolOptions {
        wsdl.set(layout.projectDirectory.file("calculator.wsdl").asFile.toPath().toAbsolutePath().toString())
        serviceName.set("Calculator") (1)
    }
}
1 Specify to generate artifacts only for the service named Calculator.

5.4. Loading A WSDL From A Maven Repository

Loading a WSDL from an artifact can be accomplished using Gradle APIs as shown below. For example, to use the WSDL published in the cxf-testutils JAR artifact:

Kotlin
import io.mateo.cxf.codegen.wsdl2java.Wsdl2Java

val myConfiguration: Configuration by configurations.creating (1)

dependencies {
    myConfiguration("org.apache.cxf:cxf-testutils:3.5.3") (2)
}

val copyArtifact = tasks.register("copyArtifact", Copy::class) { (3)
    from(myConfiguration) {
        include {
            it.name.startsWith("cxf-testutils")
        }
    }
    into(layout.buildDirectory.dir("copied-artifact"))
}

val extractWsdl = tasks.register("extractWsdl") { (4)
    val wsdlFile = layout.buildDirectory.file("extracted.wsdl")
    inputs.files(copyArtifact)
    outputs.file(wsdlFile)
    doLast {
        // Assumes single JAR artifact from previous task
        val archive = inputs.files.singleFile.walk().filter { it.isFile }.single()
        val textResource = resources.text.fromArchiveEntry(archive, "wsdl/calculator.wsdl")
        val f = wsdlFile.get().asFile
        f.createNewFile()
        f.writeText(textResource.asString())
    }
}

tasks.register("calculator", Wsdl2Java::class) { (5)
    inputs.files(extractWsdl)
    toolOptions {
        wsdl.set(extractWsdl.map { it.outputs.files.singleFile.toPath().toAbsolutePath().toString() })
    }
}
Groovy
import io.mateo.cxf.codegen.wsdl2java.Wsdl2Java

configurations {
    myConfiguration (1)
}

dependencies {
    myConfiguration "org.apache.cxf:cxf-testutils:4.0.0" (2)
}

def copyArtifact = tasks.register("copyArtifact", Copy) { (3)
    from(configurations.myConfiguration) {
        include {
            it.name.startsWith("cxf-testutils")
        }
    }
    into(layout.buildDirectory.dir("copied-artifact"))
}

def extractWsdl = tasks.register("extractWsdl") { (4)
    def wsdlFile = layout.buildDirectory.file("extracted.wsdl")
    inputs.files(copyArtifact)
    outputs.file(wsdlFile)
    doLast {
        // Assumes single JAR artifact from previous task
        def list = []
        inputs.files.singleFile.eachFileRecurse(groovy.io.FileType.FILES) { f -> list.add(f) }
        def archive = list.first()
        def textResource = resources.text.fromArchiveEntry(archive, "wsdl/calculator.wsdl")
        def f = wsdlFile.get().asFile
        f.createNewFile()
        f.write(textResource.asString())
    }
}

tasks.register("calculator", Wsdl2Java) { (5)
    inputs.files(extractWsdl)
    toolOptions {
        wsdl.set(extractWsdl.map { it.outputs.files.singleFile.toPath().toAbsolutePath().toString() })
    }
}
1 Define a configuration to hold the dependency.
2 Add the dependency containing a WSDL to the configuration.
3 Define a task to copy the dependency that contains the WSDL
4 Extract the WSDL from the artifact
5 Use the task output as input to the Wsdl2Java task

5.5. Using XJC Extensions

Kotlin
import io.mateo.cxf.codegen.wsdl2java.Wsdl2Java

// ...

dependencies {
    implementation("org.apache.cxf.xjc-utils:cxf-xjc-runtime:4.0.0") (1)
    cxfCodegen("org.apache.cxf.xjcplugins:cxf-xjc-ts:4.0.0") (2)
}

tasks.register("calculator", Wsdl2Java::class) {
    toolOptions {
        wsdl.set(layout.projectDirectory.file("calculator.wsdl").asFile.toPath().toAbsolutePath().toString())
        xjcArgs.add("-Xts") (3)
    }
}
Groovy
import io.mateo.cxf.codegen.wsdl2java.Wsdl2Java

// ...

dependencies {
    implementation "org.apache.cxf.xjc-utils:cxf-xjc-runtime:4.0.0" (1)
    cxfCodegen "org.apache.cxf.xjcplugins:cxf-xjc-ts:4.0.0" (2)
}

tasks.register("calculator", Wsdl2Java) {
    toolOptions {
        wsdl.set(layout.projectDirectory.file("calculator.wsdl").asFile.toPath().toAbsolutePath().toString())
        xjcArgs.add("-Xts") (3)
    }
}
1 Add the required CXF JXC dependency to your application dependencies.
2 Add the extension dependency to the tool classpath.
3 Specify extension ID that corresponds to the added dependency from (2); the -xjc prefix is not required when using the xjcArgs property.