Skip to content

Commit

Permalink
Add an option to disable process isolation for attaching a debugger t…
Browse files Browse the repository at this point in the history
…o the worker process
  • Loading branch information
bjornvester committed Sep 4, 2023
1 parent 5483094 commit 45a0e66
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 70 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -389,11 +389,11 @@ the [com.github.bjornvester.xjc](https://plugins.gradle.org/plugin/com.github.bj
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
### CXF does not generate resources in a deterministic way

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.
This may cause misses in the Gradle build cache, which may propagate to downstream projects, resulting in long build times.
See https://issues.apache.org/jira/browse/XMLSCHEMA-65 for more information.

## Contributions

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ open class Wsdl2JavaPluginExtension @Inject constructor(objects: ObjectFactory,
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<Boolean> = objects.property(Boolean::class.java).convention(true)
val useProcessIsolation: Property<Boolean> = objects.property(Boolean::class.java).convention(true)

override val name = "Defaults"
override val wsdlDir = objects.directoryProperty().convention(layout.projectDirectory.dir("src/main/resources"))
Expand Down
143 changes: 76 additions & 67 deletions src/main/kotlin/com/github/bjornvester/wsdl2java/Wsdl2JavaTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ import org.gradle.api.plugins.BasePlugin
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import org.gradle.jvm.toolchain.JavaLauncher
import org.gradle.process.JavaForkOptions
import org.gradle.workers.WorkerExecutor
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)
Expand Down Expand Up @@ -75,6 +75,11 @@ abstract class Wsdl2JavaTask @Inject constructor(
@get:Nested
val javaLauncher: Property<JavaLauncher> = objects.property(JavaLauncher::class.java)

@get:Input
@Optional
val useProcessIsolation = objects.property(Boolean::class.java).convention(getWsdl2JavaExtension().useProcessIsolation)


init {
group = BasePlugin.BUILD_GROUP
description = "Generates Java classes from WSDL files."
Expand All @@ -87,38 +92,16 @@ abstract class Wsdl2JavaTask @Inject constructor(
fileOperations.delete(sourcesOutputDir)
fileOperations.mkdir(sourcesOutputDir)

val workerExecutor = workerExecutor.processIsolation {
if (javaLauncher.isPresent) {
forkOptions.executable = javaLauncher.get().executablePath.asFile.absolutePath
val workerExecutor = if (useProcessIsolation.get()) {
workerExecutor.processIsolation {
configureForkOptions(forkOptions)
classpath.from(wsdl2JavaConfiguration).from(xjcPluginsConfiguration)
}

/*
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 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"] = ""
} else {
workerExecutor.classLoaderIsolation {
// More restricted, but allows attaching a debugger to the worker instance
classpath.from(wsdl2JavaConfiguration).from(xjcPluginsConfiguration)
}

// Set encoding (work-around for https://github.com/gradle/gradle/issues/
// Might be fixed in Gradle 8.3 (unreleased at the time of this writing)
forkOptions.environment("LANG", System.getenv("LANG") ?: "C.UTF-8")

classpath
.from(wsdl2JavaConfiguration)
.from(xjcPluginsConfiguration)
}

val defaultArgs = buildDefaultArguments()
Expand All @@ -142,38 +125,64 @@ abstract class Wsdl2JavaTask @Inject constructor(
}
}

private fun configureForkOptions(forkOptions: JavaForkOptions) {
if (javaLauncher.isPresent) {
forkOptions.executable = javaLauncher.get().executablePath.asFile.absolutePath
}

/*
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 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
// This is a work-around for https://github.com/gradle/gradle/issues/13843
// It should be fixed in Gradle 8.3, but we still need to support older versions
forkOptions.environment("LANG", System.getenv("LANG") ?: "C.UTF-8")
}

private fun addWsdlToArgs(
includePattern: List<String>?,
defaultArgs: List<String>,
wsdlToArgs: MutableMap<String, List<String>>
includePattern: List<String>?,
defaultArgs: List<String>,
wsdlToArgs: MutableMap<String, List<String>>
) {
wsdlInputDir
.asFileTree
.matching { if (includePattern != null) include(includePattern) }
.forEach { wsdlFile ->
val computedArgs = mutableListOf<String>()
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<String>()
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<String> {
val defaultArgs = mutableListOf(
"-xjc-disableXmlSecurity",
"-autoNameResolution",
"-d",
sourcesOutputDir.get().toString()
"-xjc-disableXmlSecurity",
"-autoNameResolution",
"-d",
sourcesOutputDir.get().toString()
)

if (suppressGeneratedDate.get()) {
Expand All @@ -195,10 +204,10 @@ abstract class Wsdl2JavaTask @Inject constructor(

if (bindingFile.isPresent) {
defaultArgs.addAll(
listOf(
"-b",
bindingFile.get().asFile.absolutePath
)
listOf(
"-b",
bindingFile.get().asFile.absolutePath
)
)
}

Expand All @@ -217,12 +226,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
Expand Down

0 comments on commit 45a0e66

Please sign in to comment.