Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problems with episodal compilation and (most likely, the solution for #1356) #1735

Open
sjaakd opened this issue Sep 3, 2023 · 1 comment

Comments

@sjaakd
Copy link

sjaakd commented Sep 3, 2023

I'm migrating to jakarta with many of our projects relying on opengis (gml, observation and measurements, sensorweb enablement). After the migration I'm having problems when running my webservices and unit test:

Can't have @XmlAnyAttribute when a base class has it already.
	this problem is related to the following location:
		at @jakarta.xml.bind.annotation.XmlAnyAttribute()
		at org.example.MyTestResultType$Values
		at protected org.example.MyTestResultType$Values org.example.MyTestResultType.values
		at org.example.MyTestResultType
		at public jakarta.xml.bind.JAXBElement org.example.ObjectFactory.createMyTestResult(org.example.MyTestResultType)
		at org.example.ObjectFactory
	this problem is related to the following location:
		at private java.util.Map net.opengis.swecommon.v_2_0.test.EncodedValuesPropertyType.otherAttributes
		at net.opengis.swecommon.v_2_0.test.EncodedValuesPropertyType
		at org.example.MyTestResultType$Values
		at protected org.example.MyTestResultType$Values org.example.MyTestResultType.values
		at org.example.MyTestResultType
		at public jakarta.xml.bind.JAXBElement org.example.ObjectFactory.createMyTestResult(org.example.MyTestResultType)
		at org.example.ObjectFactory

Closer inspection revealed an extra attribute was generated. I wrote an SO issue on that here.

It took me some time to reproduce the issue, since more behavior as changed. Especially around the mixed = true behaviour (no content getter seems to be generated and the java inheritance changed for 'Values`).

Here you can find my reproducer.

Another branch shows that problem of the 'mixed = true' behavior and inheritance of 'Values' that does no longer extend EncodedValuesPropertyType.

I'm going to push my analysis a bit further into why my production code shows the problem with the 'mixed = true' present, so why it deviates from my reproducer.

I use episodal compilation to layer the structure and write common code on the common treats. This is a large product with 20+ datatypes relying on the opengis stack. For more info see: https://schema.broservices.nl/xsd/ . I've no opportunity to change the XSD.


EDIT

I found out why the mixed=true addition caused the issue. The original SWE library has been downsized to exactly the types required in all our datatypes to relief problems that many parties had connecting to our systems. In that process, mixed = true has been added. Check here for the original and here for the profile.

By changing the SWECommon to the profile (used in our production environments) I was able to reproduce the problem I have in migrating to Jakarta. Check here.

It's in the end related to this piece of generated java code:

 @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "")
    public static class Values
        extends EncodedValuesPropertyType
    {

        @XmlAnyAttribute // <- HERE 
        private Map<QName, String> otherAttributes = new HashMap<>(); // <- HERE 

        /**
         * Gets a map that contains attributes that aren't bound to any typed property on this class.
         * 
         * <p>
         * the map is keyed by the name of the attribute and 
         * the value is the string value of the attribute.
         * 
         * the map returned by this method is live, and you can add new attribute
         * by updating the map directly. Because of this design, there's no setter.
         * 
         * 
         * @return
         *     always non-null
         */
        public Map<QName, String> getOtherAttributes() {
            return otherAttributes;
        }

    }

That brings me back to #1356. The fix in 2019 seems to be in this particular area. My suspicion is that generating Java code in one project with this scenario is covered, but the solution does not take into consideration episodal compilation.

Class: com.sun.tools.xjc.model.CClassInfo

image

Please note that there are more problems encountered in the past with episodal compilation. One of them is already 'fixed' in the provided binding for SWECommon: moving a generated base method 'out of the way' to avoid a 'clash' with the overidden one.


EDIT 2
It is still not reproducing the problem I'm having. I missed to comment in mixed = truein my second branch (.. Doing that changes the generated code back from:

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "")
    public static class Values
        extends EncodedValuesPropertyType
    {

to

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "", propOrder = {
        "content"
    })
    public static class Values {

Unlike my production code which has mixed = true in both the inherited - and the base class.. So still not precisely matching my own case. Must be something else at play. But perhaps my problem is already clear. Will keep on trying to find an exact reproducer.

problem is now truly reproduced. Proper schema import changed for reproducer.

@patrodyne
Copy link
Contributor

Here's a small example to demonstrate multiple generation of Map<QName, String> otherAttributes; which as the OP stated, leads to:

IllegalAnnotationsException

org.glassfish.jaxb.runtime.v2.runtime.IllegalAnnotationsException:
Can't have @XmlAnyAttribute when a base class has it already.

For example, this first schema defines an element XType that extends xs:anyType. And an optional XGroup of explicit attributes are added to enrich the example.

a.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="a"
    xmlns:a="a"
>

    <xs:attributeGroup name="XGroup">
        <xs:attribute name="x1" type="xs:string"/>
        <xs:attribute name="x2" type="xs:string"/>
    </xs:attributeGroup>

    <xs:element name="x" type="a:XType"/>
    <xs:complexType name="XType">
        <xs:complexContent>
            <xs:extension base="xs:anyType">
                <xs:attributeGroup ref="a:XGroup"/>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

</xs:schema>

This second schema imports the first schema and defines element YType to extend a:XType. And an optional YGroup of explicit attributes are added to enrich the example.

b.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="b"
    xmlns:b="b"
    xmlns:a="a"
>

    <xs:import namespace="a" schemaLocation="a.xsd"/>

    <xs:attributeGroup name="YGroup">
        <xs:attribute name="y1" type="xs:string"/>
        <xs:attribute name="y2" type="xs:string"/>
    </xs:attributeGroup>

    <xs:complexType name="YType">
        <xs:complexContent>
            <xs:extension base="a:XType">
                <xs:attributeGroup ref="b:YGroup"/>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>

</xs:schema>

The XJC tool is used to generate Java classes for from the first schema and an JAXB episode file. Then it is used to bind the second schema to the episode file to generate the second set of Java classes, episodically.

*xjc-run.sh

#!/bin/sh
rm -fr out
mkdir -p out
xjc -version
xjc -no-header -extension -d out -episode "out/a.episode" a.xsd
xjc -no-header -extension -d out -b "out/a.episode" b.xsd

The resulting layout is:

Example

├── a.xsd
├── b.xsd
├── out
│   ├── a
│   │   ├── ObjectFactory.java
│   │   ├── package-info.java
│   │   └── XType.java
│   ├── a.episode
│   └── b
│       ├── ObjectFactory.java
│       ├── package-info.java
│       └── YType.java
└── xjc-run.sh

Using grep to show that @XmlAnyAttribute is generated multiple times -- which leads to IllegalAnnotationsException.

$ grep -r -A1 "@XmlAnyAttribute" out
out/b/YType.java:    @XmlAnyAttribute
out/b/YType.java-    private Map<QName, String> otherAttributes = new HashMap<>();
--
out/a/XType.java:    @XmlAnyAttribute
out/a/XType.java-    private Map<QName, String> otherAttributes = new HashMap<>();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants