REST API - Breaking change in JSON roster response in 1.6.0

I think I have discovered a regression bug in the latest REST API version 1.6.0. Using the JSON response type with the GET /users/{username}/roster API the name of the array in the returned structure has changed from “rosterItem” to “roster”. This breaks our code, which expects “rosterItem”.

BEFORE (Openfire 4.6.3, REST API 1.4.0, AdoptOpenJDK 11.0.11):

{
   "**rosterItem**":[
      {
         "jid":"xxx@domain",
         "nickname":"xxx",
         "subscriptionType":0,
         "groups":[]
      }
   ]
}

AFTER (Openfire 4.6.3, REST API 1.6.0, AdoptOpenJDK 11.0.11 x64):

{
   "**roster**":[
      {
         "jid":"xxx@domain",
         "nickname":"xxx",
         "subscriptionType":0,
         "groups":[]
      }
   ]
}

I have a suspicion this might be something to do with the jackson dependency changing, because looking at the code the RosterEntities.java source I see the annotations haven’t changed for years.

I cannot reproduce this with the latest code (1.7.0-SNAPSHOT as of time of writing) of the REST API plugin on Openfire 4.6.4. It seems to return rosterItem (as expected) for me. For good measure, I tested with the last release of the plugin (1.6.0). I’m not seeing any issues there either.

curl -v -X GET --header "Authorization: foobar" --header "Accept: application/xml" http://localhost:9090/plugins/restapi/v1/users/john/roster

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<roster>
	<rosterItem>
		<jid>jane@example.org</jid>
		<nickname>Jane</nickname>
		<subscriptionType>3</subscriptionType>
		<groups/>
	</rosterItem>
</roster>

curl -v -X GET --header "Authorization: foobar" --header "Accept: application/json" http://localhost:9090/plugins/restapi/v1/users/john/roster

{
  "rosterItem":[
    {
      "jid":"jane@example.org",
      "nickname":"Jane",
      "subscriptionType":3,
      "groups":[
        
      ]
    }
  ]
}

(I’ve formatted the output to improve readability)

OK so I performed more tests using the latest Openfire 4.6.7 x64, Adoptium OpenJDK 11.0.13 64-bit, and the REST API 1.6.0 release, and I am getting exactly the same results - the JSON contains “roster” at the top level instead of “rosterItem”.

Following my hunch about the Jackson versions changing in the latest plugin version I dug into the plugin libs folder and saw there seem to be two different versions of Jackson bundled with the plugin - both 2.12.1 jars and 1.9.2 jars!

Very odd, given the previous 1.4.0 plugin release contained Jackson 1.9.11 jars (i.e. later versions than in the 1.6.0 release!).

OK, so I then deleted all the jackson-*-2.12.1.jar files from the plugin’s lib folder, leaving just the 1.9.2 versions in place. Restarted Openfire, invoked the REST API and voila, now the JSON has “rosterItem” just like it does with version 1.4.0.

I replaced just the Jackson 2.12.1 jars files and removed the 1.9.2 ones and it goes back to “roster” in the JSON.

a) I think you need to look at the project dependencies - something is bringing in two different versions of Jackson, not a good idea.
b) It seems Jackson 2.x is introducing in a breaking change to the JSON response, changing rosterItem to roster.

Here’s the cause of the two separate sets of jackson jars - one set via direct dependency (fasterxml 2.12.1) and one set (codehaus 1.9.2) being pulled in via jersey-json:

C:\Temp2\openfire-restAPI-plugin-master>mvn dependency:tree -Dincludes=*:jackson*
[INFO] Scanning for projects...
[INFO]
[INFO] ------------< org.igniterealtime.openfire.plugins:restAPI >-------------
[INFO] Building REST API Plugin 1.7.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:3.1.1:tree (default-cli) @ restAPI ---
[INFO] org.igniterealtime.openfire.plugins:restAPI:jar:1.7.0-SNAPSHOT
[INFO] +- com.fasterxml.jackson.module:jackson-module-jaxb-annotations:jar:2.12.1:compile
[INFO] |  +- com.fasterxml.jackson.core:jackson-annotations:jar:2.12.1:compile
[INFO] |  +- com.fasterxml.jackson.core:jackson-core:jar:2.12.1:compile
[INFO] |  \- com.fasterxml.jackson.core:jackson-databind:jar:2.12.1:compile
[INFO] +- com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:2.12.1:compile
[INFO] |  \- com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar:2.12.1:compile
[INFO] \- com.sun.jersey:jersey-json:jar:1.19.4:compile
[INFO]    +- org.codehaus.jackson:jackson-core-asl:jar:1.9.2:compile
[INFO]    +- org.codehaus.jackson:jackson-mapper-asl:jar:1.9.2:compile
[INFO]    +- org.codehaus.jackson:jackson-jaxrs:jar:1.9.2:compile
[INFO]    \- org.codehaus.jackson:jackson-xc:jar:1.9.2:compile
2 Likes

