com.sun.tools.xjc.Plugin: Provider <plugin> not a subtype
Asked Answered
B

4

9

I have a CXF JAX-RS app being built with Maven. I'm working on converting it to Gradle, but using the Ant XJC task.

The current build uses a couple of extensions, one of which is a copy of the "element wrapper" plugin, and the other is the "jaxb-fluent-api".

I tried putting the jars for those two plugins into the xjc classpath, but when I run the XJC task, I get the following:

java.util.ServiceConfigurationError: com.sun.tools.xjc.Plugin: Provider dk.conspicio.jaxb.plugins.XmlElementWrapperPlugin not a subtype

The XmlElementWrapperPlugin class extends "com.sun.tools.xjc.Plugin".

Any idea what's going on here?

If it matters, my Maven configuration for the xjc plugin looks something like this:

<plugin>
     <groupId>org.apache.cxf</groupId>
     <artifactId>cxf-xjc-plugin</artifactId>
     <executions>
         <execution>
             <id>generate-sources</id>
             <phase>generate-sources</phase>
             <goals>
                <goal>xsdtojava</goal>
             </goals>
             <configuration>
                <extensions>
                    <extension>JAXBXMLElementWrapperPlugin:JAXBXMLElementWrapperPlugin:1.0.0</extension>
                    <extension>net.java.dev.jaxb2-commons:jaxb-fluent-api:2.1.8</extension>
                </extensions>
                <xsdOptions>
                    <xsdOption>
                        <xsd>${basedir}/src/main/resources/schema/serviceCallResults.xsd</xsd>
                        <packagename>com.att.sunlight.service.domain.serviceCallResults</packagename>
                        <extension>true</extension>
                        <extensionArgs>
                            <extensionArg>-Xxew</extensionArg>
                            <extensionArg>-summary ${basedir}/target/xew-summary.txt</extensionArg>
                            <extensionArg>-instantiate lazy</extensionArg>
                            <extensionArg>-Xfluent-api</extensionArg>
                        </extensionArgs>
                    </xsdOption>
                </xsdOptions>
            </configuration>
        </execution>
     </executions>
</plugin>

Here's my "build.gradle", with only the repositories elided:

apply plugin: 'java'
apply plugin: 'maven'
apply plugin: 'war'

group = 'SunlightDataService'
version = '1.2.4-SNAPSHOT'

sourceCompatibility = 1.6
targetCompatibility = 1.6

repositories {
    ...
}

configurations {
    jaxb
}

