Skip to content

Since 4.0.13, jaxb-maven-plugin no longer resolves the common ancestor type for a choice group whose member types come from a dependency module (regresses to List) #713

Description

@ikvict07

Summary

When a <xs:choice> group's member elements are typed with complex types that share a common
ancestor via xs:extension, and those types are defined in a different Maven module that the
current module depends on via a plain <dependency> (not listed under the jaxb-maven-plugin's
own <configuration><plugins>), XJC/-Xinheritance correctly infers the common ancestor as the
collection element type up through 4.0.12. Starting with 4.0.13 it silently falls back to
List<Object>.

Bisected to PR #668 ("move to maven-resolver-api", fixing #663), released in 4.0.13.

Root cause (as far as I can tell)

  • Pre-4.0.13, resolveXJCPluginArtifacts() built an ArtifactResolutionRequest with
    setArtifact(project.getArtifact()) and setResolveRoot(false). That combination, via the
    legacy Maven RepositorySystem, resolved not only the declared <plugins> dependencies but
    also (as an apparent side effect) the current module's own <dependencies> graph, putting
    their compiled classes on the XJC plugin classloader.
  • Post-4.0.13 (ArtifactResolverUtils.resolveTransitively), the Aether CollectRequest uses a
    bare-coordinate root artifact (no dependency info) and only collectRequest.setDependencies(...)
    from the <plugins> list - the module's own <dependencies> are never folded in.
  • If a module's XSD choice group references types whose common ancestor class is only reachable
    through a project <dependency> (typical when that ancestor is defined and JAXB-compiled in a
    separate schema module, exposed to this module only via an <episode> reference), that
    ancestor class is no longer visible on the XJC classpath, and the common-base-type computation
    (whichever plugin/code performs it under -Xinheritance) can't resolve it, silently degrading
    to Object.

I want to stress "silently" - this doesn't fail the build, it just changes generated code, which
then breaks callers that assigned the getter's return value to a List<TheAncestorType> variable
(as would compile against 4.0.12-generated sources).

Minimal reproduction

Attached: jaxb-repro.zip — two throwaway Maven modules.

  • module-a: XSD defines Shape (base complexType) and Circle/Square (extend Shape).
    Generated + registers an episode.
  • module-b: depends on module-a as a plain <dependency> (not under <plugins>), imports
    module-a's namespace via the episode, and defines:
    <xs:element name="ShapeListResponse">
      <xs:complexType>
        <xs:sequence>
          <xs:choice minOccurs="0" maxOccurs="unbounded">
            <xs:element name="Circle" type="a:Circle"/>
            <xs:element name="Square" type="a:Square"/>
          </xs:choice>
        </xs:sequence>
      </xs:complexType>
    </xs:element>

Toggle the jaxb-plugins.version property in the parent pom.xml and rebuild:

mvn clean install -Djaxb-plugins.version=4.0.12   # -> public List<Shape> getCircleOrSquare()
mvn clean install -Djaxb-plugins.version=4.0.13   # -> public List<Object> getCircleOrSquare()

Verified on both mvn and mvnd, JDK 21.

Workaround

Explicitly redeclare the ancestor-providing module under the jaxb-maven-plugin's own
<configuration><plugins> list (with combine.children="append" if you don't want to repeat the
whole default list), so its classes stay on the XJC classloader regardless of resolver internals:

<plugins combine.children="append">
    <plugin>
        <groupId>org.example.jaxbrepro</groupId>
        <artifactId>module-a</artifactId>
        <version>${project.version}</version>
    </plugin>
</plugins>

This works but has to be applied per affected module, and is easy to miss since the failure mode
is silent (no build error, just a behavior change in generated code).

Ask

Would it make sense for resolveXJCPluginArtifacts() to also fold in the current project's own
compile-scope dependencies (optionally behind a flag, since I understand the 4.0.13 change may
have been intentional scope-narrowing rather than purely a side-effect of the API migration)?
Happy to open a PR if a maintainer can confirm the desired behavior.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions