From 5c12566524bb798727392a3fc1f5c206135f6d5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20M=C3=B8lg=C3=A5rd=20Vester?= Date: Tue, 20 Jun 2023 17:33:53 +0200 Subject: [PATCH] Fixed a problem in older versions og Gradle when not specifying a Java toolchain --- README.md | 779 +++++++++--------- build.gradle.kts | 125 ++- ....internal.java-conventions-cxf3.gradle.kts | 2 - ....internal.java-conventions-cxf4.gradle.kts | 2 - .../src/main/bindings/bindings.xml | 3 +- .../src/main/bindings/bindings.xml | 3 +- integration-test/settings.gradle.kts | 40 +- .../bjornvester/wsdl2java/Wsdl2JavaPlugin.kt | 245 +++--- .../wsdl2java/Wsdl2JavaPluginExtension.kt | 4 +- .../bjornvester/wsdl2java/Wsdl2JavaTask.kt | 89 +- .../com/github/bjornvester/IntegrationTest.kt | 65 +- 11 files changed, 672 insertions(+), 685 deletions(-) diff --git a/README.md b/README.md index 903fb4a..36b27e2 100644 --- a/README.md +++ b/README.md @@ -1,378 +1,401 @@ -[![Gradle Plugin Release](https://img.shields.io/badge/Gradle%20plugin-1.6.0-blue.svg?logo=data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA5MCA2Ni4wNiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5ncmFkbGUtZWxlcGhhbnQtaWNvbi13aGl0ZS1wcmltYXJ5PC90aXRsZT48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik04NS4xMSw0LjE4YTE0LjI3LDE0LjI3LDAsMCwwLTE5LjgzLS4zNCwxLjM4LDEuMzgsMCwwLDAsMCwyTDY3LDcuNmExLjM2LDEuMzYsMCwwLDAsMS43OC4xMkE4LjE4LDguMTgsMCwwLDEsNzkuNSwyMC4wNkM2OC4xNywzMS4zOCw1My4wNS0uMzYsMTguNzMsMTZhNC42NSw0LjY1LDAsMCwwLTIsNi41NGw1Ljg5LDEwLjE3YTQuNjQsNC42NCwwLDAsMCw2LjMsMS43M2wuMTQtLjA4LS4xMS4wOEwzMS41MywzM2E2MC4yOSw2MC4yOSwwLDAsMCw4LjIyLTYuMTMsMS40NCwxLjQ0LDAsMCwxLDEuODctLjA2aDBhMS4zNCwxLjM0LDAsMCwxLC4wNiwyQTYxLjYxLDYxLjYxLDAsMCwxLDMzLDM1LjM0bC0uMDksMC0yLjYxLDEuNDZhNy4zNCw3LjM0LDAsMCwxLTMuNjEuOTQsNy40NSw3LjQ1LDAsMCwxLTYuNDctMy43MWwtNS41Ny05LjYxQzQsMzItMi41NCw0Ni41NiwxLDY1YTEuMzYsMS4zNiwwLDAsMCwxLjMzLDEuMTFIOC42MUExLjM2LDEuMzYsMCwwLDAsMTAsNjQuODdhOS4yOSw5LjI5LDAsMCwxLDE4LjQyLDAsMS4zNSwxLjM1LDAsMCwwLDEuMzQsMS4xOUgzNS45YTEuMzYsMS4zNiwwLDAsMCwxLjM0LTEuMTksOS4yOSw5LjI5LDAsMCwxLDE4LjQyLDBBMS4zNiwxLjM2LDAsMCwwLDU3LDY2LjA2SDYzLjFhMS4zNiwxLjM2LDAsMCwwLDEuMzYtMS4zNGMuMTQtOC42LDIuNDYtMTguNDgsOS4wNy0yMy40M0M5Ni40MywyNC4xNiw5MC40MSw5LjQ4LDg1LjExLDQuMThaTTYxLjc2LDMwLjA1bC00LjM3LTIuMTloMGEyLjc0LDIuNzQsMCwxLDEsNC4zNywyLjJaIi8+PC9zdmc+)](https://plugins.gradle.org/plugin/com.github.bjornvester.xjc) -[![GitHub Actions status](https://github.com/bjornvester/wsdl2java-gradle-plugin/workflows/CI/badge.svg)](https://github.com/bjornvester/wsdl2java-gradle-plugin/actions) - -# wsdl2java-gradle-plugin - -A Gradle plugin for generating Java classes from WSDL files through CXF. - -## Requirements and main features - -* The plugin requires Gradle 6.7 or later. (Tested with Gradle 6, 7 and 8.) -* For using the Jakarta namespace (default), the plugin requires Java 17 or greater. For the older javax namespace, you can use Java 8, 11 or 17. -* It supports the Gradle build cache (enabled by setting `org.gradle.caching=true` in your gradle.properties file). -* It supports the Gradle configuration cache (enabled by setting `org.gradle.configuration-cache=true` in your gradle.properties file"). -* It supports project relocation for the build cache (e.g. you move your project to a new path, or make a new copy/clone of it). - This is especially useful in a CI context, where you might clone PRs and/or branches for a repository in their own locations. - Note, however, that there is an issue in an Apache library called `org.apache.ws.xmlschema:xmlschema-core` that might break this - see below for details. -* It supports parallel execution (enabled with `org.gradle.parallel=true` in your gradle.properties file), though it does not itself run anything in parallel. - -## Configuration - -Apply the plugin ID "com.github.bjornvester.wsdl2java" as specific in -the [Gradle Plugin portal page](https://plugins.gradle.org/plugin/com.github.bjornvester.wsdl2java), e.g. like this: - -```kotlin -plugins { - id("com.github.bjornvester.wsdl2java") version "2.0" -} -``` - -Put your WSDL and referenced XSD files somewhere in your src/main/resource directory. -By default, the plugin will create Java classes for all the WSDL files in the resource directory. - -The generated code will by default end up in the directory build/generated/sources/wsdl2java folder. - -You can configure the plugin using the "wsdl2java" extension like this: - -```kotlin -wsdl2java { - // Set properties here... -} -``` - -Here is a list of all available properties: - -| Property | Type | Default | Description | -|----------------------------|-----------------------|--------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| wsdlDir | DirectoryProperty | "$projectDir/src
/main/resources" | The directory holding the WSDL and referenced XSD files to compile. | -| includes | ListProperty\ | \["**/*.wsdl"] | Inclusion filers (Ant style) for which WSDLs to include. | -| includesWithOptions | Map\ | \[not set\] | Inclusion filters like above, but with individual options. See below. | -| generatedSourceDir | DirectoryProperty | "$buildDir/generated
/sources/wsdl2java/java" | The output directory for the generated Java sources.
Note that it will be deleted when running XJC. | -| bindingFile | RegularFileProperty | \[not set\] | A binding file to use in the schema compiler. | -| cxfVersion | Provider\ | "4.0.2" for jakarta / 3.5.6 for javax | The version of CXF to use. Use a version >= 4 for `jakarta` and below for `javax`. | -| useJakarta | Provider\ | true | Set to use the `jakarta` namespace. If false, uses the `javax` namespace. This value also determines the default version of CXF. | -| verbose | Provider\ | \[not set\] | Enables verbose output from CXF. If not set, it will be be enabled only on the info logging level. | -| markGenerated | Provider\ | "no" | Adds the @Generated annotation to the generated sources. See below for details as there are some gotchas with this. | -| generatedStyle | Provider\ | "default" | If using the @Generated annotation, select the type can be overridden by this. See below for details. | -| suppressGeneratedDate | Provider\ | true | Suppresses generating dates in CXF. Default is true to support reproducible builds and to work with the build cache. | -| packageName | Provider\ | \[not set\] | Sets the package name for the generated sources | -| options | ListProperty\ | \[empty\] | Additional options to pass to the tool. See [here](https://cxf.apache.org/docs/wsdl-to-java.html) for details. | -| addCompilationDependencies | Provider\ | true | Adds dependencies to the `implementation` configuration for compiling the generated sources. These includes `jakarta.xml.ws:jakarta.xml.ws-api`, `jakarta.jws:jakarta.jws-api` and possibly `jakarta.annotation:jakarta.annotation-api`. | - -### Configure the CXF version - -You can specify the version of CXF used for code generation like this: - -```kotlin -wsdl2java { - cxfVersion.set("4.0.2") -} -``` - -### Configure included WSDL files - -By default, the plugin will find all WSDL files in the `wsdlDir` directory, which defaults to `src/main/resources`. -It is important that if you change this, you change it to a folder that contain all resources (e.g. both WSDL and XSDs). -Otherwise, if you make changes to files outside this folder, Gradle will not see them and thus might consider the task -up-to-date. - -The plugin will set the `wsdlLocation` property of the `@WebServiceClient` to the path for the WSDL file relative to -the `wsdlDir` directory. -If you change `wsdlDir` in a way where this no longer makes sense, you need to set the option yourself. -If you have multiple WSDL files, see the section on additional options. - -If you have multiple WSDL files and want to only run the tool on some of them, you can use the `includes` property. -Example: - -```kotlin -// Only if different from the default 'src/main/resources' -wsdlDir.set(layout.projectDirectory.dir("src/main/wsdl")) - -// For Kotlin DSL -includes.set( - listOf( - "src/main/wsdls/MyFirstService.wsdl", - "src/main/wsdls/MySecondService.wsdl" - ) -) -``` - -```groovy -// For Groovy DSL -includes = [ - "src/main/wsdls/MyFirstService.wsdl", - "src/main/wsdls/MySecondService.wsdl" -] -``` - -### Configure additional options - -Besides the options given in the `wsdl2java` extension, you can provide additional options directly to CXF (and XJC) -through the `options` property: - -```kotlin -// For Kotlin DSL -includes.set( - listOf( - "xjc-no-header", - "xjc-npa" - ) -) - -// For Groovy DSL -includes = [ - "xjc-no-header", - "xjc-npa" -] -``` - -See [here](https://cxf.apache.org/docs/wsdl-to-java.html) for the available options. - -#### Configure additional options for individual WSDL files - -It is possible to pass options for individual WSDL files. -This is important especially if you need to explicitly configure the `wsdlLocation` option, as it doesn't make to have -the same location in all files. -(But note that if you leave it out, the plugin will guess it based on the file location.) - -```kotlin -// For Kotlin DSL -includesWithOptions.set( - mapOf( - "**/ServiceA.wsdl" to listOf("-wsdlLocation", "https://example.com/service-a?wsdl"), - "**/ServiceB.wsdl" to listOf("-wsdlLocation", "https://example.com/service-b?wsdl") - ) -) -``` - -```groovy -// For Groovy DSL -includes = [ - "**/ServiceA.wsdl": ["-wsdlLocation", "https://example.com/service-a?wsdl"], - "**/ServiceB.wsdl": ["-wsdlLocation", "https://example.com/service-b?wsdl"] -] -``` - -### Configure the output directory - -You can optionally specify the directory for the generated source through the `generatedSourceDir` property, which -defaults to `$buildDir/generated/sources/wsdl2java/java`. -Example: - -```kotlin -wsdl2java { - generatedSourceDir.set(layout.projectDirectory.dir("src/generated/wsdl2java")) -} -``` - -Note that the directory will be wiped completely on each run, so don't put other source files in it. - -### Configure binding files - -A binding file can be added like this: - -```kotlin -wsdl2java { - bindingFile.set(layout.projectDirectory.file("src/main/bindings/binding.xjb")) -} -``` - -If you get a warning on maximum enum member size, you can raise the maximum like this: - -```xml - - - - - - - - - -``` - -If you are using the older `javax` namespace instead of `jakarta`, set the `jxb` attribute to `http://java.sun.com/xml/ns/jaxb` instead and the version to 2.1. - -If you like to use the Java Date/Time API instead of the more clunky GregorianCalendar class, you can -use [threeten-jaxb](https://github.com/threeten-jaxb/threeten-jaxb) library with a binding file like example below. - -```kotlin -dependencies { - implementation("io.github.threeten-jaxb:threeten-jaxb-core:2.1.0") -} - -wsdl2java { - bindingFile.set(layout.projectDirectory.file("src/main/bindings/bindings.xjb")) -} -``` - -```xml - - - - - - - - - - - -``` - -In the above, for `javax`, use version 1.2.0 and the binding attributes to `xmlns="http://java.sun.com/xml/ns/jaxb" version="2.1"`. - -Note that at the moment, it is not possible to specify more than one binding file in the extension. If you require this, -use the `-b` option. - -### Configuring package names - -The package name for the generated resources can be configured with the `packageName` field: - -```kotlin -wsdl2java { - packageName.set("com.github.bjornvester.wsdl2java.group1") -} - -``` - -### Configure encoding - -If your WSDL files include non-ANSI characters, you should set the corresponding file encoding in your gradle.properties -file. E.g.: - -```properties -org.gradle.jvmargs=-Dfile.encoding=UTF-8 -``` - -If you are on a POSIX operating system (e.g. Linux), you may in addition to this need to set your operating system -locale to one that supports your encoding. -Otherwise, Java (and therefore also Gradle and CXF) may not be able to create files with names outside of what your -default locale supports. -Especially some Docker images, like the Java ECR images from AWS, are by default set to a locale supporting ASCII only. -If this is the case for you, and you want to use UTF-8, you could export an environment variable like this: - -```shell script -export LANG=C.UTF-8 -``` - -### Using javax instead of the jakarta namespace - -To use the older javax namespace in the generated files, set the `useJakarta` property to `false. - -```kotlin -wsdl2java { - useJakarta.set(false) -} -``` - -### Activating (third party) XJC plugins - -To use third party plugins for the underlying XJC tool, supply the relevant dependencies to the `xjcPlugins` -configuration. -Then set the plugin options through the `options` property. - -For example, to use the "Equals" and "Hashcode" plugin from -the [JAXB2 Basics](https://github.com/highsource/jaxb2-basics) project, configure the following: - -```kotlin -dependencies { - // For CXF 3 and javax only (does not work with CXF 4 and jakarta): - implementation("org.jvnet.jaxb2_commons:jaxb2-basics-runtime:1.11.1") - xjcPlugins("org.jvnet.jaxb2_commons:jaxb2-basics:1.11.1") -} - -wsdl2java { - options.addAll("-xjc-Xequals", "-xjc-XhashCode") -} -``` - -Note that the example above is for CXF 3 and the older `javax` namespace. -There is a fork [here](https://github.com/patrodyne/hisrc-basicjaxb) that you may try for CXF 4 and the `jakarta` namespace. - -### Enabling the use of the @Generated annotation - -CXF (and the underlying XJC tool) can add a `@Generated` annotation to the generated source code. -This is a source annotation useful for marking classes as generated by a tool. -It can also be used by some static code analytic tools to skip these classes. -You can set it by the `markGenerated` configuration: - -```kotlin -wsdl2java { - markGenerated.set(true) -} -``` - -While very useful in theory, there are some gotchas with this annotation. -One is that it is a source code annotation and thus won't work with tools that work with byte code (like JaCoCo). -Another is that here are actually three `@Generated` annotations. - -* The first is `javax.annotation.Generated` and is available in the JDK up til and including Java 8. It is also included in the `javax.annotation:javax.annotation-api` dependency. -* The second is `javax.annotation.processing.Generated` and is available in the JDK from Java 9. -* the third is `jakarta.annotation.Generated` and is included in the `jakarta.annotation:jakarta.annotation-api` dependency. - -XJC 3 uses the first, and XJC 4+ uses the third. - -Because your tools may only support a particular version of the `@Generated` annotation, you can select which one through the `generatedStyle` configuration. -Supported values are: `default`, `jdk8`, `jdk9` and `jakarta`. -Example: - -```kotlin -dependencies { - // The dependency below is only needed if using the Java 8 version of @Generated (through "jdk8") on Java 9 or later - implementation("javax.annotation:javax.annotation-api:1.3.2") -} - -wsdl2java { - markGenerated.set("jdk8") -} -``` - -The plugin will also, by default, strip any timestamps from the `@Generated` annotations. -This is to support reproducible builds and the Gradle cache. -This can be disabled by the `suppressGeneratedDate` configuration: - -```kotlin -wsdl2java { - suppressGeneratedDate.set(false) -} -``` - -## Other - -The plugin will add the following two dependencies to your `implementation` configuration: - -``` -jakarta.xml.ws:jakarta.xml.ws-api:2.3.3 -jakarta.jws:jakarta.jws-api:1.1.1 -``` - -These are required to compile the generated code. -However, depending on your runtime platform, you may want to exclude them and instead either put them in the `compileOnly` configuration, or use whatever libraries that are -provided by the platform. - -There are full examples available in the integration-test directory. - -If you need to compile additional XML schemas (xsd files) not directly referenced by the wsdl files, you can use -the [com.github.bjornvester.xjc](https://plugins.gradle.org/plugin/com.github.bjornvester.xjc) plugin in addition. - -## Limitations - -### CXF does not check for unique names in generated resources - -The CXF tool will overwrite generated classes from multiple WSDL files if they have the same qualified name. -Especially the `ObjectFactory` class might be overwritten, which is very annoying. - -### CXF is not deterministic - -When CXF generates an `ObjectFactory` class, the order of the methods are not deterministic, but depend on the absolute path of the input files. -This may cause misses in the Gradle build cache, ecp, which may propagate to downstream projects, resulting in long build times. -I have a local fix for this and I hope to create a PR for it to the relevant Apache project. - -## Contributions - -I often feel a bit stretched out in terms of maintaining this and my projects. -For that reason, I might not respond to issues and PRs very often. +[![Gradle Plugin Release](https://img.shields.io/badge/Gradle%20plugin-1.6.0-blue.svg?logo=data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA5MCA2Ni4wNiI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5ncmFkbGUtZWxlcGhhbnQtaWNvbi13aGl0ZS1wcmltYXJ5PC90aXRsZT48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik04NS4xMSw0LjE4YTE0LjI3LDE0LjI3LDAsMCwwLTE5LjgzLS4zNCwxLjM4LDEuMzgsMCwwLDAsMCwyTDY3LDcuNmExLjM2LDEuMzYsMCwwLDAsMS43OC4xMkE4LjE4LDguMTgsMCwwLDEsNzkuNSwyMC4wNkM2OC4xNywzMS4zOCw1My4wNS0uMzYsMTguNzMsMTZhNC42NSw0LjY1LDAsMCwwLTIsNi41NGw1Ljg5LDEwLjE3YTQuNjQsNC42NCwwLDAsMCw2LjMsMS43M2wuMTQtLjA4LS4xMS4wOEwzMS41MywzM2E2MC4yOSw2MC4yOSwwLDAsMCw4LjIyLTYuMTMsMS40NCwxLjQ0LDAsMCwxLDEuODctLjA2aDBhMS4zNCwxLjM0LDAsMCwxLC4wNiwyQTYxLjYxLDYxLjYxLDAsMCwxLDMzLDM1LjM0bC0uMDksMC0yLjYxLDEuNDZhNy4zNCw3LjM0LDAsMCwxLTMuNjEuOTQsNy40NSw3LjQ1LDAsMCwxLTYuNDctMy43MWwtNS41Ny05LjYxQzQsMzItMi41NCw0Ni41NiwxLDY1YTEuMzYsMS4zNiwwLDAsMCwxLjMzLDEuMTFIOC42MUExLjM2LDEuMzYsMCwwLDAsMTAsNjQuODdhOS4yOSw5LjI5LDAsMCwxLDE4LjQyLDAsMS4zNSwxLjM1LDAsMCwwLDEuMzQsMS4xOUgzNS45YTEuMzYsMS4zNiwwLDAsMCwxLjM0LTEuMTksOS4yOSw5LjI5LDAsMCwxLDE4LjQyLDBBMS4zNiwxLjM2LDAsMCwwLDU3LDY2LjA2SDYzLjFhMS4zNiwxLjM2LDAsMCwwLDEuMzYtMS4zNGMuMTQtOC42LDIuNDYtMTguNDgsOS4wNy0yMy40M0M5Ni40MywyNC4xNiw5MC40MSw5LjQ4LDg1LjExLDQuMThaTTYxLjc2LDMwLjA1bC00LjM3LTIuMTloMGEyLjc0LDIuNzQsMCwxLDEsNC4zNywyLjJaIi8+PC9zdmc+)](https://plugins.gradle.org/plugin/com.github.bjornvester.xjc) +[![GitHub Actions status](https://github.com/bjornvester/wsdl2java-gradle-plugin/workflows/CI/badge.svg)](https://github.com/bjornvester/wsdl2java-gradle-plugin/actions) + +# wsdl2java-gradle-plugin + +A Gradle plugin for generating Java classes from WSDL files through CXF. + +## Requirements and main features + +* The plugin requires Gradle 7.6 or later. (Tested with Gradle 7 and 8.) +* For using the Jakarta namespace (default), the plugin requires Java 17 or greater. For the older javax namespace, you can use Java 8, 11 or 17. +* It supports the Gradle build cache (enabled by setting `org.gradle.caching=true` in your gradle.properties file). +* It supports the Gradle configuration cache (enabled by setting `org.gradle.configuration-cache=true` in your gradle.properties file"). +* It supports project relocation for the build cache (e.g. you move your project to a new path, or make a new copy/clone of it). + This is especially useful in a CI context, where you might clone PRs and/or branches for a repository in their own locations. + Note, however, that there is an issue in an Apache library called `org.apache.ws.xmlschema:xmlschema-core` that might break this - see below for details. +* It supports parallel execution (enabled with `org.gradle.parallel=true` in your gradle.properties file), though it does not itself run anything in parallel. + +## Configuration + +Apply the plugin ID "com.github.bjornvester.wsdl2java" as specific in the [Gradle Plugin portal page](https://plugins.gradle.org/plugin/com.github.bjornvester.wsdl2java), e.g. like this: + +```kotlin +plugins { + id("com.github.bjornvester.wsdl2java") version "2.0.1" +} +``` + +Put your WSDL and referenced XSD files somewhere in your src/main/resource directory. +By default, the plugin will create Java classes for all the WSDL files in the resource directory. + +The generated code will by default end up in the directory build/generated/sources/wsdl2java folder. + +You can configure the plugin using the "wsdl2java" extension like this: + +```kotlin +wsdl2java { + // Set properties here... +} +``` + +Here is a list of all available properties: + +| Property | Type | Default | Description | +|----------------------------|-----------------------|--------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| wsdlDir | DirectoryProperty | "$projectDir/src
/main/resources" | The directory holding the WSDL and referenced XSD files to compile. | +| includes | ListProperty\ | \["**/*.wsdl"] | Inclusion filers (Ant style) for which WSDLs to include. | +| includesWithOptions | Map\ | \[not set\] | Inclusion filters like above, but with individual options. See below. | +| generatedSourceDir | DirectoryProperty | "$buildDir/generated
/sources/wsdl2java/java" | The output directory for the generated Java sources.
Note that it will be deleted when running XJC. | +| bindingFile | RegularFileProperty | \[not set\] | A binding file to use in the schema compiler. | +| useJakarta | Provider\ | true | Set to use the `jakarta` namespace. If false, uses the `javax` namespace. This value also determines the default version of CXF. | +| cxfVersion | Provider\ | "4.0.2" for jakarta / 3.5.6 for javax | The version of CXF to use. Use a version >= 4 for `jakarta` and below for `javax`. | +| verbose | Provider\ | \[not set\] | Enables verbose output from CXF. If not set, it will be be enabled only on the info logging level. | +| markGenerated | Provider\ | "no" | Adds the @Generated annotation to the generated sources. See below for details as there are some gotchas with this. | +| generatedStyle | Provider\ | "default" | If using the @Generated annotation, select the type can be overridden by this. See below for details. | +| suppressGeneratedDate | Provider\ | true | Suppresses generating dates in CXF. Default is true to support reproducible builds and to work with the build cache. | +| packageName | Provider\ | \[not set\] | Sets the package name for the generated sources | +| options | ListProperty\ | \[empty\] | Additional options to pass to the tool. See [here](https://cxf.apache.org/docs/wsdl-to-java.html) for details. | +| addCompilationDependencies | Provider\ | true | Adds dependencies to the `implementation` configuration for compiling the generated sources. These includes `jakarta.xml.ws:jakarta.xml.ws-api`, `jakarta.jws:jakarta.jws-api` and possibly `jakarta.annotation:jakarta.annotation-api`. | + +### Configure the CXF version + +You can specify the version of CXF used for code generation like this: + +```kotlin +wsdl2java { + cxfVersion.set("4.0.2") +} +``` + +### Configure included WSDL files + +By default, the plugin will find all WSDL files in the `wsdlDir` directory, which defaults to `src/main/resources`. +It is important that if you change this, you change it to a folder that contain all resources (e.g. both WSDL and XSDs). +Otherwise, if you make changes to files outside this folder, Gradle will not see them and thus might consider the task +up-to-date. + +The plugin will set the `wsdlLocation` property of the `@WebServiceClient` to the path for the WSDL file relative to +the `wsdlDir` directory. +If you change `wsdlDir` in a way where this no longer makes sense, you need to set the option yourself. +If you have multiple WSDL files, see the section on additional options. + +If you have multiple WSDL files and want to only run the tool on some of them, you can use the `includes` property. +Example: + +```kotlin +// Only if different from the default 'src/main/resources' +wsdlDir.set(layout.projectDirectory.dir("src/main/wsdl")) + +// For Kotlin DSL +includes.set( + listOf( + "src/main/wsdls/MyFirstService.wsdl", + "src/main/wsdls/MySecondService.wsdl" + ) +) +``` + +```groovy +// For Groovy DSL +includes = [ + "src/main/wsdls/MyFirstService.wsdl", + "src/main/wsdls/MySecondService.wsdl" +] +``` + +### Configure additional options + +Besides the options given in the `wsdl2java` extension, you can provide additional options directly to CXF (and XJC) +through the `options` property: + +```kotlin +// For Kotlin DSL +includes.set( + listOf( + "xjc-no-header", + "xjc-npa" + ) +) + +// For Groovy DSL +includes = [ + "xjc-no-header", + "xjc-npa" +] +``` + +See [here](https://cxf.apache.org/docs/wsdl-to-java.html) for the available options. + +#### Configure additional options for individual WSDL files + +It is possible to pass options for individual WSDL files. +This is important especially if you need to explicitly configure the `wsdlLocation` option, as it doesn't make to have +the same location in all files. +(But note that if you leave it out, the plugin will guess it based on the file location.) + +```kotlin +// For Kotlin DSL +includesWithOptions.set( + mapOf( + "**/ServiceA.wsdl" to listOf("-wsdlLocation", "https://example.com/service-a?wsdl"), + "**/ServiceB.wsdl" to listOf("-wsdlLocation", "https://example.com/service-b?wsdl") + ) +) +``` + +```groovy +// For Groovy DSL +includes = [ + "**/ServiceA.wsdl": ["-wsdlLocation", "https://example.com/service-a?wsdl"], + "**/ServiceB.wsdl": ["-wsdlLocation", "https://example.com/service-b?wsdl"] +] +``` + +### Configure the output directory + +You can optionally specify the directory for the generated source through the `generatedSourceDir` property, which +defaults to `$buildDir/generated/sources/wsdl2java/java`. +Example: + +```kotlin +wsdl2java { + generatedSourceDir.set(layout.projectDirectory.dir("src/generated/wsdl2java")) +} +``` + +Note that the directory will be wiped completely on each run, so don't put other source files in it. + +### Configure binding files + +A binding file can be added like this: + +```kotlin +wsdl2java { + bindingFile.set(layout.projectDirectory.file("src/main/bindings/binding.xjb")) +} +``` + +If you get a warning on maximum enum member size, you can raise the maximum like this: + +```xml + + + + + + + + + +``` + +If you are using the older `javax` namespace instead of `jakarta`, set the `jxb` attribute to `http://java.sun.com/xml/ns/jaxb` instead and the version to 2.1. + +If you like to use the Java Date/Time API instead of the more clunky GregorianCalendar class, you can +use [threeten-jaxb](https://github.com/threeten-jaxb/threeten-jaxb) library with a binding file like example below. + +```kotlin +dependencies { + implementation("io.github.threeten-jaxb:threeten-jaxb-core:2.1.0") +} + +wsdl2java { + bindingFile.set(layout.projectDirectory.file("src/main/bindings/bindings.xjb")) +} +``` + +```xml + + + + + + + + + + + +``` + +In the above, for `javax`, use version 1.2.0 and the binding attributes to `xmlns="http://java.sun.com/xml/ns/jaxb" version="2.1"`. + +Note that at the moment, it is not possible to specify more than one binding file in the extension. If you require this, +use the `-b` option. + +### Configuring package names + +The package name for the generated resources can be configured with the `packageName` field: + +```kotlin +wsdl2java { + packageName.set("com.github.bjornvester.wsdl2java.group1") +} + +``` + +### Configure encoding + +If your WSDL files include non-ANSI characters, you should set the corresponding file encoding in your gradle.properties +file. E.g.: + +```properties +org.gradle.jvmargs=-Dfile.encoding=UTF-8 +``` + +If you are on a POSIX operating system (e.g. Linux), you may in addition to this need to set your operating system +locale to one that supports your encoding. +Otherwise, Java (and therefore also Gradle and CXF) may not be able to create files with names outside of what your +default locale supports. +Especially some Docker images, like the Java ECR images from AWS, are by default set to a locale supporting ASCII only. +If this is the case for you, and you want to use UTF-8, you could export an environment variable like this: + +```shell script +export LANG=C.UTF-8 +``` + +### Using javax instead of the jakarta namespace + +To use the older javax namespace in the generated files, set the `useJakarta` property to `false. + +```kotlin +wsdl2java { + useJakarta.set(false) +} +``` + +### Activating (third party) XJC plugins + +To use third party plugins for the underlying XJC tool, supply the relevant dependencies to the `xjcPlugins` +configuration. +Then set the plugin options through the `options` property. + +For example, to use the "Equals" and "Hashcode" plugin from +the [JAXB2 Basics](https://github.com/highsource/jaxb2-basics) project, configure the following: + +```kotlin +dependencies { + // For CXF 3 and javax only (does not work with CXF 4 and jakarta): + implementation("org.jvnet.jaxb2_commons:jaxb2-basics-runtime:0.13.1") + xjcPlugins("org.jvnet.jaxb2_commons:jaxb2-basics:0.13.1") +} + +wsdl2java { + options.addAll("-xjc-Xequals", "-xjc-XhashCode") +} +``` + +Note that the example above is for CXF 3 and the older `javax` namespace. +There is a fork [here](https://github.com/patrodyne/hisrc-basicjaxb) that you may try for CXF 4 and the `jakarta` namespace. + +### Enabling the use of the @Generated annotation + +CXF (and the underlying XJC tool) can add a `@Generated` annotation to the generated source code. +This is a source annotation useful for marking classes as generated by a tool. +It can also be used by some static code analytic tools to skip these classes. +You can set it by the `markGenerated` configuration: + +```kotlin +wsdl2java { + markGenerated.set(true) +} +``` + +While very useful in theory, there are some gotchas with this annotation. +One is that it is a source code annotation and thus won't work with tools that work with byte code (like JaCoCo). +Another is that here are actually three `@Generated` annotations. + +* The first is `javax.annotation.Generated` and is available in the JDK up til and including Java 8. It is also included in the `javax.annotation:javax.annotation-api` dependency. +* The second is `javax.annotation.processing.Generated` and is available in the JDK from Java 9. +* the third is `jakarta.annotation.Generated` and is included in the `jakarta.annotation:jakarta.annotation-api` dependency. + +XJC 3 uses the first, and XJC 4+ uses the third. + +Because your tools may only support a particular version of the `@Generated` annotation, you can select which one through the `generatedStyle` configuration. +Supported values are: `default`, `jdk8`, `jdk9` and `jakarta`. +Example: + +```kotlin +dependencies { + // The dependency below is only needed if using the Java 8 version of @Generated (through "jdk8") on Java 9 or later + implementation("javax.annotation:javax.annotation-api:1.3.2") +} + +wsdl2java { + markGenerated.set("jdk8") +} +``` + +The plugin will also, by default, strip any timestamps from the `@Generated` annotations. +This is to support reproducible builds and the Gradle cache. +This can be disabled by the `suppressGeneratedDate` configuration: + +```kotlin +wsdl2java { + suppressGeneratedDate.set(false) +} +``` + +## Groups +If the `includesWithOptions` is not enough, you can also make groups of WSDL files, each having their own extension. +This is done like this: + +```kotlin +wsdl2java { + groups { + register("group1") { + includes.set(listOf("**/HelloWorldAService.wsdl")) + options.set(listOf("-wsdlLocation", "MyLocationA")) + packageName.set("com.github.bjornvester.wsdl2java.group1") + } + register("group2") { + includes.set(listOf("**/HelloWorldBService.wsdl")) + options.set(listOf("-wsdlLocation", "MyLocationB")) + packageName.set("com.github.bjornvester.wsdl2java.group2") + } + } +} +``` + +In order to avoid generating resources with the same name but different content (for instance like the generated `ObjectFactory` class), you should make sure each group use its own package name. + +## Other + +The plugin will add the following two dependencies to your `implementation` configuration: + +``` +jakarta.xml.ws:jakarta.xml.ws-api:2.3.3 +jakarta.jws:jakarta.jws-api:1.1.1 +``` + +These are required to compile the generated code. +However, depending on your runtime platform, you may want to exclude them and instead either put them in the `compileOnly` configuration, or use whatever libraries that are +provided by the platform. + +There are full examples available in the integration-test directory. + +If you need to compile additional XML schemas (xsd files) not directly referenced by the wsdl files, you can use +the [com.github.bjornvester.xjc](https://plugins.gradle.org/plugin/com.github.bjornvester.xjc) plugin in addition. + +## Limitations + +### CXF does not check for unique names in generated resources + +The CXF tool will overwrite generated classes from multiple WSDL files if they have the same qualified name. +Especially the `ObjectFactory` class might be overwritten, which is very annoying. + +### CXF is not deterministic + +When CXF generates an `ObjectFactory` class, the order of the methods are not deterministic, but depend on the absolute path of the input files. +This may cause misses in the Gradle build cache, ecp, which may propagate to downstream projects, resulting in long build times. +I have a local fix for this and I hope to create a PR for it to the relevant Apache project. + +## Contributions + +I often feel a bit stretched out in terms of maintaining this and my other projects. +For that reason, I might not respond to issues and PRs very often. diff --git a/build.gradle.kts b/build.gradle.kts index be30ddd..4f1c8ca 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,65 +1,60 @@ -plugins { - `kotlin-dsl` - id("java-gradle-plugin") - id("com.gradle.plugin-publish") version "1.2.0" -} - -group = "com.github.bjornvester" -version = "2.0" - -repositories { - mavenCentral() -} - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(8)) - } -} - -// The integration test folder is an input to the unit test in the root project -// Register these files as inputs -tasks.withType().configureEach { - inputs - .files(layout.projectDirectory.dir("integration-test").asFileTree.matching { - exclude("**/build/**") - exclude("**/gradle/**") - }) - .withPathSensitivity(PathSensitivity.RELATIVE) - useJUnitPlatform() - systemProperty("GRADLE_ROOT_FOLDER", projectDir.absolutePath) - systemProperty("GRADLE_PLUGIN_VERSION", version) -} - -tasks.withType { - gradleVersion = "latest" -} - -dependencies { - compileOnly("org.apache.cxf:cxf-tools-wsdlto-core:4.0.2") - testImplementation("commons-io:commons-io:2.13.0") - testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3") - testImplementation("org.junit.jupiter:junit-jupiter-params") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") -} - -gradlePlugin { - website.set("https://github.com/bjornvester/wsdl2java-gradle-plugin") - vcsUrl.set("https://github.com/bjornvester/wsdl2java-gradle-plugin") - plugins { - create("wsdl2JavaPlugin") { - id = "com.github.bjornvester.wsdl2java" - description = """Adds the CXF wsdl2java tool to your project. - |Please see the Github project page for details.""".trimMargin() - displayName = "Gradle Wsdl2Java plugin" - tags.set(listOf("wsdl2java", "cxf", "wsimport")) - implementationClass = "com.github.bjornvester.wsdl2java.Wsdl2JavaPlugin" - description = "Changes:\n" + - " - Added support for using the jakarta namespace, which is now the default. The older javax namespace can be enabled with a configuration change.\n" + - " - Added support for the Gradle configuration cache.\n" + - " - The Wsdl2Java task now runs with the configured, or default, Java Toolchain.\n" + - " - Minimum required of version of Gradle is now 6.7 (up from 6.0).\n" + - " - The configurations for marking generated code has changed to support a third variant of the Generated annotation. See the README for details." - } - } -} +plugins { + `kotlin-dsl` + id("java-gradle-plugin") + id("com.gradle.plugin-publish") version "1.2.0" +} + +group = "com.github.bjornvester" +version = "2.0.1" + +repositories { + mavenCentral() +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(8)) + } +} + +// The integration test folder is an input to the unit test in the root project +// Register these files as inputs +tasks.withType().configureEach { + inputs + .files(layout.projectDirectory.dir("integration-test").asFileTree.matching { + exclude("**/build/**") + exclude("**/gradle/**") + }) + .withPathSensitivity(PathSensitivity.RELATIVE) + useJUnitPlatform() + systemProperty("GRADLE_ROOT_FOLDER", projectDir.absolutePath) + systemProperty("GRADLE_PLUGIN_VERSION", version) +} + +tasks.withType { + gradleVersion = "latest" +} + +dependencies { + compileOnly("org.apache.cxf:cxf-tools-wsdlto-core:4.0.2") + testImplementation("commons-io:commons-io:2.13.0") + testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3") + testImplementation("org.junit.jupiter:junit-jupiter-params") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") +} + +gradlePlugin { + website.set("https://github.com/bjornvester/wsdl2java-gradle-plugin") + vcsUrl.set("https://github.com/bjornvester/wsdl2java-gradle-plugin") + plugins { + create("wsdl2JavaPlugin") { + id = "com.github.bjornvester.wsdl2java" + displayName = "Gradle Wsdl2Java plugin" + tags.set(listOf("wsdl2java", "cxf", "wsimport")) + implementationClass = "com.github.bjornvester.wsdl2java.Wsdl2JavaPlugin" + description = "Changes:\n" + + " - Fixed a problem in older versions of Gradle when not specifying a Java toolchain.\n" + + " - Due to compatability issues not found version 2.0.0 of the plugin, the plugin now requires at least Gradle 7.6" + } + } +} diff --git a/integration-test/buildSrc/src/main/kotlin/com.github.bjornvester.wsdl2java.internal.java-conventions-cxf3.gradle.kts b/integration-test/buildSrc/src/main/kotlin/com.github.bjornvester.wsdl2java.internal.java-conventions-cxf3.gradle.kts index 4c01823..206fe10 100644 --- a/integration-test/buildSrc/src/main/kotlin/com.github.bjornvester.wsdl2java.internal.java-conventions-cxf3.gradle.kts +++ b/integration-test/buildSrc/src/main/kotlin/com.github.bjornvester.wsdl2java.internal.java-conventions-cxf3.gradle.kts @@ -13,8 +13,6 @@ dependencies { java { toolchain { - // Note that the unit test in the root project modifies the line below - // Be careful making changes languageVersion.set(JavaLanguageVersion.of(8)) } } diff --git a/integration-test/buildSrc/src/main/kotlin/com.github.bjornvester.wsdl2java.internal.java-conventions-cxf4.gradle.kts b/integration-test/buildSrc/src/main/kotlin/com.github.bjornvester.wsdl2java.internal.java-conventions-cxf4.gradle.kts index 2d2b99b..f5ef569 100644 --- a/integration-test/buildSrc/src/main/kotlin/com.github.bjornvester.wsdl2java.internal.java-conventions-cxf4.gradle.kts +++ b/integration-test/buildSrc/src/main/kotlin/com.github.bjornvester.wsdl2java.internal.java-conventions-cxf4.gradle.kts @@ -14,8 +14,6 @@ dependencies { java { toolchain { - // Note that the unit test in the root project modifies the line below - // Be careful making changes languageVersion.set(JavaLanguageVersion.of(17)) } } \ No newline at end of file diff --git a/integration-test/cxf3/bindings-datetime-test/src/main/bindings/bindings.xml b/integration-test/cxf3/bindings-datetime-test/src/main/bindings/bindings.xml index 5471c8e..b0ee119 100644 --- a/integration-test/cxf3/bindings-datetime-test/src/main/bindings/bindings.xml +++ b/integration-test/cxf3/bindings-datetime-test/src/main/bindings/bindings.xml @@ -1,5 +1,6 @@ + xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" + extensionBindingPrefixes="xjc"> diff --git a/integration-test/cxf4/bindings-datetime-test-jakarta/src/main/bindings/bindings.xml b/integration-test/cxf4/bindings-datetime-test-jakarta/src/main/bindings/bindings.xml index 1e143dd..00fd265 100644 --- a/integration-test/cxf4/bindings-datetime-test-jakarta/src/main/bindings/bindings.xml +++ b/integration-test/cxf4/bindings-datetime-test-jakarta/src/main/bindings/bindings.xml @@ -1,5 +1,6 @@ + xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" + extensionBindingPrefixes="xjc"> diff --git a/integration-test/settings.gradle.kts b/integration-test/settings.gradle.kts index d6ec7d1..367005d 100644 --- a/integration-test/settings.gradle.kts +++ b/integration-test/settings.gradle.kts @@ -1,21 +1,19 @@ -plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version ("0.5.0") -} - -includeBuild("..") - -include( - // Note that the lines below are modified by the unit test in the root project. - // Be careful making changes. - "cxf4:bindings-datetime-test-jakarta", - "cxf4:filter-test", - "cxf4:grouping-test", - "cxf4:includes-options-test", - "cxf4:utf8-test", - - "cxf3:bindings-datetime-test", - "cxf3:generated-annotation-test", - "cxf3:xjc-plugins-test" -) - -//enableFeaturePreview("STABLE_CONFIGURATION_CACHE") +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version ("0.5.0") +} + +includeBuild("..") + +include( + // Note that the lines below are modified by the unit test in the root project. + // Be careful making changes. + "cxf4:bindings-datetime-test-jakarta", + "cxf4:filter-test", + "cxf4:grouping-test", + "cxf4:includes-options-test", + "cxf4:utf8-test", + + "cxf3:bindings-datetime-test", + "cxf3:generated-annotation-test", + "cxf3:xjc-plugins-test" +) diff --git a/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaPlugin.kt b/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaPlugin.kt index 9ce115b..8e17f68 100644 --- a/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaPlugin.kt +++ b/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaPlugin.kt @@ -1,125 +1,120 @@ -package com.github.bjornvester.wsdl2java - -import com.github.bjornvester.wsdl2java.Wsdl2JavaPluginExtension.Companion.GENERATED_STYLE_JDK9 -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.Dependency -import org.gradle.api.plugins.JavaPlugin -import org.gradle.api.plugins.JavaPluginExtension -import org.gradle.api.tasks.SourceSet.MAIN_SOURCE_SET_NAME -import org.gradle.api.tasks.SourceSetContainer -import org.gradle.api.tasks.TaskProvider -import org.gradle.jvm.toolchain.JavaToolchainService -import org.gradle.util.GradleVersion - -class Wsdl2JavaPlugin : Plugin { - companion object { - const val MINIMUM_GRADLE_VERSION = "6.7" - const val MINIMUM_GRADLE_VERSION_GROUPING = "7.0" - const val PLUGIN_ID = "com.github.bjornvester.wsdl2java" - const val WSDL2JAVA_TASK_NAME = "wsdl2java" - const val WSDL2JAVA_EXTENSION_NAME = "wsdl2java" - const val WSDL2JAVA_CONFIGURATION_NAME = "wsdl2java" - const val XJC_PLUGINS_CONFIGURATION_NAME = "xjcPlugins" - } - - override fun apply(project: Project) { - project.logger.info("Applying $PLUGIN_ID to project ${project.name}") - verifyGradleVersion() - project.plugins.apply(JavaPlugin::class.java) - val extension = project.extensions.create(WSDL2JAVA_EXTENSION_NAME, Wsdl2JavaPluginExtension::class.java) - val wsdl2JavaConfiguration = createResolvableConfiguration(project, WSDL2JAVA_CONFIGURATION_NAME) - createResolvableConfiguration(project, XJC_PLUGINS_CONFIGURATION_NAME) - - wsdl2JavaConfiguration.defaultDependencies { - addLater(extension.cxfVersion.map { project.dependencies.create("org.apache.cxf:cxf-tools-wsdlto-frontend-jaxws:$it") }) - addLater(extension.cxfVersion.map { project.dependencies.create("org.apache.cxf:cxf-tools-wsdlto-databinding-jaxb:$it") }) - addLater(extension.useJakarta.map { if (it) "3.0.1" else "2.3.3" }.map { project.dependencies.create("jakarta.xml.ws:jakarta.xml.ws-api:$it") }) - addLater(extension.useJakarta.map { if (it) "2.1.1" else "1.3.5" }.map { project.dependencies.create("jakarta.annotation:jakarta.annotation-api:$it") }) - add(project.dependencies.create("org.slf4j:slf4j-simple:1.7.36")) - } - - project.configurations.named("implementation") { - // The listProperty thing is a work-around for https://github.com/gradle/gradle/issues/13255 - dependencies.addAllLater(project.objects.listProperty(Dependency::class.java).convention(extension.addCompilationDependencies.map { - val deps = listOf().toMutableList(); - if (it) { - val wsApiVersion = if (extension.useJakarta.get()) "3.0.1" else "2.3.3" - val jwsApiVersion = if (extension.useJakarta.get()) "3.0.0" else "1.1.1" - deps.add(project.dependencies.create("jakarta.xml.ws:jakarta.xml.ws-api:$wsApiVersion")) - deps.add(project.dependencies.create("jakarta.jws:jakarta.jws-api:$jwsApiVersion")) - if (extension.markGenerated.get() && extension.generatedStyle.get() != GENERATED_STYLE_JDK9) { - val annotationsApiVersion = if (extension.useJakarta.get()) "2.1.1" else "1.3.5" - deps.add(project.dependencies.create("jakarta.annotation:jakarta.annotation-api:$annotationsApiVersion")) - } - } - deps - })) - } - - val defaultTask = addWsdl2JavaTask(WSDL2JAVA_TASK_NAME, project, extension) - - extension.groups.all { - if (GradleVersion.current() < GradleVersion.version(MINIMUM_GRADLE_VERSION_GROUPING)) { - throw UnsupportedOperationException("Plugin $PLUGIN_ID requires at least Gradle $MINIMUM_GRADLE_VERSION_GROUPING when using the 'groups' property, but you are using ${GradleVersion.current().version}") - } - - defaultTask.configure { - enabled = false - } - - addWsdl2JavaTask(WSDL2JAVA_TASK_NAME + name.replaceFirstChar(Char::titlecase), project, this) - } - } - - private fun addWsdl2JavaTask(name: String, project: Project, group: Wsdl2JavaPluginExtensionGroup): TaskProvider { - val wsdl2JavaTask = project.tasks.register(name, Wsdl2JavaTask::class.java) { - wsdlInputDir.convention(group.wsdlDir) - includes.convention(group.includes) - includesWithOptions.convention(group.includesWithOptions) - bindingFile.convention(group.bindingFile) - options.convention(group.options) - verbose.convention(group.verbose) - suppressGeneratedDate.convention(group.suppressGeneratedDate) - markGenerated.convention(group.markGenerated) - sourcesOutputDir.convention(group.generatedSourceDir) - packageName.convention(group.packageName) - wsdl2JavaConfiguration.from(project.configurations.named(WSDL2JAVA_CONFIGURATION_NAME)) - xjcPluginsConfiguration.from(project.configurations.named(XJC_PLUGINS_CONFIGURATION_NAME)) - - val toolchainService = project.extensions.getByType(JavaToolchainService::class.java) - val currentJavaToolchain = project.extensions.getByType(JavaPluginExtension::class.java).toolchain - val currentJvmLauncherProvider = toolchainService.launcherFor(currentJavaToolchain) - javaLauncher.convention(currentJvmLauncherProvider) - - val sourceSets = project.properties["sourceSets"] as SourceSetContainer - sourceSets.named(MAIN_SOURCE_SET_NAME) { - java.srcDir(sourcesOutputDir) - } - } - - project.tasks.named(JavaPlugin.COMPILE_JAVA_TASK_NAME) { - dependsOn(wsdl2JavaTask) - } - - return wsdl2JavaTask - } - - private fun verifyGradleVersion() { - if (GradleVersion.current() < GradleVersion.version(MINIMUM_GRADLE_VERSION)) { - throw UnsupportedOperationException( - "Plugin $PLUGIN_ID requires at least Gradle $MINIMUM_GRADLE_VERSION, " + - "but you are using ${GradleVersion.current().version}" - ) - } - } - - private fun createResolvableConfiguration(project: Project, name: String): Configuration { - return project.configurations.maybeCreate(name).apply { - isCanBeConsumed = false - isCanBeResolved = true - isVisible = false - } - } -} +package com.github.bjornvester.wsdl2java + +import com.github.bjornvester.wsdl2java.Wsdl2JavaPluginExtension.Companion.GENERATED_STYLE_JDK9 +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.Dependency +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.tasks.SourceSet.MAIN_SOURCE_SET_NAME +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.TaskProvider +import org.gradle.jvm.toolchain.JavaToolchainService +import org.gradle.util.GradleVersion + +class Wsdl2JavaPlugin : Plugin { + companion object { + const val MINIMUM_GRADLE_VERSION = "7.6" + const val PLUGIN_ID = "com.github.bjornvester.wsdl2java" + const val WSDL2JAVA_TASK_NAME = "wsdl2java" + const val WSDL2JAVA_EXTENSION_NAME = "wsdl2java" + const val WSDL2JAVA_CONFIGURATION_NAME = "wsdl2java" + const val XJC_PLUGINS_CONFIGURATION_NAME = "xjcPlugins" + } + + override fun apply(project: Project) { + project.logger.info("Applying $PLUGIN_ID to project ${project.name}") + verifyGradleVersion() + project.plugins.apply(JavaPlugin::class.java) + val extension = project.extensions.create(WSDL2JAVA_EXTENSION_NAME, Wsdl2JavaPluginExtension::class.java) + val wsdl2JavaConfiguration = createResolvableConfiguration(project, WSDL2JAVA_CONFIGURATION_NAME) + createResolvableConfiguration(project, XJC_PLUGINS_CONFIGURATION_NAME) + + wsdl2JavaConfiguration.defaultDependencies { + addLater(extension.cxfVersion.map { project.dependencies.create("org.apache.cxf:cxf-tools-wsdlto-frontend-jaxws:$it") }) + addLater(extension.cxfVersion.map { project.dependencies.create("org.apache.cxf:cxf-tools-wsdlto-databinding-jaxb:$it") }) + addLater(extension.useJakarta.map { if (it) "3.0.1" else "2.3.3" }.map { project.dependencies.create("jakarta.xml.ws:jakarta.xml.ws-api:$it") }) + addLater(extension.useJakarta.map { if (it) "2.1.1" else "1.3.5" }.map { project.dependencies.create("jakarta.annotation:jakarta.annotation-api:$it") }) + add(project.dependencies.create("org.slf4j:slf4j-simple:1.7.36")) + } + + project.configurations.named(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME) { + // The listProperty thing is a work-around for https://github.com/gradle/gradle/issues/13255 + dependencies.addAllLater(project.objects.listProperty(Dependency::class.java).convention(extension.addCompilationDependencies.map { + val deps = listOf().toMutableList() + if (it) { + val wsApiVersion = if (extension.useJakarta.get()) "3.0.1" else "2.3.3" + val jwsApiVersion = if (extension.useJakarta.get()) "3.0.0" else "1.1.1" + deps.add(project.dependencies.create("jakarta.xml.ws:jakarta.xml.ws-api:$wsApiVersion")) + deps.add(project.dependencies.create("jakarta.jws:jakarta.jws-api:$jwsApiVersion")) + if (extension.markGenerated.get() && extension.generatedStyle.get() != GENERATED_STYLE_JDK9) { + val annotationsApiVersion = if (extension.useJakarta.get()) "2.1.1" else "1.3.5" + deps.add(project.dependencies.create("jakarta.annotation:jakarta.annotation-api:$annotationsApiVersion")) + } + } + deps + })) + } + + val defaultTask = addWsdl2JavaTask(WSDL2JAVA_TASK_NAME, project, extension) + + extension.groups.all { + defaultTask.configure { + enabled = false + } + + addWsdl2JavaTask(WSDL2JAVA_TASK_NAME + name.replaceFirstChar(Char::titlecase), project, this) + } + } + + private fun addWsdl2JavaTask(name: String, project: Project, group: Wsdl2JavaPluginExtensionGroup): TaskProvider { + val wsdl2JavaTask = project.tasks.register(name, Wsdl2JavaTask::class.java) { + wsdlInputDir.convention(group.wsdlDir) + includes.convention(group.includes) + includesWithOptions.convention(group.includesWithOptions) + bindingFile.convention(group.bindingFile) + options.convention(group.options) + verbose.convention(group.verbose) + suppressGeneratedDate.convention(group.suppressGeneratedDate) + markGenerated.convention(group.markGenerated) + sourcesOutputDir.convention(group.generatedSourceDir) + packageName.convention(group.packageName) + wsdl2JavaConfiguration.from(project.configurations.named(WSDL2JAVA_CONFIGURATION_NAME)) + xjcPluginsConfiguration.from(project.configurations.named(XJC_PLUGINS_CONFIGURATION_NAME)) + + val toolchainService = project.extensions.getByType(JavaToolchainService::class.java) + val currentJavaToolchain = project.extensions.getByType(JavaPluginExtension::class.java).toolchain + val currentJvmLauncherProvider = toolchainService.launcherFor(currentJavaToolchain) + javaLauncher.convention(currentJvmLauncherProvider) + + val sourceSets = project.properties["sourceSets"] as SourceSetContainer + sourceSets.named(MAIN_SOURCE_SET_NAME) { + java.srcDir(sourcesOutputDir) + } + } + + project.tasks.named(JavaPlugin.COMPILE_JAVA_TASK_NAME) { + dependsOn(wsdl2JavaTask) + } + + return wsdl2JavaTask + } + + private fun verifyGradleVersion() { + if (GradleVersion.current() < GradleVersion.version(MINIMUM_GRADLE_VERSION)) { + throw UnsupportedOperationException( + "Plugin $PLUGIN_ID requires at least Gradle $MINIMUM_GRADLE_VERSION, " + + "but you are using ${GradleVersion.current().version}" + ) + } + } + + private fun createResolvableConfiguration(project: Project, name: String): Configuration { + return project.configurations.maybeCreate(name).apply { + isCanBeConsumed = false + isCanBeResolved = true + isVisible = false + } + } +} diff --git a/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaPluginExtension.kt b/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaPluginExtension.kt index 482a8d4..d7e4e95 100644 --- a/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaPluginExtension.kt +++ b/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaPluginExtension.kt @@ -7,8 +7,8 @@ import org.gradle.api.provider.Property import javax.inject.Inject open class Wsdl2JavaPluginExtension @Inject constructor(objects: ObjectFactory, layout: ProjectLayout) : Wsdl2JavaPluginExtensionGroup { - val useJakarta: Property = objects.property(Boolean::class.java).convention(true) - val cxfVersion: Property = objects.property(String::class.java).convention(useJakarta.map { if (it) "4.0.2" else "3.5.6" }) + val useJakarta = objects.property(Boolean::class.java).convention(true) + val cxfVersion = objects.property(String::class.java).convention(useJakarta.map { if (it) "4.0.2" else "3.5.6" }) val addCompilationDependencies: Property = objects.property(Boolean::class.java).convention(true) override val name = "Defaults" diff --git a/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaTask.kt b/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaTask.kt index bfd4eb9..0dcda0e 100644 --- a/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaTask.kt +++ b/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaTask.kt @@ -20,9 +20,9 @@ import javax.inject.Inject @CacheableTask abstract class Wsdl2JavaTask @Inject constructor( - private val workerExecutor: WorkerExecutor, - private val fileOperations: FileOperations, - objects: ObjectFactory + private val workerExecutor: WorkerExecutor, + private val fileOperations: FileOperations, + objects: ObjectFactory ) : DefaultTask() { @get:InputDirectory @get:PathSensitive(PathSensitivity.RELATIVE) @@ -71,6 +71,7 @@ abstract class Wsdl2JavaTask @Inject constructor( @get:OutputDirectory val sourcesOutputDir: DirectoryProperty = objects.directoryProperty().convention(getWsdl2JavaExtension().generatedSourceDir) + @Optional @get:Nested val javaLauncher: Property = objects.property(JavaLauncher::class.java) @@ -87,7 +88,9 @@ abstract class Wsdl2JavaTask @Inject constructor( fileOperations.mkdir(sourcesOutputDir) val workerExecutor = workerExecutor.processIsolation { - forkOptions.executable = javaLauncher.get().executablePath.asFile.absolutePath; + if (javaLauncher.isPresent) { + forkOptions.executable = javaLauncher.get().executablePath.asFile.absolutePath + } /* All gradle worker processes have Xerces2 on the classpath. @@ -98,10 +101,10 @@ abstract class Wsdl2JavaTask @Inject constructor( The JDK comes with an internal implementation of a SAXParser, also based on Xerces, but supports the properties to control external file access. */ forkOptions.systemProperties = mapOf( - "javax.xml.parsers.DocumentBuilderFactory" to "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl", - "javax.xml.parsers.SAXParserFactory" to "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl", - "javax.xml.validation.SchemaFactory:http://www.w3.org/2001/XMLSchema" to "org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory", - "javax.xml.accessExternalSchema" to "all" + "javax.xml.parsers.DocumentBuilderFactory" to "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl", + "javax.xml.parsers.SAXParserFactory" to "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl", + "javax.xml.validation.SchemaFactory:http://www.w3.org/2001/XMLSchema" to "org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory", + "javax.xml.accessExternalSchema" to "all" ) if (logger.isDebugEnabled) { @@ -114,8 +117,8 @@ abstract class Wsdl2JavaTask @Inject constructor( forkOptions.environment("LANG", System.getenv("LANG") ?: "C.UTF-8") classpath - .from(wsdl2JavaConfiguration) - .from(xjcPluginsConfiguration) + .from(wsdl2JavaConfiguration) + .from(xjcPluginsConfiguration) } val defaultArgs = buildDefaultArguments() @@ -140,37 +143,37 @@ abstract class Wsdl2JavaTask @Inject constructor( } private fun addWsdlToArgs( - includePattern: List?, - defaultArgs: List, - wsdlToArgs: MutableMap> + includePattern: List?, + defaultArgs: List, + wsdlToArgs: MutableMap> ) { wsdlInputDir - .asFileTree - .matching { if (includePattern != null) include(includePattern) } - .forEach { wsdlFile -> - val computedArgs = mutableListOf() - computedArgs.addAll(defaultArgs) - - if (!computedArgs.contains("-wsdlLocation")) { - computedArgs.addAll( - listOf( - "-wsdlLocation", - wsdlFile.relativeTo(wsdlInputDir.asFile.get()).invariantSeparatorsPath - ) + .asFileTree + .matching { if (includePattern != null) include(includePattern) } + .forEach { wsdlFile -> + val computedArgs = mutableListOf() + computedArgs.addAll(defaultArgs) + + if (!computedArgs.contains("-wsdlLocation")) { + computedArgs.addAll( + listOf( + "-wsdlLocation", + wsdlFile.relativeTo(wsdlInputDir.asFile.get()).invariantSeparatorsPath ) - } - - computedArgs.add(wsdlFile.path) - wsdlToArgs[wsdlFile.path] = computedArgs + ) } + + computedArgs.add(wsdlFile.path) + wsdlToArgs[wsdlFile.path] = computedArgs + } } private fun buildDefaultArguments(): MutableList { val defaultArgs = mutableListOf( - "-xjc-disableXmlSecurity", - "-autoNameResolution", - "-d", - sourcesOutputDir.get().toString() + "-xjc-disableXmlSecurity", + "-autoNameResolution", + "-d", + sourcesOutputDir.get().toString() ) if (suppressGeneratedDate.get()) { @@ -192,10 +195,10 @@ abstract class Wsdl2JavaTask @Inject constructor( if (bindingFile.isPresent) { defaultArgs.addAll( - listOf( - "-b", - bindingFile.get().asFile.absolutePath - ) + listOf( + "-b", + bindingFile.get().asFile.absolutePath + ) ) } @@ -214,12 +217,12 @@ abstract class Wsdl2JavaTask @Inject constructor( if (options.isPresent || includesWithOptions.isPresent) { val prohibitedOptions = mapOf( - "-verbose" to "Configured through the 'verbose' property", - "-d" to "Configured through the 'generatedSourceDir' property", - "-p" to "Configured through the 'packageName' property", - "-suppress-generated-date" to "Configured through the 'suppressGeneratedDate' property", - "-mark-generated" to "Configured through the 'markGenerated' property", - "-autoNameResolution" to "Configured automatically and cannot currently be overridden" + "-verbose" to "Configured through the 'verbose' property", + "-d" to "Configured through the 'generatedSourceDir' property", + "-p" to "Configured through the 'packageName' property", + "-suppress-generated-date" to "Configured through the 'suppressGeneratedDate' property", + "-mark-generated" to "Configured through the 'markGenerated' property", + "-autoNameResolution" to "Configured automatically and cannot currently be overridden" ) // Note that we allow specifying binding file(s) through the -b parameter, as we otherwise can't configure individual bindings pr. wsdl diff --git a/src/test/kotlin/com/github/bjornvester/IntegrationTest.kt b/src/test/kotlin/com/github/bjornvester/IntegrationTest.kt index e433119..183cf32 100644 --- a/src/test/kotlin/com/github/bjornvester/IntegrationTest.kt +++ b/src/test/kotlin/com/github/bjornvester/IntegrationTest.kt @@ -12,52 +12,33 @@ import java.lang.management.ManagementFactory import java.util.stream.Stream open class IntegrationTest { - @ParameterizedTest(name = "Test plugin with Java version {0} and Gradle version {1}") + @ParameterizedTest(name = "Test plugin with Gradle version {0}") @MethodSource("provideVersions") - fun thePluginWorks(javaVersion: String, gradleVersion: String, @TempDir tempDir: File) { - runGenericBuild(javaVersion, gradleVersion, tempDir) + fun thePluginWorks(gradleVersion: String, @TempDir tempDir: File) { + runGenericBuild(gradleVersion, tempDir) } - private fun runGenericBuild(javaVersion: String, gradleVersion: String, tempDir: File) { + private fun runGenericBuild(gradleVersion: String, tempDir: File) { copyIntegrationTestProject(tempDir) // Remove the "includedBuild" declaration from the settings file tempDir.resolve(SETTINGS_FILE).writeText(tempDir.resolve(SETTINGS_FILE).readText().replace("includeBuild(\"..\")", "")) - if (GradleVersion.version(gradleVersion) < GradleVersion.version("7.0")) { - // The grouping functionality is not supported in older versions - tempDir.resolve(SETTINGS_FILE) - .writeText(tempDir.resolve(SETTINGS_FILE).readText().replace("\"grouping-test\",", "")) + if (GradleVersion.version(gradleVersion) < GradleVersion.version("8.1")) { + // The Gradle configuration cache was not stable until version 8.1 + tempDir.resolve(PROPERTIES_FILE) + .writeText(tempDir.resolve(PROPERTIES_FILE).readText().replace("org.gradle.configuration-cache=true", "")) } - if (javaVersion.toInt() < 17) { - // CXF 4 projects do not support Java < 17 - val settingsContent = tempDir.resolve(SETTINGS_FILE).readText().replace("""\s*"cxf4:.*""".toRegex(), "") - tempDir.resolve(SETTINGS_FILE).writeText(settingsContent) - } - - if (GradleVersion.version(gradleVersion) < GradleVersion.version("7.6")) { - // The Gradle toolchain provisioning is not supported in older versions - tempDir.resolve(SETTINGS_FILE) - .writeText(tempDir.resolve(SETTINGS_FILE).readText().replace("""id\("org.gradle.toolchains.foojay-resolver-convention"\).*""".toRegex(), "")) - } - - // Set the Java version - tempDir.resolve(JAVA_CONVENTIONS_FILE) - .writeText( - tempDir.resolve(JAVA_CONVENTIONS_FILE).readText() - .replace("JavaLanguageVersion.of(8)", "JavaLanguageVersion.of($javaVersion)") - ) - GradleRunner - .create() - .forwardOutput() - .withProjectDir(tempDir) - .withPluginClasspath() - .withArguments("clean", "check", "-i", "-s", "--no-build-cache") - .withGradleVersion(gradleVersion) - .withDebug(isDebuggerAttached()) - .build() + .create() + .forwardOutput() + .withProjectDir(tempDir) + .withPluginClasspath() + .withArguments("clean", "check", "-i", "-s", "--no-build-cache") + .withGradleVersion(gradleVersion) + .withDebug(isDebuggerAttached()) + .build() } private fun copyIntegrationTestProject(tempDir: File) { @@ -78,21 +59,15 @@ open class IntegrationTest { companion object { const val SETTINGS_FILE = "settings.gradle.kts" - const val JAVA_CONVENTIONS_FILE = "buildSrc/src/main/kotlin/com.github.bjornvester.wsdl2java.internal.java-conventions.gradle.kts" + const val PROPERTIES_FILE = "gradle.properties" @JvmStatic @Suppress("unused") fun provideVersions(): Stream? { return Stream.of( - // Test various versions of Gradle, using Java 8 - // This only tests CXF 3 projects - Arguments.of("8", "6.7"), // Minimum required version of Gradle - Arguments.of("8", "7.6.1"), - Arguments.of("8", "8.1.1"), - // Test various versions of Java, other than one used above, and using the newest (at this time) version of Gradle - // This tests both CXF 3 and 4 projects - Arguments.of("11", "8.1.1"), - Arguments.of("17", "8.1.1") + // Test various versions of Gradle + Arguments.of("7.6.1"), // Minimum required version of Gradle + Arguments.of("8.1.1") ) } }