dependencies {

    jaxb 'com.sun.xml.bind:jaxb-xjc:2.2.7-b41'
    jaxb 'com.sun.xml.bind:jaxb-impl:2.2.7-b41'
    jaxb 'javax.xml.bind:jaxb-api:2.2.7'
    jaxb "JAXBXMLElementWrapperPlugin:JAXBXMLElementWrapperPlugin:1.0.0"
    jaxb "net.java.dev.jaxb2-commons:jaxb-fluent-api:2.1.8"

    compile group: 'org.springframework', name: 'spring-beans', version:'3.2.8.RELEASE'
    compile group: 'org.springframework', name: 'spring-webmvc-portlet', version:'3.2.8.RELEASE'
    compile group: 'org.apache.cxf', name: 'cxf-rt-transports-http', version:'2.7.7'
    compile group: 'log4j', name: 'log4j', version:'1.2.16'
    compile group: 'org.springframework', name: 'spring-jdbc', version:'3.2.8.RELEASE'
    compile group: 'org.springframework', name: 'spring-context', version:'3.2.8.RELEASE'
    compile group: 'org.apache.cxf', name: 'cxf-rt-frontend-jaxrs', version:'2.7.7'
    compile group: 'org.apache.cxf', name: 'cxf-rt-bindings-xml', version:'2.7.7'
    compile group: 'org.apache.cxf', name: 'cxf-rt-databinding-jaxb', version:'2.7.7'
    compile group: 'org.apache.cxf', name: 'cxf-rt-core', version:'2.7.7'
    compile group: 'org.apache.cxf', name: 'cxf-api', version:'2.7.7'
    compile group: 'org.apache.cxf', name: 'cxf-rt-rs-extension-providers', version:'2.7.7'
    compile group: 'org.codehaus.jettison', name: 'jettison', version:'1.3.4'
    compile group: 'org.perf4j', name: 'perf4j', version:'0.9.14'
    compile group: 'cglib', name: 'cglib', version:'2.2.2'
    compile group: 'org.aspectj', name: 'aspectjweaver', version:'1.6.12'
    compile group: 'commons-collections', name: 'commons-collections', version:'3.2.1'
    compile group: 'esGateKeeper', name: 'GLCookieDecryption', version:'1.0.0'
    compile group: 'joda-time', name: 'joda-time', version:'2.3'
    compile group: 'org.apache.jackrabbit', name: 'jackrabbit-core', version:'2.4.0'
    compile group: 'org.apache.commons', name: 'commons-lang3', version:'3.1'
    testCompile group: 'org.springframework', name: 'spring-test', version:'3.2.8.RELEASE'
    testCompile group: 'oracle.jdbc', name: 'oracle.jdbc.OracleDriver', version:'1.0.0'
    testCompile group: 'com.atomikos', name: 'transactions-jta', version:'3.7.0'
    testCompile group: 'org.apache.cxf', name: 'cxf-rt-transports-http-jetty', version:'2.7.7'
    testCompile group: 'com.atomikos', name: 'transactions-jdbc', version:'3.7.0'
    testCompile group: 'org.mockito', name: 'mockito-all', version:'1.9.5'
    testCompile group: 'junit', name: 'junit', version:'4.10'
    testCompile group: 'org.assertj', name: 'assertj-core', version:'1.6.1'
    providedCompile group: 'javax.transaction', name: 'jta', version:'1.1'
    providedCompile group: 'javax.servlet.jsp', name: 'jsp-api', version:'2.1'
    providedCompile group: 'javax.servlet', name: 'servlet-api', version:'2.5'
}

task processXSDs() << {
    ant.taskdef(name: 'xjc', classname: 'com.sun.tools.xjc.XJCTask',
                classpath: configurations.jaxb.asPath)

    ant.xjc(destdir: 'tmp', package: "com.att.sunlight.service.domain.serviceCallResults", extension: true) {
        schema(dir: "src/main/resources/schema", includes: "serviceCallResults.xsd")
        arg(line: "-Xxew")
        arg(line: "-summary target/xew-summary.txt")
        arg(line: "-instantiate lazy")
        arg(line: "-Xfluent-api")
    }
}

compileJava.dependsOn processXSDs

Update:

I've determined that this is not an issue with the "Element Wrapper" extension. If I remove that jar from the classpath and rebuild, it reports the same error for the "Fluent API" plugin.

I've also determined this isn't strictly a Gradle issue. I get the same symptom with an Ant "build.xml", and even a plain shell script directly calling the "XJCFacade" Java class. In fact, I can simplify this script a bit by not even specifying any schema files, which makes it clear this error happens even before trying to process any schemas.

The following is my current script:

#! /bin/bash
java -classpath "lib/commons-beanutils-1.7.0.jar;lib/commons-lang-2.2.jar;lib/commons-logging-1.1.1.jar;lib/istack-commons-runtime-2.16.jar;lib/jaxb2-basics-runtime-0.6.5.jar;lib/jaxb2-basics-tools-0.6.5.jar;lib/jaxb-api-2.2.7.jar;lib/jaxb-core-2.2.7.jar;lib/jaxb-fluent-api-2.1.8.jar;lib/jaxb-xew-plugin-1.4.jar;lib/jaxb-xjc-2.2.7.jar" com.sun.tools.xjc.XJCFacade -extension

