From 8f655d9d32b10019906b8691a656dcdee84b8d22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20M=C3=B8lg=C3=A5rd=20Vester?= Date: Mon, 17 May 2021 01:47:18 +0200 Subject: [PATCH 1/2] Make it easier to use XJC plugins --- README.md | 21 ++++++- build.gradle.kts | 2 +- integration-test/settings.gradle.kts | 3 +- .../xjc-plugins-test/build.gradle.kts | 13 +++++ .../main/resources/HelloXjcPluginService.wsdl | 58 +++++++++++++++++++ .../bjornvester/wsdl2java/HelloWorldImpl.java | 14 +++++ .../bjornvester/wsdl2java/HelloWorldTest.java | 48 +++++++++++++++ .../bjornvester/wsdl2java/Wsdl2JavaPlugin.kt | 13 ++++- .../bjornvester/wsdl2java/Wsdl2JavaTask.kt | 18 +++++- .../bjornvester/wsdl2java/Wsdl2JavaWorker.kt | 8 ++- 10 files changed, 189 insertions(+), 9 deletions(-) create mode 100644 integration-test/xjc-plugins-test/build.gradle.kts create mode 100644 integration-test/xjc-plugins-test/src/main/resources/HelloXjcPluginService.wsdl create mode 100644 integration-test/xjc-plugins-test/src/test/java/com/github/bjornvester/wsdl2java/HelloWorldImpl.java create mode 100644 integration-test/xjc-plugins-test/src/test/java/com/github/bjornvester/wsdl2java/HelloWorldTest.java diff --git a/README.md b/README.md index fef22c8..41d97ad 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Apply the plugin ID "com.github.bjornvester.wsdl2java" as specific in the [Gradl ```kotlin plugins { - id("com.github.bjornvester.wsdl2java") version "1.0" + id("com.github.bjornvester.wsdl2java") version "1.1" } ``` @@ -182,6 +182,23 @@ wsdl2java { } ``` +### 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 { + 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") +} +``` + ## Other The plugin will add the following two dependencies to your `implementation` configuration: @@ -200,5 +217,5 @@ If you need to compile additional XML schemas (xsd files) not directly reference ## Limitations The CXF tool will overwrite generated classes from multiple WSDL files if they have the same qualified name. Especially the `ObjectFactory` might be overwritten, which is annoying. -There is a similar plugin [here](https://github.com/nilsmagnus/wsdl2java) that merges them together, but it is deprecated. +There is a similar plugin [here](https://github.com/nilsmagnus/wsdl2java) that can merge them together, but it is deprecated. I hope to port that functionality into this plugin at some point. diff --git a/build.gradle.kts b/build.gradle.kts index 6186e6d..0a4c58b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ plugins { } group = "com.github.bjornvester" -version = "1.0" +version = "1.1" repositories { mavenCentral() diff --git a/integration-test/settings.gradle.kts b/integration-test/settings.gradle.kts index faf60a9..60c6c2f 100644 --- a/integration-test/settings.gradle.kts +++ b/integration-test/settings.gradle.kts @@ -4,5 +4,6 @@ include( "bindings-datetime-test", "filter-test", "generated-annotation-test", - "utf8-test" + "utf8-test", + "xjc-plugins-test" ) diff --git a/integration-test/xjc-plugins-test/build.gradle.kts b/integration-test/xjc-plugins-test/build.gradle.kts new file mode 100644 index 0000000..b703bae --- /dev/null +++ b/integration-test/xjc-plugins-test/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("com.github.bjornvester.wsdl2java") + id("com.github.bjornvester.wsdl2java.internal.java-conventions") +} + +dependencies { + 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") +} diff --git a/integration-test/xjc-plugins-test/src/main/resources/HelloXjcPluginService.wsdl b/integration-test/xjc-plugins-test/src/main/resources/HelloXjcPluginService.wsdl new file mode 100644 index 0000000..48d1041 --- /dev/null +++ b/integration-test/xjc-plugins-test/src/main/resources/HelloXjcPluginService.wsdl @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/integration-test/xjc-plugins-test/src/test/java/com/github/bjornvester/wsdl2java/HelloWorldImpl.java b/integration-test/xjc-plugins-test/src/test/java/com/github/bjornvester/wsdl2java/HelloWorldImpl.java new file mode 100644 index 0000000..875b863 --- /dev/null +++ b/integration-test/xjc-plugins-test/src/test/java/com/github/bjornvester/wsdl2java/HelloWorldImpl.java @@ -0,0 +1,14 @@ +package com.github.bjornvester.wsdl2java; + +import com.github.bjornvester.example.xjcplugins.HelloWorld; +import com.github.bjornvester.example.xjcplugins.SayHi; +import com.github.bjornvester.example.xjcplugins.SayHiResponse; + +public class HelloWorldImpl implements HelloWorld { + @Override + public SayHiResponse sayHi(SayHi request) { + SayHiResponse response = new SayHiResponse(); + response.setReturn("Hi, " + request.getArg0()); + return response; + } +} diff --git a/integration-test/xjc-plugins-test/src/test/java/com/github/bjornvester/wsdl2java/HelloWorldTest.java b/integration-test/xjc-plugins-test/src/test/java/com/github/bjornvester/wsdl2java/HelloWorldTest.java new file mode 100644 index 0000000..d35e5c5 --- /dev/null +++ b/integration-test/xjc-plugins-test/src/test/java/com/github/bjornvester/wsdl2java/HelloWorldTest.java @@ -0,0 +1,48 @@ +package com.github.bjornvester.wsdl2java; + +import com.github.bjornvester.example.xjcplugins.HelloWorld; +import com.github.bjornvester.example.xjcplugins.SayHi; +import com.github.bjornvester.example.xjcplugins.SayHiResponse; +import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.jvnet.jaxb2_commons.lang.Equals2; +import org.jvnet.jaxb2_commons.lang.HashCode2; + +import javax.xml.ws.Endpoint; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +class HelloWorldTest { + String serviceAddress = "http://localhost:8899/HellowWorldService"; + Endpoint endpoint; + + @AfterEach + void stopEndpoint() { + if (endpoint != null) { + endpoint.stop(); + } + } + + @Test + void testServerClient() { + // Create server + endpoint = Endpoint.publish(serviceAddress, new HelloWorldImpl()); + + // Create client + JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); + factory.setServiceClass(HelloWorld.class); + factory.setAddress(serviceAddress); + HelloWorld client = (HelloWorld) factory.create(); + + // Call the service + SayHi sayHi = new SayHi(); + sayHi.setArg0("Joe"); + SayHiResponse response = client.sayHi(sayHi); + assertTrue(response.getReturn().contains("Hi")); + + // Check the plugins + assertTrue(sayHi instanceof Equals2); + assertTrue(sayHi instanceof HashCode2); + } +} diff --git a/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaPlugin.kt b/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaPlugin.kt index 8d07d4a..0a542b7 100644 --- a/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaPlugin.kt +++ b/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaPlugin.kt @@ -2,6 +2,7 @@ package com.github.bjornvester.wsdl2java 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.tasks.SourceSet.MAIN_SOURCE_SET_NAME import org.gradle.api.tasks.SourceSetContainer @@ -14,6 +15,7 @@ class Wsdl2JavaPlugin : Plugin { 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) { @@ -21,7 +23,8 @@ class Wsdl2JavaPlugin : Plugin { verifyGradleVersion() project.plugins.apply(JavaPlugin::class.java) val extension = project.extensions.create(WSDL2JAVA_EXTENSION_NAME, Wsdl2JavaPluginExtension::class.java) - val wsdl2JavaConfiguration = project.configurations.maybeCreate(WSDL2JAVA_CONFIGURATION_NAME) + 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") }) @@ -57,4 +60,12 @@ class Wsdl2JavaPlugin : Plugin { ) } } + + 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/Wsdl2JavaTask.kt b/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaTask.kt index 81da821..867c2d9 100644 --- a/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaTask.kt +++ b/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaTask.kt @@ -2,10 +2,14 @@ package com.github.bjornvester.wsdl2java import com.github.bjornvester.wsdl2java.Wsdl2JavaPlugin.Companion.WSDL2JAVA_CONFIGURATION_NAME import com.github.bjornvester.wsdl2java.Wsdl2JavaPlugin.Companion.WSDL2JAVA_EXTENSION_NAME +import com.github.bjornvester.wsdl2java.Wsdl2JavaPlugin.Companion.XJC_PLUGINS_CONFIGURATION_NAME +import com.github.bjornvester.wsdl2java.Wsdl2JavaPluginExtension.Companion.MARK_GENERATED_NO import com.github.bjornvester.wsdl2java.Wsdl2JavaPluginExtension.Companion.MARK_GENERATED_YES_JDK8 import com.github.bjornvester.wsdl2java.Wsdl2JavaPluginExtension.Companion.MARK_GENERATED_YES_JDK9 import org.gradle.api.DefaultTask import org.gradle.api.GradleException +import org.gradle.api.NamedDomainObjectProvider +import org.gradle.api.artifacts.Configuration import org.gradle.api.file.DirectoryProperty import org.gradle.api.internal.file.FileOperations import org.gradle.api.model.ObjectFactory @@ -49,6 +53,9 @@ open class Wsdl2JavaTask @Inject constructor( @get:Classpath val wsdl2JavaConfiguration = project.configurations.named(WSDL2JAVA_CONFIGURATION_NAME) + @get:Classpath + val xjcPluginsConfiguration: NamedDomainObjectProvider = project.configurations.named(XJC_PLUGINS_CONFIGURATION_NAME) + @get:OutputDirectory val sourcesOutputDir: DirectoryProperty = objects.directoryProperty().convention(getWsdl2JavaExtension().generatedSourceDir) @@ -65,7 +72,9 @@ open class Wsdl2JavaTask @Inject constructor( fileOperations.mkdir(sourcesOutputDir) val workerExecutor = workerExecutor.classLoaderIsolation { - classpath.from(wsdl2JavaConfiguration) + classpath + .from(wsdl2JavaConfiguration) + .from(xjcPluginsConfiguration) } val defaultArgs = buildDefaultArguments() @@ -140,6 +149,11 @@ open class Wsdl2JavaTask @Inject constructor( } private fun validateOptions() { + val supportedMarkGeneratedValues = listOf(MARK_GENERATED_NO, MARK_GENERATED_YES_JDK8, MARK_GENERATED_YES_JDK9) + if (markGenerated.get() !in supportedMarkGeneratedValues) { + throw GradleException("The property 'markGenerated' had an invalid value '${markGenerated.get()}'. Supported values are: $supportedMarkGeneratedValues") + } + if (options.isPresent) { val prohibitedOptions = mapOf( "-verbose" to "Configured through the 'verbose' property", @@ -152,7 +166,7 @@ open class Wsdl2JavaTask @Inject constructor( options.get().forEach { option -> if (prohibitedOptions.containsKey(option)) { - throw GradleException("the option '$option' is not allowed in this plugin. Reason: ${prohibitedOptions[option]}") + throw GradleException("The option '$option' is not allowed in this plugin. Reason: ${prohibitedOptions[option]}") } } } diff --git a/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaWorker.kt b/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaWorker.kt index 1ad6911..3041477 100644 --- a/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaWorker.kt +++ b/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaWorker.kt @@ -18,8 +18,12 @@ abstract class Wsdl2JavaWorker : WorkAction { WSDLToJava(args.toTypedArray()).run(ToolContext()) } catch (e: Exception) { // We can't propagate the exception as it might contain classes from CXF which are not available outside the worker execution context - // Also, for some reason, we can't even put the message from the original exception in as it has shown to cause problems with serialization (though it is just a string) - logger.error("Failed to generate sources from WSDL", e) + // Also, for some reason, we can't even log the error as it sometimes fails with: + // java.io.StreamCorruptedException: invalid type code: 0C + // Seems like a bug in Gradle, possible with the error message contain multiple lines + // Until we have found the cause of it, we print directly to System.out + logger.error("Failed to generate sources from WSDL:") + e.printStackTrace() throw GradleException("Failed to generate Java sources from WSDL. See the log for details.") } } From ad67353574bc09f02f5dc7f079cea4a02b2cb9d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20M=C3=B8lg=C3=A5rd=20Vester?= Date: Mon, 17 May 2021 02:09:38 +0200 Subject: [PATCH 2/2] Suppress a ton of harmless warnings that normally is triggered from XJC --- build.gradle.kts | 4 +-- .../bjornvester/wsdl2java/Wsdl2JavaTask.kt | 26 ++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 0a4c58b..1dc77c2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -63,8 +63,8 @@ pluginBundle { "wsdl2JavaPlugin" { displayName = "Gradle Wsdl2Java plugin" description = "Changes:\n" + - " - New configuration options added, including the ability to specify a binding file\n" + - " - Minimum version is now 6.0 (previously 5.4) due to internal use of new APIs" + " - Make it easier to use XJC plugins\n" + + " - Suppress a ton of harmless warnings from XJC" tags = listOf("wsdl2java", "cxf", "wsimport") } } diff --git a/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaTask.kt b/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaTask.kt index 867c2d9..f863cc5 100644 --- a/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaTask.kt +++ b/src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaTask.kt @@ -71,7 +71,30 @@ open class Wsdl2JavaTask @Inject constructor( fileOperations.delete(sourcesOutputDir) fileOperations.mkdir(sourcesOutputDir) - val workerExecutor = workerExecutor.classLoaderIsolation { + val workerExecutor = workerExecutor.processIsolation { + /* + All gradle worker processes have Xerces2 on the classpath. + This version of Xerces does not support checking for external file access (even if not used). + This causes it to log a whole bunch of stack traces on the form: + -- Property "http://javax.xml.XMLConstants/property/accessExternalSchema" is not supported by used JAXP implementation. + To avoid this, we fork the worker API to a separate process where we can set system properties to select which implementation of a SAXParser to use. + 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" + ) + + if (logger.isDebugEnabled) { + // This adds debugging information on the XJC method used to find and load services (plugins) + forkOptions.systemProperties["com.sun.tools.xjc.Options.findServices"] = "" + } + + // Set encoding (work-around for https://github.com/gradle/gradle/issues/13843) + forkOptions.environment("LANG", System.getenv("LANG") ?: "C.UTF-8") + classpath .from(wsdl2JavaConfiguration) .from(xjcPluginsConfiguration) @@ -116,6 +139,7 @@ open class Wsdl2JavaTask @Inject constructor( private fun buildDefaultArguments(): MutableList { val defaultArgs = mutableListOf( + "-xjc-disableXmlSecurity", "-autoNameResolution", "-d", sourcesOutputDir.get().toString()