Xmlns lost when StandardExtensionElement Message is sent

  • Smack version : 4.3.4

Xml payload I send with “sendStanza” method :

<message to='foo@bar.foobar.com' id='2793eaf8-8b55-4e14-8b9c-e8fc4be2250c' type='groupchat'>
    <body></body>
    <event xmlns='http://foo.com/protocol/events#card_sent'>
        <card xmlns='http://foo.com/protocol/card'>
            <title>bar</title>
            <text>bar</text>
            <image xmlns='http://foo.com/protocol/card_image'>
                <url>bar</url>
                <description>bar</description>
            </image>
            <actions xmlns='http://foo.com/protocol/actions'>
                <action xmlns='http://foo.com/protocol/action#link'>
                    <title>bar</title>
                    <url>bar</url>
                </action>
                <action xmlns='http://foo.com/protocol/action#link'>
                    <title>bar</title>
                    <url>bar</url>
                </action>
                <action xmlns='http://foo.com/protocol/action#link'>
                    <title>bar</title>
                    <url>bar</url>
                </action>
                <action xmlns='http://foo.com/protocol/action#link'>
                    <title>bar</title>
                    <url>bar</url>
                </action>
                <action xmlns='http://foo.com/protocol/action#link'>
                    <title>bar</title>
                    <url>bar</url>
                </action>
                <action xmlns='http://foo.com/protocol/action#link'>
                    <title>bar</title>
                    <url>bar</url>
                </action>
            </actions>
        </card>
    </event>
</message>

Xml payload really sent to Xmpp (from log.debug) :

<message to='foo@bar.foobar.com' id='2793eaf8-8b55-4e14-8b9c-e8fc4be2250c' type='groupchat'>
    <body></body>
    <event xmlns='http://foo.com/protocol/events#card_sent'>
        <card xmlns='http://foo.com/protocol/card'>
            <title>bar</title>
            <text>bar</text>
            <image xmlns='http://foo.com/protocol/card_image'>
                <url>bar</url>
                <description>bar</description>
            </image>
            <actions xmlns='http://foo.com/protocol/actions'>
                <action xmlns='http://foo.com/protocol/action#link'>
                    <title>bar</title>
                    <url>bar</url>
                </action>
                <action>
                    <title>bar</title>
                    <url>bar</url>
                </action>
                <action>
                    <title>bar</title>
                    <url>bar</url>
                </action>
                <action>
                    <title>bar</title>
                    <url>bar</url>
                </action>
                <action>
                    <title>bar</title>
                    <url>bar</url>
                </action>
                <action>
                    <title>bar</title>
                    <url>bar</url>
                </action>
            </actions>
        </card>
    </event>
</message>

As you can see, the “xmlns” from “actions -> action” are missing (except for the first one that is still present).

  • The relevant code parts :

XmlStringBuilder.java :

public void write(Writer writer, String enclosingNamespace) throws IOException {
        for (CharSequence csq : sb.getAsList()) {
            if (csq instanceof XmlStringBuilder) {
                ((XmlStringBuilder) csq).write(writer, enclosingNamespace);
            }
            else if (csq instanceof XmlNsAttribute) {
                XmlNsAttribute xmlNsAttribute = (XmlNsAttribute) csq;
                if (!xmlNsAttribute.value.equals(enclosingNamespace)) {
                    writer.write(xmlNsAttribute.toString());
                    enclosingNamespace = xmlNsAttribute.value;
                }
            }
            else {
                writer.write(csq.toString());
            }
        }
    }

I ran the code on debug mode :
When I loop on my “actions -> action” => xmlNsAttribute.equals(enclosingNamespace) and so, the code writer.write(xmlNsAttribute.toString()); is never executed after the first “action”.

To validate my guess, I went back to the smackVersion=4.2.4. And the payload really sent was equal to the payload I wanted to send (ie : with xmlns still present / Test OK).

Here is the code of the write method in 4.2.4 :

public void write(Writer writer) throws IOException {
        for (CharSequence csq : sb.getAsList()) {
            if (csq instanceof XmlStringBuilder) {
                ((XmlStringBuilder) csq).write(writer);
            }
            else {
                writer.write(csq.toString());
            }
        }
    }

That appears to be a bug indeed.