You could construct the same test by downloading all of those artifacts to your Gradle or Maven cache and copying them into a simple folder structure.

Also note that I'm running this test mainly on Windows 7, but I zipped up this project and moved it to my CentOS VM and it gives me the exact same error.

Burschenschaft answered 16/1, 2015 at 4:49 Comment(5)
Can you post more of your build.grade? At least the dependencies {...} block.Kast
New revision has the entire script, except for the repositories.Burschenschaft
Try replacing arg(line: "...") with arg(value: "...").Kast
Additionally, you may want to try adding classpath(path: configurations.jaxb.asPath) to the ant.xjc {...} block.Kast
No change. I knew the "arg" change wouldn't matter. I didn't mention that I added those lines after I first saw this error.Burschenschaft
L
15

I have tried

java -cp jaxb-api-2.2.7.jar;jaxb-core-2.2.7.jar;jaxb-xjc-2.2.7.jar;commons-logging-1.1.1.jar;commons-lang-2.2.jar;jaxb2-basics-tools-0.6.5.jar;jaxb-xew-plugin-1.3.jar com.sun.tools.xjc.XJCFacade -verbose -extension -d src xsd

and indeed that fails with Exception in thread "main" java.util.ServiceConfigurationError: com.sun.tools.xjc.Plugin: Provider com.sun.tools.xjc.addon.xew.XmlElementWrapperPlugin not a subtype, so the problem is clearly reproducible. I have debugged java.util.ServiceLoader and it turned out that plugins should be passed as argument to XJC (not to classpath of Java). Actually, all maven plugins (like jaxb2-maven-plugin or maven-jaxb2-plugin …) know about this feature and form XJC argments correctly. So the correct command line is:

java -cp jaxb-api-2.2.7.jar;jaxb-core-2.2.7.jar;jaxb-xjc-2.2.7.jar;commons-lang-2.2.jar;commons-logging-1.1.1.jar com.sun.tools.xjc.XJCFacade -classpath jaxb-xew-plugin-1.3.jar;jaxb2-basics-tools-0.6.5.jar -verbose -extension -Xxew -d src xsd

Note that -classpath is argument for XJC. commons-xxx libs can go to system classpath as the packages they export are not screened, but jaxb2-basics-tools should be in XJC classpath. If you are interested in details:

This happens because XJC does classpath loader screening to be able to load higher versions of JAXB API in JRE that has lower build-in version of API. See XJCFacade.java line 69. That means that com.sun.tools.xjc.Plugin is loaded once by custom XJCFacade classloader while the same class is loaded by another (actually, parent) classloader again when Class.forName("com.sun.tools.xjc.addon.xew.XmlElementWrapperPlugin") is called, but now classes are not equal.

Actually you can workaround like this (solution found after source code inspection):

java -Dcom.sun.tools.xjc.XJCFacade.nohack=true -cp jaxb-core-2.2.7.jar;jaxb-xjc-2.2.7.jar;commons-lang-2.2.jar;commons-logging-1.1.1.jar;jaxb2-basics-tools-0.6.5.jar;jaxb-xew-plugin-1.3.jar com.sun.tools.xjc.XJCFacade -verbose -extension -Xxew -d src xsd

Note that I have removed jaxb-api-2.2.7.jar from Java classpath but you'd better put JAXB API into lib/endorsed as it may not work on different Java versions: works fine for Java 7, because it's JAXB API is close to 2.2.7, but may not work on Java 6 + JAXB API 2.2.11.

