From 72b67cb80078c1f8069ef26cdaaedbda6e0499da Mon Sep 17 00:00:00 2001 From: Guus der Kinderen Date: Fri, 3 Jan 2025 21:35:37 +0100 Subject: [PATCH] OF-2941: Improve Version comparison that involve SNAPSHOTs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For certain versions of plugins, Openfire incorrectly identifies that an update is available. When Openfire has installed `10.1.7.3-SNAPSHOT` it incorrectly identifies that version `10.1.7.2` is an update. This seems to be caused by Openfire’s abuse of the 4th numeric value in a version. It was originally intended to be a classifier for the status (eg: ‘the second beta release’). When that 4th value is used as part of a four-digit version, things do not quite compare as expected. To illustrate, compare these two scenarios. The numbers 3 and 2 are all stored in the same field, which gets interpreted differently. - `10.1.7 beta 3` is older than `10.1.7 release 2` - `10.1.7.3-SNAPSHOT` is expected to be newer than `10.1.7.2` (release). Prior to this commit, Openfire did not flag that `10.1.7.3-SNAPSHOT` is newer than `10.1.7.2`, which was causing confusion. In this commit, _only_ the `SNAPSHOT` release status is now compared differently (as if the 4th value is part of the version string). --- .../jivesoftware/openfire/update/Update.java | 29 ++++++++++--- .../openfire/update/UpdateManager.java | 10 ++--- .../java/org/jivesoftware/util/Version.java | 14 +++++-- .../org/jivesoftware/util/VersionTest.java | 42 ++++++++++++++++++- 4 files changed, 81 insertions(+), 14 deletions(-) diff --git a/xmppserver/src/main/java/org/jivesoftware/openfire/update/Update.java b/xmppserver/src/main/java/org/jivesoftware/openfire/update/Update.java index 5484f8cf74..5045d427fc 100644 --- a/xmppserver/src/main/java/org/jivesoftware/openfire/update/Update.java +++ b/xmppserver/src/main/java/org/jivesoftware/openfire/update/Update.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Jive Software, 2017-2018 Ignite Realtime Foundation. All rights reserved. + * Copyright (C) 2005-2008 Jive Software, 2017-2025 Ignite Realtime Foundation. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ package org.jivesoftware.openfire.update; +import org.jivesoftware.util.Version; + /** * An Update represents a component that needs to be updated. By component we can refer * to the Openfire server itself or to any of the installed plugins. @@ -28,19 +30,19 @@ public class Update { * Name of the component that is outdated. The name could be of the server * (i.e. "Openfire") or of installed plugins. */ - private String componentName; + private final String componentName; /** * Latest version of the component that was found. */ - private String latestVersion; + private final Version latestVersion; /** * URL from where the latest version of the component can be downloaded. */ - private String url; + private final String url; /** * Changelog URL of the latest version of the component. */ - private String changelog; + private final String changelog; /** * Flag that indicates if the plugin was downloaded. This flag only makes sense for @@ -48,7 +50,15 @@ public class Update { */ private boolean downloaded; + @Deprecated(forRemoval = true) // Remove in or after Openfire 5.1.0 public Update(String componentName, String latestVersion, String changelog, String url) { + this.componentName = componentName; + this.latestVersion = new Version(latestVersion); + this.changelog = changelog; + this.url = url; + } + + public Update(String componentName, Version latestVersion, String changelog, String url) { this.componentName = componentName; this.latestVersion = latestVersion; this.changelog = changelog; @@ -72,6 +82,15 @@ public String getComponentName() { * @return the latest version of the component that was found. */ public String getLatestVersion() { + return latestVersion.getVersionString(); + } + + /** + * Returns the latest version of the component that was found. + * + * @return the latest version of the component that was found. + */ + public Version getLatest() { return latestVersion; } diff --git a/xmppserver/src/main/java/org/jivesoftware/openfire/update/UpdateManager.java b/xmppserver/src/main/java/org/jivesoftware/openfire/update/UpdateManager.java index eba3ea01e7..774d499e0b 100644 --- a/xmppserver/src/main/java/org/jivesoftware/openfire/update/UpdateManager.java +++ b/xmppserver/src/main/java/org/jivesoftware/openfire/update/UpdateManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Jive Software, 2017-2023 Ignite Realtime Foundation. All rights reserved. + * Copyright (C) 2005-2008 Jive Software, 2017-2025 Ignite Realtime Foundation. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -529,7 +529,7 @@ public Update getPluginUpdate(String pluginName, Version currentVersion) { // Check if this is the requested plugin if (update.getComponentName().equals(pluginName)) { // Check if the plugin version is right - if (new Version(update.getLatestVersion()).isNewerThan( currentVersion ) ) { + if (update.getLatest().isNewerThan( currentVersion ) ) { return update; } } @@ -585,7 +585,7 @@ private void processServerUpdateResponse(String response, boolean notificationsE Log.warn( "Unable to parse URL from openfire download url value '{}'.", openfire.attributeValue("url"), e ); } // Keep information about the available server update - serverUpdate = new Update("Openfire", latestVersion.getVersionString(), String.valueOf(changelog), String.valueOf(url)); + serverUpdate = new Update("Openfire", latestVersion, String.valueOf(changelog), String.valueOf(url)); } } // Check if we need to send notifications to admins @@ -670,7 +670,7 @@ private void buildPluginsUpdateList() { continue; } - final Update update = new Update( plugin.getName(), latestPlugin.getVersion().getVersionString(), latestPlugin.getChangelog().toExternalForm(), latestPlugin.getDownloadURL().toExternalForm() ); + final Update update = new Update( plugin.getName(), latestPlugin.getVersion(), latestPlugin.getChangelog().toExternalForm(), latestPlugin.getDownloadURL().toExternalForm() ); pluginUpdates.add(update); } } @@ -832,7 +832,7 @@ private void loadLatestServerInfo() { // Check if current server version is correct Version currentServerVersion = XMPPServer.getInstance().getServerInfo().getVersion(); if (latestVersion.isNewerThan(currentServerVersion)) { - serverUpdate = new Update("Openfire", latestVersion.getVersionString(), String.valueOf(changelog), String.valueOf(url) ); + serverUpdate = new Update("Openfire", latestVersion, String.valueOf(changelog), String.valueOf(url) ); } } } diff --git a/xmppserver/src/main/java/org/jivesoftware/util/Version.java b/xmppserver/src/main/java/org/jivesoftware/util/Version.java index 6381f53b88..a63cb9681b 100644 --- a/xmppserver/src/main/java/org/jivesoftware/util/Version.java +++ b/xmppserver/src/main/java/org/jivesoftware/util/Version.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004-2008 Jive Software, 2017-2019 Ignite Realtime Foundation. All rights reserved. + * Copyright (C) 2004-2008 Jive Software, 2017-2025 Ignite Realtime Foundation. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -254,9 +254,17 @@ public int compareTo(Version that) { if (result == 0) { result = Integer.compare(getMicro(), that.getMicro()); if (result == 0) { - result = that.getStatus().compareTo(getStatus()); - if (result == 0) { + // OF-2941: Hacky solution to allow for four-digit SNAPSHOT version comparison. + if (that.status == ReleaseStatus.Snapshot || status == ReleaseStatus.Snapshot) { result = Integer.compare(getStatusVersion(), that.getStatusVersion()); + if (result == 0) { + result = that.getStatus().compareTo(getStatus()); + } + } else { + result = that.getStatus().compareTo(getStatus()); + if (result == 0) { + result = Integer.compare(getStatusVersion(), that.getStatusVersion()); + } } } } diff --git a/xmppserver/src/test/java/org/jivesoftware/util/VersionTest.java b/xmppserver/src/test/java/org/jivesoftware/util/VersionTest.java index 738dbc7b76..68fc51910c 100644 --- a/xmppserver/src/test/java/org/jivesoftware/util/VersionTest.java +++ b/xmppserver/src/test/java/org/jivesoftware/util/VersionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017-2023 Ignite Realtime Foundation. All rights reserved. + * Copyright (C) 2017-2025 Ignite Realtime Foundation. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -168,4 +168,44 @@ public void willVersionAFourDigitRelease() { assertThat(test.getStatus(), is(ReleaseStatus.Release)); assertThat(test.getVersionString(),is("1.2.3 Release 4")); } + + @Test // OF-2941 + public void testSnapshotCompareToRelease() { + final Version snapshotVersion = new Version(10,1,7, ReleaseStatus.Snapshot, 3); + final Version olderReleaseVersion = new Version(10,1,7, ReleaseStatus.Release, 2); + + assertTrue(snapshotVersion.isNewerThan(olderReleaseVersion)); + } + + @Test // OF-2941 + public void testAlphaCompareToRelease() { + final Version alphaVersion = new Version(10,1,7, ReleaseStatus.Alpha, 3); + final Version newerReleaseVersion = new Version(10,1,7, ReleaseStatus.Release, 2); + + assertTrue(newerReleaseVersion.isNewerThan(alphaVersion)); + } + + @Test // OF-2941 + public void testBetaCompareToRelease() { + final Version betaVersion = new Version(10,1,7, ReleaseStatus.Beta, 3); + final Version newerReleaseVersion = new Version(10,1,7, ReleaseStatus.Release, 2); + + assertTrue(newerReleaseVersion.isNewerThan(betaVersion)); + } + + @Test // OF-2941 + public void testReleaseCandidateCompareToRelease() { + final Version releaseCandidateVersion = new Version(10,1,7, ReleaseStatus.Release_Candidate, 3); + final Version newerReleaseVersion = new Version(10,1,7, ReleaseStatus.Release, 2); + + assertTrue(newerReleaseVersion.isNewerThan(releaseCandidateVersion)); + } + + @Test // OF-2941 + public void testReleaseCompareToRelease() { + final Version releaseVersion = new Version(10,1,7, ReleaseStatus.Release, 3); + final Version olderReleaseVersion = new Version(10,1,7, ReleaseStatus.Release, 2); + + assertTrue(releaseVersion.isNewerThan(olderReleaseVersion)); + } }