This should be fixed in Smack’s current master branch. The following test passes:

    @Test
    public void equalInnerNamespaceTest() {
        StandardExtensionElement innerOne = StandardExtensionElement.builder("inner", "inner-namespace").build();
        StandardExtensionElement innerTwo = StandardExtensionElement.builder("inner", "inner-namespace").build();

        StandardExtensionElement outer = StandardExtensionElement.builder("outer", "outer-namespace").addElement(
                        innerOne).addElement(innerTwo).build();

        String expectedXml = "<outer xmlns='outer-namespace'><inner xmlns='inner-namespace'></inner><inner xmlns='inner-namespace'></inner></outer>";
        XmlStringBuilder actualXml = outer.toXML(XmlEnvironment.EMPTY);

        XmlUnitUtils.assertXmlSimilar(expectedXml, actualXml);

        StringBuilder actualXmlTwo = actualXml.toXML(XmlEnvironment.EMPTY);

        XmlUnitUtils.assertXmlSimilar(expectedXml, actualXmlTwo);
    }

I tried the last version (4.4.0-alpha2), and it didn’t work either. I also have these kind of Unit Tests in my project, and they are all in Success (version >= 4.3.4).

But your test is only testing the method toXML, and not the actual sendMessage.

The issue here, is really in the “XmlStringBuilder => write()” method which is called in “XMPPTCPConnection.java => writePackets()”. The xml payload is modified by the “write” method just before to be send to the XMPP server, and that is causing the issue.

4.4.0-alpha2 is already a few commits behind the current master. Please try the latest master, e.g. by using the snapshots or a composite build.

@Flow Where can I find the latest snapshot ? The latest I can find in maven repositories is the “4.4.0-alpha2”

The latest snapshots can be found here.
You probably want to include the snapshot repo in your build script.

In gradle that would look like this:

repositories {
    maven {
        url 'https://igniterealtime.org/repo/'
    }
}
dependencies {
    implementation "org.igniterealtime.smack:smack-core:4.4.0-alpha3-20190909.010503-7"
    ...
}

Ok thanks @Paul_Schaub, I’ll try it!

Note that due to the way the subprojects are built, the exact version string differs from module to module (i.e. smack-core has a different version string than smack-experimental etc.).

If you want to fetch a list of the most recent version strings for a given snapshot release, you can use the script below:

smack-unique-snapshots.sh

#!/bin/sh
if [ $# -eq 0 ]; then
    echo "Usage: smack-unique-snapshots.sh 4.4.0-alpha1-SNAPSHOT";
    exit 1;
fi

VERSION=$1
SMACK="https://igniterealtime.org/repo/org/igniterealtime/smack"
META="maven-metadata.xml"

TEMPD=$(mktemp -d)
cd $TEMPD
# Determine avail smack projects by recursively fetching subdirs
wget -q -r -l1 -nH --cut-dirs=4 --no-parent -e robots=off $SMACK

PROJECTS=()
VERSIONS=()
# smack projects will result in subdir
# get index.html of project with descending change date
# from the html, extract first .pom file name, which is latest version
for d in `ls` ; do
    if  [[ -d $d ]]; then
        PRO=$(wget -q "$SMACK/$d/$VERSION?C=M;O=D" -O- | grep -Po '(?<=a href=").*?(?=\.pom")' | head -n1);

        # Repair missing projects
        if [ ${#PRO} -le ${#d} ]; then
            PRO="$d MISSING"
        fi
        PROJECTS+=("$PRO")

        echo $PRO

        # Gradle dependency generation
        # Determine snapshot version string, which begins after project name
        PROJ_LEN=$((${#d} + 1))
        SNAP=${PRO:$PROJ_LEN}
        # Make project name camelcase and append version string
        VER="$(echo $d | sed -r 's/(^|-)(\w)/\U\2/g')Version = \"$SNAP\""
        # Append to array with first char lower case'd
        VERSIONS+=("${VER,}")
    fi
done
# print out block of gradle dependecy versions
printf "\n"
echo "Gradle Versions:"
for v in "${VERSIONS[@]}"; do
    echo $v
done

# clean up
rm -rf $TEMPD

Hi Paul,

It still doesn’t work with the last version I found on “https://igniterealtime.org/repo/”. I will wait for the next stable release, and hope it will be fixed by then.

This topic was automatically closed 62 days after the last reply. New replies are no longer allowed.