Latent answered 28/1, 2015 at 16:50 Comment(3)
In order to summarize the solution for this, I'm trying to characterize the classpath required to "reference" XJC as opposed to "running" XJC. Is it reasonable to separate them on these terms? For instance, the main XJC jars are needed to "reference" XJC, but the extension jars need to be in the "running" classpath. I think calling the second part "running" is somewhat reasonable, but calling the other part "reference" might not be quite right.Burschenschaft
Perhaps "reference" should be better called "initialize", but conceptually you're right: plugins should be promoted to "another" classpath.Latent
For java8 add -Djavax.xml.accessExternalSchema=all to remove the following error: Caused by: org.xml.sax.SAXParseException; systemId: jar:file:m2/repository/com/sun/xml/bind/jaxb-xjc/2.2.8-b01/jaxb-xjc-2.2.8-b01.jar!/com/sun/tools/xjc/reader/xmlschema/bindinfo/binding.xsd; lineNumber: 52; columnNumber: 88; schema_reference: Failed to read schema document 'xjc.xsd', because 'file' access is not allowed due to restriction set by the accessExternalSchema property.Coan
D
6

Kudos to dma_k, but a working gradle solution was never provided. After some experimentation, this is what worked for me:

def xjcGeneratedSourcesDir = "$buildDir/generated-sources/xjc"

configurations {
    jaxb
    xjc
}

sourceSets {
    main {
        java.srcDir xjcGeneratedSourcesDir
    }
}

dependencies {
    jaxb 'com.sun.xml.bind:jaxb-xjc:2.2.11'
    jaxb 'com.sun.xml.bind:jaxb-core:2.2.11'
    jaxb 'com.sun.xml.bind:jaxb-impl:2.2.11'
    jaxb 'javax.xml.bind:jaxb-api:2.2.11'

    xjc "com.github.jaxb-xew-plugin:jaxb-xew-plugin:1.4"
    xjc "net.java.dev.jaxb2-commons:jaxb-fluent-api:2.1.8"

    // etc.
}

task wsdl2java << {
    file( xjcGeneratedSourcesDir ).mkdirs()

    ant.taskdef(name: 'xjc', classname: 'com.sun.tools.xjc.XJCTask',
                classpath: configurations.jaxb.asPath)

    ant.xjc(destdir: xjcGeneratedSourcesDir,
            package: "com.localhost.jaxb", extension: true) {
        schema(dir: "src/main/resources/schema", includes: "*.xsd")
        classpath(path: configurations.xjc.asPath)
        arg(line: "-Xxew")
        arg(line: "-Xxew:summary $buildDir/xew-summary.txt")
        arg(line: "-Xxew:instantiate lazy")
        arg(line: "-Xfluent-api")
    }
}

compileJava.dependsOn wsdl2java
Daina answered 6/3, 2015 at 16:9 Comment(1)
Sorry, I forgot to add more information to this. I also ended up with a similar solution, almost exactly in fact. I found I didn't need to explicitly specify the "jaxb-impl" dependency. I defined my version of "xjcGeneratedSourcesDir " in an "ext" block, which is more Gradle-like, and I also put my "ant.xjc" call in a method that I call several times with specific schemas to process.Burschenschaft
O
0

I wouldn't use the ant or maven task for gradle, but rather invoke the Java class directly, like I did this for org.apache.cxf.tools.wsdlto.WSDLToJava.

Ocieock answered 12/2, 2016 at 15:37 Comment(0)
J
-2

No build/release engineer or jaxb-xjc implementor should be forced to use the internal proprietary JAXB-JXC ClassLoader. The original design was so JDK 1.x users would not load the JAXB-2.2.0.jar ..since 2010 there have been no significant version changes to JAXB and the jaxb-xjc developers stopped maintaining this plugin for JDK 5,6,7,8. I sent an email to the Oracle gurus at CXF-Metro on 31 Jan. If we dont hear back from them lets petition Oracle to release the code to OpenSource so at least it will be maintained. Every implementor of this plugin has been tripped up by this ephemeral solution. No need to further punish future implementors with unmaintained code that suffers from a short-term hack. Also the -DClassLoaderBuilder.noHack option COMPLETELY bypasses Oracles own Security Manager..this is VERBOTEN in Banks and Financial Institutions... I'll post back when I hear back from CXF Metro

Jody answered 1/2, 2015 at 13:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.