That’s a pretty good find! I’m digging in further, but the plot seems to thicken…

In the upcoming release, we’re updating libraries. That removes Jackson 1.9.2:

$ mvn dependency:tree -Dincludes=*:jackson*
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.google.inject.internal.cglib.core.$ReflectUtils$1 (file:/usr/share/maven/lib/guice.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of com.google.inject.internal.cglib.core.$ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
[INFO] Scanning for projects...
[INFO] 
[INFO] ------------< org.igniterealtime.openfire.plugins:restAPI >-------------
[INFO] Building REST API Plugin 1.7.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-dependency-plugin:3.1.1:tree (default-cli) @ restAPI ---
[INFO] org.igniterealtime.openfire.plugins:restAPI:jar:1.7.0-SNAPSHOT
[INFO] +- org.glassfish.jersey.media:jersey-media-json-jackson:jar:2.35:compile
[INFO] |  +- com.fasterxml.jackson.core:jackson-annotations:jar:2.12.2:compile
[INFO] |  +- com.fasterxml.jackson.core:jackson-databind:jar:2.12.2:compile
[INFO] |  |  \- com.fasterxml.jackson.core:jackson-core:jar:2.12.2:compile
[INFO] |  \- com.fasterxml.jackson.module:jackson-module-jaxb-annotations:jar:2.12.2:compile
[INFO] \- io.swagger.core.v3:swagger-jaxrs2:jar:2.1.11:compile
[INFO]    +- io.swagger.core.v3:swagger-integration:jar:2.1.11:compile
[INFO]    |  \- io.swagger.core.v3:swagger-core:jar:2.1.11:compile
[INFO]    |     +- com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:jar:2.12.1:compile
[INFO]    |     \- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.12.1:compile
[INFO]    \- com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:2.12.1:compile
[INFO]       \- com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar:2.12.1:compile

Weirdly enough, I’m again getting the “old” behavior (that you’d associate with Jackson 1.9.2):

$ curl -v -X GET --header "Authorization: test" --header "Accept: application/json" http://localhost:9090/plugins/restapi/v1/users/john/roster
Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying 127.0.0.1:9090...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9090 (#0)
> GET /plugins/restapi/v1/users/john/roster HTTP/1.1
> Host: localhost:9090
> User-Agent: curl/7.68.0
> Authorization: test
> Accept: application/json
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Wed, 12 Jan 2022 18:37:39 GMT
< X-Frame-Options: SAMEORIGIN
< Set-Cookie: JSESSIONID=node0k2k6iebnpb3m7g742fq7q9nz4.node0; Path=/; HttpOnly
< Expires: Thu, 01 Jan 1970 00:00:00 GMT
< Content-Type: application/json
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Headers: origin, content-type, accept, authorization
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS, HEAD
< Content-Length: 93
< 
* Connection #0 to host localhost left intact
{"rosterItem":[{"jid":"jane@example.org","nickname":"Jane","subscriptionType":3,"group":[]}]}

When I look at the endpoint definition, then it seems to return an instance of RosterEntities. This defines “roster” (instead of “rosterItems”) in two places: at the class level and as the name of the method that returns the collection of entries.

I wonder which of these two ends up in your output. I’m not sure why there is inconsistency in Jackson’s behavior here. Maybe we can make the JSON output more predictable, by doing either one of:

  • adding @JsonProperty annotations to set the name, much like the pre-existing @XmlElement annotations.
  • renaming the method org.jivesoftware.openfire.plugin.rest.entity.RosterEntities#getRoster to getRosterItem

Thoughts?

Paging @Redeyes

I think we should go with @JsonProperty (like with XML), so we definitely know how the output will look like (in case Jackson changes the behavior).

I have raised https://github.com/igniterealtime/openfire-restAPI-plugin/issues/79 to track this issue.

The proper way to go about is possibly to configure Jackson to use the JAXB annotations. I’ve attempted to do that in this PR: https://github.com/igniterealtime/openfire-restAPI-plugin/pull/82